* [net-next 1/6] can: use skb hash instead of private variable in headroom
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
@ 2026-01-25 20:15 ` Oliver Hartkopp
2026-01-28 1:45 ` Jakub Kicinski
2026-01-25 20:15 ` [net-next 2/6] can: add CAN skb extension infrastructure Oliver Hartkopp
` (4 subsequent siblings)
5 siblings, 1 reply; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-25 20:15 UTC (permalink / raw)
To: netdev, linux-can; +Cc: Oliver Hartkopp
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
we can 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.
We use it as intended.
Patch 1/6 to remove the private CAN bus skb headroom infrastructure.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
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
@@ -200,11 +200,10 @@ static void init_can_skb_reserve(struct sk_buff *skb)
skb_reset_mac_header(skb);
skb_reset_network_header(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)
{
struct sk_buff *skb;
@@ -310,11 +309,10 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
/* af_packet does not apply CAN skb specific settings */
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;
/* perform proper loopback on capable devices */
if (dev->flags & IFF_ECHO)
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 5fb8d0e3f9c1..5c382ed61755 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -56,8 +56,9 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev,
canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *),
void *data);
extern int can_send(struct sk_buff *skb, int loop);
+extern 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
@@ -47,17 +47,15 @@ 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[];
};
static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb)
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
@@ -639,10 +639,20 @@ 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;
struct net *net = dev_net(dev);
struct can_pkg_stats *pkg_stats = net->can.pkg_stats;
@@ -650,13 +660,11 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
/* update statistics */
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();
/* deliver the packet to sockets listening on all devices */
matches = can_rcv_filter(net->can.rx_alldev_list, skb);
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
@@ -314,11 +314,10 @@ static void bcm_can_tx(struct bcm_op *op)
if (!skb)
goto out;
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
- can_skb_prv(skb)->skbcnt = 0;
skb_put_data(skb, cf, op->cfsiz);
/* send with loopback */
skb->dev = dev;
@@ -1342,11 +1341,10 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
kfree_skb(skb);
return -ENODEV;
}
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 */
dev_put(dev);
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
@@ -228,11 +228,10 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
return 1;
}
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);
ncf = (struct canfd_frame *)nskb->data;
skb_put_zero(nskb, so->ll.mtu);
@@ -778,11 +777,10 @@ static void isotp_send_cframe(struct isotp_sock *so)
return;
}
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);
/* create consecutive frame */
@@ -1007,11 +1005,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
goto err_out_drop;
}
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;
cf = (struct canfd_frame *)skb->data;
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
@@ -895,11 +895,10 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
if (!skb)
goto failure;
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);
if (ret < 0)
goto free_skb;
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
@@ -599,11 +599,10 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv,
return ERR_PTR(-ENOMEM);
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));
/* skb->cb must be large enough to hold a j1939_sk_buff_cb structure */
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*re_skcb));
@@ -1534,11 +1533,10 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
return NULL;
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));
session = j1939_session_new(priv, skb, size);
if (!session) {
diff --git a/net/can/raw.c b/net/can/raw.c
index 12293363413c..6ebc6abb5987 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -74,11 +74,11 @@ MODULE_ALIAS("can-proto-1");
* storing the single filter in dfilter, to avoid using dynamic memory.
*/
struct uniqframe {
const struct sk_buff *skb;
- int skbcnt;
+ __u32 hash;
unsigned int join_rx_count;
};
struct raw_sock {
struct sock sk;
@@ -162,21 +162,21 @@ 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;
this_cpu_inc(ro->uniq->join_rx_count);
/* drop frame until all enabled filters matched */
if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count)
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)
return;
}
@@ -956,11 +956,10 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (!skb)
goto put_dev;
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);
if (err < 0)
goto free_skb;
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread* [net-next 2/6] can: add CAN skb extension infrastructure
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
2026-01-25 20:15 ` [net-next 1/6] can: use skb hash instead of private variable in headroom Oliver Hartkopp
@ 2026-01-25 20:15 ` Oliver Hartkopp
2026-01-25 20:15 ` [net-next 3/6] can: move ifindex to CAN skb extensions Oliver Hartkopp
` (3 subsequent siblings)
5 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-25 20:15 UTC (permalink / raw)
To: netdev, linux-can; +Cc: Oliver Hartkopp
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
- introduce can_skb_ext_add() and can_skb_ext_find() helpers
Patch 2/6 to remove the private CAN bus skb headroom infrastructure.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
MAINTAINERS | 1 +
drivers/net/can/dev/skb.c | 56 +++++++++++++++++++++++++++++++--------
include/linux/can/skb.h | 11 ++++++++
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 | 13 +++++++++
net/can/isotp.c | 24 +++++++++++++++++
net/can/j1939/socket.c | 8 ++++++
net/can/j1939/transport.c | 15 +++++++++++
net/can/raw.c | 8 ++++++
net/core/skbuff.c | 4 +++
14 files changed, 182 insertions(+), 14 deletions(-)
create mode 100644 include/net/can.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 62392b61a52f..e1e2159c7105 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5632,10 +5632,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
F: Documentation/networking/can.rst
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
F: include/uapi/linux/can/gw.h
F: include/uapi/linux/can/isotp.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
@@ -4,10 +4,11 @@
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
*/
#include <linux/can/dev.h>
#include <linux/module.h>
+#include <net/can.h>
#define MOD_DESC "CAN device driver interface"
MODULE_DESCRIPTION(MOD_DESC);
MODULE_LICENSE("GPL v2");
@@ -205,40 +206,53 @@ 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);
init_can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
*cf = skb_put_zero(skb, sizeof(struct can_frame));
return skb;
+
+out_error_cc:
+ *cf = NULL;
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(alloc_can_skb);
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);
init_can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
@@ -247,26 +261,38 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
/* set CAN FD flag by default */
(*cfd)->flags = CANFD_FDF;
return skb;
+
+out_error_fd:
+ *cfd = NULL;
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(alloc_canfd_skb);
struct sk_buff *alloc_canxl_skb(struct net_device *dev,
struct canxl_frame **cxl,
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);
can_skb_prv(skb)->ifindex = dev->ifindex;
@@ -276,11 +302,11 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
(*cxl)->flags = CANXL_XLF;
(*cxl)->len = data_len;
return skb;
-out_error:
+out_error_xl:
*cxl = NULL;
return NULL;
}
EXPORT_SYMBOL_GPL(alloc_canxl_skb);
@@ -301,17 +327,25 @@ 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)
{
+ 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;
/* perform proper loopback on capable devices */
diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h
index 869ea574a40a..aeb08f47de48 100644
--- a/include/linux/can/skb.h
+++ b/include/linux/can/skb.h
@@ -12,10 +12,11 @@
#define _CAN_SKB_H
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/can.h>
+#include <net/can.h>
#include <net/sock.h>
void can_flush_echo_skb(struct net_device *dev);
int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
unsigned int idx, unsigned int frame_len);
@@ -66,10 +67,20 @@ static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb)
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)
+{
+ return skb_ext_add(skb, SKB_EXT_CAN);
+}
+
+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
* refcount may already be 0 (and the socket will be freed
* after the last TX skb has been freed). So only increase
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index e6bfe5d0c525..b5beb28e5730 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4986,10 +4986,13 @@ enum skb_ext_id {
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
SKB_EXT_MCTP,
#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..1331407a79a9
--- /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 <socketcan@hartkopp.net>
+ *
+ */
+
+#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
@@ -3,10 +3,11 @@
# Controller Area Network (CAN) network layer core configuration
#
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
1983 at Robert Bosch GmbH, and the protocol was officially
released in 1986. The CAN bus was originally mainly for automotive,
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
@@ -685,11 +685,12 @@ 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);
kfree_skb_reason(skb, SKB_DROP_REASON_CAN_RX_INVALID_FRAME);
return NET_RX_DROP;
@@ -700,11 +701,12 @@ 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);
kfree_skb_reason(skb, SKB_DROP_REASON_CANFD_RX_INVALID_FRAME);
return NET_RX_DROP;
@@ -715,11 +717,12 @@ 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);
kfree_skb_reason(skb, SKB_DROP_REASON_CANXL_RX_INVALID_FRAME);
return NET_RX_DROP;
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
@@ -57,10 +57,11 @@
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <net/can.h>
#include <net/sock.h>
#include <net/net_namespace.h>
/*
* To send multiple CAN frame content within TX_SETUP or to filter
@@ -289,10 +290,11 @@ static int bcm_proc_show(struct seq_file *m, void *v)
* of the given bcm tx op
*/
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;
/* no target device? => exit */
@@ -312,10 +314,16 @@ static void bcm_can_tx(struct bcm_op *op)
skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any());
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;
skb_put_data(skb, cf, op->cfsiz);
@@ -1315,10 +1323,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
*/
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;
/* we need a real device to send frames */
if (!ifindex)
@@ -1326,10 +1335,16 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), GFP_KERNEL);
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);
if (err < 0) {
kfree_skb(skb);
diff --git a/net/can/gw.c b/net/can/gw.c
index 55eccb1c7620..8894ab5d2087 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -53,10 +53,11 @@
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/gw.h>
+#include <net/can.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#define CAN_GW_NAME "can-gw"
@@ -457,10 +458,11 @@ static void cgw_csum_crc8_neg(struct canfd_frame *cf,
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;
/* process strictly Classic CAN or CAN FD frames */
if (gwj->flags & CGW_FLAGS_CAN_FD) {
@@ -469,10 +471,14 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
} else {
if (!can_is_can_skb(skb))
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).
*
* The Controller Area Network controllers only accept CAN frames with
@@ -516,10 +522,17 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
if (!nskb) {
gwj->dropped_frames++;
return;
}
+ ncsx = can_skb_ext_find(nskb);
+ 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;
/* first processing of this CAN frame -> adjust to private hop limit */
if (gwj->limit_hops && cgw_hops(nskb) == 1)
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 4bb60b8f9b96..c4fb8afde165 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -67,10 +67,11 @@
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/isotp.h>
#include <linux/slab.h>
+#include <net/can.h>
#include <net/sock.h>
#include <net/net_namespace.h>
MODULE_DESCRIPTION("PF_CAN ISO 15765-2 transport protocol");
MODULE_LICENSE("Dual BSD/GPL");
@@ -212,18 +213,25 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer)
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;
nskb = alloc_skb(so->ll.mtu + sizeof(struct can_skb_priv), gfp_any());
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);
return 1;
}
@@ -760,10 +768,11 @@ static void isotp_fill_dataframe(struct canfd_frame *cf, struct isotp_sock *so,
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;
int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
@@ -775,10 +784,17 @@ static void isotp_send_cframe(struct isotp_sock *so)
if (!skb) {
dev_put(dev);
return;
}
+ csx = can_skb_ext_add(skb);
+ if (!csx) {
+ kfree_skb(skb);
+ dev_put(dev);
+ return;
+ }
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
cf = (struct canfd_frame *)skb->data;
skb_put_zero(skb, so->ll.mtu);
@@ -936,10 +952,11 @@ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
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;
int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
@@ -1003,10 +1020,17 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (!skb) {
dev_put(dev);
goto err_out_drop;
}
+ csx = can_skb_ext_add(skb);
+ if (!csx) {
+ kfree_skb(skb);
+ dev_put(dev);
+ goto err_out_drop;
+ }
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = 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 1589e8ca634e..fc28a7677369 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -15,10 +15,11 @@
#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/errqueue.h>
#include <linux/if_arp.h>
+#include <net/can.h>
#include "j1939-priv.h"
#define J1939_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.j1939)
@@ -882,10 +883,11 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
int *errcode)
{
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,
size +
sizeof(struct can_frame) -
@@ -893,10 +895,16 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
sizeof(struct can_skb_priv),
msg->msg_flags & MSG_DONTWAIT, &ret);
if (!skb)
goto failure;
+ csx = can_skb_ext_add(skb);
+ if (!csx) {
+ kfree_skb(skb);
+ goto failure;
+ }
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = 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 d5d3e5320f7a..0514f3504e39 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -7,10 +7,11 @@
// Marc Kleine-Budde <kernel@pengutronix.de>
// Copyright (c) 2017-2019 Pengutronix,
// Oleksij Rempel <kernel@pengutronix.de>
#include <linux/can/skb.h>
+#include <net/can.h>
#include "j1939-priv.h"
#define J1939_XTP_TX_RETRY_LIMIT 100
@@ -589,17 +590,24 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv,
const struct j1939_sk_buff_cb *re_skcb,
bool ctl,
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),
GFP_ATOMIC);
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;
/* reserve CAN header */
skb_reserve(skb, offsetof(struct can_frame, data));
@@ -1523,17 +1531,24 @@ static struct
j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
int size,
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;
skb = alloc_skb(size + sizeof(struct can_skb_priv), GFP_ATOMIC);
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;
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 6ebc6abb5987..2eadf7e06993 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -51,10 +51,11 @@
#include <linux/can.h>
#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/raw.h>
+#include <net/can.h>
#include <net/sock.h>
#include <net/net_namespace.h>
MODULE_DESCRIPTION("PF_CAN raw protocol");
MODULE_LICENSE("Dual BSD/GPL");
@@ -916,10 +917,11 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
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;
int err = -EINVAL;
@@ -954,10 +956,16 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto put_dev;
+ csx = can_skb_ext_add(skb);
+ if (!csx) {
+ kfree_skb(skb);
+ goto put_dev;
+ }
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
/* fill the skb before testing for valid CAN frames */
err = memcpy_from_msg(skb_put(skb, size), msg, size);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 1a84c5a3c446..245f72b74e21 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -76,10 +76,11 @@
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
#include <net/mpls.h>
#include <net/mptcp.h>
#include <net/mctp.h>
+#include <net/can.h>
#include <net/page_pool/helpers.h>
#include <net/psp/types.h>
#include <net/dropreason.h>
#include <net/xdp_sock.h>
@@ -5137,10 +5138,13 @@ static const u8 skb_ext_type_len[] = {
[SKB_EXT_MCTP] = SKB_EXT_CHUNKSIZEOF(struct mctp_flow),
#endif
#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)
{
unsigned int l = SKB_EXT_CHUNKSIZEOF(struct skb_ext);
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread* [net-next 3/6] can: move ifindex to CAN skb extensions
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
2026-01-25 20:15 ` [net-next 1/6] can: use skb hash instead of private variable in headroom Oliver Hartkopp
2026-01-25 20:15 ` [net-next 2/6] can: add CAN skb extension infrastructure Oliver Hartkopp
@ 2026-01-25 20:15 ` Oliver Hartkopp
2026-01-25 20:15 ` [net-next 4/6] can: move frame_len " Oliver Hartkopp
` (2 subsequent siblings)
5 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-25 20:15 UTC (permalink / raw)
To: netdev, linux-can; +Cc: Oliver Hartkopp
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.
Patch 3/6 to remove the private CAN bus skb headroom infrastructure.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
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
@@ -221,11 +221,11 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
goto out_error_cc;
}
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));
return skb;
@@ -253,11 +253,11 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
goto out_error_fd;
}
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));
/* set CAN FD flag by default */
(*cfd)->flags = CANFD_FDF;
@@ -292,11 +292,11 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
goto out_error_xl;
}
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);
/* set CAN XL flag and length information by default */
(*cxl)->flags = CANXL_XLF;
@@ -342,12 +342,11 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
csx = can_skb_ext_add(skb);
if (!csx)
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 */
if (dev->flags & IFF_ECHO)
skb->pkt_type = PACKET_LOOPBACK;
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
@@ -321,11 +321,11 @@ static void bcm_can_tx(struct bcm_op *op)
kfree_skb(skb);
goto out;
}
can_skb_reserve(skb);
- can_skb_prv(skb)->ifindex = dev->ifindex;
+ csx->can_iif = dev->ifindex;
skb_put_data(skb, cf, op->cfsiz);
/* send with loopback */
skb->dev = dev;
@@ -1355,11 +1355,11 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
if (!dev) {
kfree_skb(skb);
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 */
dev_put(dev);
diff --git a/net/can/gw.c b/net/can/gw.c
index 8894ab5d2087..ad89a1913b34 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -503,11 +503,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
return;
}
/* 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()
*
* When there is at least one modification function activated,
diff --git a/net/can/isotp.c b/net/can/isotp.c
index c4fb8afde165..01ceb3febbb7 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -235,11 +235,11 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
kfree_skb(nskb);
return 1;
}
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);
ncf = (struct canfd_frame *)nskb->data;
skb_put_zero(nskb, so->ll.mtu);
@@ -792,11 +792,11 @@ static void isotp_send_cframe(struct isotp_sock *so)
dev_put(dev);
return;
}
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);
/* create consecutive frame */
@@ -1028,11 +1028,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
dev_put(dev);
goto err_out_drop;
}
can_skb_reserve(skb);
- can_skb_prv(skb)->ifindex = dev->ifindex;
+ csx->can_iif = dev->ifindex;
so->tx.len = size;
so->tx.idx = 0;
cf = (struct canfd_frame *)skb->data;
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
index fc28a7677369..f03bae79354e 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -902,11 +902,11 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
kfree_skb(skb);
goto failure;
}
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);
if (ret < 0)
goto free_skb;
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index 0514f3504e39..96e02ad4a726 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -606,11 +606,11 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv,
return ERR_PTR(-ENOMEM);
}
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));
/* skb->cb must be large enough to hold a j1939_sk_buff_cb structure */
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*re_skcb));
@@ -1547,11 +1547,11 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
return NULL;
}
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));
session = j1939_session_new(priv, skb, size);
if (!session) {
diff --git a/net/can/raw.c b/net/can/raw.c
index 2eadf7e06993..e26cda398e22 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -963,11 +963,11 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
kfree_skb(skb);
goto put_dev;
}
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);
if (err < 0)
goto free_skb;
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread* [net-next 4/6] can: move frame_len to CAN skb extensions
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
` (2 preceding siblings ...)
2026-01-25 20:15 ` [net-next 3/6] can: move ifindex to CAN skb extensions Oliver Hartkopp
@ 2026-01-25 20:15 ` Oliver Hartkopp
2026-01-25 20:16 ` [net-next 5/6] can: remove private CAN skb headroom infrastructure Oliver Hartkopp
2026-01-28 1:49 ` [net-next 0/6] move CAN skb headroom content to skb extensions Jakub Kicinski
5 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-25 20:15 UTC (permalink / raw)
To: netdev, linux-can; +Cc: Oliver Hartkopp
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.
Patch 4/6 to remove the private CAN bus skb headroom infrastructure.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
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
@@ -47,10 +47,11 @@ void can_flush_echo_skb(struct net_device *dev)
*/
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",
__func__, idx, priv->echo_skb_max);
return -EINVAL;
@@ -73,11 +74,13 @@ int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
/* make settings for echo to reduce code in irq context */
skb->ip_summed = CHECKSUM_UNNECESSARY;
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;
skb_tx_timestamp(skb);
@@ -110,20 +113,25 @@ __can_get_echo_skb(struct net_device *dev, unsigned int idx,
if (priv->echo_skb[idx]) {
/* Using "struct canfd_frame::len" for the frame
* 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));
/* 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;
if (skb->pkt_type == PACKET_LOOPBACK) {
skb->pkt_type = PACKET_BROADCAST;
@@ -179,14 +187,19 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx,
return;
}
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;
}
}
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread* [net-next 5/6] can: remove private CAN skb headroom infrastructure
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
` (3 preceding siblings ...)
2026-01-25 20:15 ` [net-next 4/6] can: move frame_len " Oliver Hartkopp
@ 2026-01-25 20:16 ` Oliver Hartkopp
2026-01-28 1:49 ` [net-next 0/6] move CAN skb headroom content to skb extensions Jakub Kicinski
5 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-25 20:16 UTC (permalink / raw)
To: netdev, linux-can; +Cc: Oliver Hartkopp
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.
Patch 5/6 to remove the private CAN bus skb headroom infrastructure.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
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 | 4 +---
7 files changed, 19 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
@@ -204,40 +204,33 @@ 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)
{
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;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto out_error_cc;
}
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));
return skb;
@@ -253,23 +246,22 @@ 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));
+ skb = netdev_alloc_skb(dev, sizeof(struct canfd_frame));
if (unlikely(!skb))
goto out_error_fd;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto out_error_fd;
}
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));
/* set CAN FD flag by default */
@@ -292,23 +284,22 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
struct can_skb_ext *csx;
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;
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);
+ init_can_skb(skb);
csx->can_iif = dev->ifindex;
*cxl = skb_put_zero(skb, CANXL_HDR_SIZE + data_len);
/* set CAN XL flag and length information by default */
@@ -338,18 +329,14 @@ struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
return skb;
}
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 */
if (!csx) {
csx = can_skb_ext_add(skb);
@@ -403,11 +390,11 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
default:
goto inval_skb;
}
- if (!can_skb_headroom_valid(dev, skb))
+ if (!can_skb_init_valid(dev, skb))
goto inval_skb;
return false;
inval_skb:
diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h
index aeb08f47de48..cacbbb185bb9 100644
--- a/include/linux/can/skb.h
+++ b/include/linux/can/skb.h
@@ -36,41 +36,10 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
unsigned int data_len);
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)
{
return 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
@@ -310,21 +310,20 @@ static void bcm_can_tx(struct bcm_op *op)
if (!dev) {
/* RFC: should this bcm_op remove itself here? */
return;
}
- skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any());
+ skb = alloc_skb(op->cfsiz, gfp_any());
if (!skb)
goto out;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto out;
}
- can_skb_reserve(skb);
csx->can_iif = dev->ifindex;
skb_put_data(skb, cf, op->cfsiz);
/* send with loopback */
@@ -1331,22 +1330,20 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
/* we need a real device to send frames */
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;
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);
if (err < 0) {
kfree_skb(skb);
return err;
}
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 01ceb3febbb7..434986cc8fe5 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -218,11 +218,11 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
struct can_skb_ext *csx;
struct canfd_frame *ncf;
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;
csx = can_skb_ext_add(nskb);
if (!csx) {
@@ -234,13 +234,11 @@ static int isotp_send_fc(struct sock *sk, int ae, u8 flowstatus)
if (!dev) {
kfree_skb(nskb);
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;
skb_put_zero(nskb, so->ll.mtu);
@@ -778,11 +776,11 @@ static void isotp_send_cframe(struct isotp_sock *so)
dev = dev_get_by_index(sock_net(sk), so->ifindex);
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;
}
@@ -791,11 +789,10 @@ static void isotp_send_cframe(struct isotp_sock *so)
kfree_skb(skb);
dev_put(dev);
return;
}
- can_skb_reserve(skb);
csx->can_iif = dev->ifindex;
cf = (struct canfd_frame *)skb->data;
skb_put_zero(skb, so->ll.mtu);
@@ -1013,12 +1010,12 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (!dev) {
err = -ENXIO;
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;
}
@@ -1027,11 +1024,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
kfree_skb(skb);
dev_put(dev);
goto err_out_drop;
}
- can_skb_reserve(skb);
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 f03bae79354e..10014d95c319 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -889,23 +889,21 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev,
int ret;
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;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto failure;
}
- can_skb_reserve(skb);
csx->can_iif = ndev->ifindex;
skb_reserve(skb, offsetof(struct can_frame, data));
ret = memcpy_from_msg(skb_put(skb, size), msg, size);
if (ret < 0)
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index 96e02ad4a726..e63cf79af1c4 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -593,23 +593,21 @@ sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv,
{
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),
- GFP_ATOMIC);
+ skb = alloc_skb(sizeof(struct can_frame), GFP_ATOMIC);
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);
csx->can_iif = priv->ndev->ifindex;
/* reserve CAN header */
skb_reserve(skb, offsetof(struct can_frame, data));
/* skb->cb must be large enough to hold a j1939_sk_buff_cb structure */
@@ -1535,22 +1533,21 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
struct sk_buff *skb;
struct can_skb_ext *csx;
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;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
return NULL;
}
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));
session = j1939_session_new(priv, skb, size);
diff --git a/net/can/raw.c b/net/can/raw.c
index e26cda398e22..2b4a238d2590 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -951,22 +951,20 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (can_cap_enabled(dev, CAN_CAP_RO)) {
err = -EACCES;
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;
csx = can_skb_ext_add(skb);
if (!csx) {
kfree_skb(skb);
goto put_dev;
}
- can_skb_reserve(skb);
csx->can_iif = dev->ifindex;
/* fill the skb before testing for valid CAN frames */
err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
--
2.47.3
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-25 20:15 [net-next 0/6] move CAN skb headroom content to skb extensions Oliver Hartkopp
` (4 preceding siblings ...)
2026-01-25 20:16 ` [net-next 5/6] can: remove private CAN skb headroom infrastructure Oliver Hartkopp
@ 2026-01-28 1:49 ` Jakub Kicinski
2026-01-28 8:42 ` Oliver Hartkopp
2026-01-28 11:35 ` Florian Westphal
5 siblings, 2 replies; 21+ messages in thread
From: Jakub Kicinski @ 2026-01-28 1:49 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: netdev, linux-can
On Sun, 25 Jan 2026 21:15:55 +0100 Oliver Hartkopp wrote:
> CAN bus related skbuffs (ETH_P_CAN/ETH_P_CANFD/ETH_P_CANXL) simply contain
> CAN frame structs for CAN CC/FD/XL of skb->len length at skb->data.
> Those CAN skbs do not have network/mac/transport headers nor other such
> references for encapsulated protocols like ethernet/IP protocols.
>
> To store data for CAN specific use-cases all CAN bus related skbuffs are
> created with a 16 byte private skb headroom (struct can_skb_priv).
> Using the skb headroom and accessing skb->head for this private data
> led to several problems in the past likely due to "The struct can_skb_priv
> business is highly unconventional for the networking stack." [1]
>
> This patch set aims to remove the unconventional skb headroom usage for
> CAN bus related skbuffs and use the common skb extensions instead.
This is fine. Wish we could make md_dst work, since skb_ext still burns
a bit in the skb (last extension bit in fact, next user will have to
bump the filed size). And you very much do not route these frames,
so dst would work perfectly fine. But whatever.
lore never received patch 6, tho, you'll need to repost.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 1:49 ` [net-next 0/6] move CAN skb headroom content to skb extensions Jakub Kicinski
@ 2026-01-28 8:42 ` Oliver Hartkopp
2026-01-28 9:07 ` Marc Kleine-Budde
2026-01-28 11:35 ` Florian Westphal
1 sibling, 1 reply; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-28 8:42 UTC (permalink / raw)
To: Jakub Kicinski, Marc Kleine-Budde; +Cc: netdev, linux-can
[-- Attachment #1: Type: text/plain, Size: 1795 bytes --]
On 28.01.26 02:49, Jakub Kicinski wrote:
> On Sun, 25 Jan 2026 21:15:55 +0100 Oliver Hartkopp wrote:
>> CAN bus related skbuffs (ETH_P_CAN/ETH_P_CANFD/ETH_P_CANXL) simply contain
>> CAN frame structs for CAN CC/FD/XL of skb->len length at skb->data.
>> Those CAN skbs do not have network/mac/transport headers nor other such
>> references for encapsulated protocols like ethernet/IP protocols.
>>
>> To store data for CAN specific use-cases all CAN bus related skbuffs are
>> created with a 16 byte private skb headroom (struct can_skb_priv).
>> Using the skb headroom and accessing skb->head for this private data
>> led to several problems in the past likely due to "The struct can_skb_priv
>> business is highly unconventional for the networking stack." [1]
>>
>> This patch set aims to remove the unconventional skb headroom usage for
>> CAN bus related skbuffs and use the common skb extensions instead.
>
> This is fine. Wish we could make md_dst work, since skb_ext still burns
> a bit in the skb (last extension bit in fact, next user will have to
> bump the filed size). And you very much do not route these frames,
> so dst would work perfectly fine. But whatever.
>
> lore never received patch 6, tho, you'll need to repost.
Thanks for looking at it.
In fact patch 6 stuck in my providers mail system which responded this
to (only) patch 6 ¯\_(ツ)_/¯
5.7.1 Refused by local policy. No SPAM please! (B-EX
155302::1769588601-AC38E895-223F95BA/10/71040183929) see
https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered
[MSG0011]
As this could not been solved since Sunday I'll kindly ask Marc to send
the attached v2 patchset (including your __u32 remark) to the mailing
list, so that also the AI bot can take a look at it.
Many thanks!
Oliver
[-- Attachment #2: can_skb_ext.tar.gz --]
[-- Type: application/gzip, Size: 14024 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 8:42 ` Oliver Hartkopp
@ 2026-01-28 9:07 ` Marc Kleine-Budde
2026-01-28 10:04 ` Marc Kleine-Budde
2026-01-29 6:44 ` Marc Kleine-Budde
0 siblings, 2 replies; 21+ messages in thread
From: Marc Kleine-Budde @ 2026-01-28 9:07 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
[-- Attachment #1: Type: text/plain, Size: 2323 bytes --]
On 28.01.2026 09:42:14, Oliver Hartkopp wrote:
> On 28.01.26 02:49, Jakub Kicinski wrote:
> > On Sun, 25 Jan 2026 21:15:55 +0100 Oliver Hartkopp wrote:
> > > CAN bus related skbuffs (ETH_P_CAN/ETH_P_CANFD/ETH_P_CANXL) simply contain
> > > CAN frame structs for CAN CC/FD/XL of skb->len length at skb->data.
> > > Those CAN skbs do not have network/mac/transport headers nor other such
> > > references for encapsulated protocols like ethernet/IP protocols.
> > >
> > > To store data for CAN specific use-cases all CAN bus related skbuffs are
> > > created with a 16 byte private skb headroom (struct can_skb_priv).
> > > Using the skb headroom and accessing skb->head for this private data
> > > led to several problems in the past likely due to "The struct can_skb_priv
> > > business is highly unconventional for the networking stack." [1]
> > >
> > > This patch set aims to remove the unconventional skb headroom usage for
> > > CAN bus related skbuffs and use the common skb extensions instead.
> >
> > This is fine. Wish we could make md_dst work, since skb_ext still burns
> > a bit in the skb (last extension bit in fact, next user will have to
> > bump the filed size). And you very much do not route these frames,
> > so dst would work perfectly fine. But whatever.
> >
> > lore never received patch 6, tho, you'll need to repost.
>
> Thanks for looking at it.
>
> In fact patch 6 stuck in my providers mail system which responded this to
> (only) patch 6 ¯\_(ツ)_/¯
>
> 5.7.1 Refused by local policy. No SPAM please! (B-EX
> 155302::1769588601-AC38E895-223F95BA/10/71040183929) see https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered
> [MSG0011]
>
> As this could not been solved since Sunday I'll kindly ask Marc to send the
> attached v2 patchset (including your __u32 remark) to the mailing list, so
> that also the AI bot can take a look at it.
I'll do. In the mean time, please have a look at b4, it has support for
sending patches via a web hook.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 9:07 ` Marc Kleine-Budde
@ 2026-01-28 10:04 ` Marc Kleine-Budde
2026-01-28 10:17 ` Oliver Hartkopp
2026-01-29 6:44 ` Marc Kleine-Budde
1 sibling, 1 reply; 21+ messages in thread
From: Marc Kleine-Budde @ 2026-01-28 10:04 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
[-- Attachment #1: Type: text/plain, Size: 1487 bytes --]
On 28.01.2026 10:07:13, Marc Kleine-Budde wrote:
> > In fact patch 6 stuck in my providers mail system which responded this to
> > (only) patch 6 ¯\_(ツ)_/¯
> >
> > 5.7.1 Refused by local policy. No SPAM please! (B-EX
> > 155302::1769588601-AC38E895-223F95BA/10/71040183929) see https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered
> > [MSG0011]
> >
> > As this could not been solved since Sunday I'll kindly ask Marc to send the
> > attached v2 patchset (including your __u32 remark) to the mailing list, so
> > that also the AI bot can take a look at it.
>
> I'll do. In the mean time, please have a look at b4, it has support for
> sending patches via a web hook.
Your ISP's mailserver also classified Patch#6 as spam when I was sending
it :/
| Reporting-MTA: dns; metis.whiteo.stw.pengutronix.de
|
| Action: failed
| Final-Recipient: rfc822;socketcan@hartkopp.net
| Status: 5.0.0
| Remote-MTA: dns; smtpin.rzone.de
| Diagnostic-Code: smtp; 550 5.7.1 Refused by local policy. No SPAM please! (B-EX 149500::1769594129-F202EB70-729865BA/10/71040183929) see https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered [MSG0011]
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 10:04 ` Marc Kleine-Budde
@ 2026-01-28 10:17 ` Oliver Hartkopp
0 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-28 10:17 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Jakub Kicinski, netdev, linux-can
On 28.01.26 11:04, Marc Kleine-Budde wrote:
> On 28.01.2026 10:07:13, Marc Kleine-Budde wrote:
>>> In fact patch 6 stuck in my providers mail system which responded this to
>>> (only) patch 6 ¯\_(ツ)_/¯
>>>
>>> 5.7.1 Refused by local policy. No SPAM please! (B-EX
>>> 155302::1769588601-AC38E895-223F95BA/10/71040183929) see https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered
>>> [MSG0011]
>>>
>>> As this could not been solved since Sunday I'll kindly ask Marc to send the
>>> attached v2 patchset (including your __u32 remark) to the mailing list, so
>>> that also the AI bot can take a look at it.
>>
>> I'll do. In the mean time, please have a look at b4, it has support for
>> sending patches via a web hook.
>
> Your ISP's mailserver also classified Patch#6 as spam when I was sending
> it :/
>
> | Reporting-MTA: dns; metis.whiteo.stw.pengutronix.de
> |
> | Action: failed
> | Final-Recipient: rfc822;socketcan@hartkopp.net
> | Status: 5.0.0
> | Remote-MTA: dns; smtpin.rzone.de
> | Diagnostic-Code: smtp; 550 5.7.1 Refused by local policy. No SPAM please! (B-EX 149500::1769594129-F202EB70-729865BA/10/71040183929) see https://www.strato-hosting.co.uk/faq/product/why-are-my-emails-not-being-delivered [MSG0011]
Yes. I was looking in my inbox where it was missing too o_O
Fortunately it showed up in patchwork :-)
I'll add that to my problem report ...
Best regards,
Oliver
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 9:07 ` Marc Kleine-Budde
2026-01-28 10:04 ` Marc Kleine-Budde
@ 2026-01-29 6:44 ` Marc Kleine-Budde
2026-01-29 7:49 ` Oliver Hartkopp
1 sibling, 1 reply; 21+ messages in thread
From: Marc Kleine-Budde @ 2026-01-29 6:44 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
[-- Attachment #1: Type: text/plain, Size: 477 bytes --]
On 28.01.2026 10:07:13, Marc Kleine-Budde wrote:
> I'll do. In the mean time, please have a look at b4, it has support for
> sending patches via a web hook.
Hooray for switching to b4!
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-29 6:44 ` Marc Kleine-Budde
@ 2026-01-29 7:49 ` Oliver Hartkopp
2026-01-29 8:42 ` Marc Kleine-Budde
0 siblings, 1 reply; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-29 7:49 UTC (permalink / raw)
To: Marc Kleine-Budde; +Cc: Jakub Kicinski, netdev, linux-can
On 29.01.26 07:44, Marc Kleine-Budde wrote:
> On 28.01.2026 10:07:13, Marc Kleine-Budde wrote:
>> I'll do. In the mean time, please have a look at b4, it has support for
>> sending patches via a web hook.
>
> Hooray for switching to b4!
Yes! Unfortunately one of the most relevant features does not work:
$ b4 prep --auto-to-cc
Will collect To: addresses using get_maintainer.pl
Will collect Cc: addresses using get_maintainer.pl
Traceback (most recent call last):
File "/usr/bin/b4", line 8, in <module>
sys.exit(cmd())
~~~^^
File "/usr/lib/python3/dist-packages/b4/command.py", line 417, in cmd
cmdargs.func(cmdargs)
~~~~~~~~~~~~^^^^^^^^^
File "/usr/lib/python3/dist-packages/b4/command.py", line 83, in cmd_prep
b4.ez.cmd_prep(cmdargs)
~~~~~~~~~~~~~~^^^^^^^^^
File "/usr/lib/python3/dist-packages/b4/ez.py", line 2835, in cmd_prep
auto_to_cc()
~~~~~~~~~~^^
File "/usr/lib/python3/dist-packages/b4/ez.py", line 2655, in auto_to_cc
logger.debug('added %s to seen', ltr.addr[1])
~~~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable
$ b4 --version
0.14.2
While get_maintainer.pl itself works ...
$ scripts/get_maintainer.pl net/can/bcm.c
Oliver Hartkopp <socketcan@hartkopp.net> (maintainer:CAN NETWORK LAYER)
Marc Kleine-Budde <mkl@pengutronix.de> (maintainer:CAN NETWORK LAYER)
linux-can@vger.kernel.org (open list:CAN NETWORK LAYER)
linux-kernel@vger.kernel.org (open list)
At least posting via b4 relay works fine and downloading of the v2
patchset you sent was comparably easy too.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-29 7:49 ` Oliver Hartkopp
@ 2026-01-29 8:42 ` Marc Kleine-Budde
0 siblings, 0 replies; 21+ messages in thread
From: Marc Kleine-Budde @ 2026-01-29 8:42 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
[-- Attachment #1: Type: text/plain, Size: 1688 bytes --]
On 29.01.2026 08:49:24, Oliver Hartkopp wrote:
> On 29.01.26 07:44, Marc Kleine-Budde wrote:
> > On 28.01.2026 10:07:13, Marc Kleine-Budde wrote:
> > > I'll do. In the mean time, please have a look at b4, it has support for
> > > sending patches via a web hook.
> >
> > Hooray for switching to b4!
>
> Yes! Unfortunately one of the most relevant features does not work:
>
> $ b4 prep --auto-to-cc
> Will collect To: addresses using get_maintainer.pl
> Will collect Cc: addresses using get_maintainer.pl
> Traceback (most recent call last):
> File "/usr/bin/b4", line 8, in <module>
> sys.exit(cmd())
> ~~~^^
> File "/usr/lib/python3/dist-packages/b4/command.py", line 417, in cmd
> cmdargs.func(cmdargs)
> ~~~~~~~~~~~~^^^^^^^^^
> File "/usr/lib/python3/dist-packages/b4/command.py", line 83, in cmd_prep
> b4.ez.cmd_prep(cmdargs)
> ~~~~~~~~~~~~~~^^^^^^^^^
> File "/usr/lib/python3/dist-packages/b4/ez.py", line 2835, in cmd_prep
> auto_to_cc()
> ~~~~~~~~~~^^
> File "/usr/lib/python3/dist-packages/b4/ez.py", line 2655, in auto_to_cc
> logger.debug('added %s to seen', ltr.addr[1])
> ~~~~~~~~^^^
> TypeError: 'NoneType' object is not subscriptable
>
>
> $ b4 --version
> 0.14.2
I'm using b4.git https://kernel.googlesource.com/pub/scm/utils/b4/b4.git
and it works for me.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 1:49 ` [net-next 0/6] move CAN skb headroom content to skb extensions Jakub Kicinski
2026-01-28 8:42 ` Oliver Hartkopp
@ 2026-01-28 11:35 ` Florian Westphal
2026-01-28 12:52 ` Oliver Hartkopp
1 sibling, 1 reply; 21+ messages in thread
From: Florian Westphal @ 2026-01-28 11:35 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: Oliver Hartkopp, netdev, linux-can
Jakub Kicinski <kuba@kernel.org> wrote:
> This is fine. Wish we could make md_dst work, since skb_ext still burns
> a bit in the skb (last extension bit in fact, next user will have to
> bump the filed size). And you very much do not route these frames,
> so dst would work perfectly fine. But whatever.
An alternative would be to 'union' extensions that cannot be
active at the same time. Something like the br netfilter extension
for example.
When the first extensions were added all of them could be enabled
at same time, but I think that has changed.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 11:35 ` Florian Westphal
@ 2026-01-28 12:52 ` Oliver Hartkopp
2026-01-28 13:18 ` Florian Westphal
0 siblings, 1 reply; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-28 12:52 UTC (permalink / raw)
To: Florian Westphal, Jakub Kicinski; +Cc: netdev, linux-can
On 28.01.26 12:35, Florian Westphal wrote:
> Jakub Kicinski <kuba@kernel.org> wrote:
>> This is fine. Wish we could make md_dst work, since skb_ext still burns
>> a bit in the skb (last extension bit in fact, next user will have to
>> bump the filed size). And you very much do not route these frames,
>> so dst would work perfectly fine. But whatever.
>
> An alternative would be to 'union' extensions that cannot be
> active at the same time. Something like the br netfilter extension
> for example.
>
> When the first extensions were added all of them could be enabled
> at same time, but I think that has changed.
IMO we do not need to 'union' extensions as long as automatic enum
calculation does it job with the enabled Kconfig options.
My only concern would be distribution kernels that have an all-yes
config policy ;-)
Btw. while we are at it ...
With my patch set the enum would now look like this:
#ifdef CONFIG_SKB_EXTENSIONS
enum skb_ext_id {
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
SKB_EXT_BRIDGE_NF,
#endif
#ifdef CONFIG_XFRM
SKB_EXT_SEC_PATH,
#endif
#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
TC_SKB_EXT,
#endif
#if IS_ENABLED(CONFIG_MPTCP)
SKB_EXT_MPTCP,
#endif
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
SKB_EXT_MCTP,
#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 */
};
=> SKB_EXT_NUM is then 7
When we (correctly) add another extension, SKB_EXT_NUM would become 8
which is still fine IMO. But then the BUILD_BUG_ON check in
skb_extensions_init() would need the below fix, right?
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 648c20e19038..609851d70173 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -5156,11 +5156,11 @@ static __always_inline unsigned int
skb_ext_total_length(void)
return l;
}
static void skb_extensions_init(void)
{
- BUILD_BUG_ON(SKB_EXT_NUM >= 8);
+ BUILD_BUG_ON(SKB_EXT_NUM > 8);
#if !IS_ENABLED(CONFIG_KCOV_INSTRUMENT_ALL)
BUILD_BUG_ON(skb_ext_total_length() > 255);
#endif
skbuff_ext_cache = kmem_cache_create("skbuff_ext_cache",
Should I send a proper patch?
Best regards,
Oliver
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 12:52 ` Oliver Hartkopp
@ 2026-01-28 13:18 ` Florian Westphal
2026-01-28 16:14 ` Oliver Hartkopp
0 siblings, 1 reply; 21+ messages in thread
From: Florian Westphal @ 2026-01-28 13:18 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> > When the first extensions were added all of them could be enabled
> > at same time, but I think that has changed.
>
> IMO we do not need to 'union' extensions as long as automatic enum
> calculation does it job with the enabled Kconfig options.
>
> My only concern would be distribution kernels that have an all-yes
> config policy ;-)
Well, thats the norm, no? Allmodconfig.
So we have two issues:
1. Turn active_extensions in sk_buff into u16
2. Growing memory usage of the skb_ext blob.
No need to add this 'union' now of course, but I think its something
that should be kept in mind: originally, all extensions could be
turned on for the same skb, but it looks like we now have mutually
exclusive ones.
> With my patch set the enum would now look like this:
>
> #ifdef CONFIG_SKB_EXTENSIONS
> enum skb_ext_id {
> #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
> SKB_EXT_BRIDGE_NF,
> #endif
> #ifdef CONFIG_XFRM
> SKB_EXT_SEC_PATH,
> #endif
> #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
> TC_SKB_EXT,
> #endif
> #if IS_ENABLED(CONFIG_MPTCP)
> SKB_EXT_MPTCP,
> #endif
> #if IS_ENABLED(CONFIG_MCTP_FLOWS)
> SKB_EXT_MCTP,
> #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 */
> };
>
> => SKB_EXT_NUM is then 7
>
> When we (correctly) add another extension, SKB_EXT_NUM would become 8
> which is still fine IMO. But then the BUILD_BUG_ON check in
> skb_extensions_init() would need the below fix, right?
>
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index 648c20e19038..609851d70173 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -5156,11 +5156,11 @@ static __always_inline unsigned int
> skb_ext_total_length(void)
> return l;
> }
>
> static void skb_extensions_init(void)
> {
> - BUILD_BUG_ON(SKB_EXT_NUM >= 8);
> + BUILD_BUG_ON(SKB_EXT_NUM > 8);
True, the last valid extension id can't be 8, but
SKB_EXT_NUM could be.
> Should I send a proper patch?
You can, otoh I think we should have consensus on what
to do when the 8th extension is added, do we just s/u8/u16' and
eat the growing memory cost for the skb_ext growth, or do we go
to a drawing board and figure out how to best merge mutually exclusive
extensions to save space?
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 13:18 ` Florian Westphal
@ 2026-01-28 16:14 ` Oliver Hartkopp
2026-01-28 16:25 ` Florian Westphal
0 siblings, 1 reply; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-28 16:14 UTC (permalink / raw)
To: Florian Westphal; +Cc: Jakub Kicinski, netdev, linux-can
On 28.01.26 14:18, Florian Westphal wrote:
> Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>>> When the first extensions were added all of them could be enabled
>>> at same time, but I think that has changed.
>>
>> IMO we do not need to 'union' extensions as long as automatic enum
>> calculation does it job with the enabled Kconfig options.
>>
>> My only concern would be distribution kernels that have an all-yes
>> config policy ;-)
>
> Well, thats the norm, no? Allmodconfig.
> So we have two issues:
> 1. Turn active_extensions in sk_buff into u16
> 2. Growing memory usage of the skb_ext blob.
>
> No need to add this 'union' now of course, but I think its something
> that should be kept in mind: originally, all extensions could be
> turned on for the same skb, but it looks like we now have mutually
> exclusive ones.
Ok. But then I don't see any real pressure to do the extension right
now. There doesn't seem so much changes adding new skb extensions users.
And we still have a free slot even if all users would have been
enabled (which is not the case due to the mutually exclusive options).
>> static void skb_extensions_init(void)
>> {
>> - BUILD_BUG_ON(SKB_EXT_NUM >= 8);
>> + BUILD_BUG_ON(SKB_EXT_NUM > 8);
>
> True, the last valid extension id can't be 8, but
> SKB_EXT_NUM could be.
>
>> Should I send a proper patch?
>
> You can, otoh I think we should have consensus on what
> to do when the 8th extension is added, do we just s/u8/u16' and
> eat the growing memory cost for the skb_ext growth, or do we go
> to a drawing board and figure out how to best merge mutually exclusive
> extensions to save space?
The good thing is that skb extensions are very efficient. Which leads to
the bad thing that we only can detect the problems at build time with
BUILD_BUG_ON(SKB_EXT_NUM > 8);
BUILD_BUG_ON(skb_ext_total_length() > 255);
For (SKB_EXT_NUM > 8) the upgrade of active_extensions to u16 should
simply make it. Probably with some #ifdef magic only.
But thinking about BUILD_BUG_ON(skb_ext_total_length() > 255):
Shouldn't this be SKB_EXT_CHUNKSIZEOF(struct skb_ext) + sum of the skb
ext user data which can be up to 255 * SKB_EXT_ALIGN_VALUE (= 2040) ???
The offset calculation with the multiplication of SKB_EXT_ALIGN_VALUE
(== 8) is the real magic in skb_ext_get_ptr(). therefore it should be
something like
BUILD_BUG_ON(skb_ext_total_length() - SKB_EXT_CHUNKSIZEOF(struct
skb_ext) > 255 * SKB_EXT_ALIGN_VALUE)
right?
We could create a new BUILD_BUG_ON with all SKB_EXT_CHUNKSIZEOF'ed skb
ext user data sizeof(structs) being < 255 * SKB_EXT_ALIGN_VALUE
IMO there's already plenty of space.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 16:14 ` Oliver Hartkopp
@ 2026-01-28 16:25 ` Florian Westphal
2026-01-28 16:34 ` Oliver Hartkopp
0 siblings, 1 reply; 21+ messages in thread
From: Florian Westphal @ 2026-01-28 16:25 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Jakub Kicinski, netdev, linux-can
Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> Ok. But then I don't see any real pressure to do the extension right
> now. There doesn't seem so much changes adding new skb extensions users.
> And we still have a free slot even if all users would have been
> enabled (which is not the case due to the mutually exclusive options).
Yes.
> The good thing is that skb extensions are very efficient. Which leads to
> the bad thing that we only can detect the problems at build time with
>
> BUILD_BUG_ON(SKB_EXT_NUM > 8);
> BUILD_BUG_ON(skb_ext_total_length() > 255);
>
> For (SKB_EXT_NUM > 8) the upgrade of active_extensions to u16 should
> simply make it. Probably with some #ifdef magic only.
>
> But thinking about BUILD_BUG_ON(skb_ext_total_length() > 255):
>
> Shouldn't this be SKB_EXT_CHUNKSIZEOF(struct skb_ext) + sum of the skb
> ext user data which can be up to 255 * SKB_EXT_ALIGN_VALUE (= 2040) ???
Why? Its not about the size in bytes, its there to make sure
ext->offset[] (u8) won't overflow for any of the extensions.
Maybe a comment would help.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [net-next 0/6] move CAN skb headroom content to skb extensions
2026-01-28 16:25 ` Florian Westphal
@ 2026-01-28 16:34 ` Oliver Hartkopp
0 siblings, 0 replies; 21+ messages in thread
From: Oliver Hartkopp @ 2026-01-28 16:34 UTC (permalink / raw)
To: Florian Westphal; +Cc: Jakub Kicinski, netdev, linux-can
On 28.01.26 17:25, Florian Westphal wrote:
> Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>> But thinking about BUILD_BUG_ON(skb_ext_total_length() > 255):
>>
>> Shouldn't this be SKB_EXT_CHUNKSIZEOF(struct skb_ext) + sum of the skb
>> ext user data which can be up to 255 * SKB_EXT_ALIGN_VALUE (= 2040) ???
>
> Why? Its not about the size in bytes, its there to make sure
> ext->offset[] (u8) won't overflow for any of the extensions.
>
> Maybe a comment would help.
Oh I misread SKB_EXT_CHUNKSIZEOF m(
My bad. Sorry for the noise.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 21+ messages in thread