From bbb7f436d4faa33f283ac6f756b1bc9e88056dd7 Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Mon, 13 May 2024 16:42:54 +0800 Subject: [PATCH] serial.c: should protect the fds when calling poll_notify 1. Use critical_section to protect the fds array; 2. Use critical_section and sched lock to protect the poll notify, because poll notify may cause context switch, delay the context swtich to sched_unlock(); Signed-off-by: Bowen Wang --- drivers/serial/serial.c | 62 ++++++++++++++++++++++++----------- include/nuttx/serial/serial.h | 1 - 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 6d1411d9c3..fedcc0c25c 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -85,6 +85,11 @@ * Private Function Prototypes ****************************************************************************/ +/* Poll support */ + +static void uart_poll_notify(FAR uart_dev_t *dev, unsigned int min, + unsigned int max, pollevent_t eventset); + /* Write support */ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch, @@ -154,6 +159,28 @@ static struct work_s g_serial_work; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: uart_poll_notify + ****************************************************************************/ + +static void uart_poll_notify(FAR uart_dev_t *dev, unsigned int min, + unsigned int max, pollevent_t eventset) +{ + irqstate_t flags; + + DEBUGASSERT(max > min && max - min <= CONFIG_SERIAL_NPOLLWAITERS); + + flags = enter_critical_section(); + nxsched_lock_irq(); + + /* Notify the fds in range dev->fds[min] - dev->fds[max] */ + + poll_notify(&dev->fds[min], max - min, eventset); + + nxsched_unlock_irq(); + leave_critical_section(flags); +} + /**************************************************************************** * Name: uart_putxmitchar ****************************************************************************/ @@ -762,7 +789,6 @@ static int uart_close(FAR struct file *filep) nxmutex_destroy(&dev->xmit.lock); nxmutex_destroy(&dev->recv.lock); nxmutex_destroy(&dev->closelock); - nxmutex_destroy(&dev->polllock); nxsem_destroy(&dev->xmitsem); nxsem_destroy(&dev->recvsem); uart_release(dev); @@ -1629,8 +1655,9 @@ static int uart_poll(FAR struct file *filep, FAR struct inode *inode = filep->f_inode; FAR uart_dev_t *dev = inode->i_private; pollevent_t eventset; + irqstate_t flags; int ndx; - int ret; + int ret = OK; int i; /* Some sanity checking */ @@ -1642,18 +1669,10 @@ static int uart_poll(FAR struct file *filep, } #endif + flags = enter_critical_section(); + /* Are we setting up the poll? Or tearing it down? */ - ret = nxmutex_lock(&dev->polllock); - if (ret < 0) - { - /* A signal received while waiting for access to the poll data - * will abort the operation. - */ - - return ret; - } - if (setup) { /* This is a request to set up the poll. Find an available @@ -1681,6 +1700,8 @@ static int uart_poll(FAR struct file *filep, goto errout; } + leave_critical_section(flags); + /* Should we immediately notify on any of the requested events? * First, check if the xmit buffer is full. * @@ -1729,7 +1750,7 @@ static int uart_poll(FAR struct file *filep, } #endif - poll_notify(&fds, 1, eventset); + uart_poll_notify(dev, i, i + 1, eventset); } else if (fds->priv != NULL) { @@ -1749,10 +1770,14 @@ static int uart_poll(FAR struct file *filep, *slot = NULL; fds->priv = NULL; + + leave_critical_section(flags); } + return ret; + errout: - nxmutex_unlock(&dev->polllock); + leave_critical_section(flags); return ret; } @@ -1783,7 +1808,6 @@ static int uart_unlink(FAR struct inode *inode) nxmutex_destroy(&dev->xmit.lock); nxmutex_destroy(&dev->recv.lock); nxmutex_destroy(&dev->closelock); - nxmutex_destroy(&dev->polllock); nxsem_destroy(&dev->xmitsem); nxsem_destroy(&dev->recvsem); uart_release(dev); @@ -1918,7 +1942,6 @@ int uart_register(FAR const char *path, FAR uart_dev_t *dev) nxmutex_init(&dev->closelock); nxsem_init(&dev->xmitsem, 0, 0); nxsem_init(&dev->recvsem, 0, 0); - nxmutex_init(&dev->polllock); #ifdef CONFIG_SERIAL_TERMIOS dev->timeout = 0; @@ -1954,7 +1977,7 @@ void uart_datareceived(FAR uart_dev_t *dev) { /* Notify all poll/select waiters that they can read from the recv buffer */ - poll_notify(dev->fds, CONFIG_SERIAL_NPOLLWAITERS, POLLIN); + uart_poll_notify(dev, 0, CONFIG_SERIAL_NPOLLWAITERS, POLLIN); /* Is there a thread waiting for read data? */ @@ -1988,7 +2011,7 @@ void uart_datasent(FAR uart_dev_t *dev) { /* Notify all poll/select waiters that they can write to xmit buffer */ - poll_notify(dev->fds, CONFIG_SERIAL_NPOLLWAITERS, POLLOUT); + uart_poll_notify(dev, 0, CONFIG_SERIAL_NPOLLWAITERS, POLLOUT); /* Is there a thread waiting for space in xmit.buffer? */ @@ -2027,6 +2050,7 @@ void uart_connected(FAR uart_dev_t *dev, bool connected) */ flags = enter_critical_section(); + nxsched_lock_irq(); dev->disconnected = !connected; if (!connected) { @@ -2047,6 +2071,7 @@ void uart_connected(FAR uart_dev_t *dev, bool connected) uart_wakeup(&dev->recvsem); } + nxsched_unlock_irq(); leave_critical_section(flags); } #endif @@ -2067,7 +2092,6 @@ void uart_reset_sem(FAR uart_dev_t *dev) nxsem_reset(&dev->recvsem, 0); nxmutex_reset(&dev->xmit.lock); nxmutex_reset(&dev->recv.lock); - nxmutex_reset(&dev->polllock); } /**************************************************************************** diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 82edf04f92..bb14c9a2c7 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -321,7 +321,6 @@ struct uart_dev_s sem_t xmitsem; /* Wakeup user waiting for space in xmit.buffer */ sem_t recvsem; /* Wakeup user waiting for data in recv.buffer */ mutex_t closelock; /* Locks out new open while close is in progress */ - mutex_t polllock; /* Manages exclusive access to fds[] */ /* I/O buffers */