aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/a15mpcore.c8
-rw-r--r--hw/arm/Makefile.objs2
-rw-r--r--hw/arm/pic_cpu.c26
-rw-r--r--hw/arm_gic.c23
-rw-r--r--hw/arm_gic_common.c36
-rw-r--r--hw/arm_gic_internal.h4
-rw-r--r--hw/armv7m_nvic.c15
-rw-r--r--hw/dataplane/Makefile.objs2
-rw-r--r--hw/dataplane/event-poll.c100
-rw-r--r--hw/dataplane/event-poll.h40
-rw-r--r--hw/dataplane/virtio-blk.c50
-rw-r--r--hw/e1000.c25
-rw-r--r--hw/i386/pc_piix.c4
-rw-r--r--hw/ich9.h11
-rw-r--r--hw/kvm/arm_gic.c167
-rw-r--r--hw/lpc_ich9.c57
-rw-r--r--hw/macio.c2
-rw-r--r--hw/milkymist-hw.h16
-rw-r--r--hw/nand.c3
-rw-r--r--hw/pc.h5
-rw-r--r--hw/pci/pci_host.h1
-rw-r--r--hw/piix_pci.c1
-rw-r--r--hw/ppc/prep.c1
-rw-r--r--hw/scsi-bus.c4
-rw-r--r--hw/scsi-disk.c36
-rw-r--r--hw/serial.c28
-rw-r--r--hw/serial.h2
-rw-r--r--hw/sysbus.c27
-rw-r--r--hw/sysbus.h2
-rw-r--r--hw/vhost.c49
-rw-r--r--hw/virtio-blk.c4
-rw-r--r--hw/virtio-console.c28
-rw-r--r--hw/virtio-net.c6
-rw-r--r--hw/virtio-pci.c5
-rw-r--r--hw/virtio-serial-bus.c19
35 files changed, 519 insertions, 290 deletions
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c
index d973d53f0a..648656d5b4 100644
--- a/hw/a15mpcore.c
+++ b/hw/a15mpcore.c
@@ -19,6 +19,7 @@
*/
#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
/* A15MP private memory region. */
@@ -40,8 +41,13 @@ static int a15mp_priv_init(SysBusDevice *dev)
{
A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev);
SysBusDevice *busdev;
+ const char *gictype = "arm_gic";
- s->gic = qdev_create(NULL, "arm_gic");
+ if (kvm_irqchip_in_kernel()) {
+ gictype = "kvm-arm-gic";
+ }
+
+ s->gic = qdev_create(NULL, gictype);
qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq);
qdev_prop_set_uint32(s->gic, "revision", 2);
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index aebbc866e2..2d9c69dfce 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -23,6 +23,8 @@ obj-y += bitbang_i2c.o marvell_88w8618_audio.o
obj-y += framebuffer.o
obj-y += strongarm.o
obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
+obj-$(CONFIG_FDT) += ../device_tree.o
+obj-$(CONFIG_KVM) += kvm/arm_gic.o
obj-y := $(addprefix ../,$(obj-y))
diff --git a/hw/arm/pic_cpu.c b/hw/arm/pic_cpu.c
index a7ad893cc2..82236006d2 100644
--- a/hw/arm/pic_cpu.c
+++ b/hw/arm/pic_cpu.c
@@ -9,6 +9,7 @@
#include "hw/hw.h"
#include "hw/arm-misc.h"
+#include "sysemu/kvm.h"
/* Input 0 is IRQ and input 1 is FIQ. */
static void arm_pic_cpu_handler(void *opaque, int irq, int level)
@@ -34,7 +35,32 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level)
}
}
+static void kvm_arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+#ifdef CONFIG_KVM
+ ARMCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT;
+
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_IRQ;
+ break;
+ case ARM_PIC_CPU_FIQ:
+ kvm_irq |= KVM_ARM_IRQ_CPU_FIQ;
+ break;
+ default:
+ hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq);
+ }
+ kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT;
+ kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0);
+#endif
+}
+
qemu_irq *arm_pic_init_cpu(ARMCPU *cpu)
{
+ if (kvm_enabled()) {
+ return qemu_allocate_irqs(kvm_arm_pic_cpu_handler, cpu, 2);
+ }
return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2);
}
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 6b30e0bf42..bcb072bbcf 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -659,14 +659,18 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq)
memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000);
}
-static int arm_gic_init(SysBusDevice *dev)
+static void arm_gic_realize(DeviceState *dev, Error **errp)
{
- /* Device instance init function for the GIC sysbus device */
+ /* Device instance realize function for the GIC sysbus device */
int i;
- GICState *s = FROM_SYSBUS(GICState, dev);
+ GICState *s = ARM_GIC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
ARMGICClass *agc = ARM_GIC_GET_CLASS(s);
- agc->parent_init(dev);
+ agc->parent_realize(dev, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
gic_init_irqs_and_distributor(s, s->num_irq);
@@ -686,22 +690,21 @@ static int arm_gic_init(SysBusDevice *dev)
"gic_cpu", 0x100);
}
/* Distributor */
- sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_mmio(sbd, &s->iomem);
/* cpu interfaces (one for "current cpu" plus one per cpu) */
for (i = 0; i <= NUM_CPU(s); i++) {
- sysbus_init_mmio(dev, &s->cpuiomem[i]);
+ sysbus_init_mmio(sbd, &s->cpuiomem[i]);
}
- return 0;
}
static void arm_gic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
ARMGICClass *agc = ARM_GIC_CLASS(klass);
- agc->parent_init = sbc->init;
- sbc->init = arm_gic_init;
+
dc->no_user = 1;
+ agc->parent_realize = dc->realize;
+ dc->realize = arm_gic_realize;
}
static const TypeInfo arm_gic_info = {
diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c
index 745b4b2a0a..f2dc8bf555 100644
--- a/hw/arm_gic_common.c
+++ b/hw/arm_gic_common.c
@@ -23,9 +23,14 @@
static void gic_save(QEMUFile *f, void *opaque)
{
GICState *s = (GICState *)opaque;
+ ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
int i;
int j;
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+
qemu_put_be32(f, s->enabled);
for (i = 0; i < s->num_cpu; i++) {
qemu_put_be32(f, s->cpu_enabled[i]);
@@ -57,6 +62,7 @@ static void gic_save(QEMUFile *f, void *opaque)
static int gic_load(QEMUFile *f, void *opaque, int version_id)
{
GICState *s = (GICState *)opaque;
+ ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s);
int i;
int j;
@@ -91,34 +97,42 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
s->irq_state[i].trigger = qemu_get_byte(f);
}
+ if (c->post_load) {
+ c->post_load(s);
+ }
+
return 0;
}
-static int arm_gic_common_init(SysBusDevice *dev)
+static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
- GICState *s = FROM_SYSBUS(GICState, dev);
+ GICState *s = ARM_GIC_COMMON(dev);
int num_irq = s->num_irq;
if (s->num_cpu > NCPU) {
- hw_error("requested %u CPUs exceeds GIC maximum %d\n",
- s->num_cpu, NCPU);
+ error_setg(errp, "requested %u CPUs exceeds GIC maximum %d",
+ s->num_cpu, NCPU);
+ return;
}
s->num_irq += GIC_BASE_IRQ;
if (s->num_irq > GIC_MAXIRQ) {
- hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
- num_irq, GIC_MAXIRQ);
+ error_setg(errp,
+ "requested %u interrupt lines exceeds GIC maximum %d",
+ num_irq, GIC_MAXIRQ);
+ return;
}
/* ITLinesNumber is represented as (N / 32) - 1 (see
* gic_dist_readb) so this is an implementation imposed
* restriction, not an architectural one:
*/
if (s->num_irq < 32 || (s->num_irq % 32)) {
- hw_error("%d interrupt lines unsupported: not divisible by 32\n",
- num_irq);
+ error_setg(errp,
+ "%d interrupt lines unsupported: not divisible by 32",
+ num_irq);
+ return;
}
register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s);
- return 0;
}
static void arm_gic_common_reset(DeviceState *dev)
@@ -163,12 +177,12 @@ static Property arm_gic_common_properties[] = {
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+
dc->reset = arm_gic_common_reset;
+ dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->no_user = 1;
- sc->init = arm_gic_common_init;
}
static const TypeInfo arm_gic_common_type = {
diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h
index b10ac5e9f6..3e1928b7eb 100644
--- a/hw/arm_gic_internal.h
+++ b/hw/arm_gic_internal.h
@@ -118,6 +118,8 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq);
typedef struct ARMGICCommonClass {
SysBusDeviceClass parent_class;
+ void (*pre_save)(GICState *s);
+ void (*post_load)(GICState *s);
} ARMGICCommonClass;
#define TYPE_ARM_GIC "arm_gic"
@@ -130,7 +132,7 @@ typedef struct ARMGICCommonClass {
typedef struct ARMGICClass {
ARMGICCommonClass parent_class;
- int (*parent_init)(SysBusDevice *dev);
+ DeviceRealize parent_realize;
} ARMGICClass;
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
index 4d3042348b..d198cfd96e 100644
--- a/hw/armv7m_nvic.c
+++ b/hw/armv7m_nvic.c
@@ -41,7 +41,7 @@ typedef struct NVICClass {
/*< private >*/
ARMGICClass parent_class;
/*< public >*/
- int (*parent_init)(SysBusDevice *dev);
+ DeviceRealize parent_realize;
void (*parent_reset)(DeviceState *dev);
} NVICClass;
@@ -465,7 +465,7 @@ static void armv7m_nvic_reset(DeviceState *dev)
systick_reset(s);
}
-static int armv7m_nvic_init(SysBusDevice *dev)
+static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
{
nvic_state *s = NVIC(dev);
NVICClass *nc = NVIC_GET_CLASS(s);
@@ -475,7 +475,10 @@ static int armv7m_nvic_init(SysBusDevice *dev)
/* Tell the common code we're an NVIC */
s->gic.revision = 0xffffffff;
s->num_irq = s->gic.num_irq;
- nc->parent_init(dev);
+ nc->parent_realize(dev, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
gic_init_irqs_and_distributor(&s->gic, s->num_irq);
/* The NVIC and system controller register area looks like this:
* 0..0xff : system control registers, including systick
@@ -503,7 +506,6 @@ static int armv7m_nvic_init(SysBusDevice *dev)
*/
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container);
s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s);
- return 0;
}
static void armv7m_nvic_instance_init(Object *obj)
@@ -526,13 +528,12 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
{
NVICClass *nc = NVIC_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
nc->parent_reset = dc->reset;
- nc->parent_init = sdc->init;
- sdc->init = armv7m_nvic_init;
+ nc->parent_realize = dc->realize;
dc->vmsd = &vmstate_nvic;
dc->reset = armv7m_nvic_reset;
+ dc->realize = armv7m_nvic_realize;
}
static const TypeInfo armv7m_nvic_info = {
diff --git a/hw/dataplane/Makefile.objs b/hw/dataplane/Makefile.objs
index 3e47d0537e..701111ccb9 100644
--- a/hw/dataplane/Makefile.objs
+++ b/hw/dataplane/Makefile.objs
@@ -1 +1 @@
-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += hostmem.o vring.o event-poll.o ioq.o virtio-blk.o
+obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += hostmem.o vring.o ioq.o virtio-blk.o
diff --git a/hw/dataplane/event-poll.c b/hw/dataplane/event-poll.c
deleted file mode 100644
index b98acf9aec..0000000000
--- a/hw/dataplane/event-poll.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Event loop with file descriptor polling
- *
- * Copyright 2012 IBM, Corp.
- * Copyright 2012 Red Hat, Inc. and/or its affiliates
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include <sys/epoll.h>
-#include "event-poll.h"
-
-/* Add an event notifier and its callback for polling */
-void event_poll_add(EventPoll *poll, EventHandler *handler,
- EventNotifier *notifier, EventCallback *callback)
-{
- struct epoll_event event = {
- .events = EPOLLIN,
- .data.ptr = handler,
- };
- handler->notifier = notifier;
- handler->callback = callback;
- if (epoll_ctl(poll->epoll_fd, EPOLL_CTL_ADD,
- event_notifier_get_fd(notifier), &event) != 0) {
- fprintf(stderr, "failed to add event handler to epoll: %m\n");
- exit(1);
- }
-}
-
-/* Event callback for stopping event_poll() */
-static void handle_stop(EventHandler *handler)
-{
- /* Do nothing */
-}
-
-void event_poll_init(EventPoll *poll)
-{
- /* Create epoll file descriptor */
- poll->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (poll->epoll_fd < 0) {
- fprintf(stderr, "epoll_create1 failed: %m\n");
- exit(1);
- }
-
- /* Set up stop notifier */
- if (event_notifier_init(&poll->stop_notifier, 0) < 0) {
- fprintf(stderr, "failed to init stop notifier\n");
- exit(1);
- }
- event_poll_add(poll, &poll->stop_handler,
- &poll->stop_notifier, handle_stop);
-}
-
-void event_poll_cleanup(EventPoll *poll)
-{
- event_notifier_cleanup(&poll->stop_notifier);
- close(poll->epoll_fd);
- poll->epoll_fd = -1;
-}
-
-/* Block until the next event and invoke its callback */
-void event_poll(EventPoll *poll)
-{
- EventHandler *handler;
- struct epoll_event event;
- int nevents;
-
- /* Wait for the next event. Only do one event per call to keep the
- * function simple, this could be changed later. */
- do {
- nevents = epoll_wait(poll->epoll_fd, &event, 1, -1);
- } while (nevents < 0 && errno == EINTR);
- if (unlikely(nevents != 1)) {
- fprintf(stderr, "epoll_wait failed: %m\n");
- exit(1); /* should never happen */
- }
-
- /* Find out which event handler has become active */
- handler = event.data.ptr;
-
- /* Clear the eventfd */
- event_notifier_test_and_clear(handler->notifier);
-
- /* Handle the event */
- handler->callback(handler);
-}
-
-/* Stop event_poll()
- *
- * This function can be used from another thread.
- */
-void event_poll_notify(EventPoll *poll)
-{
- event_notifier_set(&poll->stop_notifier);
-}
diff --git a/hw/dataplane/event-poll.h b/hw/dataplane/event-poll.h
deleted file mode 100644
index 3e8d3ec7d5..0000000000
--- a/hw/dataplane/event-poll.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Event loop with file descriptor polling
- *
- * Copyright 2012 IBM, Corp.
- * Copyright 2012 Red Hat, Inc. and/or its affiliates
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef EVENT_POLL_H
-#define EVENT_POLL_H
-
-#include "qemu/event_notifier.h"
-
-typedef struct EventHandler EventHandler;
-typedef void EventCallback(EventHandler *handler);
-struct EventHandler {
- EventNotifier *notifier; /* eventfd */
- EventCallback *callback; /* callback function */
-};
-
-typedef struct {
- int epoll_fd; /* epoll(2) file descriptor */
- EventNotifier stop_notifier; /* stop poll notifier */
- EventHandler stop_handler; /* stop poll handler */
-} EventPoll;
-
-void event_poll_add(EventPoll *poll, EventHandler *handler,
- EventNotifier *notifier, EventCallback *callback);
-void event_poll_init(EventPoll *poll);
-void event_poll_cleanup(EventPoll *poll);
-void event_poll(EventPoll *poll);
-void event_poll_notify(EventPoll *poll);
-
-#endif /* EVENT_POLL_H */
diff --git a/hw/dataplane/virtio-blk.c b/hw/dataplane/virtio-blk.c
index 8319c94b76..dfe5f9b9cd 100644
--- a/hw/dataplane/virtio-blk.c
+++ b/hw/dataplane/virtio-blk.c
@@ -14,7 +14,6 @@
#include "trace.h"
#include "qemu/iov.h"
-#include "event-poll.h"
#include "qemu/thread.h"
#include "qemu/error-report.h"
#include "vring.h"
@@ -22,7 +21,8 @@
#include "migration/migration.h"
#include "block/block.h"
#include "hw/virtio-blk.h"
-#include "virtio-blk.h"
+#include "hw/dataplane/virtio-blk.h"
+#include "block/aio.h"
enum {
SEG_MAX = 126, /* maximum number of I/O segments */
@@ -53,9 +53,14 @@ struct VirtIOBlockDataPlane {
Vring vring; /* virtqueue vring */
EventNotifier *guest_notifier; /* irq */
- EventPoll event_poll; /* event poller */
- EventHandler io_handler; /* Linux AIO completion handler */
- EventHandler notify_handler; /* virtqueue notify handler */
+ /* Note that these EventNotifiers are assigned by value. This is
+ * fine as long as you do not call event_notifier_cleanup on them
+ * (because you don't own the file descriptor or handle; you just
+ * use it).
+ */
+ AioContext *ctx;
+ EventNotifier io_notifier; /* Linux AIO completion */
+ EventNotifier host_notifier; /* doorbell */
IOQueue ioqueue; /* Linux AIO queue (should really be per
dataplane thread) */
@@ -258,10 +263,10 @@ static int process_request(IOQueue *ioq, struct iovec iov[],
}
}
-static void handle_notify(EventHandler *handler)
+static void handle_notify(EventNotifier *e)
{
- VirtIOBlockDataPlane *s = container_of(handler, VirtIOBlockDataPlane,
- notify_handler);
+ VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
+ host_notifier);
/* There is one array of iovecs into which all new requests are extracted
* from the vring. Requests are read from the vring and the translated
@@ -288,6 +293,7 @@ static void handle_notify(EventHandler *handler)
unsigned int out_num = 0, in_num = 0;
unsigned int num_queued;
+ event_notifier_test_and_clear(&s->host_notifier);
for (;;) {
/* Disable guest->host notifies to avoid unnecessary vmexits */
vring_disable_notification(s->vdev, &s->vring);
@@ -336,11 +342,12 @@ static void handle_notify(EventHandler *handler)
}
}
-static void handle_io(EventHandler *handler)
+static void handle_io(EventNotifier *e)
{
- VirtIOBlockDataPlane *s = container_of(handler, VirtIOBlockDataPlane,
- io_handler);
+ VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
+ io_notifier);
+ event_notifier_test_and_clear(&s->io_notifier);
if (ioq_run_completion(&s->ioqueue, complete_request, s) > 0) {
notify_guest(s);
}
@@ -350,7 +357,7 @@ static void handle_io(EventHandler *handler)
* requests.
*/
if (unlikely(vring_more_avail(&s->vring))) {
- handle_notify(&s->notify_handler);
+ handle_notify(&s->host_notifier);
}
}
@@ -359,7 +366,7 @@ static void *data_plane_thread(void *opaque)
VirtIOBlockDataPlane *s = opaque;
do {
- event_poll(&s->event_poll);
+ aio_poll(s->ctx, true);
} while (!s->stopping || s->num_reqs > 0);
return NULL;
}
@@ -447,7 +454,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
return;
}
- event_poll_init(&s->event_poll);
+ s->ctx = aio_context_new();
/* Set up guest notifier (irq) */
if (s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1,
@@ -464,17 +471,16 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
fprintf(stderr, "virtio-blk failed to set host notifier\n");
exit(1);
}
- event_poll_add(&s->event_poll, &s->notify_handler,
- virtio_queue_get_host_notifier(vq),
- handle_notify);
+ s->host_notifier = *virtio_queue_get_host_notifier(vq);
+ aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify, NULL);
/* Set up ioqueue */
ioq_init(&s->ioqueue, s->fd, REQ_MAX);
for (i = 0; i < ARRAY_SIZE(s->requests); i++) {
ioq_put_iocb(&s->ioqueue, &s->requests[i].iocb);
}
- event_poll_add(&s->event_poll, &s->io_handler,
- ioq_get_notifier(&s->ioqueue), handle_io);
+ s->io_notifier = *ioq_get_notifier(&s->ioqueue);
+ aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io, NULL);
s->started = true;
trace_virtio_blk_data_plane_start(s);
@@ -500,15 +506,17 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
qemu_bh_delete(s->start_bh);
s->start_bh = NULL;
} else {
- event_poll_notify(&s->event_poll);
+ aio_notify(s->ctx);
qemu_thread_join(&s->thread);
}
+ aio_set_event_notifier(s->ctx, &s->io_notifier, NULL, NULL);
ioq_cleanup(&s->ioqueue);
+ aio_set_event_notifier(s->ctx, &s->host_notifier, NULL, NULL);
s->vdev->binding->set_host_notifier(s->vdev->binding_opaque, 0, false);
- event_poll_cleanup(&s->event_poll);
+ aio_context_unref(s->ctx);
/* Clean up guest notifier (irq) */
s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1, false);
diff --git a/hw/e1000.c b/hw/e1000.c
index ed37061563..80b6ee3c1a 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -131,6 +131,11 @@ typedef struct E1000State_st {
} eecd_state;
QEMUTimer *autoneg_timer;
+
+/* Compatibility flags for migration to/from qemu 1.3.0 and older */
+#define E1000_FLAG_AUTONEG_BIT 0
+#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT)
+ uint32_t compat_flags;
} E1000State;
#define defreg(x) x = (E1000_##x>>2)
@@ -165,6 +170,14 @@ e1000_link_up(E1000State *s)
static void
set_phy_ctrl(E1000State *s, int index, uint16_t val)
{
+ /*
+ * QEMU 1.3 does not support link auto-negotiation emulation, so if we
+ * migrate during auto negotiation, after migration the link will be
+ * down.
+ */
+ if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+ return;
+ }
if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) {
e1000_link_down(s);
s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE;
@@ -1120,6 +1133,11 @@ static void e1000_pre_save(void *opaque)
{
E1000State *s = opaque;
NetClientState *nc = qemu_get_queue(s->nic);
+
+ if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+ return;
+ }
+
/*
* If link is down and auto-negotiation is ongoing, complete
* auto-negotiation immediately. This allows is to look at
@@ -1141,6 +1159,11 @@ static int e1000_post_load(void *opaque, int version_id)
* to link status bit in mac_reg[STATUS].
* Alternatively, restart link negotiation if it was in progress. */
nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
+
+ if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+ return 0;
+ }
+
if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN &&
s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG &&
!(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) {
@@ -1343,6 +1366,8 @@ static void qdev_e1000_reset(DeviceState *dev)
static Property e1000_properties[] = {
DEFINE_NIC_PROPERTIES(E1000State, conf),
+ DEFINE_PROP_BIT("autonegotiation", E1000State,
+ compat_flags, E1000_FLAG_AUTONEG_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 73a8656df8..0ee3b3b806 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -330,6 +330,10 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
.driver = "virtio-net-pci", \
.property = "mq", \
.value = "off", \
+ }, {\
+ .driver = "e1000",\
+ .property = "autonegotiation",\
+ .value = "off",\
}
static QEMUMachine pc_machine_v1_3 = {
diff --git a/hw/ich9.h b/hw/ich9.h
index 59c25e9aa0..e7d2df7280 100644
--- a/hw/ich9.h
+++ b/hw/ich9.h
@@ -49,6 +49,15 @@ typedef struct ICH9LPCState {
/* 10.1 Chipset Configuration registers(Memory Space)
which is pointed by RCBA */
uint8_t chip_config[ICH9_CC_SIZE];
+
+ /*
+ * 13.7.5 RST_CNT---Reset Control Register (LPC I/F---D31:F0)
+ *
+ * register contents and IO memory region
+ */
+ uint8_t rst_cnt;
+ MemoryRegion rst_cnt_mem;
+
/* isa bus */
ISABus *isa_bus;
MemoryRegion rbca_mem;
@@ -103,6 +112,8 @@ typedef struct ICH9LPCState {
#define ICH9_D2P_A2_REVISION 0x92
+/* D31:F0 LPC Processor Interface */
+#define ICH9_RST_CNT_IOPORT 0xCF9
/* D31:F1 LPC controller */
#define ICH9_A2_LPC "ICH9 A2 LPC"
diff --git a/hw/kvm/arm_gic.c b/hw/kvm/arm_gic.c
new file mode 100644
index 0000000000..22b40b4f84
--- /dev/null
+++ b/hw/kvm/arm_gic.c
@@ -0,0 +1,167 @@
+/*
+ * ARM Generic Interrupt Controller using KVM in-kernel support
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "hw/arm_gic_internal.h"
+
+#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
+#define KVM_ARM_GIC(obj) \
+ OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
+#define KVM_ARM_GIC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC)
+#define KVM_ARM_GIC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC)
+
+typedef struct KVMARMGICClass {
+ ARMGICCommonClass parent_class;
+ DeviceRealize parent_realize;
+ void (*parent_reset)(DeviceState *dev);
+} KVMARMGICClass;
+
+static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
+{
+ /* Meaning of the 'irq' parameter:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * ...
+ * Convert this to the kernel's desired encoding, which
+ * has separate fields in the irq number for type,
+ * CPU number and interrupt number.
+ */
+ GICState *s = (GICState *)opaque;
+ int kvm_irq, irqtype, cpu;
+
+ if (irq < (s->num_irq - GIC_INTERNAL)) {
+ /* External interrupt. The kernel numbers these like the GIC
+ * hardware, with external interrupt IDs starting after the
+ * internal ones.
+ */
+ irqtype = KVM_ARM_IRQ_TYPE_SPI;
+ cpu = 0;
+ irq += GIC_INTERNAL;
+ } else {
+ /* Internal interrupt: decode into (cpu, interrupt id) */
+ irqtype = KVM_ARM_IRQ_TYPE_PPI;
+ irq -= (s->num_irq - GIC_INTERNAL);
+ cpu = irq / GIC_INTERNAL;
+ irq %= GIC_INTERNAL;
+ }
+ kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT)
+ | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq;
+
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+}
+
+static void kvm_arm_gic_put(GICState *s)
+{
+ /* TODO: there isn't currently a kernel interface to set the GIC state */
+}
+
+static void kvm_arm_gic_get(GICState *s)
+{
+ /* TODO: there isn't currently a kernel interface to get the GIC state */
+}
+
+static void kvm_arm_gic_reset(DeviceState *dev)
+{
+ GICState *s = ARM_GIC_COMMON(dev);
+ KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
+
+ kgc->parent_reset(dev);
+ kvm_arm_gic_put(s);
+}
+
+static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ GICState *s = KVM_ARM_GIC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
+
+ kgc->parent_realize(dev, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ i = s->num_irq - GIC_INTERNAL;
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] SPIs
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ i += (GIC_INTERNAL * s->num_cpu);
+ qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
+ /* We never use our outbound IRQ lines but provide them so that
+ * we maintain the same interface as the non-KVM GIC.
+ */
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_irq[i]);
+ }
+ /* Distributor */
+ memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ kvm_arm_register_device(&s->iomem,
+ (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
+ | KVM_VGIC_V2_ADDR_TYPE_DIST);
+ /* CPU interface for current core. Unlike arm_gic, we don't
+ * provide the "interface for core #N" memory regions, because
+ * cores with a VGIC don't have those.
+ */
+ memory_region_init_reservation(&s->cpuiomem[0], "kvm-gic_cpu", 0x1000);
+ sysbus_init_mmio(sbd, &s->cpuiomem[0]);
+ kvm_arm_register_device(&s->cpuiomem[0],
+ (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
+ | KVM_VGIC_V2_ADDR_TYPE_CPU);
+}
+
+static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass);
+ KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass);
+
+ agcc->pre_save = kvm_arm_gic_get;
+ agcc->post_load = kvm_arm_gic_put;
+ kgc->parent_realize = dc->realize;
+ kgc->parent_reset = dc->reset;
+ dc->realize = kvm_arm_gic_realize;
+ dc->reset = kvm_arm_gic_reset;
+ dc->no_user = 1;
+}
+
+static const TypeInfo kvm_arm_gic_info = {
+ .name = TYPE_KVM_ARM_GIC,
+ .parent = TYPE_ARM_GIC_COMMON,
+ .instance_size = sizeof(GICState),
+ .class_init = kvm_arm_gic_class_init,
+ .class_size = sizeof(KVMARMGICClass),
+};
+
+static void kvm_arm_gic_register_types(void)
+{
+ type_register_static(&kvm_arm_gic_info);
+}
+
+type_init(kvm_arm_gic_register_types)
diff --git a/hw/lpc_ich9.c b/hw/lpc_ich9.c
index e55d66a7a4..0ca0a59ef7 100644
--- a/hw/lpc_ich9.c
+++ b/hw/lpc_ich9.c
@@ -466,6 +466,7 @@ static void ich9_lpc_reset(DeviceState *qdev)
ich9_lpc_rcba_update(lpc, rbca_old);
lpc->sci_level = 0;
+ lpc->rst_cnt = 0;
}
static const MemoryRegionOps rbca_mmio_ops = {
@@ -498,6 +499,32 @@ static void ich9_lpc_machine_ready(Notifier *n, void *opaque)
}
}
+/* reset control */
+static void ich9_rst_cnt_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned len)
+{
+ ICH9LPCState *lpc = opaque;
+
+ if (val & 4) {
+ qemu_system_reset_request();
+ return;
+ }
+ lpc->rst_cnt = val & 0xA; /* keep FULL_RST (bit 3) and SYS_RST (bit 1) */
+}
+
+static uint64_t ich9_rst_cnt_read(void *opaque, hwaddr addr, unsigned len)
+{
+ ICH9LPCState *lpc = opaque;
+
+ return lpc->rst_cnt;
+}
+
+static const MemoryRegionOps ich9_rst_cnt_ops = {
+ .read = ich9_rst_cnt_read,
+ .write = ich9_rst_cnt_write,
+ .endianness = DEVICE_LITTLE_ENDIAN
+};
+
static int ich9_lpc_initfn(PCIDevice *d)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
@@ -519,9 +546,32 @@ static int ich9_lpc_initfn(PCIDevice *d)
lpc->machine_ready.notify = ich9_lpc_machine_ready;
qemu_add_machine_init_done_notifier(&lpc->machine_ready);
+ memory_region_init_io(&lpc->rst_cnt_mem, &ich9_rst_cnt_ops, lpc,
+ "lpc-reset-control", 1);
+ memory_region_add_subregion_overlap(pci_address_space_io(d),
+ ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem,
+ 1);
+
return 0;
}
+static bool ich9_rst_cnt_needed(void *opaque)
+{
+ ICH9LPCState *lpc = opaque;
+
+ return (lpc->rst_cnt != 0);
+}
+
+static const VMStateDescription vmstate_ich9_rst_cnt = {
+ .name = "ICH9LPC/rst_cnt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(rst_cnt, ICH9LPCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_ich9_lpc = {
.name = "ICH9LPC",
.version_id = 1,
@@ -535,6 +585,13 @@ static const VMStateDescription vmstate_ich9_lpc = {
VMSTATE_UINT8_ARRAY(chip_config, ICH9LPCState, ICH9_CC_SIZE),
VMSTATE_UINT32(sci_level, ICH9LPCState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &vmstate_ich9_rst_cnt,
+ .needed = ich9_rst_cnt_needed
+ },
+ { 0 }
}
};
diff --git a/hw/macio.c b/hw/macio.c
index 792fa390e6..e91143e331 100644
--- a/hw/macio.c
+++ b/hw/macio.c
@@ -188,7 +188,7 @@ static int macio_newworld_initfn(PCIDevice *d)
sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]);
sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]);
sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]);
- macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x1a);
+ macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a);
ret = qdev_init(DEVICE(&ns->ide[1]));
if (ret < 0) {
return ret;
diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h
index d49f96b5d4..ced1c5f54e 100644
--- a/hw/milkymist-hw.h
+++ b/hw/milkymist-hw.h
@@ -170,22 +170,6 @@ static inline DeviceState *milkymist_ac97_create(hwaddr base,
return dev;
}
-static inline DeviceState *milkymist_minimac_create(hwaddr base,
- qemu_irq rx_irq, qemu_irq tx_irq)
-{
- DeviceState *dev;
-
- qemu_check_nic_model(&nd_table[0], "minimac");
- dev = qdev_create(NULL, "milkymist-minimac");
- qdev_set_nic_properties(dev, &nd_table[0]);
- qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, rx_irq);
- sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, tx_irq);
-
- return dev;
-}
-
static inline DeviceState *milkymist_minimac2_create(hwaddr base,
hwaddr buffers_base, qemu_irq rx_irq, qemu_irq tx_irq)
{
diff --git a/hw/nand.c b/hw/nand.c
index 4176272993..de3e502596 100644
--- a/hw/nand.c
+++ b/hw/nand.c
@@ -46,7 +46,7 @@
# define NAND_IOSTATUS_PLANE1 (1 << 2)
# define NAND_IOSTATUS_PLANE2 (1 << 3)
# define NAND_IOSTATUS_PLANE3 (1 << 4)
-# define NAND_IOSTATUS_BUSY (1 << 6)
+# define NAND_IOSTATUS_READY (1 << 6)
# define NAND_IOSTATUS_UNPROTCT (1 << 7)
# define MAX_PAGE 0x800
@@ -231,6 +231,7 @@ static void nand_reset(DeviceState *dev)
s->iolen = 0;
s->offset = 0;
s->status &= NAND_IOSTATUS_UNPROTCT;
+ s->status |= NAND_IOSTATUS_READY;
}
static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
diff --git a/hw/pc.h b/hw/pc.h
index 03a0277852..dbbd8cde9e 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -216,6 +216,11 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
.driver = "virtio-blk-pci",\
.property = "discard_granularity",\
.value = stringify(0),\
+ },{\
+ .driver = "virtio-serial-pci",\
+ .property = "vectors",\
+ /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\
+ .value = stringify(0xFFFFFFFF),\
}
#endif
diff --git a/hw/pci/pci_host.h b/hw/pci/pci_host.h
index 1845d4dfd5..236cd0f75c 100644
--- a/hw/pci/pci_host.h
+++ b/hw/pci/pci_host.h
@@ -40,7 +40,6 @@ struct PCIHostState {
MemoryRegion conf_mem;
MemoryRegion data_mem;
MemoryRegion mmcfg;
- MemoryRegion *address_space;
uint32_t config_reg;
PCIBus *bus;
};
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index ac33db5cde..e10bc1c6a0 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -244,7 +244,6 @@ static PCIBus *i440fx_common_init(const char *device_name,
dev = qdev_create(NULL, "i440FX-pcihost");
s = PCI_HOST_BRIDGE(dev);
- s->address_space = address_space_mem;
b = pci_bus_new(dev, NULL, pci_address_space,
address_space_io, 0);
s->bus = b;
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index e06dded003..292091180d 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -567,7 +567,6 @@ static void ppc_prep_init(QEMUMachineInitArgs *args)
dev = qdev_create(NULL, "raven-pcihost");
pcihost = PCI_HOST_BRIDGE(dev);
- pcihost->address_space = get_system_memory();
object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
qdev_init_nofail(dev);
pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 163d7a1a48..08787c2a9b 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -1508,6 +1508,10 @@ void scsi_req_unref(SCSIRequest *req)
will start the next chunk or complete the command. */
void scsi_req_continue(SCSIRequest *req)
{
+ if (req->io_canceled) {
+ trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag);
+ return;
+ }
trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
if (req->cmd.mode == SCSI_XFER_TO_DEV) {
req->ops->write_data(req);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 1e128182db..c5c7bf3dfa 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -178,6 +178,9 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+ if (r->req.io_canceled) {
+ goto done;
+ }
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
@@ -223,6 +226,10 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ if (r->req.io_canceled) {
+ goto done;
+ }
+
if (scsi_is_cmd_fua(&r->req.cmd)) {
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
@@ -230,6 +237,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
}
scsi_req_complete(&r->req, GOOD);
+
+done:
if (!r->req.io_canceled) {
scsi_req_unref(&r->req);
}
@@ -243,6 +252,9 @@ static void scsi_dma_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+ if (r->req.io_canceled) {
+ goto done;
+ }
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
@@ -274,6 +286,9 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+ if (r->req.io_canceled) {
+ goto done;
+ }
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
@@ -305,6 +320,9 @@ static void scsi_do_read(void *opaque, int ret)
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
+ if (r->req.io_canceled) {
+ goto done;
+ }
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
@@ -312,10 +330,6 @@ static void scsi_do_read(void *opaque, int ret)
}
}
- if (r->req.io_canceled) {
- return;
- }
-
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
@@ -423,6 +437,9 @@ static void scsi_write_complete(void * opaque, int ret)
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
+ if (r->req.io_canceled) {
+ goto done;
+ }
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
@@ -1478,13 +1495,17 @@ static void scsi_unmap_complete(void *opaque, int ret)
uint32_t nb_sectors;
r->req.aiocb = NULL;
+ if (r->req.io_canceled) {
+ goto done;
+ }
+
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
}
- if (data->count > 0 && !r->req.io_canceled) {
+ if (data->count > 0) {
sector_num = ldq_be_p(&data->inbuf[0]);
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
if (!check_lba_range(s, sector_num, nb_sectors)) {
@@ -1501,10 +1522,9 @@ static void scsi_unmap_complete(void *opaque, int ret)
return;
}
+ scsi_req_complete(&r->req, GOOD);
+
done:
- if (data->count == 0) {
- scsi_req_complete(&r->req, GOOD);
- }
if (!r->req.io_canceled) {
scsi_req_unref(&r->req);
}
diff --git a/hw/serial.c b/hw/serial.c
index 6a83543125..48a5eb62b9 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -256,16 +256,17 @@ static void serial_update_msl(SerialState *s)
qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100);
}
-static void serial_xmit(void *opaque)
+static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
{
SerialState *s = opaque;
- uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock);
if (s->tsr_retry <= 0) {
if (s->fcr & UART_FCR_FE) {
s->tsr = fifo_get(s,XMIT_FIFO);
if (!s->xmit_fifo.count)
s->lsr |= UART_LSR_THRE;
+ } else if ((s->lsr & UART_LSR_THRE)) {
+ return FALSE;
} else {
s->tsr = s->thr;
s->lsr |= UART_LSR_THRE;
@@ -277,30 +278,25 @@ static void serial_xmit(void *opaque)
/* in loopback mode, say that we just received a char */
serial_receive1(s, &s->tsr, 1);
} else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
- if ((s->tsr_retry > 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) {
+ if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
+ qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) {
s->tsr_retry++;
- qemu_mod_timer(s->transmit_timer, new_xmit_ts + s->char_transmit_time);
- return;
- } else if (s->poll_msl < 0) {
- /* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then
- drop any further failed writes instantly, until we get one that goes through.
- This is to prevent guests that log to unconnected pipes or pty's from stalling. */
- s->tsr_retry = -1;
+ return FALSE;
}
- }
- else {
+ s->tsr_retry = 0;
+ } else {
s->tsr_retry = 0;
}
s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
- if (!(s->lsr & UART_LSR_THRE))
- qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time);
if (s->lsr & UART_LSR_THRE) {
s->lsr |= UART_LSR_TEMT;
s->thr_ipending = 1;
serial_update_irq(s);
}
+
+ return FALSE;
}
@@ -330,7 +326,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
s->lsr &= ~UART_LSR_THRE;
serial_update_irq(s);
}
- serial_xmit(s);
+ serial_xmit(NULL, G_IO_OUT, s);
}
break;
case 1:
@@ -684,8 +680,6 @@ void serial_init_core(SerialState *s)
s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s);
- s->transmit_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_xmit, s);
-
qemu_register_reset(serial_reset, s);
qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
diff --git a/hw/serial.h b/hw/serial.h
index 814b8c666f..e884499607 100644
--- a/hw/serial.h
+++ b/hw/serial.h
@@ -72,8 +72,6 @@ struct SerialState {
struct QEMUTimer *fifo_timeout_timer;
int timeout_ipending; /* timeout interrupt pending state */
- struct QEMUTimer *transmit_timer;
-
uint64_t char_transmit_time; /* time to transmit a char in ticks */
int poll_msl;
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 74bb4b81de..702fc728f4 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -48,7 +48,8 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
}
}
-void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
+ bool may_overlap, unsigned priority)
{
assert(n >= 0 && n < dev->num_mmio);
@@ -61,11 +62,29 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
}
dev->mmio[n].addr = addr;
- memory_region_add_subregion(get_system_memory(),
- addr,
- dev->mmio[n].memory);
+ if (may_overlap) {
+ memory_region_add_subregion_overlap(get_system_memory(),
+ addr,
+ dev->mmio[n].memory,
+ priority);
+ }
+ else {
+ memory_region_add_subregion(get_system_memory(),
+ addr,
+ dev->mmio[n].memory);
+ }
}
+void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+{
+ sysbus_mmio_map_common(dev, n, addr, false, 0);
+}
+
+void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
+ unsigned priority)
+{
+ sysbus_mmio_map_common(dev, n, addr, true, priority);
+}
/* Request an IRQ source. The actual IRQ object may be populated later. */
void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
diff --git a/hw/sysbus.h b/hw/sysbus.h
index 17de506339..5d90a52af5 100644
--- a/hw/sysbus.h
+++ b/hw/sysbus.h
@@ -56,6 +56,8 @@ void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size);
void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq);
void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr);
+void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
+ unsigned priority);
void sysbus_add_memory(SysBusDevice *dev, hwaddr addr,
MemoryRegion *mem);
void sysbus_add_memory_overlap(SysBusDevice *dev, hwaddr addr,
diff --git a/hw/vhost.c b/hw/vhost.c
index 2d25d7e300..4d6aee3ecd 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -53,10 +53,14 @@ static void vhost_dev_sync_region(struct vhost_dev *dev,
log = __sync_fetch_and_and(from, 0);
while ((bit = sizeof(log) > sizeof(int) ?
ffsll(log) : ffs(log))) {
- ram_addr_t ram_addr;
+ hwaddr page_addr;
+ hwaddr section_offset;
+ hwaddr mr_offset;
bit -= 1;
- ram_addr = section->offset_within_region + bit * VHOST_LOG_PAGE;
- memory_region_set_dirty(section->mr, ram_addr, VHOST_LOG_PAGE);
+ page_addr = addr + bit * VHOST_LOG_PAGE;
+ section_offset = page_addr - section->offset_within_address_space;
+ mr_offset = section_offset + section->offset_within_region;
+ memory_region_set_dirty(section->mr, mr_offset, VHOST_LOG_PAGE);
log &= ~(0x1ull << bit);
}
addr += VHOST_LOG_CHUNK;
@@ -65,14 +69,21 @@ static void vhost_dev_sync_region(struct vhost_dev *dev,
static int vhost_sync_dirty_bitmap(struct vhost_dev *dev,
MemoryRegionSection *section,
- hwaddr start_addr,
- hwaddr end_addr)
+ hwaddr first,
+ hwaddr last)
{
int i;
+ hwaddr start_addr;
+ hwaddr end_addr;
if (!dev->log_enabled || !dev->started) {
return 0;
}
+ start_addr = section->offset_within_address_space;
+ end_addr = range_get_last(start_addr, section->size);
+ start_addr = MAX(first, start_addr);
+ end_addr = MIN(last, end_addr);
+
for (i = 0; i < dev->mem->nregions; ++i) {
struct vhost_memory_region *reg = dev->mem->regions + i;
vhost_dev_sync_region(dev, section, start_addr, end_addr,
@@ -93,10 +104,18 @@ static void vhost_log_sync(MemoryListener *listener,
{
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
memory_listener);
- hwaddr start_addr = section->offset_within_address_space;
- hwaddr end_addr = start_addr + section->size;
+ vhost_sync_dirty_bitmap(dev, section, 0x0, ~0x0ULL);
+}
- vhost_sync_dirty_bitmap(dev, section, start_addr, end_addr);
+static void vhost_log_sync_range(struct vhost_dev *dev,
+ hwaddr first, hwaddr last)
+{
+ int i;
+ /* FIXME: this is N^2 in number of sections */
+ for (i = 0; i < dev->n_mem_sections; ++i) {
+ MemoryRegionSection *section = &dev->mem_sections[i];
+ vhost_sync_dirty_bitmap(dev, section, first, last);
+ }
}
/* Assign/unassign. Keep an unsorted array of non-overlapping
@@ -268,16 +287,15 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
{
vhost_log_chunk_t *log;
uint64_t log_base;
- int r, i;
+ int r;
log = g_malloc0(size * sizeof *log);
log_base = (uint64_t)(unsigned long)log;
r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base);
assert(r >= 0);
- for (i = 0; i < dev->n_mem_sections; ++i) {
- /* Sync only the range covered by the old log */
- vhost_sync_dirty_bitmap(dev, &dev->mem_sections[i], 0,
- dev->log_size * VHOST_LOG_CHUNK - 1);
+ /* Sync only the range covered by the old log */
+ if (dev->log_size) {
+ vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1);
}
if (dev->log) {
g_free(dev->log);
@@ -1014,10 +1032,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
hdev->vqs + i,
hdev->vq_index + i);
}
- for (i = 0; i < hdev->n_mem_sections; ++i) {
- vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i],
- 0, (hwaddr)~0x0ull);
- }
+ vhost_log_sync_range(hdev, 0, ~0x0ull);
hdev->started = false;
g_free(hdev->log);
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 248a966357..6b69236655 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -36,6 +36,7 @@ typedef struct VirtIOBlock
VirtIOBlkConf *blk;
unsigned short sector_mask;
DeviceState *qdev;
+ VMChangeStateEntry *change;
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
VirtIOBlockDataPlane *dataplane;
#endif
@@ -681,7 +682,7 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk)
}
#endif
- qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
+ s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
s->qdev = dev;
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
virtio_blk_save, virtio_blk_load, s);
@@ -702,6 +703,7 @@ void virtio_blk_exit(VirtIODevice *vdev)
virtio_blk_data_plane_destroy(s->dataplane);
s->dataplane = NULL;
#endif
+ qemu_del_vm_change_state_handler(s->change);
unregister_savevm(s->qdev, "virtio-blk", s);
blockdev_mark_auto_del(s->bs);
virtio_cleanup(vdev);
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index b10f5f08d4..e2d1c58d9d 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -20,6 +20,18 @@ typedef struct VirtConsole {
CharDriverState *chr;
} VirtConsole;
+/*
+ * Callback function that's called from chardevs when backend becomes
+ * writable.
+ */
+static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
+ void *opaque)
+{
+ VirtConsole *vcon = opaque;
+
+ virtio_serial_throttle_port(&vcon->port, false);
+ return FALSE;
+}
/* Callback function that's called when the guest sends us data */
static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
@@ -35,19 +47,21 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
ret = qemu_chr_fe_write(vcon->chr, buf, len);
trace_virtio_console_flush_buf(port->id, len, ret);
- if (ret < 0) {
+ if (ret <= 0) {
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
/*
* Ideally we'd get a better error code than just -1, but
* that's what the chardev interface gives us right now. If
* we had a finer-grained message, like -EPIPE, we could close
- * this connection. Absent such error messages, the most we
- * can do is to return 0 here.
- *
- * This will prevent stray -1 values to go to
- * virtio-serial-bus.c and cause abort()s in
- * do_flush_queued_data().
+ * this connection.
*/
ret = 0;
+ if (!k->is_console) {
+ virtio_serial_throttle_port(port, true);
+ qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
+ vcon);
+ }
}
return ret;
}
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 0ad96eefd6..8c9d8713f3 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -44,7 +44,7 @@ typedef struct VirtIONet
VirtIODevice vdev;
uint8_t mac[ETH_ALEN];
uint16_t status;
- VirtIONetQueue vqs[MAX_QUEUE_NUM];
+ VirtIONetQueue *vqs;
VirtQueue *ctrl_vq;
NICState *nic;
uint32_t tx_timeout;
@@ -1326,8 +1326,9 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
n->vdev.set_status = virtio_net_set_status;
n->vdev.guest_notifier_mask = virtio_net_guest_notifier_mask;
n->vdev.guest_notifier_pending = virtio_net_guest_notifier_pending;
+ n->max_queues = MAX(conf->queues, 1);
+ n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
n->vqs[0].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
- n->max_queues = conf->queues;
n->curr_queues = 1;
n->vqs[0].n = n;
n->tx_timeout = net->txtimer;
@@ -1412,6 +1413,7 @@ void virtio_net_exit(VirtIODevice *vdev)
}
}
+ g_free(n->vqs);
qemu_del_nic(n->nic);
virtio_cleanup(&n->vdev);
}
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index ba4d7d5424..39c1966cfc 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -975,6 +975,9 @@ static int virtio_serial_init_pci(PCIDevice *pci_dev)
if (!vdev) {
return -1;
}
+
+ /* backwards-compatibility with machines that were created with
+ DEV_NVECTORS_UNSPECIFIED */
vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
? proxy->serial.max_virtserial_ports + 1
: proxy->nvectors;
@@ -1155,7 +1158,7 @@ static const TypeInfo virtio_net_info = {
static Property virtio_serial_properties[] = {
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
- DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31),
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index ada1d0100b..7d0515f551 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -172,24 +172,7 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
port->elem.out_sg[i].iov_base
+ port->iov_offset,
buf_size);
- if (ret < 0 && ret != -EAGAIN) {
- /* We don't handle any other type of errors here */
- abort();
- }
- if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) {
- /*
- * this is a temporary check until chardevs can signal to
- * frontends that they are writable again. This prevents
- * the console from going into throttled mode (forever)
- * if virtio-console is connected to a pty without a
- * listener. Otherwise the guest spins forever.
- * We can revert this if
- * 1: chardevs can notify frondends
- * 2: the guest driver does not spin in these cases
- */
- if (!vsc->is_console) {
- virtio_serial_throttle_port(port, true);
- }
+ if (port->throttled) {
port->iov_idx = i;
if (ret > 0) {
port->iov_offset += ret;