From d4fb6514ff8ed6912a71294e6b66a5d59ee88007 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:16 +0100 Subject: can: use skb hash instead of private variable in headroom The can_skb_priv::skbcnt variable is used to identify CAN skbs in the RX path analogue to the skb->hash. As the skb hash is not filled in CAN skbs move the private skbcnt value to skb->hash and set skb->sw_hash accordingly. The skb->hash is a value used for RPS to identify skbs. Use it as intended. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-1-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- drivers/net/can/dev/skb.c | 2 -- include/linux/can/core.h | 1 + include/linux/can/skb.h | 2 -- net/can/af_can.c | 14 +++++++++++--- net/can/bcm.c | 2 -- net/can/isotp.c | 3 --- net/can/j1939/socket.c | 1 - net/can/j1939/transport.c | 2 -- net/can/raw.c | 7 +++---- 9 files changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 3ebd4f779b9b..0da615afa04d 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -202,7 +202,6 @@ static void init_can_skb_reserve(struct sk_buff *skb) skb_reset_transport_header(skb); can_skb_reserve(skb); - can_skb_prv(skb)->skbcnt = 0; } struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) @@ -312,7 +311,6 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb) if (skb->ip_summed == CHECKSUM_NONE) { /* init headroom */ can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 5fb8d0e3f9c1..3287232e3cad 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -58,6 +58,7 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev, void *data); extern int can_send(struct sk_buff *skb, int loop); +void can_set_skb_uid(struct sk_buff *skb); void can_sock_destruct(struct sock *sk); #endif /* !_CAN_CORE_H */ diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 1abc25a8d144..869ea574a40a 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -49,13 +49,11 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb); /** * struct can_skb_priv - private additional data inside CAN sk_buffs * @ifindex: ifindex of the first interface the CAN frame appeared on - * @skbcnt: atomic counter to have an unique id together with skb pointer * @frame_len: length of CAN frame in data link layer * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv { int ifindex; - int skbcnt; unsigned int frame_len; struct can_frame cf[]; }; diff --git a/net/can/af_can.c b/net/can/af_can.c index 770173d8db42..70659987ef4d 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -641,6 +641,16 @@ static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_buf return matches; } +void can_set_skb_uid(struct sk_buff *skb) +{ + /* create non-zero unique skb identifier together with *skb */ + while (!(skb->hash)) + skb->hash = atomic_inc_return(&skbcounter); + + skb->sw_hash = 1; +} +EXPORT_SYMBOL(can_set_skb_uid); + static void can_receive(struct sk_buff *skb, struct net_device *dev) { struct can_dev_rcv_lists *dev_rcv_lists; @@ -652,9 +662,7 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) atomic_long_inc(&pkg_stats->rx_frames); atomic_long_inc(&pkg_stats->rx_frames_delta); - /* create non-zero unique skb identifier together with *skb */ - while (!(can_skb_prv(skb)->skbcnt)) - can_skb_prv(skb)->skbcnt = atomic_inc_return(&skbcounter); + can_set_skb_uid(skb); rcu_read_lock(); diff --git a/net/can/bcm.c b/net/can/bcm.c index 7eba8ae01a5b..8ed60f18c2ea 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -316,7 +316,6 @@ static void bcm_can_tx(struct bcm_op *op) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb_put_data(skb, cf, op->cfsiz); @@ -1344,7 +1343,6 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, } can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb->dev = dev; can_skb_set_owner(skb, sk); err = can_send(skb, 1); /* send with loopback */ diff --git a/net/can/isotp.c b/net/can/isotp.c index ce588b85665a..4bb60b8f9b96 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -230,7 +230,6 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) can_skb_reserve(nskb); can_skb_prv(nskb)->ifindex = dev->ifindex; - can_skb_prv(nskb)->skbcnt = 0; nskb->dev = dev; can_skb_set_owner(nskb, sk); @@ -780,7 +779,6 @@ static void isotp_send_cframe(struct isotp_sock *so) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu); @@ -1009,7 +1007,6 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; so->tx.len = size; so->tx.idx = 0; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index ff9c4fd7b433..1589e8ca634e 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -897,7 +897,6 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, can_skb_reserve(skb); can_skb_prv(skb)->ifindex = ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skb_reserve(skb, offsetof(struct can_frame, data)); ret = memcpy_from_msg(skb_put(skb, size), msg, size); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 8656ab388c83..d5d3e5320f7a 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -601,7 +601,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; /* reserve CAN header */ skb_reserve(skb, offsetof(struct can_frame, data)); @@ -1536,7 +1535,6 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; - can_skb_prv(skb)->skbcnt = 0; skcb = j1939_skb_to_cb(skb); memcpy(skcb, rel_skcb, sizeof(*skcb)); diff --git a/net/can/raw.c b/net/can/raw.c index 12293363413c..fb4f9c854df0 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -76,7 +76,7 @@ MODULE_ALIAS("can-proto-1"); struct uniqframe { const struct sk_buff *skb; - int skbcnt; + u32 hash; unsigned int join_rx_count; }; @@ -164,7 +164,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) /* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr(ro->uniq)->skb == oskb && - this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) { + this_cpu_ptr(ro->uniq)->hash == oskb->hash) { if (!ro->join_filters) return; @@ -174,7 +174,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) return; } else { this_cpu_ptr(ro->uniq)->skb = oskb; - this_cpu_ptr(ro->uniq)->skbcnt = can_skb_prv(oskb)->skbcnt; + this_cpu_ptr(ro->uniq)->hash = oskb->hash; this_cpu_ptr(ro->uniq)->join_rx_count = 1; /* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count > 1) @@ -958,7 +958,6 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - can_skb_prv(skb)->skbcnt = 0; /* fill the skb before testing for valid CAN frames */ err = memcpy_from_msg(skb_put(skb, size), msg, size); -- cgit v1.2.3 From 96ea3a1e2d317e7ecb6b65dc65c9dd917905a6a8 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:17 +0100 Subject: 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 Closes: https://lore.kernel.org/oe-kbuild-all/202602010426.PnGrYAk3-lkp@intel.com/ Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-2-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- MAINTAINERS | 1 + drivers/net/can/dev/skb.c | 56 +++++++++++++++++++++++++++++++++++++---------- drivers/net/can/vxcan.c | 13 +++++++++++ include/linux/can/skb.h | 17 ++++++++++++++ include/linux/skbuff.h | 3 +++ include/net/can.h | 28 ++++++++++++++++++++++++ net/can/Kconfig | 1 + net/can/af_can.c | 9 +++++--- net/can/bcm.c | 15 +++++++++++++ net/can/gw.c | 17 ++++++++++++++ net/can/isotp.c | 25 +++++++++++++++++++++ net/can/j1939/socket.c | 9 ++++++++ net/can/j1939/transport.c | 26 ++++++++++++++++++++++ net/can/raw.c | 9 ++++++++ net/core/skbuff.c | 4 ++++ 15 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 include/net/can.h diff --git a/MAINTAINERS b/MAINTAINERS index ac417870c477..ce6f49c054c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5634,6 +5634,7 @@ F: Documentation/networking/iso15765-2.rst F: include/linux/can/can-ml.h F: include/linux/can/core.h F: include/linux/can/skb.h +F: include/net/can.h F: include/net/netns/can.h F: include/uapi/linux/can.h F: include/uapi/linux/can/bcm.h 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 #include +#include #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; diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c index f14c6f02b662..ac2211f8070c 100644 --- a/drivers/net/can/vxcan.c +++ b/drivers/net/can/vxcan.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #define DRV_NAME "vxcan" @@ -39,6 +40,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev) struct vxcan_priv *priv = netdev_priv(dev); struct net_device *peer; struct net_device_stats *peerstats, *srcstats = &dev->stats; + struct can_skb_ext *csx; struct sk_buff *skb; unsigned int len; @@ -63,6 +65,17 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev) goto out_unlock; } + /* the cloned skb points to the skb extension of the already cloned + * oskb with an increased refcount. skb_ext_add() creates a copy to + * separate the skb extension data which is needed to start with a + * fresh can_gw_hops counter in the other namespace. + */ + csx = skb_ext_add(skb, SKB_EXT_CAN); + if (!csx) { + kfree_skb(skb); + goto out_unlock; + } + /* reset CAN GW hop counter */ skb->csum_start = 0; skb->pkt_type = PACKET_BROADCAST; diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 869ea574a40a..68c0f24e6914 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -14,6 +14,7 @@ #include #include #include +#include #include void can_flush_echo_skb(struct net_device *dev); @@ -68,6 +69,22 @@ static inline void can_skb_reserve(struct sk_buff *skb) skb_reserve(skb, sizeof(struct can_skb_priv)); } +static inline struct can_skb_ext *can_skb_ext_add(struct sk_buff *skb) +{ + struct can_skb_ext *csx = skb_ext_add(skb, SKB_EXT_CAN); + + /* skb_ext_add() returns uninitialized space */ + if (csx) + csx->can_gw_hops = 0; + + return csx; +} + +static inline struct can_skb_ext *can_skb_ext_find(struct sk_buff *skb) +{ + return skb_ext_find(skb, SKB_EXT_CAN); +} + static inline void can_skb_set_owner(struct sk_buff *skb, struct sock *sk) { /* If the socket has already been closed by user space, the diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8b399ddf1b9b..ef56dc6318d3 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -4989,6 +4989,9 @@ enum skb_ext_id { #endif #if IS_ENABLED(CONFIG_INET_PSP) SKB_EXT_PSP, +#endif +#if IS_ENABLED(CONFIG_CAN) + SKB_EXT_CAN, #endif SKB_EXT_NUM, /* must be last */ }; diff --git a/include/net/can.h b/include/net/can.h new file mode 100644 index 000000000000..6db9e826f0e0 --- /dev/null +++ b/include/net/can.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * net/can.h + * + * Definitions for the CAN network socket buffer extensions + * + * Copyright (C) 2026 Oliver Hartkopp + * + */ + +#ifndef _NET_CAN_H +#define _NET_CAN_H + +/** + * struct can_skb_ext - skb extensions for CAN specific content + * @can_iif: ifindex of the first interface the CAN frame appeared on + * @can_framelen: cached echo CAN frame length for bql + * @can_gw_hops: can-gw CAN frame time-to-live counter + * @can_ext_flags: CAN skb extensions flags + */ +struct can_skb_ext { + int can_iif; + u16 can_framelen; + u8 can_gw_hops; + u8 can_ext_flags; +}; + +#endif /* _NET_CAN_H */ diff --git a/net/can/Kconfig b/net/can/Kconfig index af64a6f76458..abbb4be7ad21 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -5,6 +5,7 @@ menuconfig CAN tristate "CAN bus subsystem support" + select SKB_EXTENSIONS help Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial communications protocol. Development of the CAN bus started in diff --git a/net/can/af_can.c b/net/can/af_can.c index 70659987ef4d..22c65a014861 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -687,7 +687,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) static int can_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_can_skb(skb))) { + if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || + !can_skb_ext_find(skb) || !can_is_can_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n", dev->type, skb->len); @@ -702,7 +703,8 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_canfd_skb(skb))) { + if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || + !can_skb_ext_find(skb) || !can_is_canfd_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n", dev->type, skb->len); @@ -717,7 +719,8 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, static int canxl_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || !can_is_canxl_skb(skb))) { + if (unlikely(dev->type != ARPHRD_CAN || !can_get_ml_priv(dev) || + !can_skb_ext_find(skb) || !can_is_canxl_skb(skb))) { pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n", dev->type, skb->len); diff --git a/net/can/bcm.c b/net/can/bcm.c index 8ed60f18c2ea..38452069dea8 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -291,6 +292,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) static void bcm_can_tx(struct bcm_op *op) { struct sk_buff *skb; + struct can_skb_ext *csx; struct net_device *dev; struct canfd_frame *cf; int err; @@ -314,6 +316,12 @@ static void bcm_can_tx(struct bcm_op *op) if (!skb) goto out; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + goto out; + } + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; @@ -1317,6 +1325,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, int cfsiz) { struct sk_buff *skb; + struct can_skb_ext *csx; struct net_device *dev; int err; @@ -1328,6 +1337,12 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, if (!skb) return -ENOMEM; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + return -ENOMEM; + } + can_skb_reserve(skb); err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz); diff --git a/net/can/gw.c b/net/can/gw.c index 55eccb1c7620..191afe3b673c 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -459,6 +460,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) struct cgw_job *gwj = (struct cgw_job *)data; struct canfd_frame *cf; struct sk_buff *nskb; + struct can_skb_ext *csx, *ncsx; struct cf_mod *mod; int modidx = 0; @@ -471,6 +473,10 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) return; } + csx = can_skb_ext_find(skb); + if (!csx) + return; + /* Do not handle CAN frames routed more than 'max_hops' times. * In general we should never catch this delimiter which is intended * to cover a misconfiguration protection (e.g. circular CAN routes). @@ -518,6 +524,17 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) return; } + /* the cloned/copied nskb points to the skb extension of the original + * skb with an increased refcount. skb_ext_add() creates a copy to + * separate the skb extension data to modify the can_gw_hops. + */ + ncsx = skb_ext_add(nskb, SKB_EXT_CAN); + if (!ncsx) { + kfree_skb(nskb); + gwj->dropped_frames++; + return; + } + /* put the incremented hop counter in the cloned skb */ cgw_hops(nskb) = cgw_hops(skb) + 1; diff --git a/net/can/isotp.c b/net/can/isotp.c index 4bb60b8f9b96..1346dac0f382 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -214,6 +215,7 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) { struct net_device *dev; struct sk_buff *nskb; + struct can_skb_ext *csx; struct canfd_frame *ncf; struct isotp_sock *so = isotp_sk(sk); int can_send_ret; @@ -222,6 +224,12 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) if (!nskb) return 1; + csx = can_skb_ext_add(nskb); + if (!csx) { + kfree_skb(nskb); + return 1; + } + dev = dev_get_by_index(sock_net(sk), so->ifindex); if (!dev) { kfree_skb(nskb); @@ -762,6 +770,7 @@ static void isotp_send_cframe(struct isotp_sock *so) { struct sock *sk = &so->sk; struct sk_buff *skb; + struct can_skb_ext *csx; struct net_device *dev; struct canfd_frame *cf; int can_send_ret; @@ -777,6 +786,13 @@ static void isotp_send_cframe(struct isotp_sock *so) return; } + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + netdev_put(dev, NULL); + return; + } + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; @@ -938,6 +954,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) struct sock *sk = sock->sk; struct isotp_sock *so = isotp_sk(sk); struct sk_buff *skb; + struct can_skb_ext *csx; struct net_device *dev; struct canfd_frame *cf; int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; @@ -1005,6 +1022,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_out_drop; } + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + netdev_put(dev, NULL); + err = -ENOMEM; + goto err_out_drop; + } + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 1589e8ca634e..b7fbc6512f16 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "j1939-priv.h" @@ -884,6 +885,7 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, struct j1939_sock *jsk = j1939_sk(sk); struct j1939_sk_buff_cb *skcb; struct sk_buff *skb; + struct can_skb_ext *csx; int ret; skb = sock_alloc_send_skb(sk, @@ -895,6 +897,13 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, if (!skb) goto failure; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + ret = -ENOMEM; + goto failure; + } + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = ndev->ifindex; skb_reserve(skb, offsetof(struct can_frame, data)); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index d5d3e5320f7a..03fea5bf0f73 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -9,6 +9,7 @@ // Oleksij Rempel #include +#include #include "j1939-priv.h" @@ -591,6 +592,7 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, bool swap_src_dst) { struct sk_buff *skb; + struct can_skb_ext *csx; struct j1939_sk_buff_cb *skcb; skb = alloc_skb(sizeof(struct can_frame) + sizeof(struct can_skb_priv), @@ -598,6 +600,12 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, if (unlikely(!skb)) return ERR_PTR(-ENOMEM); + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + return ERR_PTR(-ENOMEM); + } + skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; @@ -1051,6 +1059,17 @@ static int j1939_simple_txnext(struct j1939_session *session) goto out_free; } + /* the cloned skb points to the skb extension of the original se_skb + * with an increased refcount. skb_ext_add() creates a copy to + * separate the skb extension data which is needed to modify the + * can_framelen in can_put_echo_skb(). + */ + if (!skb_ext_add(skb, SKB_EXT_CAN)) { + kfree_skb(skb); + ret = -ENOMEM; + goto out_free; + } + can_skb_set_owner(skb, se_skb->sk); j1939_tp_set_rxtimeout(session, J1939_SIMPLE_ECHO_TIMEOUT_MS); @@ -1525,6 +1544,7 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, const struct j1939_sk_buff_cb *rel_skcb) { struct sk_buff *skb; + struct can_skb_ext *csx; struct j1939_sk_buff_cb *skcb; struct j1939_session *session; @@ -1532,6 +1552,12 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, if (unlikely(!skb)) return NULL; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + return NULL; + } + skb->dev = priv->ndev; can_skb_reserve(skb); can_skb_prv(skb)->ifindex = priv->ndev->ifindex; diff --git a/net/can/raw.c b/net/can/raw.c index fb4f9c854df0..c63af48495b7 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -918,6 +919,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) struct raw_sock *ro = raw_sk(sk); struct sockcm_cookie sockc; struct sk_buff *skb; + struct can_skb_ext *csx; struct net_device *dev; unsigned int txmtu; int ifindex; @@ -956,6 +958,13 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (!skb) goto put_dev; + csx = can_skb_ext_add(skb); + if (!csx) { + kfree_skb(skb); + err = -ENOMEM; + goto put_dev; + } + can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4d3920e5b141..648c20e19038 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -78,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -5139,6 +5140,9 @@ static const u8 skb_ext_type_len[] = { #if IS_ENABLED(CONFIG_INET_PSP) [SKB_EXT_PSP] = SKB_EXT_CHUNKSIZEOF(struct psp_skb_ext), #endif +#if IS_ENABLED(CONFIG_CAN) + [SKB_EXT_CAN] = SKB_EXT_CHUNKSIZEOF(struct can_skb_ext), +#endif }; static __always_inline unsigned int skb_ext_total_length(void) -- cgit v1.2.3 From 5a9229dbb48deb78ee1d20ccbd6453cfbf2a4a61 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:18 +0100 Subject: can: move ifindex to CAN skb extensions When routing CAN frames over different CAN interfaces the interface index skb->iif is overwritten with every single hop. To prevent sending a CAN frame back to its originating (first) incoming CAN interface another ifindex variable is needed, which was stored in can_skb_priv::ifindex. Move the can_skb_priv::ifindex content to can_skb_ext::can_iif. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-3-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- drivers/net/can/dev/skb.c | 9 ++++----- net/can/bcm.c | 4 ++-- net/can/gw.c | 2 +- net/can/isotp.c | 6 +++--- net/can/j1939/socket.c | 2 +- net/can/j1939/transport.c | 4 ++-- net/can/raw.c | 2 +- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index c572745565f6..470255fe7367 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -223,7 +223,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) skb->protocol = htons(ETH_P_CAN); init_can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; *cf = skb_put_zero(skb, sizeof(struct can_frame)); @@ -255,7 +255,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, skb->protocol = htons(ETH_P_CANFD); init_can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; *cfd = skb_put_zero(skb, sizeof(struct canfd_frame)); @@ -294,7 +294,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev, skb->protocol = htons(ETH_P_CANXL); init_can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; *cxl = skb_put_zero(skb, CANXL_HDR_SIZE + data_len); @@ -344,8 +344,7 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb) return false; } - can_skb_prv(skb)->ifindex = dev->ifindex; - + csx->can_iif = dev->ifindex; skb->ip_summed = CHECKSUM_UNNECESSARY; /* perform proper loopback on capable devices */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 38452069dea8..f102d17e8619 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -323,7 +323,7 @@ static void bcm_can_tx(struct bcm_op *op) } can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; skb_put_data(skb, cf, op->cfsiz); @@ -1357,7 +1357,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, return -ENODEV; } - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; skb->dev = dev; can_skb_set_owner(skb, sk); err = can_send(skb, 1); /* send with loopback */ diff --git a/net/can/gw.c b/net/can/gw.c index 191afe3b673c..816154a978c1 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -505,7 +505,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) /* is sending the skb back to the incoming interface not allowed? */ if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) && - can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex) + csx->can_iif == gwj->dst.dev->ifindex) return; /* clone the given skb, which has not been done in can_rcv() diff --git a/net/can/isotp.c b/net/can/isotp.c index 1346dac0f382..164c4fbf3e23 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -237,7 +237,7 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) } can_skb_reserve(nskb); - can_skb_prv(nskb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; nskb->dev = dev; can_skb_set_owner(nskb, sk); @@ -794,7 +794,7 @@ static void isotp_send_cframe(struct isotp_sock *so) } can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu); @@ -1031,7 +1031,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; so->tx.len = size; so->tx.idx = 0; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index b7fbc6512f16..cc698a6dc363 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -905,7 +905,7 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, } can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = ndev->ifindex; + csx->can_iif = ndev->ifindex; skb_reserve(skb, offsetof(struct can_frame, data)); ret = memcpy_from_msg(skb_put(skb, size), msg, size); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 03fea5bf0f73..3456ef481799 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -608,7 +608,7 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = priv->ndev->ifindex; + csx->can_iif = priv->ndev->ifindex; /* reserve CAN header */ skb_reserve(skb, offsetof(struct can_frame, data)); @@ -1560,7 +1560,7 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, skb->dev = priv->ndev; can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = priv->ndev->ifindex; + csx->can_iif = priv->ndev->ifindex; skcb = j1939_skb_to_cb(skb); memcpy(skcb, rel_skcb, sizeof(*skcb)); diff --git a/net/can/raw.c b/net/can/raw.c index c63af48495b7..022154b0f6cc 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -966,7 +966,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } can_skb_reserve(skb); - can_skb_prv(skb)->ifindex = dev->ifindex; + csx->can_iif = dev->ifindex; /* fill the skb before testing for valid CAN frames */ err = memcpy_from_msg(skb_put(skb, size), msg, size); -- cgit v1.2.3 From d2d9179751e6d876ed20483ce703ddd8e919101f Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:19 +0100 Subject: can: move frame_len to CAN skb extensions The can_skb_priv::frame_len variable is used to cache a previous calculated CAN frame length to be passed to BQL queueing disciplines. Move the can_skb_priv::frame_len content to can_skb_ext::can_framelen. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-4-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- drivers/net/can/dev/skb.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 470255fe7367..408ee49abce1 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -49,6 +49,7 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, unsigned int idx, unsigned int frame_len) { struct can_priv *priv = netdev_priv(dev); + struct can_skb_ext *csx; if (idx >= priv->echo_skb_max) { netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n", @@ -75,7 +76,9 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, skb->dev = dev; /* save frame_len to reuse it when transmission is completed */ - can_skb_prv(skb)->frame_len = frame_len; + csx = can_skb_ext_find(skb); + if (csx) + csx->can_framelen = frame_len; if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; @@ -112,7 +115,7 @@ __can_get_echo_skb(struct net_device *dev, unsigned int idx, * length is supported on both CAN and CANFD frames. */ struct sk_buff *skb = priv->echo_skb[idx]; - struct can_skb_priv *can_skb_priv = can_skb_prv(skb); + struct can_skb_ext *csx; if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) skb_tstamp_tx(skb, skb_hwtstamps(skb)); @@ -120,8 +123,13 @@ __can_get_echo_skb(struct net_device *dev, unsigned int idx, /* get the real payload length for netdev statistics */ *len_ptr = can_skb_get_data_len(skb); - if (frame_len_ptr) - *frame_len_ptr = can_skb_priv->frame_len; + if (frame_len_ptr) { + csx = can_skb_ext_find(skb); + if (csx) + *frame_len_ptr = csx->can_framelen; + else + *frame_len_ptr = 0; + } priv->echo_skb[idx] = NULL; @@ -181,10 +189,15 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx, if (priv->echo_skb[idx]) { struct sk_buff *skb = priv->echo_skb[idx]; - struct can_skb_priv *can_skb_priv = can_skb_prv(skb); - - if (frame_len_ptr) - *frame_len_ptr = can_skb_priv->frame_len; + struct can_skb_ext *csx; + + if (frame_len_ptr) { + csx = can_skb_ext_find(skb); + if (csx) + *frame_len_ptr = csx->can_framelen; + else + *frame_len_ptr = 0; + } dev_kfree_skb_any(skb); priv->echo_skb[idx] = NULL; -- cgit v1.2.3 From 9f10374bb024593a611e6845847f0444841a231b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:20 +0100 Subject: can: remove private CAN skb headroom infrastructure This patch removes struct can_skb_priv which was stored at skb->head and the can_skb_reserve() helper which was used to shift skb->head. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-5-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- drivers/net/can/dev/skb.c | 31 +++++++++---------------------- include/linux/can/skb.h | 31 ------------------------------- net/can/bcm.c | 7 ++----- net/can/isotp.c | 12 ++++-------- net/can/j1939/socket.c | 4 +--- net/can/j1939/transport.c | 7 ++----- net/can/raw.c | 5 ++--- 7 files changed, 20 insertions(+), 77 deletions(-) diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c index 408ee49abce1..95fcdc1026f8 100644 --- a/drivers/net/can/dev/skb.c +++ b/drivers/net/can/dev/skb.c @@ -206,16 +206,10 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx, EXPORT_SYMBOL_GPL(can_free_echo_skb); /* fill common values for CAN sk_buffs */ -static void init_can_skb_reserve(struct sk_buff *skb) +static void init_can_skb(struct sk_buff *skb) { skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - - can_skb_reserve(skb); } struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) @@ -223,8 +217,7 @@ 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)); + skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); if (unlikely(!skb)) goto out_error_cc; @@ -235,7 +228,7 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) } skb->protocol = htons(ETH_P_CAN); - init_can_skb_reserve(skb); + init_can_skb(skb); csx->can_iif = dev->ifindex; *cf = skb_put_zero(skb, sizeof(struct can_frame)); @@ -255,8 +248,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, struct sk_buff *skb; struct can_skb_ext *csx; - skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + - sizeof(struct canfd_frame)); + skb = netdev_alloc_skb(dev, sizeof(struct canfd_frame)); if (unlikely(!skb)) goto out_error_fd; @@ -267,7 +259,7 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, } skb->protocol = htons(ETH_P_CANFD); - init_can_skb_reserve(skb); + init_can_skb(skb); csx->can_iif = dev->ifindex; *cfd = skb_put_zero(skb, sizeof(struct canfd_frame)); @@ -294,8 +286,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev, if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN) goto out_error_xl; - skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + - CANXL_HDR_SIZE + data_len); + skb = netdev_alloc_skb(dev, CANXL_HDR_SIZE + data_len); if (unlikely(!skb)) goto out_error_xl; @@ -306,7 +297,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev, } skb->protocol = htons(ETH_P_CANXL); - init_can_skb_reserve(skb); + init_can_skb(skb); csx->can_iif = dev->ifindex; *cxl = skb_put_zero(skb, CANXL_HDR_SIZE + data_len); @@ -340,14 +331,10 @@ struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf) 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) +static bool can_skb_init_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 || !csx) { /* init CAN skb content */ @@ -405,7 +392,7 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb) goto inval_skb; } - if (!can_skb_headroom_valid(dev, skb)) + if (!can_skb_init_valid(dev, skb)) goto inval_skb; return false; diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 68c0f24e6914..a70a02967071 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -38,37 +38,6 @@ struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf); bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb); -/* - * The struct can_skb_priv is used to transport additional information along - * with the stored struct can(fd)_frame that can not be contained in existing - * struct sk_buff elements. - * N.B. that this information must not be modified in cloned CAN sk_buffs. - * To modify the CAN frame content or the struct can_skb_priv content - * skb_copy() needs to be used instead of skb_clone(). - */ - -/** - * struct can_skb_priv - private additional data inside CAN sk_buffs - * @ifindex: ifindex of the first interface the CAN frame appeared on - * @frame_len: length of CAN frame in data link layer - * @cf: align to the following CAN frame at skb->data - */ -struct can_skb_priv { - int ifindex; - unsigned int frame_len; - struct can_frame cf[]; -}; - -static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb) -{ - return (struct can_skb_priv *)(skb->head); -} - -static inline void can_skb_reserve(struct sk_buff *skb) -{ - skb_reserve(skb, sizeof(struct can_skb_priv)); -} - static inline struct can_skb_ext *can_skb_ext_add(struct sk_buff *skb) { struct can_skb_ext *csx = skb_ext_add(skb, SKB_EXT_CAN); diff --git a/net/can/bcm.c b/net/can/bcm.c index f102d17e8619..b7324e9c955b 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -312,7 +312,7 @@ static void bcm_can_tx(struct bcm_op *op) return; } - skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any()); + skb = alloc_skb(op->cfsiz, gfp_any()); if (!skb) goto out; @@ -322,7 +322,6 @@ static void bcm_can_tx(struct bcm_op *op) goto out; } - can_skb_reserve(skb); csx->can_iif = dev->ifindex; skb_put_data(skb, cf, op->cfsiz); @@ -1333,7 +1332,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, if (!ifindex) return -ENODEV; - skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), GFP_KERNEL); + skb = alloc_skb(cfsiz, GFP_KERNEL); if (!skb) return -ENOMEM; @@ -1343,8 +1342,6 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, return -ENOMEM; } - can_skb_reserve(skb); - err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz); if (err < 0) { kfree_skb(skb); diff --git a/net/can/isotp.c b/net/can/isotp.c index 164c4fbf3e23..da3b72e7afcc 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -220,7 +220,7 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) struct isotp_sock *so = isotp_sk(sk); int can_send_ret; - nskb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), gfp_any()); + nskb = alloc_skb(so->ll.mtu, gfp_any()); if (!nskb) return 1; @@ -236,9 +236,7 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus) return 1; } - can_skb_reserve(nskb); csx->can_iif = dev->ifindex; - nskb->dev = dev; can_skb_set_owner(nskb, sk); ncf = (struct canfd_frame *)nskb->data; @@ -780,7 +778,7 @@ static void isotp_send_cframe(struct isotp_sock *so) if (!dev) return; - skb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), GFP_ATOMIC); + skb = alloc_skb(so->ll.mtu, GFP_ATOMIC); if (!skb) { dev_put(dev); return; @@ -793,7 +791,6 @@ static void isotp_send_cframe(struct isotp_sock *so) return; } - can_skb_reserve(skb); csx->can_iif = dev->ifindex; cf = (struct canfd_frame *)skb->data; @@ -1015,8 +1012,8 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_out_drop; } - skb = sock_alloc_send_skb(sk, so->ll.mtu + sizeof(struct can_skb_priv), - msg->msg_flags & MSG_DONTWAIT, &err); + skb = sock_alloc_send_skb(sk, so->ll.mtu, msg->msg_flags & MSG_DONTWAIT, + &err); if (!skb) { dev_put(dev); goto err_out_drop; @@ -1030,7 +1027,6 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto err_out_drop; } - can_skb_reserve(skb); csx->can_iif = dev->ifindex; so->tx.len = size; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index cc698a6dc363..0502b030d238 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -891,8 +891,7 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, skb = sock_alloc_send_skb(sk, size + sizeof(struct can_frame) - - sizeof(((struct can_frame *)NULL)->data) + - sizeof(struct can_skb_priv), + sizeof(((struct can_frame *)NULL)->data), msg->msg_flags & MSG_DONTWAIT, &ret); if (!skb) goto failure; @@ -904,7 +903,6 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, goto failure; } - can_skb_reserve(skb); csx->can_iif = ndev->ifindex; skb_reserve(skb, offsetof(struct can_frame, data)); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 3456ef481799..2cbe94fc487a 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -595,8 +595,7 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, struct can_skb_ext *csx; struct j1939_sk_buff_cb *skcb; - skb = alloc_skb(sizeof(struct can_frame) + sizeof(struct can_skb_priv), - GFP_ATOMIC); + skb = alloc_skb(sizeof(struct can_frame), GFP_ATOMIC); if (unlikely(!skb)) return ERR_PTR(-ENOMEM); @@ -607,7 +606,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, } skb->dev = priv->ndev; - can_skb_reserve(skb); csx->can_iif = priv->ndev->ifindex; /* reserve CAN header */ skb_reserve(skb, offsetof(struct can_frame, data)); @@ -1548,7 +1546,7 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, struct j1939_sk_buff_cb *skcb; struct j1939_session *session; - skb = alloc_skb(size + sizeof(struct can_skb_priv), GFP_ATOMIC); + skb = alloc_skb(size, GFP_ATOMIC); if (unlikely(!skb)) return NULL; @@ -1559,7 +1557,6 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, } skb->dev = priv->ndev; - can_skb_reserve(skb); csx->can_iif = priv->ndev->ifindex; skcb = j1939_skb_to_cb(skb); memcpy(skcb, rel_skcb, sizeof(*skcb)); diff --git a/net/can/raw.c b/net/can/raw.c index 022154b0f6cc..eee244ffc31e 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -953,8 +953,8 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto put_dev; } - skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv), - msg->msg_flags & MSG_DONTWAIT, &err); + skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, + &err); if (!skb) goto put_dev; @@ -965,7 +965,6 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) goto put_dev; } - can_skb_reserve(skb); csx->can_iif = dev->ifindex; /* fill the skb before testing for valid CAN frames */ -- cgit v1.2.3 From 3ffecc14ec7ee8eb941e50c0076798a042924c62 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 1 Feb 2026 15:33:21 +0100 Subject: can: gw: use can_gw_hops instead of sk_buff::csum_start As CAN skbs don't use IP checksums the skb->csum_start variable was used to store the can-gw CAN frame time-to-live counter together with skb->ip_summed set to CHECKSUM_UNNECESSARY. Remove the 'hack' using the skb->csum_start variable and move the content to can_skb_ext::can_gw_hops of the CAN skb extensions. The module parameter 'max_hops' has been reduced to a single byte to fit can_skb_ext::can_gw_hops as the maximum value to be stored is 6. Signed-off-by: Marc Kleine-Budde Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260201-can_skb_ext-v8-6-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni --- drivers/net/can/vxcan.c | 2 +- net/can/gw.c | 23 ++++++----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c index ac2211f8070c..e882250180ef 100644 --- a/drivers/net/can/vxcan.c +++ b/drivers/net/can/vxcan.c @@ -77,7 +77,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev) } /* reset CAN GW hop counter */ - skb->csum_start = 0; + csx->can_gw_hops = 0; skb->pkt_type = PACKET_BROADCAST; skb->dev = peer; skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/net/can/gw.c b/net/can/gw.c index 816154a978c1..61a1e6b1b83f 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -71,8 +71,8 @@ MODULE_ALIAS(CAN_GW_NAME); #define CGW_MAX_HOPS 6 #define CGW_DEFAULT_HOPS 1 -static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS; -module_param(max_hops, uint, 0444); +static unsigned char max_hops __read_mostly = CGW_DEFAULT_HOPS; +module_param(max_hops, byte, 0444); MODULE_PARM_DESC(max_hops, "maximum " CAN_GW_NAME " routing hops for CAN frames " "(valid values: " __stringify(CGW_MIN_HOPS) "-" @@ -480,19 +480,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) /* Do not handle CAN frames routed more than 'max_hops' times. * In general we should never catch this delimiter which is intended * to cover a misconfiguration protection (e.g. circular CAN routes). - * - * The Controller Area Network controllers only accept CAN frames with - * correct CRCs - which are not visible in the controller registers. - * According to skbuff.h documentation the csum_start element for IP - * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY. - * Only CAN skbs can be processed here which already have this property. */ - -#define cgw_hops(skb) ((skb)->csum_start) - - BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY); - - if (cgw_hops(skb) >= max_hops) { + if (csx->can_gw_hops >= max_hops) { /* indicate deleted frames due to misconfiguration */ gwj->deleted_frames++; return; @@ -536,11 +525,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) } /* put the incremented hop counter in the cloned skb */ - cgw_hops(nskb) = cgw_hops(skb) + 1; + ncsx->can_gw_hops = csx->can_gw_hops + 1; /* first processing of this CAN frame -> adjust to private hop limit */ - if (gwj->limit_hops && cgw_hops(nskb) == 1) - cgw_hops(nskb) = max_hops - gwj->limit_hops + 1; + if (gwj->limit_hops && ncsx->can_gw_hops == 1) + ncsx->can_gw_hops = max_hops - gwj->limit_hops + 1; nskb->dev = gwj->dst.dev; -- cgit v1.2.3