From 2eeb502df74cb5bed630c4d3b0e5841da58fe459 Mon Sep 17 00:00:00 2001 From: Justin M. Forbes Date: Apr 15 2010 18:15:44 +0000 Subject: Update virtio console patches from upstream --- diff --git a/0052-virtio-console-patches.patch b/0052-virtio-console-patches.patch new file mode 100644 index 0000000..c8570d9 --- /dev/null +++ b/0052-virtio-console-patches.patch @@ -0,0 +1,936 @@ +From 8fe39316da28bdff610da708148f87b21e7ba045 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Tue, 6 Apr 2010 23:36:50 +0530 +Subject: [PATCH] virtio-console patches + +Hey Justin, + +Attached are the kernel and qemu patches for the new abi. Rusty is ok +with the kernel patches and Juan and Gerd are ok with the userspace +ones. + +The kernel one is big because of some code movement. + +The qemu one is big because of code being moved and some fixes being +applied to make the host less vulnerable to malicious guests. + +There's also a patch at + +http://lkml.org/lkml/2010/4/6/110 + +that we should apply (affects the virt-console-fix-race.patch in the +repo). + +Please let me know if you need any more information! + +Thanks, + Amit + +Content-Disposition: attachment; filename=",qemu-virtio-serial-rollup-2.patch" +--- + Makefile.hw | 1 + + hw/iov.c | 70 ++++++++++ + hw/iov.h | 19 +++ + hw/virtio-balloon.c | 31 +---- + hw/virtio-console.c | 11 +- + hw/virtio-net.c | 20 +--- + hw/virtio-serial-bus.c | 332 ++++++++++++++++++++++++++++++++++++------------ + hw/virtio-serial.h | 34 ++++-- + 8 files changed, 372 insertions(+), 146 deletions(-) + create mode 100644 hw/iov.c + create mode 100644 hw/iov.h + +diff --git a/Makefile.hw b/Makefile.hw +index 43ca541..079c5d2 100644 +--- a/Makefile.hw ++++ b/Makefile.hw +@@ -13,6 +13,7 @@ QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu + + obj-y = + obj-y += loader.o ++obj-y += iov.o + obj-y += virtio.o virtio-console.o + obj-y += fw_cfg.o + obj-y += watchdog.o +diff --git a/hw/iov.c b/hw/iov.c +new file mode 100644 +index 0000000..588cd04 +--- /dev/null ++++ b/hw/iov.c +@@ -0,0 +1,70 @@ ++/* ++ * Helpers for getting linearized buffers from iov / filling buffers into iovs ++ * ++ * Copyright IBM, Corp. 2007, 2008 ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Anthony Liguori ++ * Amit Shah ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include "iov.h" ++ ++size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, ++ const void *buf, size_t size) ++{ ++ size_t offset; ++ unsigned int i; ++ ++ offset = 0; ++ for (i = 0; offset < size && i < iovcnt; i++) { ++ size_t len; ++ ++ len = MIN(iov[i].iov_len, size - offset); ++ ++ memcpy(iov[i].iov_base, buf + offset, len); ++ offset += len; ++ } ++ return offset; ++} ++ ++size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, ++ void *buf, size_t offset, size_t size) ++{ ++ uint8_t *ptr; ++ size_t iov_off, buf_off; ++ unsigned int i; ++ ++ ptr = buf; ++ iov_off = 0; ++ buf_off = 0; ++ for (i = 0; i < iovcnt && size; i++) { ++ if (offset < (iov_off + iov[i].iov_len)) { ++ size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); ++ ++ memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len); ++ ++ buf_off += len; ++ offset += len; ++ size -= len; ++ } ++ iov_off += iov[i].iov_len; ++ } ++ return buf_off; ++} ++ ++size_t iov_size(const struct iovec *iov, const unsigned int iovcnt) ++{ ++ size_t len; ++ unsigned int i; ++ ++ len = 0; ++ for (i = 0; i < iovcnt; i++) { ++ len += iov[i].iov_len; ++ } ++ return len; ++} +diff --git a/hw/iov.h b/hw/iov.h +new file mode 100644 +index 0000000..60a8547 +--- /dev/null ++++ b/hw/iov.h +@@ -0,0 +1,19 @@ ++/* ++ * Helpers for getting linearized buffers from iov / filling buffers into iovs ++ * ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Amit Shah ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu-common.h" ++ ++size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, ++ const void *buf, size_t size); ++size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, ++ void *buf, size_t offset, size_t size); ++size_t iov_size(const struct iovec *iov, const unsigned int iovcnt); +diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c +index 242c6c8..10e2647 100644 +--- a/hw/virtio-balloon.c ++++ b/hw/virtio-balloon.c +@@ -11,6 +11,7 @@ + * + */ + ++#include "iov.h" + #include "qemu-common.h" + #include "virtio.h" + #include "pc.h" +@@ -47,33 +48,6 @@ static void balloon_page(void *addr, int deflate) + #endif + } + +-/* FIXME: once we do a virtio refactoring, this will get subsumed into common +- * code */ +-static size_t memcpy_from_iovector(void *data, size_t offset, size_t size, +- struct iovec *iov, int iovlen) +-{ +- int i; +- uint8_t *ptr = data; +- size_t iov_off = 0; +- size_t data_off = 0; +- +- for (i = 0; i < iovlen && size; i++) { +- if (offset < (iov_off + iov[i].iov_len)) { +- size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); +- +- memcpy(ptr + data_off, iov[i].iov_base + (offset - iov_off), len); +- +- data_off += len; +- offset += len; +- size -= len; +- } +- +- iov_off += iov[i].iov_len; +- } +- +- return data_off; +-} +- + static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) + { + VirtIOBalloon *s = to_virtio_balloon(vdev); +@@ -83,8 +57,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) + size_t offset = 0; + uint32_t pfn; + +- while (memcpy_from_iovector(&pfn, offset, 4, +- elem.out_sg, elem.out_num) == 4) { ++ while (iov_to_buf(elem.out_sg, elem.out_num, &pfn, offset, 4) == 4) { + ram_addr_t pa; + ram_addr_t addr; + +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index bd44ec6..caea11f 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -1,7 +1,7 @@ + /* + * Virtio Console and Generic Serial Port Devices + * +- * Copyright Red Hat, Inc. 2009 ++ * Copyright Red Hat, Inc. 2009, 2010 + * + * Authors: + * Amit Shah +@@ -20,14 +20,11 @@ typedef struct VirtConsole { + + + /* Callback function that's called when the guest sends us data */ +-static size_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) ++static void flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) + { + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); +- ssize_t ret; + +- ret = qemu_chr_write(vcon->chr, buf, len); +- +- return ret < 0 ? 0 : ret; ++ qemu_chr_write(vcon->chr, buf, len); + } + + /* Readiness of the guest to accept data on a port */ +@@ -99,6 +96,7 @@ static VirtIOSerialPortInfo virtconsole_info = { + .exit = virtconsole_exitfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console, 1), ++ DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_CHR("chardev", VirtConsole, chr), + DEFINE_PROP_STRING("name", VirtConsole, port.name), + DEFINE_PROP_END_OF_LIST(), +@@ -133,6 +131,7 @@ static VirtIOSerialPortInfo virtserialport_info = { + .init = virtserialport_initfn, + .exit = virtconsole_exitfn, + .qdev.props = (Property[]) { ++ DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_CHR("chardev", VirtConsole, chr), + DEFINE_PROP_STRING("name", VirtConsole, port.name), + DEFINE_PROP_END_OF_LIST(), +diff --git a/hw/virtio-net.c b/hw/virtio-net.c +index f8e228f..320e99f 100644 +--- a/hw/virtio-net.c ++++ b/hw/virtio-net.c +@@ -11,6 +11,7 @@ + * + */ + ++#include "iov.h" + #include "virtio.h" + #include "net.h" + #include "net/checksum.h" +@@ -436,21 +437,6 @@ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, + } + } + +-static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) +-{ +- int offset, i; +- +- offset = i = 0; +- while (offset < count && i < iovcnt) { +- int len = MIN(iov[i].iov_len, count - offset); +- memcpy(iov[i].iov_base, buf + offset, len); +- offset += len; +- i++; +- } +- +- return offset; +-} +- + static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, + const void *buf, size_t size, size_t hdr_len) + { +@@ -586,8 +572,8 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ + } + + /* copy in packet. ugh */ +- len = iov_fill(sg, elem.in_num, +- buf + offset, size - offset); ++ len = iov_from_buf(sg, elem.in_num, ++ buf + offset, size - offset); + total += len; + + /* signal other side */ +diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c +index d0e0219..6245f6e 100644 +--- a/hw/virtio-serial-bus.c ++++ b/hw/virtio-serial-bus.c +@@ -1,7 +1,7 @@ + /* + * A bus for connecting virtio serial and console ports + * +- * Copyright (C) 2009 Red Hat, Inc. ++ * Copyright (C) 2009, 2010 Red Hat, Inc. + * + * Author(s): + * Amit Shah +@@ -15,6 +15,7 @@ + * the COPYING file in the top-level directory. + */ + ++#include "iov.h" + #include "monitor.h" + #include "qemu-queue.h" + #include "sysbus.h" +@@ -41,6 +42,10 @@ struct VirtIOSerial { + VirtIOSerialBus *bus; + + QTAILQ_HEAD(, VirtIOSerialPort) ports; ++ ++ /* bitmap for identifying active ports */ ++ uint32_t *ports_map; ++ + struct virtio_console_config config; + }; + +@@ -48,6 +53,10 @@ static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) + { + VirtIOSerialPort *port; + ++ if (id == VIRTIO_CONSOLE_BAD_ID) { ++ return NULL; ++ } ++ + QTAILQ_FOREACH(port, &vser->ports, next) { + if (port->id == id) + return port; +@@ -76,30 +85,25 @@ static size_t write_to_port(VirtIOSerialPort *port, + { + VirtQueueElement elem; + VirtQueue *vq; +- size_t offset = 0; +- size_t len = 0; ++ size_t offset; + + vq = port->ivq; + if (!virtio_queue_ready(vq)) { + return 0; + } +- if (!size) { +- return 0; +- } + ++ offset = 0; + while (offset < size) { +- int i; ++ size_t len; + + if (!virtqueue_pop(vq, &elem)) { + break; + } + +- for (i = 0; offset < size && i < elem.in_num; i++) { +- len = MIN(elem.in_sg[i].iov_len, size - offset); ++ len = iov_from_buf(elem.in_sg, elem.in_num, ++ buf + offset, size - offset); ++ offset += len; + +- memcpy(elem.in_sg[i].iov_base, buf + offset, len); +- offset += len; +- } + virtqueue_push(vq, &elem, len); + } + +@@ -107,6 +111,29 @@ static size_t write_to_port(VirtIOSerialPort *port, + return offset; + } + ++static void flush_queued_data(VirtIOSerialPort *port, bool discard) ++{ ++ VirtQueue *vq; ++ VirtQueueElement elem; ++ ++ vq = port->ovq; ++ while (virtqueue_pop(vq, &elem)) { ++ uint8_t *buf; ++ size_t ret, buf_size; ++ ++ if (!discard) { ++ buf_size = iov_size(elem.out_sg, elem.out_num); ++ buf = qemu_malloc(buf_size); ++ ret = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, buf_size); ++ ++ port->info->have_data(port, buf, ret); ++ qemu_free(buf); ++ } ++ virtqueue_push(vq, &elem, 0); ++ } ++ virtio_notify(&port->vser->vdev, vq); ++} ++ + static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) + { + VirtQueueElement elem; +@@ -158,6 +185,13 @@ int virtio_serial_open(VirtIOSerialPort *port) + int virtio_serial_close(VirtIOSerialPort *port) + { + port->host_connected = false; ++ /* ++ * If there's any data the guest sent which the app didn't ++ * consume, discard it and reset the throttling flag. ++ */ ++ flush_queued_data(port, true); ++ port->throttled = false; ++ + send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + + return 0; +@@ -199,8 +233,23 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port) + return 0; + } + ++void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) ++{ ++ if (!port) { ++ return; ++ } ++ ++ if (throttle) { ++ port->throttled = true; ++ return; ++ } ++ ++ port->throttled = false; ++ flush_queued_data(port, false); ++} ++ + /* Guest wants to notify us of some event */ +-static void handle_control_message(VirtIOSerial *vser, void *buf) ++static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) + { + struct VirtIOSerialPort *port; + struct virtio_console_control cpkt, *gcpkt; +@@ -208,15 +257,41 @@ static void handle_control_message(VirtIOSerial *vser, void *buf) + size_t buffer_len; + + gcpkt = buf; +- port = find_port_by_id(vser, ldl_p(&gcpkt->id)); +- if (!port) ++ ++ if (len < sizeof(cpkt)) { ++ /* The guest sent an invalid control packet */ + return; ++ } + + cpkt.event = lduw_p(&gcpkt->event); + cpkt.value = lduw_p(&gcpkt->value); + ++ port = find_port_by_id(vser, ldl_p(&gcpkt->id)); ++ if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY) ++ return; ++ + switch(cpkt.event) { ++ case VIRTIO_CONSOLE_DEVICE_READY: ++ if (!cpkt.value) { ++ qemu_error("virtio-serial-bus: Guest failure in adding device %s\n", ++ vser->bus->qbus.name); ++ break; ++ } ++ /* ++ * The device is up, we can now tell the device about all the ++ * ports we have here. ++ */ ++ QTAILQ_FOREACH(port, &vser->ports, next) { ++ send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1); ++ } ++ break; ++ + case VIRTIO_CONSOLE_PORT_READY: ++ if (!cpkt.value) { ++ qemu_error("virtio-serial-bus: Guest failure in adding port %u for device %s\n", ++ port->id, vser->bus->qbus.name); ++ break; ++ } + /* + * Now that we know the guest asked for the port name, we're + * sure the guest has initialised whatever state is necessary +@@ -281,12 +356,35 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) + { + VirtQueueElement elem; + VirtIOSerial *vser; ++ uint8_t *buf; ++ size_t len; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + ++ len = 0; ++ buf = NULL; + while (virtqueue_pop(vq, &elem)) { +- handle_control_message(vser, elem.out_sg[0].iov_base); +- virtqueue_push(vq, &elem, elem.out_sg[0].iov_len); ++ size_t cur_len, copied; ++ ++ cur_len = iov_size(elem.out_sg, elem.out_num); ++ /* ++ * Allocate a new buf only if we didn't have one previously or ++ * if the size of the buf differs ++ */ ++ if (cur_len > len) { ++ if (len) { ++ qemu_free(buf); ++ } ++ buf = qemu_malloc(cur_len); ++ len = cur_len; ++ } ++ copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len); ++ ++ handle_control_message(vser, buf, copied); ++ virtqueue_push(vq, &elem, 0); ++ } ++ if (len) { ++ qemu_free(buf); + } + virtio_notify(vdev, vq); + } +@@ -295,38 +393,22 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) + static void handle_output(VirtIODevice *vdev, VirtQueue *vq) + { + VirtIOSerial *vser; +- VirtQueueElement elem; ++ VirtIOSerialPort *port; ++ bool discard; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); ++ port = find_port_by_vq(vser, vq); + +- while (virtqueue_pop(vq, &elem)) { +- VirtIOSerialPort *port; +- size_t ret; +- +- port = find_port_by_vq(vser, vq); +- if (!port) { +- ret = 0; +- goto next_buf; +- } +- +- /* +- * A port may not have any handler registered for consuming the +- * data that the guest sends or it may not have a chardev associated +- * with it. Just ignore the data in that case. +- */ +- if (!port->info->have_data) { +- ret = 0; +- goto next_buf; +- } +- +- /* The guest always sends only one sg */ +- ret = port->info->have_data(port, elem.out_sg[0].iov_base, +- elem.out_sg[0].iov_len); ++ discard = false; ++ if (!port || !port->host_connected || !port->info->have_data) { ++ discard = true; ++ } + +- next_buf: +- virtqueue_push(vq, &elem, ret); ++ if (!discard && port->throttled) { ++ return; + } +- virtio_notify(vdev, vq); ++ ++ flush_queued_data(port, discard); + } + + static void handle_input(VirtIODevice *vdev, VirtQueue *vq) +@@ -335,7 +417,10 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) + + static uint32_t get_features(VirtIODevice *vdev, uint32_t features) + { +- VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); ++ VirtIOSerial *vser; ++ ++ vser = DO_UPCAST(VirtIOSerial, vdev, vdev); ++ + if (vser->bus->max_nr_ports > 1) { + features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); + } +@@ -370,14 +455,20 @@ static void virtio_serial_save(QEMUFile *f, void *opaque) + /* The config space */ + qemu_put_be16s(f, &s->config.cols); + qemu_put_be16s(f, &s->config.rows); +- qemu_put_be32s(f, &s->config.nr_ports); + +- /* Items in struct VirtIOSerial */ ++ qemu_put_be32s(f, &s->config.max_nr_ports); ++ ++ /* The ports map */ ++ ++ qemu_put_buffer(f, (uint8_t *)s->ports_map, ++ sizeof(uint32_t) * (s->config.max_nr_ports + 31) / 32); ++ ++ /* Ports */ + +- /* Do this because we might have hot-unplugged some ports */ + nr_active_ports = 0; +- QTAILQ_FOREACH(port, &s->ports, next) ++ QTAILQ_FOREACH(port, &s->ports, next) { + nr_active_ports++; ++ } + + qemu_put_be32s(f, &nr_active_ports); + +@@ -385,13 +476,9 @@ static void virtio_serial_save(QEMUFile *f, void *opaque) + * Items in struct VirtIOSerialPort. + */ + QTAILQ_FOREACH(port, &s->ports, next) { +- /* +- * We put the port number because we may not have an active +- * port at id 0 that's reserved for a console port, or in case +- * of ports that might have gotten unplugged +- */ + qemu_put_be32s(f, &port->id); + qemu_put_byte(f, port->guest_connected); ++ qemu_put_byte(f, port->host_connected); + } + } + +@@ -399,7 +486,8 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) + { + VirtIOSerial *s = opaque; + VirtIOSerialPort *port; +- uint32_t nr_active_ports; ++ size_t ports_map_size; ++ uint32_t max_nr_ports, nr_active_ports, *ports_map; + unsigned int i; + + if (version_id > 2) { +@@ -416,22 +504,50 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) + /* The config space */ + qemu_get_be16s(f, &s->config.cols); + qemu_get_be16s(f, &s->config.rows); +- s->config.nr_ports = qemu_get_be32(f); + +- /* Items in struct VirtIOSerial */ ++ qemu_get_be32s(f, &max_nr_ports); ++ if (max_nr_ports > s->config.max_nr_ports) { ++ /* Source could have had more ports than us. Fail migration. */ ++ return -EINVAL; ++ } ++ ++ ports_map_size = sizeof(uint32_t) * (max_nr_ports + 31) / 32; ++ ports_map = qemu_malloc(ports_map_size); ++ qemu_get_buffer(f, (uint8_t *)ports_map, ports_map_size); ++ ++ for (i = 0; i < (max_nr_ports + 31) / 32; i++) { ++ if (ports_map[i] != s->ports_map[i]) { ++ /* ++ * Ports active on source and destination don't ++ * match. Fail migration. ++ */ ++ qemu_free(ports_map); ++ return -EINVAL; ++ } ++ } ++ qemu_free(ports_map); + + qemu_get_be32s(f, &nr_active_ports); + + /* Items in struct VirtIOSerialPort */ + for (i = 0; i < nr_active_ports; i++) { + uint32_t id; ++ bool host_connected; + + id = qemu_get_be32(f); + port = find_port_by_id(s, id); + + port->guest_connected = qemu_get_byte(f); ++ host_connected = qemu_get_byte(f); ++ if (host_connected != port->host_connected) { ++ /* ++ * We have to let the guest know of the host connection ++ * status change ++ */ ++ send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, ++ port->host_connected); ++ } + } +- + return 0; + } + +@@ -466,6 +582,54 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) + indent, "", port->host_connected); + } + ++/* This function is only used if a port id is not provided by the user */ ++static uint32_t find_free_port_id(VirtIOSerial *vser) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) { ++ uint32_t map, bit; ++ ++ map = vser->ports_map[i]; ++ bit = ffs(~map); ++ if (bit) { ++ return (bit - 1) + i * 32; ++ } ++ } ++ return VIRTIO_CONSOLE_BAD_ID; ++} ++ ++static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) ++{ ++ unsigned int i; ++ ++ i = port_id / 32; ++ vser->ports_map[i] |= 1U << (port_id % 32); ++} ++ ++static void add_port(VirtIOSerial *vser, uint32_t port_id) ++{ ++ mark_port_added(vser, port_id); ++ ++ send_control_event(find_port_by_id(vser, port_id), ++ VIRTIO_CONSOLE_PORT_ADD, 1); ++} ++ ++static void remove_port(VirtIOSerial *vser, uint32_t port_id) ++{ ++ VirtIOSerialPort *port; ++ unsigned int i; ++ ++ i = port_id / 32; ++ vser->ports_map[i] &= ~(1U << (port_id % 32)); ++ ++ port = find_port_by_id(vser, port_id); ++ /* Flush out any unconsumed buffers first */ ++ flush_queued_data(port, true); ++ ++ send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1); ++} ++ + static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) + { + VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); +@@ -484,19 +648,36 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) + */ + plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0); + +- if (port->vser->config.nr_ports == bus->max_nr_ports && !plugging_port0) { +- qemu_error("virtio-serial-bus: Maximum device limit reached\n"); ++ if (find_port_by_id(port->vser, port->id)) { ++ qemu_error("virtio-serial-bus: A port already exists at id %u\n", ++ port->id); + return -1; + } +- dev->info = info; + ++ if (port->id == VIRTIO_CONSOLE_BAD_ID) { ++ if (plugging_port0) { ++ port->id = 0; ++ } else { ++ port->id = find_free_port_id(port->vser); ++ if (port->id == VIRTIO_CONSOLE_BAD_ID) { ++ qemu_error("virtio-serial-bus: Maximum port limit for this device reached\n"); ++ return -1; ++ } ++ } ++ } ++ ++ if (port->id >= port->vser->config.max_nr_ports) { ++ qemu_error("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n", ++ port->vser->config.max_nr_ports - 1); ++ return -1; ++ } ++ ++ dev->info = info; + ret = info->init(dev); + if (ret) { + return ret; + } + +- port->id = plugging_port0 ? 0 : port->vser->config.nr_ports++; +- + if (!use_multiport(port->vser)) { + /* + * Allow writes to guest in this case; we have no way of +@@ -509,6 +690,8 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) + port->ivq = port->vser->ivqs[port->id]; + port->ovq = port->vser->ovqs[port->id]; + ++ add_port(port->vser, port->id); ++ + /* Send an update to the guest about this new port added */ + virtio_notify_config(&port->vser->vdev); + +@@ -521,26 +704,8 @@ static int virtser_port_qdev_exit(DeviceState *qdev) + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtIOSerial *vser = port->vser; + +- send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1); ++ remove_port(port->vser, port->id); + +- /* +- * Don't decrement nr_ports here; thus we keep a linearly +- * increasing port id. Not utilising an id again saves us a couple +- * of complications: +- * +- * - Not having to bother about sending the port id to the guest +- * kernel on hotplug or on addition of new ports; the guest can +- * also linearly increment the port number. This is preferable +- * because the config space won't have the need to store a +- * ports_map. +- * +- * - Extra state to be stored for all the "holes" that got created +- * so that we keep filling in the ids from the least available +- * index. +- * +- * When such a functionality is desired, a control message to add +- * a port can be introduced. +- */ + QTAILQ_REMOVE(&vser->ports, port, next); + + if (port->info->exit) +@@ -600,11 +765,12 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) + } + + vser->config.max_nr_ports = max_nr_ports; ++ vser->ports_map = qemu_mallocz((max_nr_ports + 31) / 32); + /* + * Reserve location 0 for a console port for backward compat + * (old kernel, new qemu) + */ +- vser->config.nr_ports = 1; ++ mark_port_added(vser, 0); + + vser->vdev.get_features = get_features; + vser->vdev.get_config = get_config; +diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h +index f297b00..a93b545 100644 +--- a/hw/virtio-serial.h ++++ b/hw/virtio-serial.h +@@ -2,7 +2,7 @@ + * Virtio Serial / Console Support + * + * Copyright IBM, Corp. 2008 +- * Copyright Red Hat, Inc. 2009 ++ * Copyright Red Hat, Inc. 2009, 2010 + * + * Authors: + * Christian Ehrhardt +@@ -27,6 +27,8 @@ + /* Features supported */ + #define VIRTIO_CONSOLE_F_MULTIPORT 1 + ++#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) ++ + struct virtio_console_config { + /* + * These two fields are used by VIRTIO_CONSOLE_F_SIZE which +@@ -36,7 +38,6 @@ struct virtio_console_config { + uint16_t rows; + + uint32_t max_nr_ports; +- uint32_t nr_ports; + } __attribute__((packed)); + + struct virtio_console_control { +@@ -46,12 +47,14 @@ struct virtio_console_control { + }; + + /* Some events for the internal messages (control packets) */ +-#define VIRTIO_CONSOLE_PORT_READY 0 +-#define VIRTIO_CONSOLE_CONSOLE_PORT 1 +-#define VIRTIO_CONSOLE_RESIZE 2 +-#define VIRTIO_CONSOLE_PORT_OPEN 3 +-#define VIRTIO_CONSOLE_PORT_NAME 4 +-#define VIRTIO_CONSOLE_PORT_REMOVE 5 ++#define VIRTIO_CONSOLE_DEVICE_READY 0 ++#define VIRTIO_CONSOLE_PORT_ADD 1 ++#define VIRTIO_CONSOLE_PORT_REMOVE 2 ++#define VIRTIO_CONSOLE_PORT_READY 3 ++#define VIRTIO_CONSOLE_CONSOLE_PORT 4 ++#define VIRTIO_CONSOLE_RESIZE 5 ++#define VIRTIO_CONSOLE_PORT_OPEN 6 ++#define VIRTIO_CONSOLE_PORT_NAME 7 + + /* == In-qemu interface == */ + +@@ -107,6 +110,8 @@ struct VirtIOSerialPort { + bool guest_connected; + /* Is this device open for IO on the host? */ + bool host_connected; ++ /* Do apps not want to receive data? */ ++ bool throttled; + }; + + struct VirtIOSerialPortInfo { +@@ -133,10 +138,10 @@ struct VirtIOSerialPortInfo { + + /* + * Guest wrote some data to the port. This data is handed over to +- * the app via this callback. The app should return the number of +- * bytes it successfully consumed. ++ * the app via this callback. The app is supposed to consume all ++ * the data that is presented to it. + */ +- size_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, size_t len); ++ void (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, size_t len); + }; + + /* Interface to the virtio-serial bus */ +@@ -170,4 +175,11 @@ ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, + */ + size_t virtio_serial_guest_ready(VirtIOSerialPort *port); + ++/* ++ * Flow control: Ports can signal to the virtio-serial core to stop ++ * sending data or re-start sending data, depending on the 'throttle' ++ * value here. ++ */ ++void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle); ++ + #endif +-- +1.6.6.1 + diff --git a/qemu.spec b/qemu.spec index d7e06e0..309b6f0 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,7 +1,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 0.12.3 -Release: 4%{?dist} +Release: 5%{?dist} # Epoch because we pushed a qemu-1.0 package Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD @@ -85,6 +85,7 @@ Patch48: 0048-virtio-serial-pci-Allow-MSI-to-be-disabled.patch Patch49: 0049-migration-Clear-fd-also-in-error-cases.patch Patch50: 0050-raw-posix-Detect-CDROM-via-ioctl-on-linux.patch Patch51: 0051-usb-linux-increase-buffer-for-USB-control-requests.patch +Patch52: 0052-virtio-console-patches.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -328,6 +329,7 @@ such as kvmtrace and kvm_stat. %patch49 -p1 %patch50 -p1 %patch51 -p1 +%patch52 -p1 %build # By default we build everything, but allow x86 to build a minimal version @@ -630,6 +632,9 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Thu Apr 15 2010 Justin M. Forbes - 2:0.12.3-5 +- Update virtio console patches from upstream + * Mon Mar 11 2010 Justin M. Forbes - 2:0.12.3-4 - Detect cdrom via ioctl (#473154) - re add increased buffer for USB control requests (#546483)