diff --git a/subsys/net/l2/ethernet/Kconfig b/subsys/net/l2/ethernet/Kconfig index 82dd969023df..a07734fc0f87 100644 --- a/subsys/net/l2/ethernet/Kconfig +++ b/subsys/net/l2/ethernet/Kconfig @@ -16,6 +16,14 @@ module-str = Log level for Ethernet L2 layer module-help = Enables Ethernet L2 to output debug messages. source "subsys/net/Kconfig.template.log_config.net" +config NET_L2_ETHERNET_RESERVE_HEADER + bool "Reserve space for Ethernet header in first net_buf in TX" + help + If enabled, then reserve space for Ethernet header to the first + net_buf when sending data. The default is still to have layer 2 + header in a separate net_buf. In RX side the Ethernet header + is always part of the first net_buf. + config NET_L2_ETHERNET_MGMT bool "Ethernet network management interface" select NET_MGMT diff --git a/subsys/net/l2/ethernet/ethernet.c b/subsys/net/l2/ethernet/ethernet.c index 276909a88a47..3f369e983b51 100644 --- a/subsys/net/l2/ethernet/ethernet.c +++ b/subsys/net/l2/ethernet/ethernet.c @@ -518,27 +518,79 @@ static bool ethernet_fill_in_dst_on_ipv6_mcast(struct net_pkt *pkt, #define ethernet_fill_in_dst_on_ipv6_mcast(...) false #endif /* CONFIG_NET_IPV6 */ +static inline size_t get_reserve_ll_header_size(struct net_if *iface) +{ + bool is_vlan = false; + +#if defined(CONFIG_NET_VLAN) + if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { + iface = net_eth_get_vlan_main(iface); + is_vlan = true; + } +#endif + + if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) { + return 0U; + } + + if (!IS_ENABLED(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER)) { + return 0U; + } + + if (is_vlan) { + return sizeof(struct net_eth_vlan_hdr); + } else { + return sizeof(struct net_eth_hdr); + } +} + static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, struct net_if *iface, struct net_pkt *pkt, uint32_t ptype) { + struct net_if *orig_iface = iface; struct net_buf *hdr_frag; struct net_eth_hdr *hdr; - size_t hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ? - sizeof(struct net_eth_vlan_hdr) : - sizeof(struct net_eth_hdr); + size_t reserve_ll_header; + size_t hdr_len; + bool is_vlan; + + is_vlan = IS_ENABLED(CONFIG_NET_VLAN) && + net_eth_is_vlan_enabled(ctx, iface) && + net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC; + if (is_vlan) { + orig_iface = net_eth_get_vlan_iface(iface, net_pkt_vlan_tag(pkt)); + } - hdr_frag = net_pkt_get_frag(pkt, hdr_len, NET_BUF_TIMEOUT); - if (!hdr_frag) { - return NULL; + reserve_ll_header = get_reserve_ll_header_size(orig_iface); + if (reserve_ll_header > 0) { + hdr_len = reserve_ll_header; + hdr_frag = pkt->buffer; + + NET_DBG("Making room for link header %zd bytes", hdr_len); + + /* Make room for the header */ + net_buf_push(pkt->buffer, hdr_len); + } else { + hdr_len = IS_ENABLED(CONFIG_NET_VLAN) ? + sizeof(struct net_eth_vlan_hdr) : + sizeof(struct net_eth_hdr); + + hdr_frag = net_pkt_get_frag(pkt, hdr_len, NET_BUF_TIMEOUT); + if (!hdr_frag) { + return NULL; + } } - if (IS_ENABLED(CONFIG_NET_VLAN) && - net_eth_is_vlan_enabled(ctx, iface) && - net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC) { + if (is_vlan) { struct net_eth_vlan_hdr *hdr_vlan; + if (reserve_ll_header == 0U) { + hdr_len = sizeof(struct net_eth_vlan_hdr); + net_buf_add(hdr_frag, hdr_len); + } + hdr_vlan = (struct net_eth_vlan_hdr *)(hdr_frag->data); if (ptype == htons(NET_ETH_PTYPE_ARP) || @@ -554,15 +606,19 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, hdr_vlan->type = ptype; hdr_vlan->vlan.tpid = htons(NET_ETH_PTYPE_VLAN); hdr_vlan->vlan.tci = htons(net_pkt_vlan_tci(pkt)); - net_buf_add(hdr_frag, sizeof(struct net_eth_vlan_hdr)); print_vlan_ll_addrs(pkt, ntohs(hdr_vlan->type), net_pkt_vlan_tci(pkt), - hdr_frag->len, + hdr_len, &hdr_vlan->src, &hdr_vlan->dst, false); } else { hdr = (struct net_eth_hdr *)(hdr_frag->data); + if (reserve_ll_header == 0U) { + hdr_len = sizeof(struct net_eth_hdr); + net_buf_add(hdr_frag, hdr_len); + } + if (ptype == htons(NET_ETH_PTYPE_ARP) || (!ethernet_fill_in_dst_on_ipv4_mcast(pkt, &hdr->dst) && !ethernet_fill_in_dst_on_ipv6_mcast(pkt, &hdr->dst))) { @@ -574,13 +630,14 @@ static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx, sizeof(struct net_eth_addr)); hdr->type = ptype; - net_buf_add(hdr_frag, sizeof(struct net_eth_hdr)); print_ll_addrs(pkt, ntohs(hdr->type), - hdr_frag->len, &hdr->src, &hdr->dst); + hdr_len, &hdr->src, &hdr->dst); } - net_pkt_frag_insert(pkt, hdr_frag); + if (reserve_ll_header == 0U) { + net_pkt_frag_insert(pkt, hdr_frag); + } return hdr_frag; } @@ -605,14 +662,19 @@ static void ethernet_update_tx_stats(struct net_if *iface, struct net_pkt *pkt) static void ethernet_remove_l2_header(struct net_pkt *pkt) { + size_t reserve = get_reserve_ll_header_size(net_pkt_iface(pkt)); struct net_buf *buf; /* Remove the buffer added in ethernet_fill_header() */ - buf = pkt->buffer; - pkt->buffer = buf->frags; - buf->frags = NULL; + if (reserve == 0U) { + buf = pkt->buffer; + pkt->buffer = buf->frags; + buf->frags = NULL; - net_pkt_frag_unref(buf); + net_pkt_frag_unref(buf); + } else { + net_buf_pull(pkt->buffer, reserve); + } } static int ethernet_send(struct net_if *iface, struct net_pkt *pkt) @@ -813,8 +875,21 @@ enum net_l2_flags ethernet_flags(struct net_if *iface) return ctx->ethernet_l2_flags; } +#if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) +static int ethernet_l2_alloc(struct net_if *iface, struct net_pkt *pkt, + size_t size, enum net_ip_protocol proto, + k_timeout_t timeout) +{ + return net_pkt_alloc_buffer_with_reserve(pkt, size, + get_reserve_ll_header_size(iface), + proto, timeout); +} +#else +#define ethernet_l2_alloc NULL +#endif + NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, ethernet_enable, - ethernet_flags); + ethernet_flags, ethernet_l2_alloc); static void carrier_on_off(struct k_work *work) { diff --git a/subsys/net/l2/ethernet/vlan.c b/subsys/net/l2/ethernet/vlan.c index 8e6bb7a63d05..a746225ae6bf 100644 --- a/subsys/net/l2/ethernet/vlan.c +++ b/subsys/net/l2/ethernet/vlan.c @@ -610,6 +610,22 @@ static enum net_verdict vlan_interface_recv(struct net_if *iface, return NET_OK; } +int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt, + size_t size, uint16_t proto, k_timeout_t timeout) +{ + enum virtual_interface_caps caps; + int ret = 0; + + caps = net_virtual_get_iface_capabilities(iface); + if (caps & VIRTUAL_INTERFACE_VLAN) { + ret = net_pkt_alloc_buffer_with_reserve(pkt, size, + sizeof(struct net_eth_vlan_hdr), + proto, timeout); + } + + return ret; +} + static int vlan_interface_attach(struct net_if *vlan_iface, struct net_if *iface) { diff --git a/subsys/net/l2/virtual/virtual.c b/subsys/net/l2/virtual/virtual.c index 583581f7acae..ef58a99a5fa8 100644 --- a/subsys/net/l2/virtual/virtual.c +++ b/subsys/net/l2/virtual/virtual.c @@ -165,8 +165,22 @@ enum net_l2_flags virtual_flags(struct net_if *iface) return ctx->virtual_l2_flags; } +#if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN) +extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt, + size_t size, uint16_t proto, k_timeout_t timeout); + +static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt, + size_t size, enum net_ip_protocol proto, + k_timeout_t timeout) +{ + return vlan_alloc_buffer(iface, pkt, size, proto, timeout); +} +#else +#define virtual_l2_alloc NULL +#endif + NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable, - virtual_flags); + virtual_flags, virtual_l2_alloc); static void random_linkaddr(uint8_t *linkaddr, size_t len) {