From 45ac1f98480520aa3bb1496e9302c8b7b61aa96c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 11 Apr 2024 18:08:03 +0300 Subject: [PATCH] net: pkt: Add possibility to reserve link layer header Allow network device driver to configure the system so that when a network packet is sent to it, the link layer header is stored just before the L2 payload in the same packet. Currently the link layer header is stored in a separate net_buf that is linked in front of the L2 payload. This option can typically save one net_buf when sending a network packet. Note that if you are using variable data size buffers (CONFIG_NET_BUF_VARIABLE_DATA_SIZE) then this embedding is not needed because one can allocate just the right size network buffers and not waste any memory. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_l2.h | 13 +++- include/zephyr/net/net_pkt.h | 37 +++++++++ subsys/net/ip/net_pkt.c | 143 ++++++++++++++++++++++++++++------- 3 files changed, 164 insertions(+), 29 deletions(-) diff --git a/include/zephyr/net/net_l2.h b/include/zephyr/net/net_l2.h index 37c9b083dd4d..a7aa8530ee29 100644 --- a/include/zephyr/net/net_l2.h +++ b/include/zephyr/net/net_l2.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -79,6 +80,13 @@ struct net_l2 { * Return L2 flags for the network interface. */ enum net_l2_flags (*get_flags)(struct net_if *iface); + + /** + * Optional function for reserving L2 header space for this technology. + */ + int (*alloc)(struct net_if *iface, struct net_pkt *pkt, + size_t size, enum net_ip_protocol proto, + k_timeout_t timeout); }; /** @cond INTERNAL_HIDDEN */ @@ -121,13 +129,16 @@ NET_L2_DECLARE_PUBLIC(CANBUS_RAW_L2); NET_L2_DECLARE_PUBLIC(CUSTOM_IEEE802154_L2); #endif /* CONFIG_NET_L2_CUSTOM_IEEE802154 */ -#define NET_L2_INIT(_name, _recv_fn, _send_fn, _enable_fn, _get_flags_fn) \ +#define NET_L2_INIT(_name, _recv_fn, _send_fn, _enable_fn, _get_flags_fn, ...) \ const STRUCT_SECTION_ITERABLE(net_l2, \ NET_L2_GET_NAME(_name)) = { \ .recv = (_recv_fn), \ .send = (_send_fn), \ .enable = (_enable_fn), \ .get_flags = (_get_flags_fn), \ + .alloc = COND_CODE_0(NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ } #define NET_L2_GET_DATA(name, sfx) _net_l2_data_##name##sfx diff --git a/include/zephyr/net/net_pkt.h b/include/zephyr/net/net_pkt.h index 1a3b4a042aee..5fd05f658dac 100644 --- a/include/zephyr/net/net_pkt.h +++ b/include/zephyr/net/net_pkt.h @@ -1907,6 +1907,18 @@ struct net_pkt *net_pkt_rx_alloc_with_buffer_debug(struct net_if *iface, net_pkt_rx_alloc_with_buffer_debug(_iface, _size, _family, \ _proto, _timeout, \ __func__, __LINE__) + +int net_pkt_alloc_buffer_with_reserve_debug(struct net_pkt *pkt, + size_t size, + size_t reserve, + enum net_ip_protocol proto, + k_timeout_t timeout, + const char *caller, + int line); +#define net_pkt_alloc_buffer_with_reserve(_pkt, _size, _reserve, _proto, _timeout) \ + net_pkt_alloc_buffer_with_reserve_debug(_pkt, _size, _reserve, _proto, \ + _timeout, __func__, __LINE__) + #endif /* NET_PKT_DEBUG_ENABLED */ /** @endcond */ @@ -2001,6 +2013,31 @@ int net_pkt_alloc_buffer(struct net_pkt *pkt, #endif #if !defined(NET_PKT_DEBUG_ENABLED) +/** + * @brief Allocate buffer for a net_pkt and reserve some space in the first net_buf. + * + * @details: such allocator will take into account space necessary for headers, + * MTU, and existing buffer (if any). Beware that, due to all these + * criteria, the allocated size might be smaller/bigger than + * requested one. + * + * @param pkt The network packet requiring buffer to be allocated. + * @param size The size of buffer being requested. + * @param reserve The L2 header size to reserve. This can be 0, in which case + * the L2 header is placed into a separate net_buf. + * @param proto The IP protocol type (can be 0 for none). + * @param timeout Maximum time to wait for an allocation. + * + * @return 0 on success, negative errno code otherwise. + */ +#if !defined(NET_PKT_DEBUG_ENABLED) +int net_pkt_alloc_buffer_with_reserve(struct net_pkt *pkt, + size_t size, + size_t reserve, + enum net_ip_protocol proto, + k_timeout_t timeout); +#endif + /** * @brief Allocate buffer for a net_pkt, of specified size, w/o any additional * preconditions diff --git a/subsys/net/ip/net_pkt.c b/subsys/net/ip/net_pkt.c index aa679c7bbc5a..792e9f12a9fc 100644 --- a/subsys/net/ip/net_pkt.c +++ b/subsys/net/ip/net_pkt.c @@ -924,12 +924,14 @@ static struct net_pkt_alloc_stats_slab *find_alloc_stats(struct k_mem_slab *slab #if NET_LOG_LEVEL >= LOG_LEVEL_DBG static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, struct net_buf_pool *pool, - size_t size, k_timeout_t timeout, + size_t size, size_t headroom, + k_timeout_t timeout, const char *caller, int line) #else static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, struct net_buf_pool *pool, - size_t size, k_timeout_t timeout) + size_t size, size_t headroom, + k_timeout_t timeout) #endif { #if defined(CONFIG_NET_PKT_ALLOC_STATS) @@ -958,11 +960,25 @@ static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, } current = new; - if (current->size > size) { - current->size = size; - } - size -= current->size; + /* If there is headroom reserved, then allocate that to the + * first buf. + */ + if (current == first && headroom > 0) { + if (current->size > (headroom + size)) { + current->size = size + headroom; + + size = 0U; + } else { + size -= current->size; + } + } else { + if (current->size > size) { + current->size = size; + } + + size -= current->size; + } timeout = sys_timepoint_timeout(end); @@ -1003,12 +1019,14 @@ static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, #if NET_LOG_LEVEL >= LOG_LEVEL_DBG static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, struct net_buf_pool *pool, - size_t size, k_timeout_t timeout, + size_t size, size_t headroom, + k_timeout_t timeout, const char *caller, int line) #else static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, struct net_buf_pool *pool, - size_t size, k_timeout_t timeout) + size_t size, size_t headroom, + k_timeout_t timeout) #endif { struct net_buf *buf; @@ -1019,6 +1037,7 @@ static struct net_buf *pkt_alloc_buffer(struct net_pkt *pkt, #else ARG_UNUSED(pkt); #endif + ARG_UNUSED(headroom); buf = net_buf_alloc_len(pool, size, timeout); @@ -1231,17 +1250,19 @@ int net_pkt_remove_tail(struct net_pkt *pkt, size_t length) } #if NET_LOG_LEVEL >= LOG_LEVEL_DBG -int net_pkt_alloc_buffer_debug(struct net_pkt *pkt, - size_t size, - enum net_ip_protocol proto, - k_timeout_t timeout, - const char *caller, - int line) +int net_pkt_alloc_buffer_with_reserve_debug(struct net_pkt *pkt, + size_t size, + size_t reserve, + enum net_ip_protocol proto, + k_timeout_t timeout, + const char *caller, + int line) #else -int net_pkt_alloc_buffer(struct net_pkt *pkt, - size_t size, - enum net_ip_protocol proto, - k_timeout_t timeout) +int net_pkt_alloc_buffer_with_reserve(struct net_pkt *pkt, + size_t size, + size_t reserve, + enum net_ip_protocol proto, + k_timeout_t timeout) #endif { struct net_buf_pool *pool = NULL; @@ -1271,8 +1292,8 @@ int net_pkt_alloc_buffer(struct net_pkt *pkt, /* Calculate the maximum that can be allocated depending on size */ alloc_len = pkt_buffer_length(pkt, size + hdr_len, proto, alloc_len); - NET_DBG("Data allocation maximum size %zu (requested %zu)", - alloc_len, size); + NET_DBG("Data allocation maximum size %zu (requested %zu, reserve %zu)", + alloc_len, size, reserve); if (pkt->context) { pool = get_data_pool(pkt->context); @@ -1283,26 +1304,92 @@ int net_pkt_alloc_buffer(struct net_pkt *pkt, } #if NET_LOG_LEVEL >= LOG_LEVEL_DBG - buf = pkt_alloc_buffer(pkt, pool, alloc_len, timeout, caller, line); + buf = pkt_alloc_buffer(pkt, pool, alloc_len, reserve, + timeout, caller, line); #else - buf = pkt_alloc_buffer(pkt, pool, alloc_len, timeout); + buf = pkt_alloc_buffer(pkt, pool, alloc_len, reserve, timeout); #endif if (!buf) { #if NET_LOG_LEVEL >= LOG_LEVEL_DBG - NET_ERR("Data buffer (%zd) allocation failed (%s:%d)", - alloc_len, caller, line); + NET_ERR("Data buffer (%zu) allocation failed (%s:%d)", + alloc_len + reserve, caller, line); #else - NET_ERR("Data buffer (%zd) allocation failed.", alloc_len); + NET_ERR("Data buffer (%zu) allocation failed.", + alloc_len + reserve); #endif return -ENOMEM; } net_pkt_append_buffer(pkt, buf); + /* Hide the link layer header for now. The space is used when + * link layer header needs to be written to the packet by L2 send. + */ + if (reserve > 0U) { + NET_DBG("Reserving %zu bytes for L2 header", reserve); + + net_buf_reserve(pkt->buffer, reserve); + + net_pkt_cursor_init(pkt); + } + return 0; } +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG +int net_pkt_alloc_buffer_debug(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + k_timeout_t timeout, + const char *caller, + int line) +#else +int net_pkt_alloc_buffer(struct net_pkt *pkt, + size_t size, + enum net_ip_protocol proto, + k_timeout_t timeout) +#endif +{ + struct net_if *iface; + int ret; + + if (!size && proto == 0 && net_pkt_family(pkt) == AF_UNSPEC) { + return 0; + } + + if (k_is_in_isr()) { + timeout = K_NO_WAIT; + } + + iface = net_pkt_iface(pkt); + + if (iface != NULL && net_if_l2(iface)->alloc != NULL) { + ret = net_if_l2(iface)->alloc(iface, pkt, size, proto, timeout); + if (ret != -ENOTSUP) { + return ret; + } + } + +#if NET_LOG_LEVEL >= LOG_LEVEL_DBG + ret = net_pkt_alloc_buffer_with_reserve_debug(pkt, + size, + 0U, + proto, + timeout, + caller, + line); +#else + ret = net_pkt_alloc_buffer_with_reserve(pkt, + size, + 0U, + proto, + timeout); +#endif + + return ret; +} + #if NET_LOG_LEVEL >= LOG_LEVEL_DBG int net_pkt_alloc_buffer_raw_debug(struct net_pkt *pkt, size_t size, @@ -1335,9 +1422,9 @@ int net_pkt_alloc_buffer_raw(struct net_pkt *pkt, size_t size, } #if NET_LOG_LEVEL >= LOG_LEVEL_DBG - buf = pkt_alloc_buffer(pkt, pool, size, timeout, caller, line); + buf = pkt_alloc_buffer(pkt, pool, size, 0U, timeout, caller, line); #else - buf = pkt_alloc_buffer(pkt, pool, size, timeout); + buf = pkt_alloc_buffer(pkt, pool, size, 0U, timeout); #endif if (!buf) { @@ -1569,7 +1656,7 @@ pkt_alloc_with_buffer(struct k_mem_slab *slab, struct net_pkt *pkt; int ret; - NET_DBG("On iface %p size %zu", iface, size); + NET_DBG("On iface %d (%p) size %zu", net_if_get_by_iface(iface), iface, size); #if NET_LOG_LEVEL >= LOG_LEVEL_DBG pkt = pkt_alloc_on_iface(slab, iface, timeout, caller, line);