diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/a15mpcore.c | 8 | ||||
-rw-r--r-- | hw/arm/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/arm/pic_cpu.c | 26 | ||||
-rw-r--r-- | hw/arm_gic.c | 23 | ||||
-rw-r--r-- | hw/arm_gic_common.c | 36 | ||||
-rw-r--r-- | hw/arm_gic_internal.h | 4 | ||||
-rw-r--r-- | hw/armv7m_nvic.c | 15 | ||||
-rw-r--r-- | hw/dataplane/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/dataplane/event-poll.c | 100 | ||||
-rw-r--r-- | hw/dataplane/event-poll.h | 40 | ||||
-rw-r--r-- | hw/dataplane/virtio-blk.c | 50 | ||||
-rw-r--r-- | hw/e1000.c | 25 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 4 | ||||
-rw-r--r-- | hw/ich9.h | 11 | ||||
-rw-r--r-- | hw/kvm/arm_gic.c | 167 | ||||
-rw-r--r-- | hw/lpc_ich9.c | 57 | ||||
-rw-r--r-- | hw/macio.c | 2 | ||||
-rw-r--r-- | hw/milkymist-hw.h | 16 | ||||
-rw-r--r-- | hw/nand.c | 3 | ||||
-rw-r--r-- | hw/pc.h | 5 | ||||
-rw-r--r-- | hw/pci/pci_host.h | 1 | ||||
-rw-r--r-- | hw/piix_pci.c | 1 | ||||
-rw-r--r-- | hw/ppc/prep.c | 1 | ||||
-rw-r--r-- | hw/scsi-bus.c | 4 | ||||
-rw-r--r-- | hw/scsi-disk.c | 36 | ||||
-rw-r--r-- | hw/serial.c | 28 | ||||
-rw-r--r-- | hw/serial.h | 2 | ||||
-rw-r--r-- | hw/sysbus.c | 27 | ||||
-rw-r--r-- | hw/sysbus.h | 2 | ||||
-rw-r--r-- | hw/vhost.c | 49 | ||||
-rw-r--r-- | hw/virtio-blk.c | 4 | ||||
-rw-r--r-- | hw/virtio-console.c | 28 | ||||
-rw-r--r-- | hw/virtio-net.c | 6 | ||||
-rw-r--r-- | hw/virtio-pci.c | 5 | ||||
-rw-r--r-- | hw/virtio-serial-bus.c | 19 |
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 = { @@ -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) { @@ -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) @@ -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; |