aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2010-10-13 12:55:11 +0200
committerAvi Kivity <avi@redhat.com>2010-10-13 12:55:11 +0200
commit3d6d86a236579d99aa4856f6a3ad241d4824e488 (patch)
treef01b1c7db332622eaee8c47cad7e3010c02db6ae
parentc9c217977a97452aea54740c2b49e919f3c66b8d (diff)
parenta9c3c65b9f7cd7ba49e28a95c902378e3f2c4228 (diff)
Merge branch 'upstream-merge' into stable-0.13qemu-kvm-0.13.0-rc3
* 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.target3
-rw-r--r--QMP/README73
-rw-r--r--VERSION2
-rw-r--r--arch_init.c7
-rw-r--r--block.c4
-rw-r--r--block.h2
-rw-r--r--block/qcow2-cluster.c16
-rw-r--r--block/raw-posix.c3
-rw-r--r--block/raw.c130
-rw-r--r--block/vvfat.c26
-rw-r--r--block_int.h1
-rw-r--r--buffered_file.c12
-rw-r--r--cpu-common.h2
-rw-r--r--docs/specs/ivshmem_device_spec.txt96
-rw-r--r--exec.c2
-rw-r--r--hw/device-assignment.c2
-rw-r--r--hw/escc.c56
-rw-r--r--hw/hw.h2
-rw-r--r--hw/ivshmem.c829
-rw-r--r--hw/scsi-disk.c90
-rw-r--r--hw/slavio_timer.c4
-rw-r--r--hw/virtio-balloon.c8
-rw-r--r--kvm-all.c32
-rw-r--r--kvm-stub.c5
-rw-r--r--kvm.h1
-rw-r--r--migration.c10
-rw-r--r--monitor.c27
-rw-r--r--qemu-char.c6
-rw-r--r--qemu-char.h3
-rw-r--r--qemu-doc.texi43
-rw-r--r--qemu-img.c3
-rw-r--r--qemu-monitor.hx42
-rw-r--r--qerror.c4
-rw-r--r--qerror.h3
-rw-r--r--savevm.c44
-rw-r--r--sysemu.h1
-rw-r--r--ui/vnc.c2
-rw-r--r--vl.c2
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
diff --git a/VERSION b/VERSION
index 0eb28840f..73b08a130 100644
--- a/VERSION
+++ b/VERSION
@@ -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) {
diff --git a/block.c b/block.c
index e6087f3cb..85c8d3e75 100644
--- a/block.c
+++ b/block.c
@@ -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];
diff --git a/block.h b/block.h
index db131a343..5f6438010 100644
--- a/block.h
+++ b/block.h
@@ -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.
diff --git a/exec.c b/exec.c
index b14bad93f..dbeab5a7c 100644
--- a/exec.c
+++ b/exec.c
@@ -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
diff --git a/hw/escc.c b/hw/escc.c
index 6d2fd36b1..871423978 100644
--- a/hw/escc.c
+++ b/hw/escc.c
@@ -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;
diff --git a/hw/hw.h b/hw/hw.h
index ec6985d3b..44d1a3c4f 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -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 {
diff --git a/kvm-all.c b/kvm-all.c
index 0b5ce9281..00cecf6d7 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -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;
+}
diff --git a/kvm.h b/kvm.h
index 95f841922..d321fcecb 100644
--- a/kvm.h
+++ b/kvm.h
@@ -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;
}
diff --git a/monitor.c b/monitor.c
index e51df62d6..dd5469f91 100644
--- a/monitor.c
+++ b/monitor.c
@@ -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
diff --git a/qerror.c b/qerror.c
index 2f6f59061..0af3ab32b 100644
--- a/qerror.c
+++ b/qerror.c
@@ -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",
},
diff --git a/qerror.h b/qerror.h
index 9ad00b4b8..62802ea08 100644
--- a/qerror.h
+++ b/qerror.h
@@ -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 } }"
diff --git a/savevm.c b/savevm.c
index 681f0fb61..8d7e257b6 100644
--- a/savevm.c
+++ b/savevm.c
@@ -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);
diff --git a/sysemu.h b/sysemu.h
index bf1d68add..98bd47da3 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -100,6 +100,7 @@ typedef enum DisplayType
} DisplayType;
extern int autostart;
+extern int incoming_expected;
extern int bios_size;
typedef enum {
diff --git a/ui/vnc.c b/ui/vnc.c
index 7fc40acae..c7a18311f 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -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);
diff --git a/vl.c b/vl.c
index de8bad183..84b5a9531 100644
--- a/vl.c
+++ b/vl.c
@@ -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;