Skip to content

Commit

Permalink
net: pkt: Add possibility to reserve link layer header
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
jukkar authored and kartben committed Dec 19, 2024
1 parent df20c6d commit 45ac1f9
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 29 deletions.
13 changes: 12 additions & 1 deletion include/zephyr/net/net_l2.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <zephyr/device.h>
#include <zephyr/net_buf.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/capture.h>
#include <zephyr/sys/iterable_sections.h>

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions include/zephyr/net/net_pkt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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
Expand Down
143 changes: 115 additions & 28 deletions subsys/net/ip/net_pkt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 45ac1f9

Please sign in to comment.