FAILED: patch "[PATCH] can: fix unique skb identifier regression under RPS" failed to apply to 5.10-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x 7093c3b8314a4ef7405c4066a2521398c455efe7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101253-guzzler-atom-1993@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 7093c3b8314a4ef7405c4066a2521398c455efe7 Mon Sep 17 00:00:00 2001
From: Oliver Hartkopp <[email protected]>
Date: Thu, 1 Oct 2026 13:57:24 +0200
Subject: [PATCH] can: fix unique skb identifier regression under RPS
Commit d4fb6514ff8e ("can: use skb hash instead of private variable in headroom") moved the per-skb unique identifier used for raw_rcv() duplicate detection into skb->hash.
With RPS enabled, get_rps_cpu() calls skb_get_hash() before the frame reaches the CAN subsystem. Since CAN skbs have no L3/L4 headers, the flow dissector assigns every CAN frame the same non-zero software hash. can_set_skb_uid() only generated a new identifier when skb->hash was 0, so it kept this constant hash. When the SLAB allocator later reused the same skb address, raw_rcv() mistook the next legitimate frame for a duplicate and dropped it.
Fix this by storing the CAN UID in the CAN skb extension (struct can_skb_ext::can_skb_uid) instead of skb->hash, decoupling it from any hash the network stack may compute. can_set_skb_uid() keeps its "assign only if unset" behaviour, which preserves UIDs set before transmission (e.g. by isotp to identify echo frames) across the local loopback path.
Frames whose extension is shared with another clone (e.g. via tc mirred or netem duplicate) are given a private extension copy before the UID is assigned, so that independently received skb clones from real CAN interfaces (can_skb_uid = 0) don't end up with the same UID. The limitation that tc mirred/netem skb duplicates might be dropped in raw_rcv for looped back isotp echo frame skbs is accepted. There is no valid use-case for tc mirred or netem together with isotp which is not capable to operate on different CAN interfaces simultaneously. For routing and modifying CAN frames together with isotp use can-gw.
can-gw and vxcan additionally clear the UID of forwarded/duplicated frames so each newly routed frame gets its own unique identifier.
drivers/net/can/vxcan.c +3 -0
@@ -79,6 +79,9 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)/* reset CAN GW hop counter */ csx->can_gw_hops =0 ; +/* start with new CAN skb UID in the other namespace */ + csx->can_skb_uid =0 ; + skb->pkt_type =PACKET_BROADCAST ; skb->dev = peer; skb->ip_summed =CHECKSUM_UNNECESSARY ;
include/linux/can/core.h +2 -1
@@ -17,6 +17,7 @@#include <linux/can.h> #include <linux/skbuff.h> #include <linux/netdevice.h> +#include <net/can.h> #define DNAME (dev) ((dev) ? (dev)->name : "any")
@@ -58,7 +59,7 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev, void *data);extern int can_send (struct sk_buff *skb,int loop); -void can_set_skb_uid (struct sk_buff *skb); +void can_set_skb_uid (struct can_skb_ext *csx);void can_sock_destruct (struct sock *sk);#endif /* !_CAN_CORE_H */
include/linux/can/skb.h +3 -1
@@ -43,8 +43,10 @@ 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) +if (csx) { csx->can_gw_hops =0 ; + csx->can_skb_uid =0 ; + }return csx; }
include/net/can.h +2 -0
@@ -17,12 +17,14 @@* @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 +* @can_skb_uid: CAN skb UID for raw_rcv and isotp echo handling */ struct can_skb_ext {int can_iif;u16 can_framelen;u8 can_gw_hops;u8 can_ext_flags; +u32 can_skb_uid; };#endif /* _NET_CAN_H */
net/can/af_can.c +36 -11
99 lines not on this page · raw message
net/can/gw.c +3 -0
@@ -527,6 +527,9 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)/* put the incremented hop counter in the cloned skb */ ncsx->can_gw_hops = csx->can_gw_hops +1 ; +/* force a new CAN UID generation for the routed frame */ + ncsx->can_skb_uid =0 ; +/* first processing of this CAN frame -> adjust to private hop limit */ if (gwj->limit_hops && ncsx->can_gw_hops ==1 ) ncsx->can_gw_hops = max_hops - gwj->limit_hops +1 ;
net/can/isotp.c +10 -6
@@ -891,7 +891,7 @@ static void isotp_send_cframe(struct isotp_sock *so) csx->can_iif = dev->ifindex;/* set uid in tx skb to identify CF echo frames */ -can_set_skb_uid (skb); +can_set_skb_uid (csx); cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);
@@ -917,7 +917,7 @@ static void isotp_send_cframe(struct isotp_sock *so)pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , old_cfecho);/* set consecutive frame echo tag */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid);/* send frame with local echo enabled */ can_send_ret =can_send (skb,1 );
@@ -969,18 +969,22 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) {struct sock *sk = (struct sock *)data;struct isotp_sock *so =isotp_sk (sk); +struct can_skb_ext *csx =can_skb_ext_find (skb);/* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk)return ; +if (WARN_ON_ONCE (!csx)) +return ; +/* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock * (no isotp_rcv() caller here), so take it ourselves */ spin_lock (&so->rx_lock);/* so->cfecho may since belong to a new transfer; recheck under lock */ -if (READ_ONCE (so->cfecho) != skb->hash) +if (READ_ONCE (so->cfecho) != csx->can_skb_uid) goto out_unlock;/* cancel local echo timeout */
@@ -1222,7 +1226,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) csx->can_iif = dev->ifindex;/* set uid in tx skb to identify CF echo frames */ -can_set_skb_uid (skb); +can_set_skb_uid (csx); so->tx.len = size; so->tx.idx =0 ;
@@ -1261,7 +1265,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[ae] |= size;/* set CF echo tag for isotp_rcv_echo() (SF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid); }else {/* send first frame */
@@ -1278,7 +1282,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->txfc.bs =0 ;/* set CF echo tag for isotp_rcv_echo() (CF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid); }else {/* standard flow control check */ new_state =ISOTP_WAIT_FIRST_FC ;
net/can/raw.c +7 -3
@@ -77,7 +77,7 @@ MODULE_ALIAS("can-proto-1");struct uniqframe {const struct sk_buff *skb; -u32 hash; +u32 can_skb_uid;unsigned int join_rx_count; };
@@ -133,12 +133,16 @@ static void raw_rcv(struct sk_buff *oskb, void *data)enum skb_drop_reason reason;struct sockaddr_can *addr;struct sk_buff *skb; +struct can_skb_ext *csx =can_skb_ext_find (oskb);unsigned int *pflags;/* check the received tx sock reference */ if (!ro->recv_own_msgs && oskb->sk == sk)return ; +if (WARN_ON_ONCE (!csx)) +return ; +/* make sure to not pass oversized frames to the socket */ if (!ro->fd_frames &&can_is_canfd_skb (oskb))return ;
@@ -165,7 +169,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data)/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr (ro->uniq)->skb == oskb && -this_cpu_ptr (ro->uniq)->hash == oskb->hash) { +this_cpu_ptr (ro->uniq)->can_skb_uid == csx->can_skb_uid) {if (!ro->join_filters)return ;
@@ -175,7 +179,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data)return ; }else {this_cpu_ptr (ro->uniq)->skb = oskb; -this_cpu_ptr (ro->uniq)->hash = oskb->hash; +this_cpu_ptr (ro->uniq)->can_skb_uid = csx->can_skb_uid;this_cpu_ptr (ro->uniq)->join_rx_count =1 ;/* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count >1 )
Oliver Hartkopp
maintainerQuoted 59 lines · On 12.10.26 13:33, [email protected] wrote:
The patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkoutFETCH_HEAD
git cherry-pick -x7093c3b8314a4ef7405c4066a2521398c455efe7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101253-guzzler-atom-1993@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From
7093c3b8314a4ef7405c4066a2521398c455efe7Mon Sep 17 00:00:00 2001
From: Oliver Hartkopp <[email protected]>
Date: Thu, 1 Oct 2026 13:57:24 +0200
Subject: [PATCH] can: fix unique skb identifier regression under RPSCommit
d4fb6514ff8e("can: use skb hash instead of private variable in headroom") moved the per-skb unique identifier used forraw_rcv()duplicate detection intoskb->hash.With RPS enabled,
get_rps_cpu()callsskb_get_hash()before the frame reaches the CAN subsystem. Since CAN skbs have no L3/L4 headers, the flow dissector assigns every CAN frame the same non-zero software hash.can_set_skb_uid()only generated a new identifier whenskb->hashwas 0, so it kept this constant hash. When the SLAB allocator later reused the same skb address,raw_rcv()mistook the next legitimate frame for a duplicate and dropped it.Fix this by storing the CAN UID in the CAN skb extension (struct
can_skb_ext::can_skb_uid) instead ofskb->hash, decoupling it from any hash the network stack may compute.can_set_skb_uid()keeps its "assign only if unset" behaviour, which preserves UIDs set before transmission (e.g. by isotp to identify echo frames) across the local loopback path.Frames whose extension is shared with another clone (e.g. via tc mirred or netem duplicate) are given a private extension copy before the UID is assigned, so that independently received skb clones from real CAN interfaces (
can_skb_uid= 0) don't end up with the same UID. The limitation that tc mirred/netem skb duplicates might be dropped inraw_rcvfor looped back isotp echo frame skbs is accepted. There is no valid use-case for tc mirred or netem together with isotp which is not capable to operate on different CAN interfaces simultaneously. For routing and modifying CAN frames together with isotp use can-gw.
Quoted 4 lines
can-gw and vxcan additionally clear the UID of forwarded/duplicated frames so each newly routed frame gets its own unique identifier.
Fixes d4fb6514ff8e ("can: use skb hash instead of private variable in headroom")
This patch to be fixed has been introduced in 7.0 - no backport needed for this kernel.
Best regards,
Oliver
Quoted 326 lines
Reported-by Joerg Willmann <[email protected]>Tested-by Oliver Hartkopp <[email protected]>Tested-by Joerg Willmann <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>Signed-off-by Marc Kleine-Budde <[email protected]>drivers/net/can/vxcan.c +3
-0@@ -79,6 +79,9 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)/* reset CAN GW hop counter */ csx->can_gw_hops =0 ; +/* start with new CAN skb UID in the other namespace */ + csx->can_skb_uid =0 ; + skb->pkt_type =PACKET_BROADCAST ; skb->dev = peer; skb->ip_summed =CHECKSUM_UNNECESSARY ;include/linux/can/core.h +2
-1@@ -17,6 +17,7 @@#include <linux/can.h> #include <linux/skbuff.h> #include <linux/netdevice.h> +#include <net/can.h> #define DNAME (dev) ((dev) ? (dev)->name : "any")@@ -58,7 +59,7 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev, void *data);extern int can_send (struct sk_buff *skb,int loop); -void can_set_skb_uid (struct sk_buff *skb); +void can_set_skb_uid (struct can_skb_ext *csx);void can_sock_destruct (struct sock *sk);#endif /* !_CAN_CORE_H */ include/linux/can/skb.h +3
-1@@ -43,8 +43,10 @@ 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) +if (csx) { csx->can_gw_hops =0 ; + csx->can_skb_uid =0 ; + }return csx; }include/net/can.h +2
-0@@ -17,12 +17,14 @@* @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 +* @can_skb_uid: CAN skb UID for raw_rcv and isotp echo handling */ struct can_skb_ext {int can_iif;u16 can_framelen;u8 can_gw_hops;u8 can_ext_flags; +u32 can_skb_uid; };#endif /* _NET_CAN_H */ net/can/af_can.c +36
-11@@ -641,13 +641,10 @@ static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_bufreturn matches; } -void can_set_skb_uid (struct sk_buff *skb) +void can_set_skb_uid (struct can_skb_ext *csx) { -/* create non-zero unique skb identifier together with *skb */ -while (!(skb->hash)) - skb->hash =atomic_inc_return (&skbcounter); - - skb->sw_hash =1 ; +while (!(csx->can_skb_uid)) + csx->can_skb_uid =atomic_inc_return (&skbcounter); }EXPORT_SYMBOL (can_set_skb_uid);@@ -662,8 +659,6 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)atomic_long_inc (&pkg_stats->rx_frames);atomic_long_inc (&pkg_stats->rx_frames_delta); -can_set_skb_uid (skb); -rcu_read_lock ();/* deliver the packet to sockets listening on all devices */ @@ -687,8 +682,10 @@ 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) { +struct can_skb_ext *csx =can_skb_ext_find (skb); +if (unlikely (dev->type !=ARPHRD_CAN || !can_get_ml_priv (dev) || - !can_skb_ext_find (skb) || !can_is_can_skb (skb))) { + !csx || !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);@@ -696,6 +693,14 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,return NET_RX_DROP ; } +/* create unshared CAN skb_extension for netem/mirred skb clones */ + csx =skb_ext_add (skb,SKB_EXT_CAN ); +if (unlikely (!csx)) { +kfree_skb_reason (skb,SKB_DROP_REASON_NOMEM ); +return NET_RX_DROP ; + } + +can_set_skb_uid (csx);can_receive (skb, dev);return NET_RX_SUCCESS ; }@@ -703,8 +708,10 @@ 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) { +struct can_skb_ext *csx =can_skb_ext_find (skb); +if (unlikely (dev->type !=ARPHRD_CAN || !can_get_ml_priv (dev) || - !can_skb_ext_find (skb) || !can_is_canfd_skb (skb))) { + !csx || !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);@@ -712,6 +719,14 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,return NET_RX_DROP ; } +/* create unshared CAN skb_extension for netem/mirred skb clones */ + csx =skb_ext_add (skb,SKB_EXT_CAN ); +if (unlikely (!csx)) { +kfree_skb_reason (skb,SKB_DROP_REASON_NOMEM ); +return NET_RX_DROP ; + } + +can_set_skb_uid (csx);can_receive (skb, dev);return NET_RX_SUCCESS ; }@@ -719,8 +734,10 @@ 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) { +struct can_skb_ext *csx =can_skb_ext_find (skb); +if (unlikely (dev->type !=ARPHRD_CAN || !can_get_ml_priv (dev) || - !can_skb_ext_find (skb) || !can_is_canxl_skb (skb))) { + !csx || !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);@@ -728,6 +745,14 @@ static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,return NET_RX_DROP ; } +/* create unshared CAN skb_extension for netem/mirred skb clones */ + csx =skb_ext_add (skb,SKB_EXT_CAN ); +if (unlikely (!csx)) { +kfree_skb_reason (skb,SKB_DROP_REASON_NOMEM ); +return NET_RX_DROP ; + } + +can_set_skb_uid (csx);can_receive (skb, dev);return NET_RX_SUCCESS ; }net/can/gw.c +3
-0@@ -527,6 +527,9 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)/* put the incremented hop counter in the cloned skb */ ncsx->can_gw_hops = csx->can_gw_hops +1 ; +/* force a new CAN UID generation for the routed frame */ + ncsx->can_skb_uid =0 ; +/* first processing of this CAN frame -> adjust to private hop limit */ if (gwj->limit_hops && ncsx->can_gw_hops ==1 ) ncsx->can_gw_hops = max_hops - gwj->limit_hops +1 ;net/can/isotp.c +10
-6@@ -891,7 +891,7 @@ static void isotp_send_cframe(struct isotp_sock *so) csx->can_iif = dev->ifindex;/* set uid in tx skb to identify CF echo frames */ -can_set_skb_uid (skb); +can_set_skb_uid (csx); cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);@@ -917,7 +917,7 @@ static void isotp_send_cframe(struct isotp_sock *so)pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , old_cfecho);/* set consecutive frame echo tag */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid);/* send frame with local echo enabled */ can_send_ret =can_send (skb,1 );@@ -969,18 +969,22 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) {struct sock *sk = (struct sock *)data;struct isotp_sock *so =isotp_sk (sk); +struct can_skb_ext *csx =can_skb_ext_find (skb);/* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk)return ; +if (WARN_ON_ONCE (!csx)) +return ; +/* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock * (no isotp_rcv() caller here), so take it ourselves */ spin_lock (&so->rx_lock);/* so->cfecho may since belong to a new transfer; recheck under lock */ -if (READ_ONCE (so->cfecho) != skb->hash) +if (READ_ONCE (so->cfecho) != csx->can_skb_uid) goto out_unlock;/* cancel local echo timeout */ @@ -1222,7 +1226,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) csx->can_iif = dev->ifindex;/* set uid in tx skb to identify CF echo frames */ -can_set_skb_uid (skb); +can_set_skb_uid (csx); so->tx.len = size; so->tx.idx =0 ;@@ -1261,7 +1265,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[ae] |= size;/* set CF echo tag for isotp_rcv_echo() (SF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid); }else {/* send first frame */ @@ -1278,7 +1282,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->txfc.bs =0 ;/* set CF echo tag for isotp_rcv_echo() (CF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho, csx->can_skb_uid); }else {/* standard flow control check */ new_state =ISOTP_WAIT_FIRST_FC ;net/can/raw.c +7
-3@@ -77,7 +77,7 @@ MODULE_ALIAS("can-proto-1");struct uniqframe {const struct sk_buff *skb; -u32 hash; +u32 can_skb_uid;unsigned int join_rx_count; };@@ -133,12 +133,16 @@ static void raw_rcv(struct sk_buff *oskb, void *data)enum skb_drop_reason reason;struct sockaddr_can *addr;struct sk_buff *skb; +struct can_skb_ext *csx =can_skb_ext_find (oskb);unsigned int *pflags;/* check the received tx sock reference */ if (!ro->recv_own_msgs && oskb->sk == sk)return ; +if (WARN_ON_ONCE (!csx)) +return ; +/* make sure to not pass oversized frames to the socket */ if (!ro->fd_frames &&can_is_canfd_skb (oskb))return ;@@ -165,7 +169,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data)/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr (ro->uniq)->skb == oskb && -this_cpu_ptr (ro->uniq)->hash == oskb->hash) { +this_cpu_ptr (ro->uniq)->can_skb_uid == csx->can_skb_uid) {if (!ro->join_filters)return ;@@ -175,7 +179,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data)return ; }else {this_cpu_ptr (ro->uniq)->skb = oskb; -this_cpu_ptr (ro->uniq)->hash = oskb->hash; +this_cpu_ptr (ro->uniq)->can_skb_uid = csx->can_skb_uid;this_cpu_ptr (ro->uniq)->join_rx_count =1 ;/* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count >1 )
Oliver Hartkopp
maintainerHi Greg,
my fault. In fact the problem has been introduced in Linux 7.0 but we already backported exactly the problematic regression (the use of skb->hash) down to 5.10y :
("can: use skb hash instead of private variable in headroom")
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit?id=b5e8ec4344c4d9d355c819ba89d949167dda0e66
So this backported patch finally has to be reverted in 5.10.y to 6.18.y .
I'll take a look at it to provide the six patches that revert the patch based on the latest stable trees.
Sorry for the noise.
Best regards,
Oliver
Quoted 379 lines · Oliver Hartkopp
Quoted 59 lines · On 12.10.26 13:33, [email protected] wrote:
The patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/ linux.git/ linux-5.10.y
git checkoutFETCH_HEAD
git cherry-pick -x7093c3b8314a4ef7405c4066a2521398c455efe7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101253-guzzler-atom-1993@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From
7093c3b8314a4ef7405c4066a2521398c455efe7Mon Sep 17 00:00:00 2001
From: Oliver Hartkopp <[email protected]>
Date: Thu, 1 Oct 2026 13:57:24 +0200
Subject: [PATCH] can: fix unique skb identifier regression under RPSCommit
d4fb6514ff8e("can: use skb hash instead of private variable in headroom") moved the per-skb unique identifier used forraw_rcv()duplicate detection intoskb->hash.With RPS enabled,
get_rps_cpu()callsskb_get_hash()before the frame reaches the CAN subsystem. Since CAN skbs have no L3/L4 headers, the flow dissector assigns every CAN frame the same non-zero software hash.can_set_skb_uid()only generated a new identifier whenskb->hashwas 0, so it kept this constant hash. When the SLAB allocator later reused the same skb address,raw_rcv()mistook the next legitimate frame for a duplicate and dropped it.Fix this by storing the CAN UID in the CAN skb extension (struct
can_skb_ext::can_skb_uid) instead ofskb->hash, decoupling it from any hash the network stack may compute.can_set_skb_uid()keeps its "assign only if unset" behaviour, which preserves UIDs set before transmission (e.g. by isotp to identify echo frames) across the local loopback path.Frames whose extension is shared with another clone (e.g. via tc mirred or netem duplicate) are given a private extension copy before the UID is assigned, so that independently received skb clones from real CAN interfaces (
can_skb_uid= 0) don't end up with the same UID. The limitation that tc mirred/netem skb duplicates might be dropped inraw_rcvfor looped back isotp echo frame skbs is accepted. There is no valid use-case for tc mirred or netem together with isotp which is not capable to operate on different CAN interfaces simultaneously. For routing and modifying CAN frames together with isotp use can-gw.Quoted 4 lines
can-gw and vxcan additionally clear the UID of forwarded/duplicated frames so each newly routed frame gets its own unique identifier.
Fixes d4fb6514ff8e ("can: use skb hash instead of private variable in headroom")This patch to be fixed has been introduced in 7.0 - no backport needed for this kernel.
Best regards,
OliverQuoted 306 lines
Reported-by Joerg Willmann <[email protected]>Tested-by Oliver Hartkopp <[email protected]>Tested-by Joerg Willmann <[email protected]>Signed-off-by Oliver Hartkopp <[email protected]>Signed-off-by Marc Kleine-Budde <[email protected]>drivers/net/can/vxcan.c +3
-0@@ -79,6 +79,9 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev) /* reset CAN GW hop counter */ csx->can_gw_hops = 0; + /* start with new CAN skb UID in the other namespace */ + csx->can_skb_uid = 0; + skb->pkt_type = PACKET_BROADCAST; skb->dev = peer; skb->ip_summed = CHECKSUM_UNNECESSARY;include/linux/can/core.h +2
-1@@ -17,6 +17,7 @@ #include <linux/can.h> #include <linux/skbuff.h> #include <linux/netdevice.h> +#include <net/can.h> #define DNAME(dev) ((dev) ? (dev)->name : "any")@@ -58,7 +59,7 @@ extern void can_rx_unregister(struct net *net,struct net_device *dev, void *data); extern int can_send(struct sk_buff *skb, int loop); -void can_set_skb_uid (struct sk_buff *skb); +void can_set_skb_uid (struct can_skb_ext *csx); void can_sock_destruct(struct sock *sk); #endif /* !_CAN_CORE_H */include/linux/can/skb.h +3
-1@@ -43,8 +43,10 @@ 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) + if (csx) { csx->can_gw_hops = 0; + csx->can_skb_uid = 0; + } return csx; }include/net/can.h +2
-0@@ -17,12 +17,14 @@* @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 +* @can_skb_uid: CAN skb UID for raw_rcv and isotp echo handling */ struct can_skb_ext { int can_iif; u16 can_framelen; u8 can_gw_hops; u8 can_ext_flags; + u32 can_skb_uid; }; #endif /* _NET_CAN_H */net/can/af_can.c +0
-0@@ -641,13 +641,10 @@ static int can_rcv_filter(struct
can_dev_rcv_lists*dev_rcv_lists, structsk_buf
return matches;
}net/can/af_can.c +1
-1-void can_set_skb_uid (struct sk_buff *skb) +void can_set_skb_uid (struct can_skb_ext *csx){
- /* create non-zero unique skb identifier together with *skb */ - while (!(skb->hash))
-skb->hash=atomic_inc_return(&skbcounter);
-
-skb->sw_hash= 1;
+ while (!(csx->can_skb_uid))
+csx->can_skb_uid=atomic_inc_return(&skbcounter); }
EXPORT_SYMBOL(can_set_skb_uid);net/can/af_can.c +2
-2@@ -662,8 +659,6 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) atomic_long_inc(&pkg_stats->rx_frames); atomic_long_inc(&pkg_stats->rx_frames_delta); - can_set_skb_uid(skb); - rcu_read_lock(); /* deliver the packet to sockets listening on all devices */@@ -687,8 +682,10 @@ 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) { + struct can_skb_ext *csx = can_skb_ext_find(skb); +if (unlikely(dev->type !=
ARPHRD_CAN|| !can_get_ml_priv(dev) ||
- !can_skb_ext_find(skb) || !can_is_can_skb(skb))) {
+ !csx || !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);net/can/af_can.c +10
-0@@ -696,6 +693,14 @@ static int can_rcv(struct sk_buff *skb, structnet_device *dev, return NET_RX_DROP; } + /* create unshared CAN skb_extension for netem/mirred skb clones */ + csx = skb_ext_add(skb, SKB_EXT_CAN); + if (unlikely(!csx)) { + kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM); + return NET_RX_DROP; + } + + can_set_skb_uid(csx); can_receive(skb, dev); return NET_RX_SUCCESS; }@@ -703,8 +708,10 @@ static int can_rcv(struct sk_buff *skb, structnet_device *dev, static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { + struct can_skb_ext *csx = can_skb_ext_find(skb); +if (unlikely(dev->type !=
ARPHRD_CAN|| !can_get_ml_priv(dev) ||
- !can_skb_ext_find(skb) || !can_is_canfd_skb(skb))) {
+ !csx || !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);net/can/af_can.c +10
-0@@ -712,6 +719,14 @@ static int canfd_rcv(struct sk_buff *skb, structnet_device *dev, return NET_RX_DROP; } + /* create unshared CAN skb_extension for netem/mirred skb clones */ + csx = skb_ext_add(skb, SKB_EXT_CAN); + if (unlikely(!csx)) { + kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM); + return NET_RX_DROP; + } + + can_set_skb_uid(csx); can_receive(skb, dev); return NET_RX_SUCCESS; }@@ -719,8 +734,10 @@ static int canfd_rcv(struct sk_buff *skb, structnet_device *dev, static int canxl_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { + struct can_skb_ext *csx = can_skb_ext_find(skb); +if (unlikely(dev->type !=
ARPHRD_CAN|| !can_get_ml_priv(dev) ||
- !can_skb_ext_find(skb) || !can_is_canxl_skb(skb))) {
+ !csx || !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);net/can/af_can.c +8
-0@@ -728,6 +745,14 @@ static int canxl_rcv(struct sk_buff *skb, structnet_device *dev, return NET_RX_DROP; } + /* create unshared CAN skb_extension for netem/mirred skb clones */ + csx = skb_ext_add(skb, SKB_EXT_CAN); + if (unlikely(!csx)) { + kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM); + return NET_RX_DROP; + } + + can_set_skb_uid(csx); can_receive(skb, dev); return NET_RX_SUCCESS; }net/can/gw.c +3
-0@@ -527,6 +527,9 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) /* put the incremented hop counter in the cloned skb */ ncsx->can_gw_hops = csx->can_gw_hops + 1; + /* force a new CAN UID generation for the routed frame */ + ncsx->can_skb_uid = 0; + /* first processing of this CAN frame -> adjust to private hop limit */ if (gwj->limit_hops && ncsx->can_gw_hops == 1) ncsx->can_gw_hops = max_hops - gwj->limit_hops + 1;net/can/isotp.c +6
-2@@ -891,7 +891,7 @@ static void isotp_send_cframe(struct isotp_sock *so) csx->can_iif = dev->ifindex; /* set uid in tx skb to identify CF echo frames */ - can_set_skb_uid(skb); + can_set_skb_uid(csx); cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu);@@ -917,7 +917,7 @@ static void isotp_send_cframe(struct isotp_sock *so) pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho); /* set consecutive frame echo tag */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, csx->can_skb_uid); /* send frame with local echo enabled */ can_send_ret = can_send(skb, 1);@@ -969,18 +969,22 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) { struct sock *sk = (struct sock *)data; struct isotp_sock *so = isotp_sk(sk); + struct can_skb_ext *csx = can_skb_ext_find(skb); /* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk) return; + if (WARN_ON_ONCE(!csx)) + return; + /* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so-Quoted 1 line
rx_lock* (no
isotp_rcv()caller here), so take it ourselves
*/
spin_lock(&so->rx_lock);
/*so->cfechomay since belong to a new transfer; recheck under lock */ - if (READ_ONCE(so->cfecho)!=skb->hash)
+ if (READ_ONCE(so->cfecho)!=csx->can_skb_uid)
gotoout_unlock;
/* cancel local echo timeout */net/can/isotp.c +3
-3@@ -1222,7 +1226,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) csx->can_iif = dev->ifindex; /* set uid in tx skb to identify CF echo frames */ - can_set_skb_uid(skb); + can_set_skb_uid(csx); so->tx.len = size; so->tx.idx = 0;@@ -1261,7 +1265,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[ae] |= size; /* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, csx->can_skb_uid); } else { /* send first frame */@@ -1278,7 +1282,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->txfc.bs = 0; /* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - WRITE_ONCE(so->cfecho, skb->hash); + WRITE_ONCE(so->cfecho, csx->can_skb_uid); } else { /* standard flow control check */ new_state = ISOTP_WAIT_FIRST_FC;net/can/raw.c +5
-1@@ -77,7 +77,7 @@ MODULE_ALIAS("can-proto-1"); struct uniqframe { const struct sk_buff *skb; - u32 hash; + u32 can_skb_uid; unsigned int join_rx_count; };@@ -133,12 +133,16 @@ static void raw_rcv(struct sk_buff *oskb, void *data) enum skb_drop_reason reason; struct sockaddr_can *addr; struct sk_buff *skb; + struct can_skb_ext *csx = can_skb_ext_find(oskb); unsigned int *pflags; /* check the received tx sock reference */ if (!ro->recv_own_msgs && oskb->sk == sk) return; + if (WARN_ON_ONCE(!csx)) + return; + /* make sure to not pass oversized frames to the socket */ if (!ro->fd_frames && can_is_canfd_skb(oskb)) return;@@ -165,7 +169,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) /* eliminate multiple filter matches for the same skb */if (this_cpu_ptr(ro->uniq)->skb == oskb &&
- this_cpu_ptr(ro->uniq)->hash ==oskb->hash) {
+ this_cpu_ptr(ro->uniq)->can_skb_uid ==csx->can_skb_uid) { if (!ro->join_filters)
return;net/can/raw.c +1
-1@@ -175,7 +179,7 @@ static void raw_rcv(struct sk_buff *oskb, void *data) return; } else { this_cpu_ptr(ro->uniq)->skb = oskb; - this_cpu_ptr(ro->uniq)->hash = oskb->hash; + this_cpu_ptr(ro->uniq)->can_skb_uid = csx->can_skb_uid; this_cpu_ptr(ro->uniq)->join_rx_count = 1; /* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count > 1)
Oliver Hartkopp
maintainerPATCH 1/1 can: fix unique skb identifier regression under RPS
In reply to [email protected]commit 7093c3b8314a4ef7405c4066a2521398c455efe7 upstream.
Commit d4fb6514ff8e ("can: use skb hash instead of private variable in headroom") moved the per-skb unique identifier used for raw_rcv() duplicate detection into skb->hash.
With RPS enabled, get_rps_cpu() calls skb_get_hash() before the frame reaches the CAN subsystem. Since CAN skbs have no L3/L4 headers, the flow dissector assigns every CAN frame the same non-zero software hash. can_set_skb_uid() only generated a new identifier when skb->hash was 0, so it kept this constant hash. When the SLAB allocator later reused the same skb address, raw_rcv() mistook the next legitimate frame for a duplicate and dropped it.
Fix this by storing the CAN UID in the CAN skb extension (struct can_skb_ext::can_skb_uid) instead of skb->hash, decoupling it from any hash the network stack may compute. can_set_skb_uid() keeps its "assign only if unset" behaviour, which preserves UIDs set before transmission (e.g. by isotp to identify echo frames) across the local loopback path.
Frames whose extension is shared with another clone (e.g. via tc mirred or netem duplicate) are given a private extension copy before the UID is assigned, so that independently received skb clones from real CAN interfaces (skbcnt = 0) don't end up with the same UID.
The limitation that tc mirred/netem skb duplicates might be dropped in raw_rcv for looped back isotp echo frame skbs is accepted. There is no valid use-case for tc mirred or netem together with isotp which is not capable to operate on different CAN interfaces simultaneously. For routing and modifying CAN frames together with isotp use can-gw.
can-gw and vxcan additionally clear the UID of forwarded/duplicated frames so each newly routed frame gets its own unique identifier.
[ backport to 5.10: no CAN skb extensions available, so the UID is kept
in struct can_skb_priv::skbcnt in the headroom (effectively reverting d4fb6514ff8e) and isotp is adapted accordingly. As skb clones share the headroom, the private extension copy for tc mirred/netem duplicates is not applicable. The UID resets in can-gw and vxcan are omitted, as writing to the shared headroom of cloned skbs would also alter the UID of the original skb. This keeps the pre-d4fb6514ff8e behaviour for routed and vxcan forwarded frames. ]
--- drivers/net/can/dev/dev.c | 2 ++ drivers/net/can/slcan.c | 1 + include/linux/can/dev.h | 1 + include/linux/can/skb.h | 2 ++ net/can/af_can.c | 6 ++---- net/can/bcm.c | 2 ++ net/can/isotp.c | 11 +++++++---- net/can/j1939/socket.c | 1 + net/can/j1939/transport.c | 2 ++ net/can/raw.c | 7 ++++--- 10 files changed, 24 insertions(+), 11 deletions(-)
drivers/net/can/dev/dev.c +2 -0
@@ -692,10 +692,11 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)skb_reset_network_header (skb);skb_reset_transport_header (skb);can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +can_skb_prv (skb)->skbcnt =0 ; *cf =skb_put_zero (skb,sizeof (struct can_frame ));return skb; }
@@ -719,10 +720,11 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,skb_reset_network_header (skb);skb_reset_transport_header (skb);can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +can_skb_prv (skb)->skbcnt =0 ; *cfd =skb_put_zero (skb,sizeof (struct canfd_frame ));return skb; }
drivers/net/can/slcan.c +1 -0
@@ -211,10 +211,11 @@ static void slc_bump(struct slcan *sl) skb->pkt_type =PACKET_BROADCAST ; skb->ip_summed =CHECKSUM_UNNECESSARY ;can_skb_reserve (skb);can_skb_prv (skb)->ifindex = sl->dev->ifindex; +can_skb_prv (skb)->skbcnt =0 ;skb_put_data (skb, &cf,sizeof (struct can_frame )); sl->dev->stats.rx_packets++; sl->dev->stats.rx_bytes += cf.can_dlc;
include/linux/can/dev.h +1 -0
@@ -117,10 +117,11 @@ static inline bool can_skb_headroom_valid(struct net_device *dev,/* 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 )
include/linux/can/skb.h +2 -0
@@ -26,14 +26,16 @@*/ /** * 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 * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv {int ifindex; +u32 skbcnt;struct can_frame cf[]; };static inline struct can_skb_priv *can_skb_prv (struct sk_buff *skb) {
net/can/af_can.c +2 -4
@@ -642,14 +642,12 @@ static int can_rcv_filter(struct can_dev_rcv_lists *dev_rcv_lists, struct sk_buf }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 ; +while (!(can_skb_prv (skb)->skbcnt)) +can_skb_prv (skb)->skbcnt =atomic_inc_return (&skbcounter); }EXPORT_SYMBOL (can_set_skb_uid);static void can_receive (struct sk_buff *skb,struct net_device *dev) {
net/can/bcm.c +2 -0
@@ -336,10 +336,11 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)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;
@@ -1574,10 +1575,11 @@ 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);
net/can/isotp.c +7 -4
@@ -285,10 +285,11 @@ 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);
@@ -846,10 +847,11 @@ 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 ;/* set uid in tx skb to identify CF echo frames */ can_set_skb_uid (skb); cf = (struct canfd_frame *)skb->data;
@@ -874,11 +876,11 @@ static void isotp_send_cframe(struct isotp_sock *so) old_cfecho =READ_ONCE (so->cfecho);if (old_cfecho)pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , old_cfecho);/* set consecutive frame echo tag */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho,can_skb_prv (skb)->skbcnt);/* send frame with local echo enabled */ can_send_ret =can_send (skb,1 );if (can_send_ret) { pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
@@ -937,11 +939,11 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)* (no isotp_rcv() caller here), so take it ourselves */ spin_lock (&so->rx_lock);/* so->cfecho may since belong to a new transfer; recheck under lock */ -if (READ_ONCE (so->cfecho) != skb->hash) +if (READ_ONCE (so->cfecho) !=can_skb_prv (skb)->skbcnt) goto out_unlock;/* cancel local echo timeout */ hrtimer_cancel (&so->echotimer);
@@ -1159,10 +1161,11 @@ 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 ;/* set uid in tx skb to identify CF echo frames */ can_set_skb_uid (skb); so->tx.len = size;
@@ -1200,11 +1203,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[SF_PCI_SZ4 + ae] = size; else cf->data[ae] |= size;/* set CF echo tag for isotp_rcv_echo() (SF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho,can_skb_prv (skb)->skbcnt); }else {/* send first frame */ isotp_create_fframe (cf, so, ae);
@@ -1217,11 +1220,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)/* disable wait for FCs due to activated block size */ so->txfc.bs =0 ;/* set CF echo tag for isotp_rcv_echo() (CF-mode) */ -WRITE_ONCE (so->cfecho, skb->hash); +WRITE_ONCE (so->cfecho,can_skb_prv (skb)->skbcnt); }else {/* standard flow control check */ new_state =ISOTP_WAIT_FIRST_FC ;/* start timeout for FC */
net/can/j1939/socket.c +1 -0
@@ -887,10 +887,11 @@ 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;
net/can/j1939/transport.c +2 -0
@@ -606,10 +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; +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));
@@ -1532,10 +1533,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; +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) {
net/can/raw.c +4 -3
@@ -72,12 +72,12 @@ MODULE_ALIAS("can-proto-1");* list containing only one item. This common case is optimized by * storing the single filter in dfilter, to avoid using dynamic memory. */ struct uniqframe { +u32 skbcnt;const struct sk_buff *skb; -u32 hash;unsigned int join_rx_count; };struct raw_sock {struct sock sk;
@@ -133,22 +133,22 @@ static void raw_rcv(struct sk_buff *oskb, void *data)if (!ro->fd_frames && oskb->len !=CAN_MTU )return ;/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr (ro->uniq)->skb == oskb && -this_cpu_ptr (ro->uniq)->hash == oskb->hash) { +this_cpu_ptr (ro->uniq)->skbcnt ==can_skb_prv (oskb)->skbcnt) {if (ro->join_filters) {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 {return ; } }else {this_cpu_ptr (ro->uniq)->skb = oskb; -this_cpu_ptr (ro->uniq)->hash = oskb->hash; +this_cpu_ptr (ro->uniq)->skbcnt =can_skb_prv (oskb)->skbcnt;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 ; }
@@ -810,10 +810,11 @@ 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 ; err =memcpy_from_msg (skb_put (skb, size), msg, size);if (err <0 ) goto free_skb;
Signature · 1 line
2.53.0