Files
nuttx/net/utils/net_lock.c
T
zhanghongyu 24ee7cba86 net_lock: add net_sem_timedwait2, passing in rmutex that needs to be break
two rmutexes can be passed in, and later the wait scenarios that require
break the conn and netdev locks will be replaced.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2026-01-01 07:35:12 -03:00

520 lines
15 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;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: net_sem_timedwait2
****************************************************************************/
int net_sem_timedwait2(FAR sem_t *sem, bool interruptible,
unsigned int timeout, FAR rmutex_t *mutex1,
FAR rmutex_t *mutex2)
{
unsigned int count1 = 0;
unsigned int count2 = 0;
int blresult1 = -ENOENT;
int blresult2 = -ENOENT;
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.
*/
if (mutex1 != NULL)
{
blresult1 = nxrmutex_breaklock(mutex1, &count1);
}
if (mutex2 != NULL)
{
blresult2 = nxrmutex_breaklock(mutex2, &count2);
}
/* 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 (blresult2 >= 0)
{
nxrmutex_restorelock(mutex2, count2);
}
if (blresult1 >= 0)
{
nxrmutex_restorelock(mutex1, count1);
}
return ret;
}
/****************************************************************************
* 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_sem_timedwait2(sem, true, timeout, &g_netlock, NULL);
}
/****************************************************************************
* 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_sem_timedwait2(sem, false, timeout, &g_netlock, NULL);
}
/****************************************************************************
* 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);
}
}