net/can: can protocol uses a separate buffer to cache can data

To avoid memory waste, can add support for using an independent iob buffer
The IP protocol often configures CONFIG_IOB_BUFSIZE to be relatively large,
such as 512, for higher throughput. However, the buffer required by CAN is
fixed at a length of 16 or 64. If the system's IOB is directly used,
a lot of memory will be wasted.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
This commit is contained in:
zhanghongyu
2025-04-15 22:36:17 +08:00
committed by Alan C. Assis
parent 48e9b4fc7a
commit 3e025b5a03
12 changed files with 228 additions and 13 deletions
+43
View File
@@ -44,6 +44,7 @@
#include <nuttx/config.h>
#include <nuttx/can.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netconfig.h>
#include <stdint.h>
@@ -126,6 +127,48 @@ extern "C"
struct net_driver_s; /* Forward reference */
int can_input(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: can_iob_timedalloc
*
* Description:
* Allocate an CAN I/O buffer from the CAN buffer pool.
*
****************************************************************************/
#if CONFIG_NET_CAN_NBUFFERS > 0
FAR struct iob_s *can_iob_timedalloc(unsigned int timeout);
#else
#define can_iob_timedalloc(t) net_iobtimedalloc(true, t)
#endif
/****************************************************************************
* Name: can_iob_clone
*
* Description:
* Clone an I/O buffer from the CAN buffer pool.
*
****************************************************************************/
#if CONFIG_NET_CAN_NBUFFERS > 0
FAR struct iob_s *can_iob_clone(FAR struct net_driver_s *dev);
#else
#define can_iob_clone(d) netdev_iob_clone(d, false)
#endif
/****************************************************************************
* Name: can_iob_navail
*
* Description:
* Return the number of available CAN I/O buffers.
*
****************************************************************************/
#if CONFIG_NET_CAN_NBUFFERS > 0
int can_iob_navail(void);
#else
#define can_iob_navail() iob_navail(false)
#endif
#undef EXTERN
#ifdef __cplusplus
}
+4
View File
@@ -39,6 +39,10 @@ if(CONFIG_NET_CAN)
list(APPEND SRCS can_setsockopt.c can_getsockopt.c)
endif()
if(CONFIG_NET_CAN_NBUFFERS GREATER 0)
list(APPEND SRCS can_bufpool.c)
endif()
list(APPEND SRCS can_conn.c can_input.c can_callback.c can_poll.c)
target_sources(net PRIVATE ${SRCS})
+9
View File
@@ -156,5 +156,14 @@ config NET_CAN_NOTIFIER
purpose notifier, but was developed specifically to support poll()
logic where the poll must wait for these events.
config NET_CAN_NBUFFERS
int "Number of CAN buffers"
depends on IOB_ALLOC
default 0
---help---
Each CAN packet is represented by a CAN buffers.
This setting determines the number of preallocated CAN buffers
available for packet data.
endif # NET_CAN
endmenu # CAN Socket Support
+6
View File
@@ -43,6 +43,12 @@ ifeq ($(CONFIG_NET_CANPROTO_OPTIONS),y)
SOCK_CSRCS += can_setsockopt.c can_getsockopt.c
endif
ifdef CONFIG_NET_CAN_NBUFFERS
ifneq (${CONFIG_NET_CAN_NBUFFERS},0)
SOCK_CSRCS += can_bufpool.c
endif
endif
NET_CSRCS += can_conn.c
NET_CSRCS += can_input.c
NET_CSRCS += can_callback.c
+5 -1
View File
@@ -57,6 +57,10 @@
#define can_callback_free(dev,conn,cb) \
devif_conn_callback_free(dev, cb, &conn->sconn.list, &conn->sconn.list_tail)
#ifndef CONFIG_NET_CAN_NBUFFERS
# define CONFIG_NET_CAN_NBUFFERS 0
#endif
/****************************************************************************
* Public Type Definitions
****************************************************************************/
@@ -90,7 +94,7 @@ struct can_conn_s
struct iob_queue_s readahead; /* remove Read-ahead buffering */
#if CONFIG_NET_RECV_BUFSIZE > 0
int32_t recv_buffnum; /* Recv buffer number */
int32_t rcvbufs; /* Maximum amount of bytes queued in receive */
#endif
#if CONFIG_NET_SEND_BUFSIZE > 0
+145
View File
@@ -0,0 +1,145 @@
/****************************************************************************
* net/can/can_bufpool.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 <debug.h>
#include <nuttx/net/can.h>
#include "utils/utils.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_NET_TIMESTAMP
# define CAN_BUFFER_SIZE ALIGN_UP(sizeof(struct iob_s) + NET_CAN_PKTSIZE + \
CONFIG_NET_LL_GUARDSIZE + IOB_ALIGNMENT - \
1, IOB_ALIGNMENT)
#else
# define CAN_BUFFER_SIZE ALIGN_UP(sizeof(struct iob_s) + NET_CAN_PKTSIZE + \
IOB_ALIGNMENT - 1, IOB_ALIGNMENT)
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* The array containing all CAN buffers */
NET_BUFPOOL_DECLARE(g_can_buffer, CAN_BUFFER_SIZE,
CONFIG_NET_CAN_NBUFFERS, 0, 0);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: can_buf_free
*
* Description:
* Free the CAN buffer to the buffer pool
*
* Input Parameters:
* data - The CAN buffer to be freed
*
****************************************************************************/
static void can_buf_free(FAR void *buf)
{
NET_BUFPOOL_FREE(g_can_buffer, buf);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: can_iob_timedalloc
*
* Description:
* Allocate an CAN I/O buffer from the CAN buffer pool.
*
****************************************************************************/
FAR struct iob_s *can_iob_timedalloc(unsigned int timeout)
{
FAR void *buf = NET_BUFPOOL_TIMEDALLOC(g_can_buffer, timeout);
if (buf == NULL)
{
nwarn("Failed to allocate CAN buffer\n");
return NULL;
}
return iob_init_with_data(buf, CAN_BUFFER_SIZE, can_buf_free);
}
/****************************************************************************
* Name: can_iob_clone
*
* Description:
* Clone an I/O buffer from the CAN buffer pool.
*
****************************************************************************/
FAR struct iob_s *can_iob_clone(FAR struct net_driver_s *dev)
{
FAR struct iob_s *iob;
iob = can_iob_timedalloc(0);
if (iob == NULL)
{
return NULL;
}
#ifdef CONFIG_NET_TIMESTAMP
iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
#endif
/* CAN data length is fixed, So when we use iob_clone_partial to copy
* data, we don't have to worry about distributing other iob.
*/
iob_clone_partial(dev->d_iob, dev->d_iob->io_pktlen, 0, iob, 0,
false, false);
return iob;
}
/****************************************************************************
* Name: can_iob_navail
*
* Description:
* Return the number of available CAN I/O buffers.
*
****************************************************************************/
int can_iob_navail(void)
{
return NET_BUFPOOL_NAVAIL(g_can_buffer);
}
+8 -2
View File
@@ -34,6 +34,7 @@
#include <nuttx/net/netdev.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/can.h>
#include "devif/devif.h"
#include "can/can.h"
@@ -202,13 +203,18 @@ uint16_t can_datahandler(FAR struct net_driver_s *dev,
int ret = 0;
#if CONFIG_NET_RECV_BUFSIZE > 0
# if CONFIG_NET_CAN_NBUFFERS > 0
int bufnum = div_const_roundup(conn->rcvbufs, NET_CAN_PKTSIZE);
# else
int bufnum = div_const_roundup(conn->rcvbufs, CONFIG_IOB_BUFSIZE);
# endif
/* Check the frame count pending on conn->readahead */
if (iob_get_queue_entry_count(&conn->readahead) >= conn->recv_buffnum)
if (iob_get_queue_entry_count(&conn->readahead) >= bufnum)
{
nwarn("WARNING: There are no free receive buffer to retain the data. "
"Receive buffer number:%"PRId32", received frames:%"PRIuPTR" \n",
conn->recv_buffnum, iob_get_queue_entry_count(&conn->readahead));
bufnum, iob_get_queue_entry_count(&conn->readahead));
goto errout;
}
#endif
+1 -1
View File
@@ -180,7 +180,7 @@ int can_getsockopt(FAR struct socket *psock, int level, int option,
return -EINVAL;
}
*(FAR int *)value = conn->recv_buffnum * CONFIG_IOB_BUFSIZE;
*(FAR int *)value = conn->rcvbufs;
break;
#endif
+1 -1
View File
@@ -227,7 +227,7 @@ static int can_in(FAR struct net_driver_s *dev)
* We need to clone the packet and deliver it to each listener.
*/
FAR struct iob_s *iob = netdev_iob_clone(dev, false);
FAR struct iob_s *iob = can_iob_clone(dev);
if (iob == NULL)
{
+4 -3
View File
@@ -39,6 +39,7 @@
#include <arch/irq.h>
#include <nuttx/net/can.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
@@ -273,11 +274,11 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
if (nonblock)
{
wb_iob = iob_tryalloc(false);
wb_iob = can_iob_timedalloc(0);
}
else
{
wb_iob = net_iobtimedalloc(true, _SO_TIMEOUT(conn->sconn.s_sndtimeo));
wb_iob = can_iob_timedalloc(_SO_TIMEOUT(conn->sconn.s_sndtimeo));
}
if (wb_iob == NULL)
@@ -446,7 +447,7 @@ int psock_can_cansend(FAR struct socket *psock)
conn = psock->s_conn;
#endif
if (iob_navail(false) <= 0
if (can_iob_navail() <= 0
#if CONFIG_NET_SEND_BUFSIZE > 0
|| iob_get_queue_size(&conn->write_q) >= conn->sndbufs
#endif
+1 -3
View File
@@ -199,9 +199,7 @@ int can_setsockopt(FAR struct socket *psock, int level, int option,
#if CONFIG_NET_MAX_RECV_BUFSIZE > 0
buffersize = MIN(buffersize, CONFIG_NET_MAX_RECV_BUFSIZE);
#endif
conn->recv_buffnum = (buffersize + CONFIG_IOB_BUFSIZE - 1)
/ CONFIG_IOB_BUFSIZE;
conn->rcvbufs = buffersize;
break;
}
+1 -2
View File
@@ -228,8 +228,7 @@ static int can_setup(FAR struct socket *psock)
*/
#if CONFIG_NET_RECV_BUFSIZE > 0
conn->recv_buffnum = (CONFIG_NET_RECV_BUFSIZE + CONFIG_IOB_BUFSIZE - 1)
/ CONFIG_IOB_BUFSIZE;
conn->rcvbufs = CONFIG_NET_RECV_BUFSIZE;
#endif
#if CONFIG_NET_SEND_BUFSIZE > 0