nuttx/net/utils/net_lock.c
zhanghongyu 0900b21217 net: change conn lock from mutex to rmutex
UDP and PKT will hold the dev lock and conn lock when calling back the
protocol stack from the network dirver. At this time, if the poll_notify
notifies usrsock_server and executes poll_teardown, a conn double lock
will appear. so we changed the lock to a recursive lock

sched_dumpstack
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/Libs/libc/sched/sched_dumpstack.c:71
__assert
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/libs/libc/assert/lib_assert.c:49
nxsem_wait_slow
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/sched/semaphore/sem_wait.c:102 (discriminator 1)
nxmutex_lock
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/include/nuttx/mutex.h:514
udp_pollteardown
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/udp/udp_netpoll.c:278
usrsock_rpmsg_poll_setup
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/usrsock/usrsock_rpmsg_server.c:1232
usrsock_rpmsg_poll_cb
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/usrsock/usrsock_rpmsg_server.c:1299
poll_notify
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/fs/vfs/fs_poll.c:296
udp_poll_eventhandler
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/udp/udp_netpoll.c:105
devif_conn_event
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/devif/devif_callback.c:481
udp_callback
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/udp/udp_callback.c:324
udp_input_conn
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/udp/udp_input.c:150
ipv4_in
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/devif/ipv4_input.c:425
ipv4_input
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/net/devif/ipv4_input.c:533
tun_net_receive_tun
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/net/tun.c:574
tun_net_receive_tun
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/net/tun.c:574
file_writev_compat
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/fs/vfs/fs_write.c:89
rpmsgdev_write_handler
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/misc/rpmsgdev_server.c:354
rpmsg_virtio_process_rx_buffer
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/rpmsg/rpmsg_virtio.c:256
rpmsg_virtio_rx_worker
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/drivers/rpmsg/rpmsg_virtio.c:291
work_dispatch
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/sched/wqueue/kwork_thread.c:233
work_thread
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/sched/wqueue/kwork_thread.c:293
nxtask_start
/home/work/ssd1/workspace/MiRTOS-062-dev-system-CI/nuttx/sched/task/task_start.c:99

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2025-12-29 09:42:18 +08:00

507 lines
14 KiB
C

