forked from mirrors/linux
		
	can: add destructor for self generated skbs
Self generated skbuffs in net/can/bcm.c are setting a skb->sk reference but
no explicit destructor which is enforced since Linux 3.11 with commit
376c7311bd (net: add a temporary sanity check in skb_orphan()).
This patch adds some helper functions to make sure that a destructor is
properly defined when a sock reference is assigned to a CAN related skb.
To create an unshared skb owned by the original sock a common helper function
has been introduced to replace open coded functions to create CAN echo skbs.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Tested-by: Andre Naujoks <nautsch2@gmail.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									920a0fde5a
								
							
						
					
					
						commit
						0ae89beb28
					
				
					 6 changed files with 53 additions and 34 deletions
				
			
		| 
						 | 
					@ -323,19 +323,10 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!priv->echo_skb[idx]) {
 | 
						if (!priv->echo_skb[idx]) {
 | 
				
			||||||
		struct sock *srcsk = skb->sk;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (atomic_read(&skb->users) != 1) {
 | 
							skb = can_create_echo_skb(skb);
 | 
				
			||||||
			struct sk_buff *old_skb = skb;
 | 
							if (!skb)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
			skb = skb_clone(old_skb, GFP_ATOMIC);
 | 
					 | 
				
			||||||
			kfree_skb(old_skb);
 | 
					 | 
				
			||||||
			if (!skb)
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
			skb_orphan(skb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		skb->sk = srcsk;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* make settings for echo to reduce code in irq context */
 | 
							/* make settings for echo to reduce code in irq context */
 | 
				
			||||||
		skb->protocol = htons(ETH_P_CAN);
 | 
							skb->protocol = htons(ETH_P_CAN);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@
 | 
				
			||||||
#include <linux/netdevice.h>
 | 
					#include <linux/netdevice.h>
 | 
				
			||||||
#include <linux/can.h>
 | 
					#include <linux/can.h>
 | 
				
			||||||
#include <linux/can/dev.h>
 | 
					#include <linux/can/dev.h>
 | 
				
			||||||
 | 
					#include <linux/can/skb.h>
 | 
				
			||||||
#include <linux/can/error.h>
 | 
					#include <linux/can/error.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/mfd/janz.h>
 | 
					#include <linux/mfd/janz.h>
 | 
				
			||||||
| 
						 | 
					@ -1133,20 +1134,9 @@ static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb)
 | 
					static void ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock *srcsk = skb->sk;
 | 
						skb = can_create_echo_skb(skb);
 | 
				
			||||||
 | 
						if (!skb)
 | 
				
			||||||
	if (atomic_read(&skb->users) != 1) {
 | 
							return;
 | 
				
			||||||
		struct sk_buff *old_skb = skb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		skb = skb_clone(old_skb, GFP_ATOMIC);
 | 
					 | 
				
			||||||
		kfree_skb(old_skb);
 | 
					 | 
				
			||||||
		if (!skb)
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		skb_orphan(skb);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	skb->sk = srcsk;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* save this skb for tx interrupt echo handling */
 | 
						/* save this skb for tx interrupt echo handling */
 | 
				
			||||||
	skb_queue_tail(&mod->echoq, skb);
 | 
						skb_queue_tail(&mod->echoq, skb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,7 @@
 | 
				
			||||||
#include <linux/if_ether.h>
 | 
					#include <linux/if_ether.h>
 | 
				
			||||||
#include <linux/can.h>
 | 
					#include <linux/can.h>
 | 
				
			||||||
#include <linux/can/dev.h>
 | 
					#include <linux/can/dev.h>
 | 
				
			||||||
 | 
					#include <linux/can/skb.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <net/rtnetlink.h>
 | 
					#include <net/rtnetlink.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,25 +110,23 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev)
 | 
				
			||||||
			stats->rx_packets++;
 | 
								stats->rx_packets++;
 | 
				
			||||||
			stats->rx_bytes += cfd->len;
 | 
								stats->rx_bytes += cfd->len;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		kfree_skb(skb);
 | 
							consume_skb(skb);
 | 
				
			||||||
		return NETDEV_TX_OK;
 | 
							return NETDEV_TX_OK;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* perform standard echo handling for CAN network interfaces */
 | 
						/* perform standard echo handling for CAN network interfaces */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (loop) {
 | 
						if (loop) {
 | 
				
			||||||
		struct sock *srcsk = skb->sk;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		skb = skb_share_check(skb, GFP_ATOMIC);
 | 
							skb = can_create_echo_skb(skb);
 | 
				
			||||||
		if (!skb)
 | 
							if (!skb)
 | 
				
			||||||
			return NETDEV_TX_OK;
 | 
								return NETDEV_TX_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* receive with packet counting */
 | 
							/* receive with packet counting */
 | 
				
			||||||
		skb->sk = srcsk;
 | 
					 | 
				
			||||||
		vcan_rx(skb, dev);
 | 
							vcan_rx(skb, dev);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* no looped packets => no counting */
 | 
							/* no looped packets => no counting */
 | 
				
			||||||
		kfree_skb(skb);
 | 
							consume_skb(skb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return NETDEV_TX_OK;
 | 
						return NETDEV_TX_OK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,9 @@
 | 
				
			||||||
#define CAN_SKB_H
 | 
					#define CAN_SKB_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					#include <linux/skbuff.h>
 | 
				
			||||||
#include <linux/can.h>
 | 
					#include <linux/can.h>
 | 
				
			||||||
 | 
					#include <net/sock.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * The struct can_skb_priv is used to transport additional information along
 | 
					 * The struct can_skb_priv is used to transport additional information along
 | 
				
			||||||
| 
						 | 
					@ -42,4 +44,40 @@ static inline void can_skb_reserve(struct sk_buff *skb)
 | 
				
			||||||
	skb_reserve(skb, sizeof(struct can_skb_priv));
 | 
						skb_reserve(skb, sizeof(struct can_skb_priv));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void can_skb_destructor(struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						sock_put(skb->sk);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void can_skb_set_owner(struct sk_buff *skb, struct sock *sk)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (sk) {
 | 
				
			||||||
 | 
							sock_hold(sk);
 | 
				
			||||||
 | 
							skb->destructor = can_skb_destructor;
 | 
				
			||||||
 | 
							skb->sk = sk;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * returns an unshared skb owned by the original sock to be echo'ed back
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (skb_shared(skb)) {
 | 
				
			||||||
 | 
							struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (likely(nskb)) {
 | 
				
			||||||
 | 
								can_skb_set_owner(nskb, skb->sk);
 | 
				
			||||||
 | 
								consume_skb(skb);
 | 
				
			||||||
 | 
								return nskb;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								kfree_skb(skb);
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* we can assume to have an unshared skb with proper owner */
 | 
				
			||||||
 | 
						return skb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* CAN_SKB_H */
 | 
					#endif /* CAN_SKB_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@
 | 
				
			||||||
#include <linux/skbuff.h>
 | 
					#include <linux/skbuff.h>
 | 
				
			||||||
#include <linux/can.h>
 | 
					#include <linux/can.h>
 | 
				
			||||||
#include <linux/can/core.h>
 | 
					#include <linux/can/core.h>
 | 
				
			||||||
 | 
					#include <linux/can/skb.h>
 | 
				
			||||||
#include <linux/ratelimit.h>
 | 
					#include <linux/ratelimit.h>
 | 
				
			||||||
#include <net/net_namespace.h>
 | 
					#include <net/net_namespace.h>
 | 
				
			||||||
#include <net/sock.h>
 | 
					#include <net/sock.h>
 | 
				
			||||||
| 
						 | 
					@ -290,7 +291,7 @@ int can_send(struct sk_buff *skb, int loop)
 | 
				
			||||||
				return -ENOMEM;
 | 
									return -ENOMEM;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			newskb->sk = skb->sk;
 | 
								can_skb_set_owner(newskb, skb->sk);
 | 
				
			||||||
			newskb->ip_summed = CHECKSUM_UNNECESSARY;
 | 
								newskb->ip_summed = CHECKSUM_UNNECESSARY;
 | 
				
			||||||
			newskb->pkt_type = PACKET_BROADCAST;
 | 
								newskb->pkt_type = PACKET_BROADCAST;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -268,7 +268,7 @@ static void bcm_can_tx(struct bcm_op *op)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* send with loopback */
 | 
						/* send with loopback */
 | 
				
			||||||
	skb->dev = dev;
 | 
						skb->dev = dev;
 | 
				
			||||||
	skb->sk = op->sk;
 | 
						can_skb_set_owner(skb, op->sk);
 | 
				
			||||||
	can_send(skb, 1);
 | 
						can_send(skb, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* update statistics */
 | 
						/* update statistics */
 | 
				
			||||||
| 
						 | 
					@ -1223,7 +1223,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	can_skb_prv(skb)->ifindex = dev->ifindex;
 | 
						can_skb_prv(skb)->ifindex = dev->ifindex;
 | 
				
			||||||
	skb->dev = dev;
 | 
						skb->dev = dev;
 | 
				
			||||||
	skb->sk  = sk;
 | 
						can_skb_set_owner(skb, sk);
 | 
				
			||||||
	err = can_send(skb, 1); /* send with loopback */
 | 
						err = can_send(skb, 1); /* send with loopback */
 | 
				
			||||||
	dev_put(dev);
 | 
						dev_put(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue