drivers/serial: fetch uart_writev data from the iovec segment
Some checks failed
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Build Documentation / build-html (push) Has been cancelled

Since 00010089b8 uart_writev() takes the data one byte at a time with
uio_copyto() plus uio_advance().  Both of them walk the iovec list and
redo the byte counters for every single byte, so most of the work is
bookkeeping rather than copying.  On slow cores this is what limits how
fast the TX buffer can be filled.

Take a pointer to the current iovec segment and read the bytes straight
from it, and move the uio forward once per segment instead of once per
byte.  nseg counts only the bytes that really went into the buffer: it
is increased at the end of a loop pass, and that step is skipped when
uart_putxmitchar() fails.

Measured on nRF52840 (Cortex-M4, 64 MHz), 8 MiB write() to a CDC/ACM
port: 223 KB/s to 481 KB/s.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
This commit is contained in:
raiden00pl 2026-07-21 15:07:56 +02:00 committed by Xiang Xiao
parent e7ef45d39a
commit 753d2a3466

View file

@ -1480,6 +1480,9 @@ static ssize_t uart_writev(FAR struct file *filep, FAR struct uio *uio)
{
FAR struct inode *inode = filep->f_inode;
FAR uart_dev_t *dev = inode->i_private;
FAR const char *segbuf = NULL;
size_t seglen = 0;
size_t nseg = 0;
ssize_t nwritten;
ssize_t buflen;
bool oktoblock;
@ -1554,9 +1557,22 @@ static ssize_t uart_writev(FAR struct file *filep, FAR struct uio *uio)
*/
uart_disabletxint(dev);
for (; buflen; uio_advance(uio, 1), buflen--)
for (; buflen; buflen--, nseg++)
{
uio_copyto(uio, 0, &ch, 1);
if (nseg >= seglen)
{
/* Consume the current segment and take a pointer to the next.
* uio_advance() steps over any zero-length iovec.
*/
uio_advance(uio, nseg);
segbuf = (FAR const char *)uio->uio_iov->iov_base +
uio->uio_offset_in_iov;
seglen = uio->uio_iov->iov_len - uio->uio_offset_in_iov;
nseg = 0;
}
ch = segbuf[nseg];
ret = OK;
/* Do output post-processing */
@ -1631,6 +1647,10 @@ static ssize_t uart_writev(FAR struct file *filep, FAR struct uio *uio)
}
}
/* Consume the bytes that were successfully queued */
uio_advance(uio, nseg);
if (dev->xmit.head != dev->xmit.tail)
{
#ifdef CONFIG_SERIAL_TXDMA