diff --git a/drivers/serial/serial_io.c b/drivers/serial/serial_io.c index 0f8bc6b270869..3361c11f3f416 100644 --- a/drivers/serial/serial_io.c +++ b/drivers/serial/serial_io.c @@ -57,14 +57,22 @@ void uart_xmitchars(FAR uart_dev_t *dev) { uint16_t nbytes = 0; + sbuf_size_t head; #ifdef CONFIG_SMP irqstate_t flags = enter_critical_section(); #endif - /* Send while we still have data in the TX buffer & room in the fifo */ + /* Send while we still have data in the TX buffer & room in the fifo. + * + * uart_putxmitchar() advances xmit.head from thread context without + * holding the critical section, so on SMP it can move (and wrap) while + * we are in here. Sample it once per iteration: a stale value only + * makes us send less now, whereas reading it twice can turn the batch + * length negative and send from far beyond the buffer. + */ - while (dev->xmit.head != dev->xmit.tail && uart_txready(dev)) + while ((head = dev->xmit.head) != dev->xmit.tail && uart_txready(dev)) { /* Send the next byte */ @@ -72,9 +80,9 @@ void uart_xmitchars(FAR uart_dev_t *dev) { ssize_t sent; - if (dev->xmit.tail < dev->xmit.head) + if (dev->xmit.tail < head) { - sent = dev->xmit.head - dev->xmit.tail; + sent = head - dev->xmit.tail; } else { @@ -164,8 +172,16 @@ void uart_recvchars(FAR uart_dev_t *dev) while (uart_rxavailable(dev)) { + /* uart_read() advances recv.tail from thread context without holding + * the critical section, so on SMP it can move (and wrap) while we are + * in here. Sample it once per iteration and derive the free space + * from that snapshot: a stale value only makes us store less now, + * whereas reading it twice can turn the batch length negative. + */ + int nexthead = rxbuf->head + 1 < rxbuf->size ? rxbuf->head + 1 : 0; - bool is_full = (nexthead == rxbuf->tail); + sbuf_size_t tail = rxbuf->tail; + bool is_full = (nexthead == tail); FAR char *pbuf = NULL; char ch; @@ -174,13 +190,13 @@ void uart_recvchars(FAR uart_dev_t *dev) /* How many bytes are buffered */ - if (rxbuf->head >= rxbuf->tail) + if (rxbuf->head >= tail) { - nbuffered = rxbuf->head - rxbuf->tail; + nbuffered = rxbuf->head - tail; } else { - nbuffered = rxbuf->size - rxbuf->tail + rxbuf->head; + nbuffered = rxbuf->size - tail + rxbuf->head; } /* Is the level now above the watermark level that we need to report? */ @@ -223,11 +239,11 @@ void uart_recvchars(FAR uart_dev_t *dev) if (!is_full) { - if (rxbuf->tail > rxbuf->head) + if (tail > rxbuf->head) { - nbytes = rxbuf->tail - rxbuf->head - 1; + nbytes = tail - rxbuf->head - 1; } - else if (rxbuf->tail) + else if (tail) { nbytes = rxbuf->size - rxbuf->head; }