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 <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
df20c6d70f
commit
45ac1f9848
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 @@ error:
|
||||
#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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user