/****************************************************************************
* net/utils/net_lock.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <unistd.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <time.h>
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/semaphore.h>
#include <nuttx/sched.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/net.h>
#include "utils/utils.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define NO_HOLDER (INVALID_PROCESS_ID)
/****************************************************************************
* Private Data
****************************************************************************/
static rmutex_t g_netlock = NXRMUTEX_INITIALIZER;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: _net_timedwait
****************************************************************************/
static int
_net_timedwait(FAR sem_t *sem, bool interruptible, unsigned int timeout)
{
unsigned int count;
int blresult;
int ret;
/* Release the network lock, remembering my count. net_breaklock will
* return a negated value if the caller does not hold the network lock.
*/
blresult = net_breaklock(&count);
/* Now take the semaphore, waiting if so requested. */
if (timeout != UINT_MAX)
{
/* Wait until we get the lock or until the timeout expires */
if (interruptible)
{
ret = nxsem_tickwait(sem, MSEC2TICK(timeout));
}
else
{
ret = nxsem_tickwait_uninterruptible(sem, MSEC2TICK(timeout));
}
}
else
{
/* Wait as long as necessary to get the lock */
if (interruptible)
{
ret = nxsem_wait(sem);
}
else
{
ret = nxsem_wait_uninterruptible(sem);
}
}
/* Recover the network lock at the proper count (if we held it before) */
if (blresult >= 0)
{
net_restorelock(count);
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: net_lock
*
* Description:
* Take the network lock
*
* Input Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* failure (probably -ECANCELED).
*
****************************************************************************/
int net_lock(void)
{
return nxrmutex_lock(&g_netlock);
}
/****************************************************************************
* Name: net_trylock
*
* Description:
* Try to take the network lock only when it is currently not locked.
* Otherwise, it locks the semaphore. In either
* case, the call returns without blocking.
*
* Input Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* failure (probably -EAGAIN).
*
****************************************************************************/
int net_trylock(void)
{
return nxrmutex_trylock(&g_netlock);
}
/****************************************************************************
* Name: net_unlock
*
* Description:
* Release the network lock.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void net_unlock(void)
{
nxrmutex_unlock(&g_netlock);
}
/****************************************************************************
* Name: net_breaklock
*
* Description:
* Break the lock, return information needed to restore re-entrant lock
* state.
*
****************************************************************************/
int net_breaklock(FAR unsigned int *count)
{
DEBUGASSERT(count != NULL);
return nxrmutex_breaklock(&g_netlock, count);
}
/****************************************************************************
* Name: net_restorelock
*
* Description:
* Restore the locked state
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* failure (probably -ECANCELED).
*
****************************************************************************/
int net_restorelock(unsigned int count)
{
return nxrmutex_restorelock(&g_netlock, count);
}
/****************************************************************************
* Name: net_sem_timedwait
*
* Description:
* Atomically wait for sem (or a timeout) while temporarily releasing
* the lock on the network.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* sem - A reference to the semaphore to be taken.
* timeout - The relative time to wait until a timeout is declared.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_sem_timedwait(FAR sem_t *sem, unsigned int timeout)
{
return _net_timedwait(sem, true, timeout);
}
/****************************************************************************
* Name: net_mutex_timedlock
*
* Description:
* Atomically wait for mutex (or a timeout) while temporarily releasing
* the lock on the network.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* mutex - A reference to the mutex to be taken.
* timeout - The relative time to wait until a timeout is declared.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_mutex_timedlock(FAR mutex_t *mutex, unsigned int timeout)
{
unsigned int count;
int blresult;
int ret;
/* Release the network lock, remembering my count. net_breaklock will
* return a negated value if the caller does not hold the network lock.
*/
blresult = net_breaklock(&count);
/* Now take the mutex, waiting if so requested. */
if (timeout != UINT_MAX)
{
ret = nxmutex_timedlock(mutex, timeout);
}
else
{
/* Wait as long as necessary to get the lock */
ret = nxmutex_lock(mutex);
}
/* Recover the network lock at the proper count (if we held it before) */
if (blresult >= 0)
{
net_restorelock(count);
}
return ret;
}
/****************************************************************************
* Name: net_sem_wait
*
* Description:
* Atomically wait for sem while temporarily releasing the network lock.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* sem - A reference to the semaphore to be taken.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_sem_wait(FAR sem_t *sem)
{
return net_sem_timedwait(sem, UINT_MAX);
}
/****************************************************************************
* Name: net_mutex_lock
*
* Description:
* Atomically wait for mutex while temporarily releasing the network lock.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* mutex - A reference to the mutex to be taken.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_mutex_lock(FAR mutex_t *mutex)
{
return net_mutex_timedlock(mutex, UINT_MAX);
}
/****************************************************************************
* Name: net_sem_timedwait_uninterruptible
*
* Description:
* This function is wrapped version of net_sem_timedwait(), which is
* uninterruptible and convenient for use.
*
* Input Parameters:
* sem - A reference to the semaphore to be taken.
* timeout - The relative time to wait until a timeout is declared.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_sem_timedwait_uninterruptible(FAR sem_t *sem, unsigned int timeout)
{
return _net_timedwait(sem, false, timeout);
}
/****************************************************************************
* Name: net_sem_wait_uninterruptible
*
* Description:
* This function is wrapped version of net_sem_wait(), which is
* uninterruptible and convenient for use.
*
* Input Parameters:
* sem - A reference to the semaphore to be taken.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
****************************************************************************/
int net_sem_wait_uninterruptible(FAR sem_t *sem)
{
return net_sem_timedwait_uninterruptible(sem, UINT_MAX);
}
#ifdef CONFIG_MM_IOB
/****************************************************************************
* Name: net_timedalloc
*
* Description:
* Allocate an IOB. If no IOBs are available, then atomically wait for
* for the IOB while temporarily releasing the lock on the network.
* This function is wrapped version of nxsem_tickwait(), this wait will
* be terminated when the specified timeout expires.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* throttled - An indication of the IOB allocation is "throttled"
* timeout - The relative time to wait until a timeout is declared.
*
* Returned Value:
* A pointer to the newly allocated IOB is returned on success. NULL is
* returned on any allocation failure.
*
****************************************************************************/
FAR struct iob_s *net_iobtimedalloc(bool throttled, unsigned int timeout)
{
FAR struct iob_s *iob;
iob = iob_tryalloc(throttled);
if (iob == NULL && timeout != 0)
{
unsigned int count;
int blresult;
/* There are no buffers available now. We will have to wait for one to
* become available. But let's not do that with the network locked.
*/
blresult = net_breaklock(&count);
iob = iob_timedalloc(throttled, timeout);
if (blresult >= 0)
{
net_restorelock(count);
}
}
return iob;
}
/****************************************************************************
* Name: net_ioballoc
*
* Description:
* Allocate an IOB. If no IOBs are available, then atomically wait for
* for the IOB while temporarily releasing the lock on the network.
*
* Caution should be utilized. Because the network lock is relinquished
* during the wait, there could be changes in the network state that occur
* before the lock is recovered. Your design should account for this
* possibility.
*
* Input Parameters:
* throttled - An indication of the IOB allocation is "throttled"
*
* Returned Value:
* A pointer to the newly allocated IOB is returned on success. NULL is
* returned on any allocation failure.
*
****************************************************************************/
FAR struct iob_s *net_ioballoc(bool throttled)
{
return net_iobtimedalloc(throttled, UINT_MAX);
}
#endif
/****************************************************************************
* Name: conn_lock, conn_unlock, conn_dev_lock, conn_dev_unlock
*
* Description:
* Lock and unlock the connection and device.
*
****************************************************************************/
void conn_lock(FAR struct socket_conn_s *sconn)
{
nxrmutex_lock(&sconn->s_lock);
}
void conn_unlock(FAR struct socket_conn_s *sconn)
{
nxrmutex_unlock(&sconn->s_lock);
}
void conn_dev_lock(FAR struct socket_conn_s *sconn,
FAR struct net_driver_s *dev)
{
if (dev != NULL)
{
netdev_lock(dev);
}
nxrmutex_lock(&sconn->s_lock);
}
void conn_dev_unlock(FAR struct socket_conn_s *sconn,
FAR struct net_driver_s *dev)
{
nxrmutex_unlock(&sconn->s_lock);
if (dev != NULL)
{
netdev_unlock(dev);
}
}