diff options
author | 2010-10-13 12:55:11 +0200 | |
---|---|---|
committer | 2010-10-13 12:55:11 +0200 | |
commit | 3d6d86a236579d99aa4856f6a3ad241d4824e488 (patch) | |
tree | f01b1c7db332622eaee8c47cad7e3010c02db6ae | |
parent | Don't launch guest if -no-kvm when tcg is not configured in (diff) | |
parent | Merge commit 'a62e5f41204e71f375a5ba0f93f06b699003f694' into upstream-merge (diff) | |
download | qemu-kvm-3d6d86a236579d99aa4856f6a3ad241d4824e488.tar.gz qemu-kvm-3d6d86a236579d99aa4856f6a3ad241d4824e488.tar.bz2 qemu-kvm-3d6d86a236579d99aa4856f6a3ad241d4824e488.zip |
Merge branch 'upstream-merge' into stable-0.13
* upstream-merge: (39 commits)
vnc: check fd before calling qemu_set_fd_handler2() in vnc_client_write()
disable guest-provided stats on "info balloon" command
Revert "Make default invocation of block drivers safer (v3)"
QEMUFileBuffered: indicate that we're ready when the underlying file is ready
sparc escc IUS improvements (SunOS 4.1.4 fix)
fix last cpu timer initialization
QMP/README: Update QMP homepage address
disable guest-provided stats on "info balloon" command
QMP: Update README file
QMP doc: Add 'Stability Considerations' section
QMP/monitor: update do_info_version() to output broken down version string
QMP: update 'query-version' documentation
savevm: Reset last block info at beginning of each save
set proper migration status on ->write error (v5)
migration: Accept 'cont' only after successful incoming migration
Update for v0.13.0-rc3
Update version for 0.13.0-rc2
Fix ivshmem build on 32-bit hosts
hw/ivshmem.c don't check for negative values on unsigned data types
Disable build of ivshmem on non-KVM systems
...
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | Makefile.target | 3 | ||||
-rw-r--r-- | QMP/README | 73 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | arch_init.c | 7 | ||||
-rw-r--r-- | block.c | 4 | ||||
-rw-r--r-- | block.h | 2 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 16 | ||||
-rw-r--r-- | block/raw-posix.c | 3 | ||||
-rw-r--r-- | block/raw.c | 130 | ||||
-rw-r--r-- | block/vvfat.c | 26 | ||||
-rw-r--r-- | block_int.h | 1 | ||||
-rw-r--r-- | buffered_file.c | 12 | ||||
-rw-r--r-- | cpu-common.h | 2 | ||||
-rw-r--r-- | docs/specs/ivshmem_device_spec.txt | 96 | ||||
-rw-r--r-- | exec.c | 2 | ||||
-rw-r--r-- | hw/device-assignment.c | 2 | ||||
-rw-r--r-- | hw/escc.c | 56 | ||||
-rw-r--r-- | hw/hw.h | 2 | ||||
-rw-r--r-- | hw/ivshmem.c | 829 | ||||
-rw-r--r-- | hw/scsi-disk.c | 90 | ||||
-rw-r--r-- | hw/slavio_timer.c | 4 | ||||
-rw-r--r-- | hw/virtio-balloon.c | 8 | ||||
-rw-r--r-- | kvm-all.c | 32 | ||||
-rw-r--r-- | kvm-stub.c | 5 | ||||
-rw-r--r-- | kvm.h | 1 | ||||
-rw-r--r-- | migration.c | 10 | ||||
-rw-r--r-- | monitor.c | 27 | ||||
-rw-r--r-- | qemu-char.c | 6 | ||||
-rw-r--r-- | qemu-char.h | 3 | ||||
-rw-r--r-- | qemu-doc.texi | 43 | ||||
-rw-r--r-- | qemu-img.c | 3 | ||||
-rw-r--r-- | qemu-monitor.hx | 42 | ||||
-rw-r--r-- | qerror.c | 4 | ||||
-rw-r--r-- | qerror.h | 3 | ||||
-rw-r--r-- | savevm.c | 44 | ||||
-rw-r--r-- | sysemu.h | 1 | ||||
-rw-r--r-- | ui/vnc.c | 2 | ||||
-rw-r--r-- | vl.c | 2 |
38 files changed, 1355 insertions, 243 deletions
diff --git a/Makefile.target b/Makefile.target index 9e13d99cb..9643f88ec 100644 --- a/Makefile.target +++ b/Makefile.target @@ -205,6 +205,9 @@ obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-y += rtl8139.o obj-y += e1000.o +# Inter-VM PCI shared memory +obj-$(CONFIG_KVM) += ivshmem.o + # Hardware support obj-i386-y += vga.o obj-i386-y += mc146818rtc.o i8259.o pc.o diff --git a/QMP/README b/QMP/README index 35a80c73e..d1430b0ab 100644 --- a/QMP/README +++ b/QMP/README @@ -7,60 +7,85 @@ Introduction The QEMU Monitor Protocol (QMP) allows applications to communicate with QEMU's Monitor. -QMP is JSON[1] based and has the following features: +QMP is JSON[1] based and currently has the following features: - Lightweight, text-based, easy to parse data format -- Asynchronous events support -- Stability +- Asynchronous messages support (ie. events) +- Capabilities Negotiation -For more information, please, refer to the following files: +For detailed information on QMP's usage, please, refer to the following files: o qmp-spec.txt QEMU Monitor Protocol current specification -o qmp-commands.txt QMP supported commands +o qmp-commands.txt QMP supported commands (auto-generated at build-time) o qmp-events.txt List of available asynchronous events There are also two simple Python scripts available: -o qmp-shell A shell -o vm-info Show some information about the Virtual Machine +o qmp-shell A shell +o vm-info Show some information about the Virtual Machine + +IMPORTANT: It's strongly recommended to read the 'Stability Considerations' +section in the qmp-commands.txt file before making any serious use of QMP. + [1] http://www.json.org Usage ----- -To enable QMP, QEMU has to be started in "control mode". There are -two ways of doing this, the simplest one is using the the '-qmp' -command-line option. +To enable QMP, you need a QEMU monitor instance in "control mode". There are +two ways of doing this. + +The simplest one is using the '-qmp' command-line option. The following +example makes QMP available on localhost port 4444: -For example: + $ qemu [...] -qmp tcp:localhost:4444,server -$ qemu [...] -qmp tcp:localhost:4444,server +However, in order to have more complex combinations, like multiple monitors, +the '-mon' command-line option should be used along with the '-chardev' one. +For instance, the following example creates one user monitor on stdio and one +QMP monitor on localhost port 4444. -Will start QEMU in control mode, waiting for a client TCP connection -on localhost port 4444. + $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ + -chardev socket,id=mon1,host=localhost,port=4444,server \ + -mon chardev=mon1,mode=control -It is also possible to use the '-mon' command-line option to have -more complex combinations. Please, refer to the QEMU's manpage for -more information. +Please, refer to QEMU's manpage for more information. Simple Testing -------------- -To manually test QMP one can connect with telnet and issue commands: +To manually test QMP one can connect with telnet and issue commands by hand: $ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. -{"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}} +{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}} { "execute": "qmp_capabilities" } {"return": {}} { "execute": "query-version" } -{"return": {"qemu": "0.12.50", "package": ""}} +{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}} + +Development Process +------------------- + +When changing QMP's interface (by adding new commands, events or modifying +existing ones) it's mandatory to update the relevant documentation, which is +one (or more) of the files listed in the 'Introduction' section*. + +Also, it's strongly recommended to send the documentation patch first, before +doing any code change. This is so because: + + 1. Avoids the code dictating the interface + + 2. Review can improve your interface. Letting that happen before + you implement it can save you work. + +* The qmp-commands.txt file is generated from the qemu-monitor.hx one, which + is the file that should be edited. -Contact -------- +Homepage +-------- -http://www.linux-kvm.org/page/MonitorProtocol -Luiz Fernando N. Capitulino <lcapitulino@redhat.com> +http://wiki.qemu.org/QMP @@ -1 +1 @@ -0.12.91 +0.12.93 diff --git a/arch_init.c b/arch_init.c index 47bb4b2d8..e0bd18c88 100644 --- a/arch_init.c +++ b/arch_init.c @@ -104,10 +104,11 @@ static int is_dup_page(uint8_t *page, uint8_t ch) return 1; } +static RAMBlock *last_block; +static ram_addr_t last_offset; + static int ram_save_block(QEMUFile *f) { - static RAMBlock *last_block = NULL; - static ram_addr_t last_offset = 0; RAMBlock *block = last_block; ram_addr_t offset = last_offset; ram_addr_t current_addr; @@ -231,6 +232,8 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) if (stage == 1) { RAMBlock *block; bytes_transferred = 0; + last_block = NULL; + last_offset = 0; /* Make sure all dirty bits are set */ QLIST_FOREACH(block, &ram_list.blocks, next) { @@ -523,7 +523,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, BlockDriver *drv) { int ret; - int probed = 0; if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; @@ -584,7 +583,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, /* Find the right image format driver */ if (!drv) { ret = find_image_format(filename, &drv); - probed = 1; } if (!drv) { @@ -597,8 +595,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, goto unlink_and_fail; } - bs->probed = probed; - /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') { char backing_filename[PATH_MAX]; @@ -35,7 +35,7 @@ typedef struct QEMUSnapshotInfo { #define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ #define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ -#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB) +#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) #define BDRV_SECTOR_BITS 9 #define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 166922f8b..f562b1602 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -655,7 +655,7 @@ static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table, int ret; BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); - ret = bdrv_pwrite_sync(bs->file, l2_offset + start_offset, + ret = bdrv_pwrite(bs->file, l2_offset + start_offset, &l2_table[l2_start_index], len); if (ret < 0) { return ret; @@ -718,9 +718,17 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) goto err; } - for (i = 0; i < j; i++) - qcow2_free_any_clusters(bs, - be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + /* + * If this was a COW, we need to decrease the refcount of the old cluster. + * Also flush bs->file to get the right order for L2 and refcount update. + */ + if (j != 0) { + bdrv_flush(bs->file); + for (i = 0; i < j; i++) { + qcow2_free_any_clusters(bs, + be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + } + } ret = 0; err: diff --git a/block/raw-posix.c b/block/raw-posix.c index af47e82d5..1c4966659 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1156,9 +1156,6 @@ static int cdrom_probe_device(const char *filename) int fd, ret; int prio = 0; - if (strstart(filename, "/dev/cd", NULL)) - prio = 50; - fd = open(filename, O_RDONLY | O_NONBLOCK); if (fd < 0) { goto out; diff --git a/block/raw.c b/block/raw.c index 61e674856..91087792f 100644 --- a/block/raw.c +++ b/block/raw.c @@ -9,82 +9,15 @@ static int raw_open(BlockDriverState *bs, int flags) return 0; } -/* check for the user attempting to write something that looks like a - block format header to the beginning of the image and fail out. -*/ -static int check_for_block_signature(BlockDriverState *bs, const uint8_t *buf) -{ - static const uint8_t signatures[][4] = { - { 'Q', 'F', 'I', 0xfb }, /* qcow/qcow2 */ - { 'C', 'O', 'W', 'D' }, /* VMDK3 */ - { 'V', 'M', 'D', 'K' }, /* VMDK4 */ - { 'O', 'O', 'O', 'M' }, /* UML COW */ - {} - }; - int i; - - for (i = 0; signatures[i][0] != 0; i++) { - if (memcmp(buf, signatures[i], 4) == 0) { - return 1; - } - } - - return 0; -} - -static int check_write_unsafe(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) -{ - /* assume that if the user specifies the format explicitly, then assume - that they will continue to do so and provide no safety net */ - if (!bs->probed) { - return 0; - } - - if (sector_num == 0 && nb_sectors > 0) { - return check_for_block_signature(bs, buf); - } - - return 0; -} - static int raw_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { return bdrv_read(bs->file, sector_num, buf, nb_sectors); } -static int raw_write_scrubbed_bootsect(BlockDriverState *bs, - const uint8_t *buf) -{ - uint8_t bootsect[512]; - - /* scrub the dangerous signature */ - memcpy(bootsect, buf, 512); - memset(bootsect, 0, 4); - - return bdrv_write(bs->file, 0, bootsect, 1); -} - static int raw_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - if (check_write_unsafe(bs, sector_num, buf, nb_sectors)) { - int ret; - - ret = raw_write_scrubbed_bootsect(bs, buf); - if (ret < 0) { - return ret; - } - - ret = bdrv_write(bs->file, 1, buf + 512, nb_sectors - 1); - if (ret < 0) { - return ret; - } - - return ret + 512; - } - return bdrv_write(bs->file, sector_num, buf, nb_sectors); } @@ -95,73 +28,10 @@ static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } -typedef struct RawScrubberBounce -{ - BlockDriverCompletionFunc *cb; - void *opaque; - QEMUIOVector qiov; -} RawScrubberBounce; - -static void raw_aio_writev_scrubbed(void *opaque, int ret) -{ - RawScrubberBounce *b = opaque; - - if (ret < 0) { - b->cb(b->opaque, ret); - } else { - b->cb(b->opaque, ret + 512); - } - - qemu_iovec_destroy(&b->qiov); - qemu_free(b); -} - static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { - const uint8_t *first_buf; - int first_buf_index = 0, i; - - /* This is probably being paranoid, but handle cases of zero size - vectors. */ - for (i = 0; i < qiov->niov; i++) { - if (qiov->iov[i].iov_len) { - assert(qiov->iov[i].iov_len >= 512); - first_buf_index = i; - break; - } - } - - first_buf = qiov->iov[first_buf_index].iov_base; - - if (check_write_unsafe(bs, sector_num, first_buf, nb_sectors)) { - RawScrubberBounce *b; - int ret; - - /* write the first sector using sync I/O */ - ret = raw_write_scrubbed_bootsect(bs, first_buf); - if (ret < 0) { - return NULL; - } - - /* adjust request to be everything but first sector */ - - b = qemu_malloc(sizeof(*b)); - b->cb = cb; - b->opaque = opaque; - - qemu_iovec_init(&b->qiov, qiov->nalloc); - qemu_iovec_concat(&b->qiov, qiov, qiov->size); - - b->qiov.size -= 512; - b->qiov.iov[first_buf_index].iov_base += 512; - b->qiov.iov[first_buf_index].iov_len -= 512; - - return bdrv_aio_writev(bs->file, sector_num + 1, &b->qiov, - nb_sectors - 1, raw_aio_writev_scrubbed, b); - } - return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } diff --git a/block/vvfat.c b/block/vvfat.c index 365332aa2..53e57bf22 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2665,6 +2665,11 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num, DLOG(checkpoint()); + /* Check if we're operating in read-only mode */ + if (s->qcow == NULL) { + return -EACCES; + } + vvfat_close_current_file(s); /* @@ -2763,12 +2768,12 @@ static int vvfat_is_allocated(BlockDriverState *bs, static int write_target_commit(BlockDriverState *bs, int64_t sector_num, const uint8_t* buffer, int nb_sectors) { - BDRVVVFATState* s = bs->opaque; + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); return try_commit(s); } static void write_target_close(BlockDriverState *bs) { - BDRVVVFATState* s = bs->opaque; + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); bdrv_delete(s->qcow); free(s->qcow_filename); } @@ -2783,6 +2788,7 @@ static int enable_write_target(BDRVVVFATState *s) { BlockDriver *bdrv_qcow; QEMUOptionParameter *options; + int ret; int size = sector2cluster(s, s->sector_count); s->used_clusters = calloc(size, 1); @@ -2798,11 +2804,16 @@ static int enable_write_target(BDRVVVFATState *s) if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) return -1; + s->qcow = bdrv_new(""); - if (s->qcow == NULL || - bdrv_open(s->qcow, s->qcow_filename, BDRV_O_RDWR, bdrv_qcow) < 0) - { - return -1; + if (s->qcow == NULL) { + return -1; + } + + ret = bdrv_open(s->qcow, s->qcow_filename, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); + if (ret < 0) { + return ret; } #ifndef _WIN32 @@ -2811,7 +2822,8 @@ static int enable_write_target(BDRVVVFATState *s) s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); s->bs->backing_hd->drv = &vvfat_write_target; - s->bs->backing_hd->opaque = s; + s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*)); + *(void**)s->bs->backing_hd->opaque = s; return 0; } diff --git a/block_int.h b/block_int.h index b86345177..e8e7156c9 100644 --- a/block_int.h +++ b/block_int.h @@ -148,7 +148,6 @@ struct BlockDriverState { int encrypted; /* if true, the media is encrypted */ int valid_key; /* if true, a valid encryption key has been set */ int sg; /* if true, the device is a /dev/sg* */ - int probed; /* if true, format was probed automatically */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque); void *change_opaque; diff --git a/buffered_file.c b/buffered_file.c index 54dc6c29b..1836e7e24 100644 --- a/buffered_file.c +++ b/buffered_file.c @@ -156,6 +156,14 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in offset = size; } + if (pos == 0 && size == 0) { + DPRINTF("file is ready\n"); + if (s->bytes_xfer <= s->xfer_limit) { + DPRINTF("notifying client\n"); + s->put_ready(s->opaque); + } + } + return offset; } @@ -222,8 +230,10 @@ static void buffered_rate_tick(void *opaque) { QEMUFileBuffered *s = opaque; - if (s->has_error) + if (s->has_error) { + buffered_close(s); return; + } qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); diff --git a/cpu-common.h b/cpu-common.h index f430f2d35..81e824cf7 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -40,7 +40,7 @@ static inline void cpu_register_physical_memory(target_phys_addr_t start_addr, } ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr); -ram_addr_t qemu_ram_map(DeviceState *dev, const char *name, +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, ram_addr_t size, void *host); ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size); void qemu_ram_unmap(ram_addr_t addr); diff --git a/docs/specs/ivshmem_device_spec.txt b/docs/specs/ivshmem_device_spec.txt new file mode 100644 index 000000000..23dd2ba89 --- /dev/null +++ b/docs/specs/ivshmem_device_spec.txt @@ -0,0 +1,96 @@ + +Device Specification for Inter-VM shared memory device +------------------------------------------------------ + +The Inter-VM shared memory device is designed to share a region of memory to +userspace in multiple virtual guests. The memory region does not belong to any +guest, but is a POSIX memory object on the host. Optionally, the device may +support sending interrupts to other guests sharing the same memory region. + + +The Inter-VM PCI device +----------------------- + +*BARs* + +The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support +registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is +used to map the shared memory object from the host. The size of BAR2 is +specified when the guest is started and must be a power of 2 in size. + +*Registers* + +The device currently supports 4 registers of 32-bits each. Registers +are used for synchronization between guests sharing the same memory object when +interrupts are supported (this requires using the shared memory server). + +The server assigns each VM an ID number and sends this ID number to the Qemu +process when the guest starts. + +enum ivshmem_registers { + IntrMask = 0, + IntrStatus = 4, + IVPosition = 8, + Doorbell = 12 +}; + +The first two registers are the interrupt mask and status registers. Mask and +status are only used with pin-based interrupts. They are unused with MSI +interrupts. + +Status Register: The status register is set to 1 when an interrupt occurs. + +Mask Register: The mask register is bitwise ANDed with the interrupt status +and the result will raise an interrupt if it is non-zero. However, since 1 is +the only value the status will be set to, it is only the first bit of the mask +that has any effect. Therefore interrupts can be masked by setting the first +bit to 0 and unmasked by setting the first bit to 1. + +IVPosition Register: The IVPosition register is read-only and reports the +guest's ID number. The guest IDs are non-negative integers. When using the +server, since the server is a separate process, the VM ID will only be set when +the device is ready (shared memory is received from the server and accessible via +the device). If the device is not ready, the IVPosition will return -1. +Applications should ensure that they have a valid VM ID before accessing the +shared memory. + +Doorbell Register: To interrupt another guest, a guest must write to the +Doorbell register. The doorbell register is 32-bits, logically divided into +two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low +16-bits are the interrupt vector to trigger. The semantics of the value +written to the doorbell depends on whether the device is using MSI or a regular +pin-based interrupt. In short, MSI uses vectors while regular interrupts set the +status register. + +Regular Interrupts + +If regular interrupts are used (due to either a guest not supporting MSI or the +user specifying not to use them on startup) then the value written to the lower +16-bits of the Doorbell register results is arbitrary and will trigger an +interrupt in the destination guest. + +Message Signalled Interrupts + +A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits +written to the Doorbell register must be between 0 and the maximum number of +vectors the guest supports. The lower 16 bits written to the doorbell is the +MSI vector that will be raised in the destination guest. The number of MSI +vectors is configurable but it is set when the VM is started. + +The important thing to remember with MSI is that it is only a signal, no status +is set (since MSI interrupts are not shared). All information other than the +interrupt itself should be communicated via the shared memory region. Devices +supporting multiple MSI vectors can use different vectors to indicate different +events have occurred. The semantics of interrupt vectors are left to the +user's discretion. + + +Usage in the Guest +------------------ + +The shared memory device is intended to be used with the provided UIO driver. +Very little configuration is needed. The guest should map BAR0 to access the +registers (an array of 32-bit ints allows simple writing) and map BAR2 to +access the shared memory region itself. The size of the shared memory region +is specified when the guest (or shared memory server) is started. A guest may +map the whole shared memory region or only part of it. @@ -2824,7 +2824,7 @@ static ram_addr_t last_ram_offset(void) return last; } -ram_addr_t qemu_ram_map(DeviceState *dev, const char *name, +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, ram_addr_t size, void *host) { RAMBlock *new_block, *block; diff --git a/hw/device-assignment.c b/hw/device-assignment.c index c56870ea6..d00a20e38 100644 --- a/hw/device-assignment.c +++ b/hw/device-assignment.c @@ -582,7 +582,7 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, snprintf(name, sizeof(name), "%s.bar%d", pci_dev->dev.qdev.info->name, i); pci_dev->v_addrs[i].memory_index = - qemu_ram_map(&pci_dev->dev.qdev, + qemu_ram_alloc_from_ptr(&pci_dev->dev.qdev, name, cur_region->size, virtbase); } else @@ -65,6 +65,8 @@ * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented * serial mouse queue. * Implemented serial mouse protocol. + * + * 2010-May-23 Artyom Tarasenko: Reworked IUS logic */ #ifdef DEBUG_SERIAL @@ -279,7 +281,7 @@ static uint32_t get_queue(void *opaque) static int escc_update_irq_chn(ChannelState *s) { - if ((((s->wregs[W_INTR] & INTR_TXINT) && s->txint == 1) || + if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || // tx ints enabled, pending ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && @@ -342,24 +344,22 @@ static void escc_reset(DeviceState *d) static inline void set_rxint(ChannelState *s) { s->rxint = 1; - if (!s->txint_under_svc) { - s->rxint_under_svc = 1; - if (s->chn == chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; - else - s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; - } else { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HIRXINTB; - else - s->rregs[R_IVEC] = IVEC_LORXINTB; - } - } - if (s->chn == chn_a) + /* XXX: missing daisy chainnig: chn_b rx should have a lower priority + than chn_a rx/tx/special_condition service*/ + s->rxint_under_svc = 1; + if (s->chn == chn_a) { s->rregs[R_INTR] |= INTR_RXINTA; - else + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; + else + s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; + } else { s->otherchn->rregs[R_INTR] |= INTR_RXINTB; + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->rregs[R_IVEC] = IVEC_HIRXINTB; + else + s->rregs[R_IVEC] = IVEC_LORXINTB; + } escc_update_irq(s); } @@ -369,19 +369,17 @@ static inline void set_txint(ChannelState *s) if (!s->rxint_under_svc) { s->txint_under_svc = 1; if (s->chn == chn_a) { + s->rregs[R_INTR] |= INTR_TXINTA; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; else s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; } else { s->rregs[R_IVEC] = IVEC_TXINTB; + s->otherchn->rregs[R_INTR] |= INTR_TXINTB; } - } - if (s->chn == chn_a) - s->rregs[R_INTR] |= INTR_TXINTA; - else - s->otherchn->rregs[R_INTR] |= INTR_TXINTB; escc_update_irq(s); + } } static inline void clr_rxint(ChannelState *s) @@ -417,6 +415,7 @@ static inline void clr_txint(ChannelState *s) s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; s->rregs[R_INTR] &= ~INTR_TXINTA; } else { + s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->rregs[R_IVEC] = IVEC_HINOINT; else @@ -515,10 +514,15 @@ static void escc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) clr_txint(s); break; case CMD_CLR_IUS: - if (s->rxint_under_svc) - clr_rxint(s); - else if (s->txint_under_svc) - clr_txint(s); + if (s->rxint_under_svc) { + s->rxint_under_svc = 0; + if (s->txint) { + set_txint(s); + } + } else if (s->txint_under_svc) { + s->txint_under_svc = 0; + } + escc_update_irq(s); break; default: break; @@ -264,6 +264,8 @@ int register_savevm_live(DeviceState *dev, void *opaque); void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque); +void register_device_unmigratable(DeviceState *dev, const char *idstr, + void *opaque); typedef void QEMUResetHandler(void *opaque); diff --git a/hw/ivshmem.c b/hw/ivshmem.c new file mode 100644 index 000000000..06dce70e7 --- /dev/null +++ b/hw/ivshmem.c @@ -0,0 +1,829 @@ +/* + * Inter-VM Shared Memory PCI device. + * + * Author: + * Cam Macdonell <cam@cs.ualberta.ca> + * + * Based On: cirrus_vga.c + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * and rtl8139.c + * Copyright (c) 2006 Igor Kovalenko + * + * This code is licensed under the GNU GPL v2. + */ +#include "hw.h" +#include "pc.h" +#include "pci.h" +#include "msix.h" +#include "kvm.h" + +#include <sys/mman.h> +#include <sys/types.h> + +#define IVSHMEM_IOEVENTFD 0 +#define IVSHMEM_MSI 1 + +#define IVSHMEM_PEER 0 +#define IVSHMEM_MASTER 1 + +#define IVSHMEM_REG_BAR_SIZE 0x100 + +//#define DEBUG_IVSHMEM +#ifdef DEBUG_IVSHMEM +#define IVSHMEM_DPRINTF(fmt, ...) \ + do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0) +#else +#define IVSHMEM_DPRINTF(fmt, ...) +#endif + +typedef struct Peer { + int nb_eventfds; + int *eventfds; +} Peer; + +typedef struct EventfdEntry { + PCIDevice *pdev; + int vector; +} EventfdEntry; + +typedef struct IVShmemState { + PCIDevice dev; + uint32_t intrmask; + uint32_t intrstatus; + uint32_t doorbell; + + CharDriverState **eventfd_chr; + CharDriverState *server_chr; + int ivshmem_mmio_io_addr; + + pcibus_t mmio_addr; + pcibus_t shm_pci_addr; + uint64_t ivshmem_offset; + uint64_t ivshmem_size; /* size of shared memory region */ + int shm_fd; /* shared memory file descriptor */ + + Peer *peers; + int nb_peers; /* how many guests we have space for */ + int max_peer; /* maximum numbered peer */ + + int vm_id; + uint32_t vectors; + uint32_t features; + EventfdEntry *eventfd_table; + + char * shmobj; + char * sizearg; + char * role; + int role_val; /* scalar to avoid multiple string comparisons */ +} IVShmemState; + +/* registers for the Inter-VM shared memory device */ +enum ivshmem_registers { + INTRMASK = 0, + INTRSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, + unsigned int feature) { + return (ivs->features & (1 << feature)); +} + +static inline bool is_power_of_two(uint64_t x) { + return (x & (x - 1)) == 0; +} + +static void ivshmem_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); + + s->shm_pci_addr = addr; + + if (s->ivshmem_offset > 0) { + cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, + s->ivshmem_offset); + } + + IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" + PRIu64 ", size = %" FMT_PCIBUS "\n", addr, s->ivshmem_offset, size); + +} + +/* accessing registers - based on rtl8139 */ +static void ivshmem_update_irq(IVShmemState *s, int val) +{ + int isr; + isr = (s->intrstatus & s->intrmask) & 0xffffffff; + + /* don't print ISR resets */ + if (isr) { + IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", + isr ? 1 : 0, s->intrstatus, s->intrmask); + } + + qemu_set_irq(s->dev.irq[0], (isr != 0)); +} + +static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); + + s->intrmask = val; + + ivshmem_update_irq(s, val); +} + +static uint32_t ivshmem_IntrMask_read(IVShmemState *s) +{ + uint32_t ret = s->intrmask; + + IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); + + return ret; +} + +static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); + + s->intrstatus = val; + + ivshmem_update_irq(s, val); + return; +} + +static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) +{ + uint32_t ret = s->intrstatus; + + /* reading ISR clears all interrupts */ + s->intrstatus = 0; + + ivshmem_update_irq(s, 0); + + return ret; +} + +static void ivshmem_io_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + + IVSHMEM_DPRINTF("We shouldn't be writing words\n"); +} + +static void ivshmem_io_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + IVShmemState *s = opaque; + + uint64_t write_one = 1; + uint16_t dest = val >> 16; + uint16_t vector = val & 0xff; + + addr &= 0xfc; + + IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr); + switch (addr) + { + case INTRMASK: + ivshmem_IntrMask_write(s, val); + break; + + case INTRSTATUS: + ivshmem_IntrStatus_write(s, val); + break; + + case DOORBELL: + /* check that dest VM ID is reasonable */ + if (dest > s->max_peer) { + IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); + break; + } + + /* check doorbell range */ + if (vector < s->peers[dest].nb_eventfds) { + IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n", + write_one, dest, vector); + if (write(s->peers[dest].eventfds[vector], + &(write_one), 8) != 8) { + IVSHMEM_DPRINTF("error writing to eventfd\n"); + } + } + break; + default: + IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest); + } +} + +static void ivshmem_io_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + IVSHMEM_DPRINTF("We shouldn't be writing bytes\n"); +} + +static uint32_t ivshmem_io_readw(void *opaque, target_phys_addr_t addr) +{ + + IVSHMEM_DPRINTF("We shouldn't be reading words\n"); + return 0; +} + +static uint32_t ivshmem_io_readl(void *opaque, target_phys_addr_t addr) +{ + + IVShmemState *s = opaque; + uint32_t ret; + + switch (addr) + { + case INTRMASK: + ret = ivshmem_IntrMask_read(s); + break; + + case INTRSTATUS: + ret = ivshmem_IntrStatus_read(s); + break; + + case IVPOSITION: + /* return my VM ID if the memory is mapped */ + if (s->shm_fd > 0) { + ret = s->vm_id; + } else { + ret = -1; + } + break; + + default: + IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr); + ret = 0; + } + + return ret; +} + +static uint32_t ivshmem_io_readb(void *opaque, target_phys_addr_t addr) +{ + IVSHMEM_DPRINTF("We shouldn't be reading bytes\n"); + + return 0; +} + +static CPUReadMemoryFunc * const ivshmem_mmio_read[3] = { + ivshmem_io_readb, + ivshmem_io_readw, + ivshmem_io_readl, +}; + +static CPUWriteMemoryFunc * const ivshmem_mmio_write[3] = { + ivshmem_io_writeb, + ivshmem_io_writew, + ivshmem_io_writel, +}; + +static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + + ivshmem_IntrStatus_write(s, *buf); + + IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); +} + +static int ivshmem_can_receive(void * opaque) +{ + return 8; +} + +static void ivshmem_event(void *opaque, int event) +{ + IVSHMEM_DPRINTF("ivshmem_event %d\n", event); +} + +static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { + + EventfdEntry *entry = opaque; + PCIDevice *pdev = entry->pdev; + + IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector); + msix_notify(pdev, entry->vector); +} + +static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + int vector) +{ + /* create a event character device based on the passed eventfd */ + IVShmemState *s = opaque; + CharDriverState * chr; + + chr = qemu_chr_open_eventfd(eventfd); + + if (chr == NULL) { + fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd); + exit(-1); + } + + /* if MSI is supported we need multiple interrupts */ + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + s->eventfd_table[vector].pdev = &s->dev; + s->eventfd_table[vector].vector = vector; + + qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, + ivshmem_event, &s->eventfd_table[vector]); + } else { + qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, + ivshmem_event, s); + } + + return chr; + +} + +static int check_shm_size(IVShmemState *s, int fd) { + /* check that the guest isn't going to try and map more memory than the + * the object has allocated return -1 to indicate error */ + + struct stat buf; + + fstat(fd, &buf); + + if (s->ivshmem_size > buf.st_size) { + fprintf(stderr, + "IVSHMEM ERROR: Requested memory size greater" + " than shared object size (%" PRIu64 " > %" PRIu64")\n", + s->ivshmem_size, (uint64_t)buf.st_size); + return -1; + } else { + return 0; + } +} + +/* create the shared memory BAR when we are not using the server, so we can + * create the BAR and map the memory immediately */ +static void create_shared_memory_BAR(IVShmemState *s, int fd) { + + void * ptr; + + s->shm_fd = fd; + + ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, "ivshmem.bar2", + s->ivshmem_size, ptr); + + /* region for shared memory */ + pci_register_bar(&s->dev, 2, s->ivshmem_size, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); +} + +static void close_guest_eventfds(IVShmemState *s, int posn) +{ + int i, guest_curr_max; + + guest_curr_max = s->peers[posn].nb_eventfds; + + for (i = 0; i < guest_curr_max; i++) { + kvm_set_ioeventfd_mmio_long(s->peers[posn].eventfds[i], + s->mmio_addr + DOORBELL, (posn << 16) | i, 0); + close(s->peers[posn].eventfds[i]); + } + + qemu_free(s->peers[posn].eventfds); + s->peers[posn].nb_eventfds = 0; +} + +static void setup_ioeventfds(IVShmemState *s) { + + int i, j; + + for (i = 0; i <= s->max_peer; i++) { + for (j = 0; j < s->peers[i].nb_eventfds; j++) { + kvm_set_ioeventfd_mmio_long(s->peers[i].eventfds[j], + s->mmio_addr + DOORBELL, (i << 16) | j, 1); + } + } +} + +/* this function increase the dynamic storage need to store data about other + * guests */ +static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { + + int j, old_nb_alloc; + + old_nb_alloc = s->nb_peers; + + while (new_min_size >= s->nb_peers) + s->nb_peers = s->nb_peers * 2; + + IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); + s->peers = qemu_realloc(s->peers, s->nb_peers * sizeof(Peer)); + + /* zero out new pointers */ + for (j = old_nb_alloc; j < s->nb_peers; j++) { + s->peers[j].eventfds = NULL; + s->peers[j].nb_eventfds = 0; + } +} + +static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) +{ + IVShmemState *s = opaque; + int incoming_fd, tmp_fd; + int guest_max_eventfd; + long incoming_posn; + + memcpy(&incoming_posn, buf, sizeof(long)); + /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ + tmp_fd = qemu_chr_get_msgfd(s->server_chr); + IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); + + /* make sure we have enough space for this guest */ + if (incoming_posn >= s->nb_peers) { + increase_dynamic_storage(s, incoming_posn); + } + + if (tmp_fd == -1) { + /* if posn is positive and unseen before then this is our posn*/ + if ((incoming_posn >= 0) && + (s->peers[incoming_posn].eventfds == NULL)) { + /* receive our posn */ + s->vm_id = incoming_posn; + return; + } else { + /* otherwise an fd == -1 means an existing guest has gone away */ + IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); + close_guest_eventfds(s, incoming_posn); + return; + } + } + + /* because of the implementation of get_msgfd, we need a dup */ + incoming_fd = dup(tmp_fd); + + if (incoming_fd == -1) { + fprintf(stderr, "could not allocate file descriptor %s\n", + strerror(errno)); + return; + } + + /* if the position is -1, then it's shared memory region fd */ + if (incoming_posn == -1) { + + void * map_ptr; + + s->max_peer = 0; + + if (check_shm_size(s, incoming_fd) == -1) { + exit(-1); + } + + /* mmap the region and map into the BAR2 */ + map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, + incoming_fd, 0); + s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, + "ivshmem.bar2", s->ivshmem_size, map_ptr); + + IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" + PRIu64 ", size = %" PRIu64 "\n", s->shm_pci_addr, + s->ivshmem_offset, s->ivshmem_size); + + if (s->shm_pci_addr > 0) { + /* map memory into BAR2 */ + cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, + s->ivshmem_offset); + } + + /* only store the fd if it is successfully mapped */ + s->shm_fd = incoming_fd; + + return; + } + + /* each guest has an array of eventfds, and we keep track of how many + * guests for each VM */ + guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; + + if (guest_max_eventfd == 0) { + /* one eventfd per MSI vector */ + s->peers[incoming_posn].eventfds = (int *) qemu_malloc(s->vectors * + sizeof(int)); + } + + /* this is an eventfd for a particular guest VM */ + IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, + guest_max_eventfd, incoming_fd); + s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd; + + /* increment count for particular guest */ + s->peers[incoming_posn].nb_eventfds++; + + /* keep track of the maximum VM ID */ + if (incoming_posn > s->max_peer) { + s->max_peer = incoming_posn; + } + + if (incoming_posn == s->vm_id) { + s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, + s->peers[s->vm_id].eventfds[guest_max_eventfd], + guest_max_eventfd); + } + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + if (kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr + DOORBELL, + (incoming_posn << 16) | guest_max_eventfd, 1) < 0) { + fprintf(stderr, "ivshmem: ioeventfd not available\n"); + } + } + + return; +} + +static void ivshmem_reset(DeviceState *d) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d); + + s->intrstatus = 0; + return; +} + +static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); + + s->mmio_addr = addr; + cpu_register_physical_memory(addr + 0, IVSHMEM_REG_BAR_SIZE, + s->ivshmem_mmio_io_addr); + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + setup_ioeventfds(s); + } +} + +static uint64_t ivshmem_get_size(IVShmemState * s) { + + uint64_t value; + char *ptr; + + value = strtoull(s->sizearg, &ptr, 10); + switch (*ptr) { + case 0: case 'M': case 'm': + value <<= 20; + break; + case 'G': case 'g': + value <<= 30; + break; + default: + fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg); + exit(1); + } + + /* BARs must be a power of 2 */ + if (!is_power_of_two(value)) { + fprintf(stderr, "ivshmem: size must be power of 2\n"); + exit(1); + } + + return value; +} + +static void ivshmem_setup_msi(IVShmemState * s) { + + int i; + + /* allocate the MSI-X vectors */ + + if (!msix_init(&s->dev, s->vectors, 1, 0)) { + pci_register_bar(&s->dev, 1, + msix_bar_size(&s->dev), + PCI_BASE_ADDRESS_SPACE_MEMORY, + msix_mmio_map); + IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + } else { + IVSHMEM_DPRINTF("msix initialization failed\n"); + exit(1); + } + + /* 'activate' the vectors */ + for (i = 0; i < s->vectors; i++) { + msix_vector_use(&s->dev, i); + } + + /* allocate Qemu char devices for receiving interrupts */ + s->eventfd_table = qemu_mallocz(s->vectors * sizeof(EventfdEntry)); +} + +static void ivshmem_save(QEMUFile* f, void *opaque) +{ + IVShmemState *proxy = opaque; + + IVSHMEM_DPRINTF("ivshmem_save\n"); + pci_device_save(&proxy->dev, f); + + if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { + msix_save(&proxy->dev, f); + } else { + qemu_put_be32(f, proxy->intrstatus); + qemu_put_be32(f, proxy->intrmask); + } + +} + +static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) +{ + IVSHMEM_DPRINTF("ivshmem_load\n"); + + IVShmemState *proxy = opaque; + int ret, i; + + if (version_id > 0) { + return -EINVAL; + } + + if (proxy->role_val == IVSHMEM_PEER) { + fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n"); + return -EINVAL; + } + + ret = pci_device_load(&proxy->dev, f); + if (ret) { + return ret; + } + + if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { + msix_load(&proxy->dev, f); + for (i = 0; i < proxy->vectors; i++) { + msix_vector_use(&proxy->dev, i); + } + } else { + proxy->intrstatus = qemu_get_be32(f); + proxy->intrmask = qemu_get_be32(f); + } + + return 0; +} + +static int pci_ivshmem_init(PCIDevice *dev) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); + uint8_t *pci_conf; + + if (s->sizearg == NULL) + s->ivshmem_size = 4 << 20; /* 4 MB default */ + else { + s->ivshmem_size = ivshmem_get_size(s); + } + + register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load, + dev); + + /* IRQFD requires MSI */ + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && + !ivshmem_has_feature(s, IVSHMEM_MSI)) { + fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n"); + exit(1); + } + + /* check that role is reasonable */ + if (s->role) { + if (strncmp(s->role, "peer", 5) == 0) { + s->role_val = IVSHMEM_PEER; + } else if (strncmp(s->role, "master", 7) == 0) { + s->role_val = IVSHMEM_MASTER; + } else { + fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n"); + exit(1); + } + } else { + s->role_val = IVSHMEM_MASTER; /* default */ + } + + if (s->role_val == IVSHMEM_PEER) { + register_device_unmigratable(&s->dev.qdev, "ivshmem", s); + } + + pci_conf = s->dev.config; + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET); + pci_conf[0x02] = 0x10; + pci_conf[0x03] = 0x11; + pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM); + pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; + + pci_config_set_interrupt_pin(pci_conf, 1); + + s->shm_pci_addr = 0; + s->ivshmem_offset = 0; + s->shm_fd = 0; + + s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read, + ivshmem_mmio_write, s); + /* region for registers*/ + pci_register_bar(&s->dev, 0, IVSHMEM_REG_BAR_SIZE, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map); + + if ((s->server_chr != NULL) && + (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { + /* if we get a UNIX socket as the parameter we will talk + * to the ivshmem server to receive the memory region */ + + if (s->shmobj != NULL) { + fprintf(stderr, "WARNING: do not specify both 'chardev' " + "and 'shm' with ivshmem\n"); + } + + IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", + s->server_chr->filename); + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_setup_msi(s); + } + + /* we allocate enough space for 16 guests and grow as needed */ + s->nb_peers = 16; + s->vm_id = -1; + + /* allocate/initialize space for interrupt handling */ + s->peers = qemu_mallocz(s->nb_peers * sizeof(Peer)); + + pci_register_bar(&s->dev, 2, s->ivshmem_size, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); + + s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *)); + + qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, + ivshmem_event, s); + } else { + /* just map the file immediately, we're not using a server */ + int fd; + + if (s->shmobj == NULL) { + fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n"); + } + + IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); + + /* try opening with O_EXCL and if it succeeds zero the memory + * by truncating to 0 */ + if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { + /* truncate file to length PCI device's memory */ + if (ftruncate(fd, s->ivshmem_size) != 0) { + fprintf(stderr, "ivshmem: could not truncate shared file\n"); + } + + } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, + S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { + fprintf(stderr, "ivshmem: could not open shared file\n"); + exit(-1); + + } + + if (check_shm_size(s, fd) == -1) { + exit(-1); + } + + create_shared_memory_BAR(s, fd); + + } + + return 0; +} + +static int pci_ivshmem_uninit(PCIDevice *dev) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); + + cpu_unregister_io_memory(s->ivshmem_mmio_io_addr); + unregister_savevm(&dev->qdev, "ivshmem", s); + + return 0; +} + +static PCIDeviceInfo ivshmem_info = { + .qdev.name = "ivshmem", + .qdev.size = sizeof(IVShmemState), + .qdev.reset = ivshmem_reset, + .init = pci_ivshmem_init, + .exit = pci_ivshmem_uninit, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), + DEFINE_PROP_STRING("size", IVShmemState, sizearg), + DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false), + DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), + DEFINE_PROP_STRING("shm", IVShmemState, shmobj), + DEFINE_PROP_STRING("role", IVShmemState, role), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void ivshmem_register_devices(void) +{ + pci_qdev_register(&ivshmem_info); +} + +device_init(ivshmem_register_devices) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index f43f2d097..b64dd3146 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -485,16 +485,26 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return buflen; } -static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) +static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, + int page_control) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); BlockDriverState *bdrv = s->bs; int cylinders, heads, secs; + /* + * If Changeable Values are requested, a mask denoting those mode parameters + * that are changeable shall be returned. As we currently don't support + * parameter changes via MODE_SELECT all bits are returned set to zero. + * The buffer was already menset to zero by the caller of this function. + */ switch (page) { case 4: /* Rigid disk device geometry page. */ p[0] = 4; p[1] = 0x16; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } /* if a geometry hint is available, use it */ bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); p[2] = (cylinders >> 16) & 0xff; @@ -519,11 +529,14 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) /* Medium rotation rate [rpm], 5400 rpm */ p[20] = (5400 >> 8) & 0xff; p[21] = 5400 & 0xff; - return 0x16; + return p[1] + 2; case 5: /* Flexible disk device geometry page. */ p[0] = 5; p[1] = 0x1e; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } /* Transfer rate [kbit/s], 5Mbit/s */ p[2] = 5000 >> 8; p[3] = 5000 & 0xff; @@ -555,21 +568,27 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) /* Medium rotation rate [rpm], 5400 rpm */ p[28] = (5400 >> 8) & 0xff; p[29] = 5400 & 0xff; - return 0x1e; + return p[1] + 2; case 8: /* Caching page. */ p[0] = 8; p[1] = 0x12; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } if (bdrv_enable_write_cache(s->bs)) { p[2] = 4; /* WCE */ } - return 20; + return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) return 0; p[0] = 0x2a; p[1] = 0x14; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } p[2] = 3; // CD-R & CD-RW read p[3] = 0; // Writing not supported p[4] = 0x7f; /* Audio, composite, digital out, @@ -593,7 +612,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) p[19] = (16 * 176) & 0xff; p[20] = (16 * 176) >> 8; // 16x write speed current p[21] = (16 * 176) & 0xff; - return 22; + return p[1] + 2; default: return 0; @@ -604,29 +623,46 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); uint64_t nb_sectors; - int page, dbd, buflen; + int page, dbd, buflen, page_control; uint8_t *p; + uint8_t dev_specific_param; dbd = req->cmd.buf[1] & 0x8; page = req->cmd.buf[2] & 0x3f; - DPRINTF("Mode Sense (page %d, len %zd)\n", page, req->cmd.xfer); + page_control = (req->cmd.buf[2] & 0xc0) >> 6; + DPRINTF("Mode Sense(%d) (page %d, len %d, page_control %d)\n", + (req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, len, page_control); memset(outbuf, 0, req->cmd.xfer); p = outbuf; - p[1] = 0; /* Default media type. */ - p[3] = 0; /* Block descriptor length. */ if (bdrv_is_read_only(s->bs)) { - p[2] = 0x80; /* Readonly. */ + dev_specific_param = 0x80; /* Readonly. */ + } else { + dev_specific_param = 0x00; + } + + if (req->cmd.buf[0] == MODE_SENSE) { + p[1] = 0; /* Default media type. */ + p[2] = dev_specific_param; + p[3] = 0; /* Block descriptor length. */ + p += 4; + } else { /* MODE_SENSE_10 */ + p[2] = 0; /* Default media type. */ + p[3] = dev_specific_param; + p[6] = p[7] = 0; /* Block descriptor length. */ + p += 8; } - p += 4; bdrv_get_geometry(s->bs, &nb_sectors); - if ((~dbd) & nb_sectors) { - outbuf[3] = 8; /* Block descriptor length */ + if (!dbd && nb_sectors) { + if (req->cmd.buf[0] == MODE_SENSE) { + outbuf[3] = 8; /* Block descriptor length */ + } else { /* MODE_SENSE_10 */ + outbuf[7] = 8; /* Block descriptor length */ + } nb_sectors /= s->cluster_size; - nb_sectors--; if (nb_sectors > 0xffffff) - nb_sectors = 0xffffff; + nb_sectors = 0; p[0] = 0; /* media density code */ p[1] = (nb_sectors >> 16) & 0xff; p[2] = (nb_sectors >> 8) & 0xff; @@ -638,21 +674,37 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) p += 8; } + if (page_control == 3) { /* Saved Values */ + return -1; /* ILLEGAL_REQUEST */ + } + switch (page) { case 0x04: case 0x05: case 0x08: case 0x2a: - p += mode_sense_page(req, page, p); + p += mode_sense_page(req, page, p, page_control); break; case 0x3f: - p += mode_sense_page(req, 0x08, p); - p += mode_sense_page(req, 0x2a, p); + p += mode_sense_page(req, 0x08, p, page_control); + p += mode_sense_page(req, 0x2a, p, page_control); break; + default: + return -1; /* ILLEGAL_REQUEST */ } buflen = p - outbuf; - outbuf[0] = buflen - 4; + /* + * The mode data length field specifies the length in bytes of the + * following data that is available to be transferred. The mode data + * length does not include itself. + */ + if (req->cmd.buf[0] == MODE_SENSE) { + outbuf[0] = buflen - 1; + } else { /* MODE_SENSE_10 */ + outbuf[0] = ((buflen - 2) >> 8) & 0xff; + outbuf[1] = (buflen - 2) & 0xff; + } if (buflen > req->cmd.xfer) buflen = req->cmd.xfer; return buflen; diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c index d7875536b..c125de4b6 100644 --- a/hw/slavio_timer.c +++ b/hw/slavio_timer.c @@ -377,12 +377,12 @@ static void slavio_timer_reset(DeviceState *d) curr_timer->limit = 0; curr_timer->count = 0; curr_timer->reached = 0; - if (i < s->num_cpus) { + if (i <= s->num_cpus) { ptimer_set_limit(curr_timer->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); ptimer_run(curr_timer->timer, 0); + curr_timer->running = 1; } - curr_timer->running = 1; } s->cputimer_mode = 0; } diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 1e4dfdd0e..910438ff2 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -30,6 +30,10 @@ #include <sys/mman.h> #endif +/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */ +#define ENABLE_GUEST_STATS 0 + + typedef struct VirtIOBalloon { VirtIODevice vdev; @@ -84,12 +88,14 @@ static QObject *get_stats_qobject(VirtIOBalloon *dev) VIRTIO_BALLOON_PFN_SHIFT); stat_put(dict, "actual", actual); +#if ENABLE_GUEST_STATS stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]); stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]); stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]); stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]); stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]); stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]); +#endif return QOBJECT(dict); } @@ -215,7 +221,7 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target, } dev->stats_callback = cb; dev->stats_opaque_callback_data = cb_data; - if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { + if (ENABLE_GUEST_STATS && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); virtio_notify(&dev->vdev, dev->svq); } else { @@ -1268,6 +1268,38 @@ int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) return r; } +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t addr, uint32_t val, bool assign) +{ +#ifdef KVM_IOEVENTFD + int ret; + struct kvm_ioeventfd iofd; + + iofd.datamatch = val; + iofd.addr = addr; + iofd.len = 4; + iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH; + iofd.fd = fd; + + if (!kvm_enabled()) { + return -ENOSYS; + } + + if (!assign) { + iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd); + + if (ret < 0) { + return -errno; + } + + return 0; +#else + return -ENOSYS; +#endif +} + int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) { #ifdef KVM_IOEVENTFD diff --git a/kvm-stub.c b/kvm-stub.c index 3378bd3b2..d45f9fa1d 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -136,3 +136,8 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) { return -ENOSYS; } + +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign) +{ + return -ENOSYS; +} @@ -182,6 +182,7 @@ static inline void cpu_synchronize_post_init(CPUState *env) } #endif +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign); #if defined(KVM_IRQFD) && defined(CONFIG_KVM) int kvm_set_irqfd(int gsi, int fd, bool assigned); diff --git a/migration.c b/migration.c index 650eb78d2..468d51749 100644 --- a/migration.c +++ b/migration.c @@ -67,6 +67,8 @@ void process_incoming_migration(QEMUFile *f) qemu_announce_self(); DPRINTF("successfully loaded vm state\n"); + incoming_expected = false; + if (autostart) vm_start(); } @@ -314,8 +316,14 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size) if (ret == -1) ret = -(s->get_error(s)); - if (ret == -EAGAIN) + if (ret == -EAGAIN) { qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s); + } else if (ret < 0) { + if (s->mon) { + monitor_resume(s->mon); + } + s->state = MIG_STATE_ERROR; + } return ret; } @@ -670,17 +670,32 @@ help: static void do_info_version_print(Monitor *mon, const QObject *data) { QDict *qdict; + QDict *qemu; qdict = qobject_to_qdict(data); + qemu = qdict_get_qdict(qdict, "qemu"); - monitor_printf(mon, "%s%s\n", qdict_get_str(qdict, "qemu"), - qdict_get_str(qdict, "package")); + monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n", + qdict_get_int(qemu, "major"), + qdict_get_int(qemu, "minor"), + qdict_get_int(qemu, "micro"), + qdict_get_str(qdict, "package")); } static void do_info_version(Monitor *mon, QObject **ret_data) { - *ret_data = qobject_from_jsonf("{ 'qemu': %s, 'package': %s }", - QEMU_VERSION, QEMU_PKGVERSION); + const char *version = QEMU_VERSION; + int major = 0, minor = 0, micro = 0; + char *tmp; + + major = strtol(version, &tmp, 10); + tmp++; + minor = strtol(tmp, &tmp, 10); + tmp++; + micro = strtol(tmp, &tmp, 10); + + *ret_data = qobject_from_jsonf("{ 'qemu': { 'major': %d, 'minor': %d, \ + 'micro': %d }, 'package': %s }", major, minor, micro, QEMU_PKGVERSION); } static void do_info_name_print(Monitor *mon, const QObject *data) @@ -1082,6 +1097,10 @@ static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data) { struct bdrv_iterate_context context = { mon, 0 }; + if (incoming_expected) { + qerror_report(QERR_MIGRATION_EXPECTED); + return -1; + } bdrv_iterate(encrypted_bdrv_it, &context); /* only resume the vm if all keys are set and valid */ if (!context.err) { diff --git a/qemu-char.c b/qemu-char.c index 9b69d928e..6a3952ccd 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2087,6 +2087,12 @@ static void tcp_chr_read(void *opaque) } } +CharDriverState *qemu_chr_open_eventfd(int eventfd){ + + return qemu_chr_open_fd(eventfd, eventfd); + +} + static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; diff --git a/qemu-char.h b/qemu-char.h index e3a07838a..6ea01ba17 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -94,6 +94,9 @@ void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); +/* add an eventfd to the qemu devices that are polled */ +CharDriverState *qemu_chr_open_eventfd(int eventfd); + extern int term_escape_char; /* async I/O support */ diff --git a/qemu-doc.texi b/qemu-doc.texi index e67bf44ff..55a966fe7 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -706,6 +706,49 @@ Using the @option{-net socket} option, it is possible to make VLANs that span several QEMU instances. See @ref{sec_invocation} to have a basic example. +@section Other Devices + +@subsection Inter-VM Shared Memory device + +With KVM enabled on a Linux host, a shared memory device is available. Guests +map a POSIX shared memory region into the guest as a PCI device that enables +zero-copy communication to the application level of the guests. The basic +syntax is: + +@example +qemu -device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>] +@end example + +If desired, interrupts can be sent between guest VMs accessing the same shared +memory region. Interrupt support requires using a shared memory server and +using a chardev socket to connect to it. The code for the shared memory server +is qemu.git/contrib/ivshmem-server. An example syntax when using the shared +memory server is: + +@example +qemu -device ivshmem,size=<size in format accepted by -m>[,chardev=<id>] + [,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master] +qemu -chardev socket,path=<path>,id=<id> +@end example + +When using the server, the guest will be assigned a VM ID (>=0) that allows guests +using the same server to communicate via interrupts. Guests can read their +VM ID from a device register (see example code). Since receiving the shared +memory region from the server is asynchronous, there is a (small) chance the +guest may boot before the shared memory is attached. To allow an application +to ensure shared memory is attached, the VM ID register will return -1 (an +invalid VM ID) until the memory is attached. Once the shared memory is +attached, the VM ID will return the guest's valid VM ID. With these semantics, +the guest application can check to ensure the shared memory is attached to the +guest before proceeding. + +The @option{role} argument can be set to either master or peer and will affect +how the shared memory is migrated. With @option{role=master}, the guest will +copy the shared memory on migration to the destination host. With +@option{role=peer}, the guest will not be able to migrate with the device attached. +With the @option{peer} case, the device should be detached and then reattached +after migration using the PCI hotplug support. + @node direct_linux_boot @section Direct Linux Boot diff --git a/qemu-img.c b/qemu-img.c index d2a978b91..4e035e44c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -783,7 +783,8 @@ static int img_convert(int argc, char **argv) goto out; } - out_bs = bdrv_new_open(out_filename, out_fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + out_bs = bdrv_new_open(out_filename, out_fmt, + BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH); if (!out_bs) { ret = -1; goto out; diff --git a/qemu-monitor.hx b/qemu-monitor.hx index da7b796de..595d362ae 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -35,7 +35,29 @@ information on the Server command and response formats. NOTE: This document is temporary and will be replaced soon. -1. Regular Commands +1. Stability Considerations +=========================== + +The current QMP command set (described in this file) may be useful for a +number of use cases, however it's limited and several commands have bad +defined semantics, specially with regard to command completion. + +These problems are going to be solved incrementally in the next QEMU releases +and we're going to establish a deprecation policy for badly defined commands. + +If you're planning to adopt QMP, please observe the following: + + 1. The deprecation policy will take efect and be documented soon, please + check the documentation of each used command as soon as a new release of + QEMU is available + + 2. DO NOT rely on anything which is not explicit documented + + 3. Errors, in special, are not documented. Applications should NOT check + for specific errors classes or data (it's strongly recommended to only + check for the "error" key) + +2. Regular Commands =================== Server's responses in the examples below are always a success response, please @@ -1605,7 +1627,7 @@ HXCOMM This is required for the QMP documentation layout. SQMP -2. Query Commands +3. Query Commands ================= EQMP @@ -1636,13 +1658,25 @@ Show QEMU version. Return a json-object with the following information: -- "qemu": QEMU's version (json-string) +- "qemu": A json-object containing three integer values: + - "major": QEMU's major version (json-int) + - "minor": QEMU's minor version (json-int) + - "micro": QEMU's micro version (json-int) - "package": package's version (json-string) Example: -> { "execute": "query-version" } -<- { "return": { "qemu": "0.11.50", "package": "" } } +<- { + "return":{ + "qemu":{ + "major":0, + "minor":11, + "micro":5 + }, + "package":"" + } + } EQMP @@ -141,6 +141,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Using KVM without %(capability), %(feature) unavailable", }, { + .error_fmt = QERR_MIGRATION_EXPECTED, + .desc = "An incoming migration is expected before this command can be executed", + }, + { .error_fmt = QERR_MISSING_PARAMETER, .desc = "Parameter '%(name)' is missing", }, @@ -121,6 +121,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_KVM_MISSING_CAP \ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" +#define QERR_MIGRATION_EXPECTED \ + "{ 'class': 'MigrationExpected', 'data': {} }" + #define QERR_MISSING_PARAMETER \ "{ 'class': 'MissingParameter', 'data': { 'name': %s } }" @@ -1041,6 +1041,7 @@ typedef struct SaveStateEntry { const VMStateDescription *vmsd; void *opaque; CompatEntry *compat; + int no_migrate; } SaveStateEntry; @@ -1104,6 +1105,7 @@ int register_savevm_live(DeviceState *dev, se->load_state = load_state; se->opaque = opaque; se->vmsd = NULL; + se->no_migrate = 0; if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { char *id = dev->parent_bus->info->get_dev_path(dev); @@ -1170,6 +1172,31 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) } } +/* mark a device as not to be migrated, that is the device should be + unplugged before migration */ +void register_device_unmigratable(DeviceState *dev, const char *idstr, + void *opaque) +{ + SaveStateEntry *se; + char id[256] = ""; + + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *path = dev->parent_bus->info->get_dev_path(dev); + if (path) { + pstrcpy(id, sizeof(id), path); + pstrcat(id, sizeof(id), "/"); + qemu_free(path); + } + } + pstrcat(id, sizeof(id), idstr); + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { + se->no_migrate = 1; + } + } +} + int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, @@ -1376,13 +1403,19 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) return vmstate_load_state(f, se->vmsd, se->opaque, version_id); } -static void vmstate_save(QEMUFile *f, SaveStateEntry *se) +static int vmstate_save(QEMUFile *f, SaveStateEntry *se) { + if (se->no_migrate) { + return -1; + } + if (!se->vmsd) { /* Old style */ se->save_state(f, se->opaque); - return; + return 0; } vmstate_save_state(f,se->vmsd, se->opaque); + + return 0; } #define QEMU_VM_FILE_MAGIC 0x5145564d @@ -1477,6 +1510,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; + int r; cpu_synchronize_all_states(); @@ -1509,7 +1543,11 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - vmstate_save(f, se); + r = vmstate_save(f, se); + if (r < 0) { + monitor_printf(mon, "cannot migrate with device '%s'\n", se->idstr); + return r; + } } qemu_put_byte(f, QEMU_VM_EOF); @@ -100,6 +100,7 @@ typedef enum DisplayType } DisplayType; extern int autostart; +extern int incoming_expected; extern int bios_size; typedef enum { @@ -1184,7 +1184,7 @@ void vnc_client_write(void *opaque) vnc_lock_output(vs); if (vs->output.offset) { vnc_client_write_locked(opaque); - } else { + } else if (vs->csock != -1) { qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); } vnc_unlock_output(vs); @@ -183,6 +183,7 @@ int nb_nics; NICInfo nd_table[MAX_NICS]; int vm_running; int autostart; +int incoming_expected; /* Started with -incoming and waiting for incoming */ static int rtc_utc = 1; static int rtc_date_offset = -1; /* -1 means no change */ QEMUClock *rtc_clock; @@ -2629,6 +2630,7 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_incoming: incoming = optarg; + incoming_expected = true; break; case QEMU_OPTION_nodefaults: default_serial = 0; |