From patchwork Tue Mar 23 14:30:16 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amit Shah X-Patchwork-Id: 48348 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 3AEE8B7C98 for ; Wed, 24 Mar 2010 01:43:32 +1100 (EST) Received: from localhost ([127.0.0.1]:35019 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Nu5KK-0004UJ-HO for incoming@patchwork.ozlabs.org; Tue, 23 Mar 2010 10:43:28 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Nu59y-0001QB-My for qemu-devel@nongnu.org; Tue, 23 Mar 2010 10:32:46 -0400 Received: from [199.232.76.173] (port=35898 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Nu59y-0001Ph-51 for qemu-devel@nongnu.org; Tue, 23 Mar 2010 10:32:46 -0400 Received: from Debian-exim by monty-python.gnu.org with spam-scanned (Exim 4.60) (envelope-from ) id 1Nu59u-0001YJ-VL for qemu-devel@nongnu.org; Tue, 23 Mar 2010 10:32:45 -0400 Received: from mx1.redhat.com ([209.132.183.28]:51808) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Nu59u-0001YF-CU for qemu-devel@nongnu.org; Tue, 23 Mar 2010 10:32:42 -0400 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2NEWflw030245 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 23 Mar 2010 10:32:41 -0400 Received: from localhost (vpn-235-214.phx2.redhat.com [10.3.235.214]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o2NEWXlc005121; Tue, 23 Mar 2010 10:32:37 -0400 From: Amit Shah To: qemu list Date: Tue, 23 Mar 2010 20:00:16 +0530 Message-Id: <1269354619-10201-7-git-send-email-amit.shah@redhat.com> In-Reply-To: <1269354619-10201-6-git-send-email-amit.shah@redhat.com> References: <1269354619-10201-1-git-send-email-amit.shah@redhat.com> <1269354619-10201-2-git-send-email-amit.shah@redhat.com> <1269354619-10201-3-git-send-email-amit.shah@redhat.com> <1269354619-10201-4-git-send-email-amit.shah@redhat.com> <1269354619-10201-5-git-send-email-amit.shah@redhat.com> <1269354619-10201-6-git-send-email-amit.shah@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. Cc: Amit Shah , quintela@redhat.com, Gerd Hoffmann , "Michael S. Tsirkin" Subject: [Qemu-devel] [PATCH 6/9] virtio-serial-bus: Use control messages to notify guest of new ports X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Allow the port 'id's to be set by a user on the command line. This is needed by management apps that will want a stable port numbering scheme for hot-plug/unplug and migration. Since the port numbers are shared with the guest (to identify ports in control messages), we just send a control message to the guest indicating addition of new ports (hot-plug) or notifying the guest of the available ports when the guest sends us a DEVICE_READY control message. Signed-off-by: Amit Shah --- hw/virtio-console.c | 2 + hw/virtio-serial-bus.c | 182 +++++++++++++++++++++++++++++++----------------- hw/virtio-serial.h | 17 +++-- 3 files changed, 130 insertions(+), 71 deletions(-) diff --git a/hw/virtio-console.c b/hw/virtio-console.c index e915491..bbbb6b8 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -99,6 +99,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 +134,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-serial-bus.c b/hw/virtio-serial-bus.c index e8eb5aa..dd50f2d 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -41,6 +41,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 +52,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; @@ -205,14 +213,25 @@ 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) - 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: + /* + * 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: /* * Now that we know the guest asked for the port name, we're @@ -367,13 +386,16 @@ 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_be32s(f, &s->bus->max_nr_ports); + 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) nr_active_ports++; @@ -384,11 +406,6 @@ 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); } @@ -398,7 +415,8 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) { VirtIOSerial *s = opaque; VirtIOSerialPort *port; - uint32_t max_nr_ports, nr_active_ports, nr_ports; + size_t ports_map_size; + uint32_t max_nr_ports, nr_active_ports, *ports_map; unsigned int i; if (version_id > 2) { @@ -415,29 +433,28 @@ 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); - nr_ports = qemu_get_be32(f); - if (nr_ports != s->config.nr_ports) { - /* - * Source hot-plugged/unplugged ports and we don't have all of - * them here. - * - * Note: This condition cannot check for all hotplug/unplug - * events: eg, if one port was hot-plugged and one was - * unplugged, the nr_ports remains the same but the port id's - * would have changed and we won't catch it here. A later - * check for !find_port_by_id() will confirm if this happened. - */ + 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; } - /* Items in struct VirtIOSerial */ + 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); - qemu_get_be32s(f, &max_nr_ports); - if (max_nr_ports > s->bus->max_nr_ports) { - /* Source could have more ports than us. Fail migration. */ - return -EINVAL; + 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); @@ -447,20 +464,11 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) id = qemu_get_be32(f); port = find_port_by_id(s, id); - if (!port) { - /* - * The requested port was hot-plugged on the source but we - * don't have it - */ - return -EINVAL; - } port->guest_connected = qemu_get_byte(f); } - return 0; } - static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); static struct BusInfo virtser_bus_info = { @@ -492,6 +500,50 @@ 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) +{ + unsigned int i; + + i = port_id / 32; + vser->ports_map[i] &= ~(1U << (port_id % 32)); + + send_control_event(find_port_by_id(vser, port_id), + VIRTIO_CONSOLE_PORT_REMOVE, 1); +} + static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) { VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); @@ -510,19 +562,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) { - error_report("virtio-serial-bus: Maximum device limit reached"); + if (find_port_by_id(port->vser, port->id)) { + error_report("virtio-serial-bus: A port already exists at id %u\n", + port->id); return -1; } - dev->info = info; + if (plugging_port0) { + port->id = 0; + } + + if (port->id == VIRTIO_CONSOLE_BAD_ID) { + port->id = find_free_port_id(port->vser); + if (port->id == VIRTIO_CONSOLE_BAD_ID) { + error_report("virtio-serial-bus: Maximum device limit reached\n"); + return -1; + } + } + + if (port->id >= port->vser->config.max_nr_ports) { + error_report("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 @@ -535,6 +604,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); @@ -547,26 +618,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) @@ -626,11 +679,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 632d31b..f023873 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -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 == */