diff options
| author | Oliver Hartkopp <socketcan@hartkopp.net> | 2026-02-01 15:33:17 +0100 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2026-02-05 11:58:39 +0100 |
| commit | 96ea3a1e2d317e7ecb6b65dc65c9dd917905a6a8 (patch) | |
| tree | 2780347dd8e2aea4538e15f43a207e193edf7ab7 /drivers/net/can/dev | |
| parent | d4fb6514ff8ed6912a71294e6b66a5d59ee88007 (diff) | |
can: add CAN skb extension infrastructure
To remove the private CAN bus skb headroom infrastructure 8 bytes need to
be stored in the skb. The skb extensions are a common pattern and an easy
and efficient way to hold private data travelling along with the skb. We
only need the skb_ext_add() and skb_ext_find() functions to allocate and
access CAN specific content as the skb helpers to copy/clone/free skbs
automatically take care of skb extensions and their final removal.
This patch introduces the complete CAN skb extensions infrastructure:
- add struct can_skb_ext in new file include/net/can.h
- add include/net/can.h in MAINTAINERS
- add SKB_EXT_CAN to skbuff.c and skbuff.h
- select SKB_EXTENSIONS in Kconfig when CONFIG_CAN is enabled
- check for existing CAN skb extensions in can_rcv() in af_can.c
- add CAN skb extensions allocation at every skb_alloc() location
- duplicate the skb extensions if cloning outgoing skbs (framelen/gw_hops)
- introduce can_skb_ext_add() and can_skb_ext_find() helpers
The patch also corrects an indention issue in the original code from 2018:
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202602010426.PnGrYAk3-lkp@intel.com/
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20260201-can_skb_ext-v8-2-3635d790fe8b@hartkopp.net
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'drivers/net/can/dev')
| -rw-r--r-- | drivers/net/can/dev/skb.c | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 0da615afa04d..c572745565f6 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -6,6 +6,7 @@ #include <linux/can/dev.h> #include <linux/module.h> +#include <net/can.h> #define MOD_DESC "CAN device driver interface" @@ -207,13 +208,17 @@ static void init_can_skb_reserve(struct sk_buff *skb) struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) { struct sk_buff *skb; + struct can_skb_ext *csx; skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + sizeof(struct can_frame)); - if (unlikely(!skb)) { - *cf = NULL; + if (unlikely(!skb)) + goto out_error_cc; - return NULL; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + goto out_error_cc; } skb->protocol = htons(ETH_P_CAN); @@ -223,6 +228,11 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) *cf = skb_put_zero(skb, sizeof(struct can_frame)); return skb; + +out_error_cc: + *cf = NULL; + + return NULL; } EXPORT_SYMBOL_GPL(alloc_can_skb); @@ -230,13 +240,17 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, struct canfd_frame **cfd) { struct sk_buff *skb; + struct can_skb_ext *csx; skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + sizeof(struct canfd_frame)); - if (unlikely(!skb)) { - *cfd = NULL; + if (unlikely(!skb)) + goto out_error_fd; - return NULL; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + goto out_error_fd; } skb->protocol = htons(ETH_P_CANFD); @@ -249,6 +263,11 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, (*cfd)->flags = CANFD_FDF; return skb; + +out_error_fd: + *cfd = NULL; + + return NULL; } EXPORT_SYMBOL_GPL(alloc_canfd_skb); @@ -257,14 +276,21 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev, unsigned int data_len) { struct sk_buff *skb; + struct can_skb_ext *csx; if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN) - goto out_error; + goto out_error_xl; skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + CANXL_HDR_SIZE + data_len); if (unlikely(!skb)) - goto out_error; + goto out_error_xl; + + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + goto out_error_xl; + } skb->protocol = htons(ETH_P_CANXL); init_can_skb_reserve(skb); @@ -278,7 +304,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev, return skb; -out_error: +out_error_xl: *cxl = NULL; return NULL; @@ -303,13 +329,21 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb); /* Check for outgoing skbs that have not been created by the CAN subsystem */ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb) { + struct can_skb_ext *csx = can_skb_ext_find(skb); + /* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */ if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv))) return false; /* af_packet does not apply CAN skb specific settings */ - if (skb->ip_summed == CHECKSUM_NONE) { - /* init headroom */ + if (skb->ip_summed == CHECKSUM_NONE || !csx) { + /* init CAN skb content */ + if (!csx) { + csx = can_skb_ext_add(skb); + if (!csx) + return false; + } + can_skb_prv(skb)->ifindex = dev->ifindex; skb->ip_summed = CHECKSUM_UNNECESSARY; |
