dcavalca / rpms / qemu

Forked from rpms/qemu a year ago
Clone

Blame 0052-virtio-console-patches.patch

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