nuttx/net/can/can_conn.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

314 lines
8.3 KiB
C

/****************************************************************************
* net/can/can_conn.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 <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <arch/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/queue.h>
#include <nuttx/mutex.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/net.h>
#include "utils/utils.h"
#include "can/can.h"
#ifdef CONFIG_NET_CAN
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_CAN_MAX_CONNS
# define CONFIG_CAN_MAX_CONNS 0
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* The array containing all NetLink connections. */
NET_BUFPOOL_DECLARE(g_can_connections, sizeof(struct can_conn_s),
CONFIG_CAN_PREALLOC_CONNS, CONFIG_CAN_ALLOC_CONNS,
CONFIG_CAN_MAX_CONNS);
/* A list of all allocated NetLink connections */
static dq_queue_t g_active_can_connections;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: can_alloc()
*
* Description:
* Allocate a new, uninitialized NetLink connection structure. This is
* normally something done by the implementation of the socket() API
*
****************************************************************************/
FAR struct can_conn_s *can_alloc(void)
{
FAR struct can_conn_s *conn;
/* The free list is protected by a a mutex. */
NET_BUFPOOL_LOCK(g_can_connections);
conn = NET_BUFPOOL_TRYALLOC(g_can_connections);
if (conn != NULL)
{
/* FIXME SocketCAN default behavior enables loopback */
#ifdef CONFIG_NET_CANPROTO_OPTIONS
/* By default the filter is configured to catch all,
* this is done in commented filter code below:
*
* struct can_filter_t catchall_filter;
* filter.can_id = 0;
* filter.can_mask = 0;
* conn->filters[0] = catchall_filter;
*
* However memset already sets the filter to 0
* therefore we only have to set the filter count to 1
*/
conn->filter_count = 1;
#endif
/* Enqueue the connection into the active list */
dq_addlast(&conn->sconn.node, &g_active_can_connections);
}
NET_BUFPOOL_UNLOCK(g_can_connections);
return conn;
}
/****************************************************************************
* Name: can_free()
*
* Description:
* Free a NetLink connection structure that is no longer in use. This
* should be done by the implementation of close().
*
****************************************************************************/
void can_free(FAR struct can_conn_s *conn)
{
/* The free list is protected by a mutex. */
DEBUGASSERT(conn->crefs == 0);
NET_BUFPOOL_LOCK(g_can_connections);
/* Remove the connection from the active list */
dq_rem(&conn->sconn.node, &g_active_can_connections);
nxrmutex_destroy(&conn->sconn.s_lock);
#ifdef CONFIG_NET_CAN_WRITE_BUFFERS
/* Free the write queue */
iob_free_queue(&conn->write_q);
# if CONFIG_NET_SEND_BUFSIZE > 0
/* Notify the send buffer available */
can_sendbuffer_notify(conn);
# endif /* CONFIG_NET_SEND_BUFSIZE */
#endif
#if CONFIG_NET_SEND_BUFSIZE > 0
nxsem_destroy(&conn->sndsem);
#endif
/* Free the readahead queue */
iob_free_queue(&conn->readahead);
/* Free the connection. */
NET_BUFPOOL_FREE(g_can_connections, conn);
NET_BUFPOOL_UNLOCK(g_can_connections);
}
/****************************************************************************
* Name: can_recv_filter
*
* Description:
* filter incoming packet
*
* Input Parameters:
* conn - A pointer to the CAN connection structure
* id - The CAN identifier
*
* Returned Value: 0 - Filter not passed, 1 - Filter passed
*
****************************************************************************/
#ifdef CONFIG_NET_CANPROTO_OPTIONS
static int can_recv_filter(FAR struct can_conn_s *conn, canid_t id)
{
uint32_t i;
#ifdef CONFIG_NET_CAN_ERRORS
/* error message frame */
if ((id & CAN_ERR_FLAG) != 0)
{
return id & conn->err_mask ? 1 : 0;
}
#endif
for (i = 0; i < conn->filter_count; i++)
{
if (conn->filters[i].can_id & CAN_INV_FILTER)
{
if ((id & conn->filters[i].can_mask) !=
((conn->filters[i].can_id & ~CAN_INV_FILTER) &
conn->filters[i].can_mask))
{
return 1;
}
}
else
{
if ((id & conn->filters[i].can_mask) ==
(conn->filters[i].can_id & conn->filters[i].can_mask))
{
return 1;
}
}
}
return 0;
}
#endif
/****************************************************************************
* Name: can_nextconn()
*
* Description:
* Traverse the list of allocated NetLink connections
*
* Assumptions:
* This function is called from NetLink device logic.
*
****************************************************************************/
FAR struct can_conn_s *can_nextconn(FAR struct can_conn_s *conn)
{
if (conn == NULL)
{
return (FAR struct can_conn_s *)g_active_can_connections.head;
}
else
{
return (FAR struct can_conn_s *)conn->sconn.node.flink;
}
}
/****************************************************************************
* Name: can_conn_list_lock
* can_conn_list_unlock
*
* Description:
* Lock and unlock the CAN connection list. This is used to protect
* the list of active connections.
*
* Assumptions:
* This function is called from driver.
*
****************************************************************************/
void can_conn_list_lock(void)
{
NET_BUFPOOL_LOCK(g_can_connections);
}
void can_conn_list_unlock(void)
{
NET_BUFPOOL_UNLOCK(g_can_connections);
}
/****************************************************************************
* Name: can_active()
*
* Description:
* Traverse the list of NetLink connections that match dev
*
* Input Parameters:
* dev - The device to search for.
* conn - The current connection; may be NULL to start the search at the
* beginning
*
* Assumptions:
* This function is called from NetLink device logic.
*
****************************************************************************/
FAR struct can_conn_s *can_active(FAR struct net_driver_s *dev,
FAR struct can_conn_s *conn)
{
#ifdef CONFIG_NET_CANPROTO_OPTIONS
canid_t can_id;
memcpy(&can_id, NETLLBUF, sizeof(canid_t));
#endif
can_conn_list_lock();
while ((conn = can_nextconn(conn)) != NULL)
{
if ((conn->dev == NULL && _SS_ISBOUND(conn->sconn.s_flags)) ||
conn->dev == dev)
{
#ifdef CONFIG_NET_CANPROTO_OPTIONS
if (can_recv_filter(conn, can_id) == 0)
{
continue;
}
#endif
break;
}
}
can_conn_list_unlock();
return conn;
}
#endif /* CONFIG_NET_CAN */