summaryrefslogtreecommitdiff
path: root/net/can
diff options
context:
space:
mode:
authorOliver Hartkopp <socketcan@hartkopp.net>2026-02-01 15:33:17 +0100
committerPaolo Abeni <pabeni@redhat.com>2026-02-05 11:58:39 +0100
commit96ea3a1e2d317e7ecb6b65dc65c9dd917905a6a8 (patch)
tree2780347dd8e2aea4538e15f43a207e193edf7ab7 /net/can
parentd4fb6514ff8ed6912a71294e6b66a5d59ee88007 (diff)
downloadlinux-next-96ea3a1e2d317e7ecb6b65dc65c9dd917905a6a8.tar.gz
linux-next-96ea3a1e2d317e7ecb6b65dc65c9dd917905a6a8.zip
can: add CAN skb extension infrastructure
To remove the private CAN bus skb headroom infrastructure 8 bytes need to be stored in the skb. The skb extensions are a common pattern and an easy and efficient way to hold private data travelling along with the skb. We only need the skb_ext_add() and skb_ext_find() functions to allocate and access CAN specific content as the skb helpers to copy/clone/free skbs automatically take care of skb extensions and their final removal. This patch introduces the complete CAN skb extensions infrastructure: - add struct can_skb_ext in new file include/net/can.h - add include/net/can.h in MAINTAINERS - add SKB_EXT_CAN to skbuff.c and skbuff.h - select SKB_EXTENSIONS in Kconfig when CONFIG_CAN is enabled - check for existing CAN skb extensions in can_rcv() in af_can.c - add CAN skb extensions allocation at every skb_alloc() location - duplicate the skb extensions if cloning outgoing skbs (framelen/gw_hops) - introduce can_skb_ext_add() and can_skb_ext_find() helpers The patch also corrects an indention issue in the original code from 2018: Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202602010426.PnGrYAk3-lkp@intel.com/ Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Link: https://patch.msgid.link/20260201-can_skb_ext-v8-2-3635d790fe8b@hartkopp.net Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'net/can')
-rw-r--r--net/can/Kconfig1
-rw-r--r--net/can/af_can.c9
-rw-r--r--net/can/bcm.c15
-rw-r--r--net/can/gw.c17
-rw-r--r--net/can/isotp.c25
-rw-r--r--net/can/j1939/socket.c9
-rw-r--r--net/can/j1939/transport.c26
-rw-r--r--net/can/raw.c9
8 files changed, 108 insertions, 3 deletions
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 <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>
@@ -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 <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>
@@ -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 <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>
@@ -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 <linux/can/skb.h>
#include <linux/errqueue.h>
#include <linux/if_arp.h>
+#include <net/can.h>
#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 <kernel@pengutronix.de>
#include <linux/can/skb.h>
+#include <net/can.h>
#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 <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>
@@ -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;