From patchwork Mon Aug 1 14:23:01 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 107759 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id F0AFAB7090 for ; Tue, 2 Aug 2011 00:26:13 +1000 (EST) Received: from localhost ([::1]:46373 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QntPa-0002sC-7L for incoming@patchwork.ozlabs.org; Mon, 01 Aug 2011 10:24:06 -0400 Received: from eggs.gnu.org ([140.186.70.92]:40463) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QntOr-0000c5-OA for qemu-devel@nongnu.org; Mon, 01 Aug 2011 10:23:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QntOq-00050O-Bb for qemu-devel@nongnu.org; Mon, 01 Aug 2011 10:23:21 -0400 Received: from e5.ny.us.ibm.com ([32.97.182.145]:57163) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QntOq-00050J-2s for qemu-devel@nongnu.org; Mon, 01 Aug 2011 10:23:20 -0400 Received: from d01relay06.pok.ibm.com (d01relay06.pok.ibm.com [9.56.227.116]) by e5.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p71Ds2Sh028058 for ; Mon, 1 Aug 2011 09:54:02 -0400 Received: from d01av03.pok.ibm.com (d01av03.pok.ibm.com [9.56.224.217]) by d01relay06.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p71ENIiL1548510 for ; Mon, 1 Aug 2011 10:23:18 -0400 Received: from d01av03.pok.ibm.com (loopback [127.0.0.1]) by d01av03.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p71AN5pK017020 for ; Mon, 1 Aug 2011 07:23:05 -0300 Received: from titi.austin.rr.com (sig-9-76-195-114.mts.ibm.com [9.76.195.114]) by d01av03.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p71AN0qC016760; Mon, 1 Aug 2011 07:23:04 -0300 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Mon, 1 Aug 2011 09:23:01 -0500 Message-Id: <1312208590-25502-4-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1312208590-25502-1-git-send-email-aliguori@us.ibm.com> References: <1312208590-25502-1-git-send-email-aliguori@us.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.145 Cc: Amit Shah , Hans de Goede , Anthony Liguori Subject: [Qemu-devel] [PATCH 03/12] char: introduce tx queue to enable Unix style flow control X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The char layer tries very hard to avoid using an intermediate buffer. The implication of this is that when the backend does a write(), the data for that write must be immediately passed to the front end. Flow control is needed to handle the likely event that the front end is not able to handle the data at this point in time. We implement flow control today by allowing the front ends to register a polling function. The polling function returns non-zero when it is able to receive data. This works okay because most backends are tied to some sort of file descriptor and our main loop allows polling to be included with file descriptor registration. This falls completely apart when dealing with the front end writing to the back end though because the front end (devices) don't have an obvious place to integrate polling. Short summary: we're broken by design. A way to fix this is to eliminate polling entirely and use a Unix style flow control mechanism. This involves using an intermediate buffer and allowing registration of notifications when the buffer either has data in it (readability) or is not full (writability). This patch introduces a queue and uses it for front end -> back end writes. Signed-off-by: Anthony Liguori --- qemu-char.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- qemu-char.h | 11 +++++++++++ 2 files changed, 68 insertions(+), 1 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 795a3cc..3f9b32c 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -139,9 +139,65 @@ void qemu_chr_generic_open(CharDriverState *s) } } +static size_t char_queue_write(CharQueue *q, const void *data, size_t size) +{ + const uint8_t *ptr = data; + size_t i; + + for (i = 0; i < size; i++) { + if ((q->prod - q->cons) == sizeof(q->ring)) { + break; + } + + q->ring[q->prod % sizeof(q->ring)] = ptr[i]; + q->prod++; + } + + return i; +} + +static size_t char_queue_read(CharQueue *q, void *data, size_t size) +{ + uint8_t *ptr = data; + size_t i; + + for (i = 0; i < size; i++) { + if (q->cons == q->prod) { + break; + } + + ptr[i] = q->ring[q->cons % sizeof(q->ring)]; + q->cons++; + } + + return i; +} + +static void qemu_chr_flush_fe_tx(CharDriverState *s) +{ + uint8_t buf[MAX_CHAR_QUEUE_RING]; + int len, written_len; + + /* Drain the queue into a flat buffer */ + len = char_queue_read(&s->fe_tx, buf, sizeof(buf)); + + written_len = s->chr_write(s, buf, len); + if (written_len < len) { + /* If the backend didn't accept the full write, queue the unwritten + * data back in the queue. */ + char_queue_write(&s->fe_tx, &buf[written_len], len - written_len); + } +} + int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) { - return s->chr_write(s, buf, len); + int ret; + + ret = char_queue_write(&s->fe_tx, buf, len); + + qemu_chr_flush_fe_tx(s); + + return ret; } int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) diff --git a/qemu-char.h b/qemu-char.h index bcd413c..bb9c1a7 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -51,6 +51,15 @@ typedef struct { typedef void IOEventHandler(void *opaque, int event); +#define MAX_CHAR_QUEUE_RING 1024 + +typedef struct CharQueue +{ + uint32_t prod; + uint32_t cons; + uint8_t ring[MAX_CHAR_QUEUE_RING]; +} CharQueue; + struct CharDriverState { void (*init)(struct CharDriverState *s); int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); @@ -75,6 +84,8 @@ struct CharDriverState { int opened; int avail_connections; + CharQueue fe_tx; + QTAILQ_ENTRY(CharDriverState) next; };