diff options
author | Amit Shah <amit.shah@redhat.com> | 2010-12-10 16:51:14 +0530 |
---|---|---|
committer | Amit Shah <amit.shah@redhat.com> | 2011-01-20 14:38:22 +0530 |
commit | f1925dff7e6c4799f5951cf167a437c0737a266c (patch) | |
tree | 25cbad7d9dfb64ec7f7ae4a4ef932e6ad0cb35bb /hw/virtio-serial-bus.c | |
parent | e300ac275bbf19b31cf5968b8de8abe52c26e163 (diff) |
virtio-serial: Add support for flow control
This commit lets apps signal an incomplete write. When that happens,
stop sending out any more data to the app and wait for it to unthrottle
the port.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Diffstat (limited to 'hw/virtio-serial-bus.c')
-rw-r--r-- | hw/virtio-serial-bus.c | 49 |
1 files changed, 38 insertions, 11 deletions
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 6726f72b6f..32938b5ec3 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -126,24 +126,49 @@ static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, VirtIODevice *vdev) { - VirtQueueElement elem; - assert(port); assert(virtio_queue_ready(vq)); - while (!port->throttled && virtqueue_pop(vq, &elem)) { + while (!port->throttled) { unsigned int i; - for (i = 0; i < elem.out_num; i++) { - size_t buf_size; - - buf_size = elem.out_sg[i].iov_len; + /* Pop an elem only if we haven't left off a previous one mid-way */ + if (!port->elem.out_num) { + if (!virtqueue_pop(vq, &port->elem)) { + break; + } + port->iov_idx = 0; + port->iov_offset = 0; + } - port->info->have_data(port, - elem.out_sg[i].iov_base, - buf_size); + for (i = port->iov_idx; i < port->elem.out_num; i++) { + size_t buf_size; + ssize_t ret; + + buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; + ret = port->info->have_data(port, + port->elem.out_sg[i].iov_base + + port->iov_offset, + buf_size); + if (ret < 0 && ret != -EAGAIN) { + /* We don't handle any other type of errors here */ + abort(); + } + if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { + virtio_serial_throttle_port(port, true); + port->iov_idx = i; + if (ret > 0) { + port->iov_offset += ret; + } + break; + } + port->iov_offset = 0; } - virtqueue_push(vq, &elem, 0); + if (port->throttled) { + break; + } + virtqueue_push(vq, &port->elem, 0); + port->elem.out_num = 0; } virtio_notify(vdev, vq); } @@ -709,6 +734,8 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) port->guest_connected = true; } + port->elem.out_num = 0; + QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); port->ivq = port->vser->ivqs[port->id]; port->ovq = port->vser->ovqs[port->id]; |