From 7b0d9d26042054b7a29076804d8d33d75a179cf5 Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Wed, 25 Aug 2021 19:34:00 +0200 Subject: usbredirparser: introduce usbredirparser_get_bufferered_output_size() The application does not have a way to know how much data is being queued by usbredirparser due data output being slower than input. This function is a simple way to get that value. Also, this function can be used in usbredirhost to allow dropping isoc packets before calling usbredirparser's functions. Related: https://gitlab.freedesktop.org/spice/usbredir/-/issues/19 Signed-off-by: Victor Toso --- usbredirparser/usbredirparser.c | 33 ++++++++++++++++++++++++++++----- usbredirparser/usbredirparser.h | 5 +++++ usbredirparser/usbredirparser.map | 6 ++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/usbredirparser/usbredirparser.c b/usbredirparser/usbredirparser.c index e4d5f0e..b36608a 100644 --- a/usbredirparser/usbredirparser.c +++ b/usbredirparser/usbredirparser.c @@ -82,8 +82,9 @@ struct usbredirparser_priv { int data_len; int data_read; int to_skip; - struct usbredirparser_buf *write_buf; int write_buf_count; + struct usbredirparser_buf *write_buf; + uint64_t write_buf_total_size; }; static void @@ -128,6 +129,7 @@ usbredirparser_assert_invariants(const struct usbredirparser_priv *parser) assert((parser->data_len != 0) ^ (parser->data == NULL)); int write_buf_count = 0; + uint64_t total_size = 0; const struct usbredirparser_buf *write_buf = parser->write_buf; for (; write_buf != NULL ; write_buf = write_buf->next) { assert(write_buf->pos >= 0); @@ -135,8 +137,10 @@ usbredirparser_assert_invariants(const struct usbredirparser_priv *parser) assert(write_buf->pos <= write_buf->len); assert(write_buf->len == 0 || write_buf->buf != NULL); write_buf_count++; + total_size += write_buf->len; } assert(parser->write_buf_count == write_buf_count); + assert(parser->write_buf_total_size == total_size); #endif } @@ -249,6 +253,19 @@ void usbredirparser_destroy(struct usbredirparser *parser_pub) free(parser); } +USBREDIR_VISIBLE +uint64_t usbredirparser_get_bufferered_output_size(struct usbredirparser *parser_pub) +{ + struct usbredirparser_priv *parser = + (struct usbredirparser_priv *)parser_pub; + uint64_t size; + + LOCK(parser); + size = parser->write_buf_total_size; + UNLOCK(parser); + return size; +} + static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser, uint32_t *caps, int cap) { @@ -1166,8 +1183,10 @@ int usbredirparser_do_write(struct usbredirparser *parser_pub) parser->write_buf = wbuf->next; if (!(parser->flags & usbredirparser_fl_write_cb_owns_buffer)) free(wbuf->buf); - free(wbuf); + + parser->write_buf_total_size -= wbuf->len; parser->write_buf_count--; + free(wbuf); } } UNLOCK(parser); @@ -1197,7 +1216,7 @@ static void usbredirparser_queue(struct usbredirparser *parser_pub, uint8_t *buf, *type_header_out, *data_out; struct usb_redir_header *header; struct usbredirparser_buf *wbuf, *new_wbuf; - int header_len, type_header_len; + int header_len, type_header_len, total_size; header_len = usbredirparser_get_header_len(parser_pub); type_header_len = usbredirparser_get_type_header_len(parser_pub, type, 1); @@ -1212,8 +1231,9 @@ static void usbredirparser_queue(struct usbredirparser *parser_pub, return; } + total_size = header_len + type_header_len + data_len; new_wbuf = calloc(1, sizeof(*new_wbuf)); - buf = malloc(header_len + type_header_len + data_len); + buf = malloc(total_size); if (!new_wbuf || !buf) { ERROR("Out of memory allocating buffer to send packet, dropping!"); free(new_wbuf); free(buf); @@ -1221,7 +1241,7 @@ static void usbredirparser_queue(struct usbredirparser *parser_pub, } new_wbuf->buf = buf; - new_wbuf->len = header_len + type_header_len + data_len; + new_wbuf->len = total_size; header = (struct usb_redir_header *)buf; type_header_out = buf + header_len; @@ -1247,6 +1267,7 @@ static void usbredirparser_queue(struct usbredirparser *parser_pub, wbuf->next = new_wbuf; } + parser->write_buf_total_size += total_size; parser->write_buf_count++; UNLOCK(parser); } @@ -1796,6 +1817,7 @@ int usbredirparser_unserialize(struct usbredirparser *parser_pub, } if (!(parser->write_buf_count == 0 && parser->write_buf == NULL && + parser->write_buf_total_size == 0 && parser->data == NULL && parser->header_read == 0 && parser->type_header_read == 0 && parser->data_read == 0)) { ERROR("unserialization must use a pristine parser"); @@ -1962,6 +1984,7 @@ int usbredirparser_unserialize(struct usbredirparser *parser_pub, wbuf->len = l; *next = wbuf; next = &wbuf->next; + parser->write_buf_total_size += wbuf->len; parser->write_buf_count++; i--; } diff --git a/usbredirparser/usbredirparser.h b/usbredirparser/usbredirparser.h index fbadbb8..9f87d39 100644 --- a/usbredirparser/usbredirparser.h +++ b/usbredirparser/usbredirparser.h @@ -245,6 +245,11 @@ int usbredirparser_do_read(struct usbredirparser *parser); /* This returns the number of usbredir packets queued up for writing */ int usbredirparser_has_data_to_write(struct usbredirparser *parser); +/* This returns the number of bytes queued to be written out. Can be used by control plane + * to drop data from being queued, see issue: + * https://gitlab.freedesktop.org/spice/usbredir/-/issues/19 */ +uint64_t usbredirparser_get_bufferered_output_size(struct usbredirparser *parser_pub); + /* Call this when usbredirparser_has_data_to_write returns > 0 returns 0 on success, -1 if a write error happened. If a write error happened, this function will retry writing any queued data diff --git a/usbredirparser/usbredirparser.map b/usbredirparser/usbredirparser.map index ab45fed..7ae2fd5 100644 --- a/usbredirparser/usbredirparser.map +++ b/usbredirparser/usbredirparser.map @@ -59,4 +59,10 @@ global: usbredirfilter_free; } USBREDIRPARSER_0.8.0; +USBREDIRPARSER_0.11.0 { +global: + usbredirparser_get_bufferered_output_size; +} USBREDIRPARSER_0.10.0; + + # .... define new API here using predicted next version number .... -- cgit v1.2.3