Commit 96ea3a1e authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Paolo Abeni
Browse files

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: default avatarkernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202602010426.PnGrYAk3-lkp@intel.com/


Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20260201-can_skb_ext-v8-2-3635d790fe8b@hartkopp.net


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent d4fb6514
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5634,6 +5634,7 @@ F: Documentation/networking/iso15765-2.rst
F:	include/linux/can/can-ml.h
F:	include/linux/can/core.h
F:	include/linux/can/skb.h
F:	include/net/can.h
F:	include/net/netns/can.h
F:	include/uapi/linux/can.h
F:	include/uapi/linux/can/bcm.h
+45 −11
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

#include <linux/can/dev.h>
#include <linux/module.h>
#include <net/can.h>

#define MOD_DESC "CAN device driver interface"

@@ -207,13 +208,17 @@ static void init_can_skb_reserve(struct sk_buff *skb)
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
{
	struct sk_buff *skb;
	struct can_skb_ext *csx;

	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
			       sizeof(struct can_frame));
	if (unlikely(!skb)) {
		*cf = NULL;
	if (unlikely(!skb))
		goto out_error_cc;

		return NULL;
	csx = can_skb_ext_add(skb);
	if (!csx) {
		kfree_skb(skb);
		goto out_error_cc;
	}

	skb->protocol = htons(ETH_P_CAN);
@@ -223,6 +228,11 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
	*cf = skb_put_zero(skb, sizeof(struct can_frame));

	return skb;

out_error_cc:
	*cf = NULL;

	return NULL;
}
EXPORT_SYMBOL_GPL(alloc_can_skb);

@@ -230,13 +240,17 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
				struct canfd_frame **cfd)
{
	struct sk_buff *skb;
	struct can_skb_ext *csx;

	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
			       sizeof(struct canfd_frame));
	if (unlikely(!skb)) {
		*cfd = NULL;
	if (unlikely(!skb))
		goto out_error_fd;

		return NULL;
	csx = can_skb_ext_add(skb);
	if (!csx) {
		kfree_skb(skb);
		goto out_error_fd;
	}

	skb->protocol = htons(ETH_P_CANFD);
@@ -249,6 +263,11 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
	(*cfd)->flags = CANFD_FDF;

	return skb;

out_error_fd:
	*cfd = NULL;

	return NULL;
}
EXPORT_SYMBOL_GPL(alloc_canfd_skb);

@@ -257,14 +276,21 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,
				unsigned int data_len)
{
	struct sk_buff *skb;
	struct can_skb_ext *csx;

	if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN)
		goto out_error;
		goto out_error_xl;

	skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
			       CANXL_HDR_SIZE + data_len);
	if (unlikely(!skb))
		goto out_error;
		goto out_error_xl;

	csx = can_skb_ext_add(skb);
	if (!csx) {
		kfree_skb(skb);
		goto out_error_xl;
	}

	skb->protocol = htons(ETH_P_CANXL);
	init_can_skb_reserve(skb);
@@ -278,7 +304,7 @@ struct sk_buff *alloc_canxl_skb(struct net_device *dev,

	return skb;

out_error:
out_error_xl:
	*cxl = NULL;

	return NULL;
@@ -303,13 +329,21 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb);
/* Check for outgoing skbs that have not been created by the CAN subsystem */
static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
{
	struct can_skb_ext *csx = can_skb_ext_find(skb);

	/* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */
	if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv)))
		return false;

	/* af_packet does not apply CAN skb specific settings */
	if (skb->ip_summed == CHECKSUM_NONE) {
		/* init headroom */
	if (skb->ip_summed == CHECKSUM_NONE || !csx) {
		/* init CAN skb content */
		if (!csx) {
			csx = can_skb_ext_add(skb);
			if (!csx)
				return false;
		}

		can_skb_prv(skb)->ifindex = dev->ifindex;

		skb->ip_summed = CHECKSUM_UNNECESSARY;
+13 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/can/vxcan.h>
#include <linux/can/can-ml.h>
#include <linux/slab.h>
#include <net/can.h>
#include <net/rtnetlink.h>

#define DRV_NAME "vxcan"
@@ -39,6 +40,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
	struct vxcan_priv *priv = netdev_priv(dev);
	struct net_device *peer;
	struct net_device_stats *peerstats, *srcstats = &dev->stats;
	struct can_skb_ext *csx;
	struct sk_buff *skb;
	unsigned int len;

@@ -63,6 +65,17 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
		goto out_unlock;
	}

	/* the cloned skb points to the skb extension of the already cloned
	 * oskb with an increased refcount. skb_ext_add() creates a copy to
	 * separate the skb extension data which is needed to start with a
	 * fresh can_gw_hops counter in the other namespace.
	 */
	csx = skb_ext_add(skb, SKB_EXT_CAN);
	if (!csx) {
		kfree_skb(skb);
		goto out_unlock;
	}

	/* reset CAN GW hop counter */
	skb->csum_start = 0;
	skb->pkt_type   = PACKET_BROADCAST;
+17 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#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);
@@ -68,6 +69,22 @@ static inline void can_skb_reserve(struct sk_buff *skb)
	skb_reserve(skb, sizeof(struct can_skb_priv));
}

static inline struct can_skb_ext *can_skb_ext_add(struct sk_buff *skb)
{
	struct can_skb_ext *csx = skb_ext_add(skb, SKB_EXT_CAN);

	/* skb_ext_add() returns uninitialized space */
	if (csx)
		csx->can_gw_hops = 0;

	return csx;
}

static inline struct can_skb_ext *can_skb_ext_find(struct sk_buff *skb)
{
	return skb_ext_find(skb, SKB_EXT_CAN);
}

static inline void can_skb_set_owner(struct sk_buff *skb, struct sock *sk)
{
	/* If the socket has already been closed by user space, the
+3 −0
Original line number Diff line number Diff line
@@ -4989,6 +4989,9 @@ enum skb_ext_id {
#endif
#if IS_ENABLED(CONFIG_INET_PSP)
	SKB_EXT_PSP,
#endif
#if IS_ENABLED(CONFIG_CAN)
	SKB_EXT_CAN,
#endif
	SKB_EXT_NUM, /* must be last */
};
Loading