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:
Oliver Hartkopp 2014-01-30 10:11:28 +01:00 коммит произвёл David S. Miller
Родитель 920a0fde5a
Коммит 0ae89beb28
6 изменённых файлов: 53 добавлений и 34 удалений

Просмотреть файл

@ -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);