From: Rasmus Villemoes <ravi@prevas.dk>
To: u-boot@lists.denx.de
Cc: Stefan Roese <sr@denx.de>, Tom Rini <trini@konsulko.com>,
Rasmus Villemoes <ravi@prevas.dk>
Subject: [PATCH 1/4] serial: fix circular rx buffer edge case
Date: Thu, 3 Oct 2024 16:10:26 +0200 [thread overview]
Message-ID: <20241003141029.920035-2-ravi@prevas.dk> (raw)
In-Reply-To: <20241003141029.920035-1-ravi@prevas.dk>
The current implementation of the circular rx buffer falls into a
common trap with circular buffers: It keeps the head/tail indices
reduced modulo the buffer size. The problem with that is that it makes
it impossible to distinguish "buffer full" from "buffer empty",
because in both situations one has head==tail.
This can easily be demonstrated: Build sandbox with RX_BUFFER enabled,
set the RX_BUFFER_SIZE to 32, and try pasting the string
01234567890123456789012345678901
Nothing seems to happen, but in reality, all characters have been read
and put into the buffer, but then tstc ends up believing nothing is in
the buffer anyway because upriv->rd_ptr == upriv->wr_ptr.
A better approach is to let the indices be free-running, and only
reduce them modulo the buffer size when accessing the array. Then
"empty" is head-tail==0 and "full" is head-tail==size. This does rely
on the buffer size being a power-of-two and the free-running
indices simply wrapping around to 0 when incremented beyond the
maximal positive value.
Incidentally, that change from signed to unsigned int also improves
code generation quite a bit: In C, (signed int)%(signed int) is
defined to have the sign of the dividend (so (-35) % 32 is -3, not
29), and hence despite the modulus being a power-of-two, x % 32 does
not actually compile to the same as a simple x & 31 - on x86 with -Os,
it seems that gcc ends up emitting an idiv instruction, which is quite
expensive.
Signed-off-by: Rasmus Villemoes <ravi@prevas.dk>
---
drivers/serial/serial-uclass.c | 10 ++++++----
include/serial.h | 4 ++--
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 84f02f7ac76..05fe9645bee 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -328,11 +328,12 @@ static int __serial_tstc(struct udevice *dev)
static int _serial_tstc(struct udevice *dev)
{
struct serial_dev_priv *upriv = dev_get_uclass_priv(dev);
+ uint wr;
/* Read all available chars into the RX buffer */
while (__serial_tstc(dev)) {
- upriv->buf[upriv->wr_ptr++] = __serial_getc(dev);
- upriv->wr_ptr %= CONFIG_SERIAL_RX_BUFFER_SIZE;
+ wr = upriv->wr_ptr++ % CONFIG_SERIAL_RX_BUFFER_SIZE;
+ upriv->buf[wr] = __serial_getc(dev);
}
return upriv->rd_ptr != upriv->wr_ptr ? 1 : 0;
@@ -342,12 +343,13 @@ static int _serial_getc(struct udevice *dev)
{
struct serial_dev_priv *upriv = dev_get_uclass_priv(dev);
char val;
+ uint rd;
if (upriv->rd_ptr == upriv->wr_ptr)
return __serial_getc(dev);
- val = upriv->buf[upriv->rd_ptr++];
- upriv->rd_ptr %= CONFIG_SERIAL_RX_BUFFER_SIZE;
+ rd = upriv->rd_ptr++ % CONFIG_SERIAL_RX_BUFFER_SIZE;
+ val = upriv->buf[rd];
return val;
}
diff --git a/include/serial.h b/include/serial.h
index d129dc3253c..14563239b7d 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -299,8 +299,8 @@ struct serial_dev_priv {
struct stdio_dev *sdev;
char *buf;
- int rd_ptr;
- int wr_ptr;
+ uint rd_ptr;
+ uint wr_ptr;
};
/* Access the serial operations for a device */
--
2.46.2
next prev parent reply other threads:[~2024-10-03 14:10 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-03 14:10 [PATCH 0/4] some serial rx buffer patches Rasmus Villemoes
2024-10-03 14:10 ` Rasmus Villemoes [this message]
2024-10-09 1:51 ` [PATCH 1/4] serial: fix circular rx buffer edge case Simon Glass
2024-10-09 11:03 ` Rasmus Villemoes
2024-10-17 23:23 ` Simon Glass
2024-10-03 14:10 ` [PATCH 2/4] serial: do not overwrite not-consumed characters in rx buffer Rasmus Villemoes
2024-10-09 1:51 ` Simon Glass
2024-10-03 14:10 ` [PATCH 3/4] serial: add build-time sanity check of CONFIG_SERIAL_RX_BUFFER_SIZE Rasmus Villemoes
2024-10-09 1:51 ` Simon Glass
2024-10-03 14:10 ` [PATCH 4/4] serial: embed the rx buffer in struct serial_dev_priv Rasmus Villemoes
2024-10-09 1:51 ` Simon Glass
2024-10-17 2:22 ` [PATCH 0/4] some serial rx buffer patches Tom Rini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241003141029.920035-2-ravi@prevas.dk \
--to=ravi@prevas.dk \
--cc=sr@denx.de \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox