caif: Stash away hijacked skb destructor and call it later
authorsjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com>
Sun, 4 Dec 2011 11:22:55 +0000 (11:22 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 5 Dec 2011 23:27:56 +0000 (18:27 -0500)
This patch adds functionality for avoiding orphaning SKB too early.
The original skb is stashed away and the original destructor is called
from the hi-jacked flow-on callback. If CAIF interface goes down and a
hi-jacked SKB exists, the original skb->destructor is restored.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/caif/caif_dev.c

index 74c1273..9b298c1 100644 (file)
@@ -36,6 +36,8 @@ struct caif_device_entry {
        struct net_device *netdev;
        int __percpu *pcpu_refcnt;
        spinlock_t flow_lock;
+       struct sk_buff *xoff_skb;
+       void (*xoff_skb_dtor)(struct sk_buff *skb);
        bool xoff;
 };
 
@@ -133,6 +135,7 @@ static struct caif_device_entry *caif_get(struct net_device *dev)
 void caif_flow_cb(struct sk_buff *skb)
 {
        struct caif_device_entry *caifd;
+       void (*dtor)(struct sk_buff *skb) = NULL;
        bool send_xoff;
 
        WARN_ON(skb->dev == NULL);
@@ -145,8 +148,17 @@ void caif_flow_cb(struct sk_buff *skb)
        spin_lock_bh(&caifd->flow_lock);
        send_xoff = caifd->xoff;
        caifd->xoff = 0;
+       if (!WARN_ON(caifd->xoff_skb_dtor == NULL)) {
+               WARN_ON(caifd->xoff_skb != skb);
+               dtor = caifd->xoff_skb_dtor;
+               caifd->xoff_skb = NULL;
+               caifd->xoff_skb_dtor = NULL;
+       }
        spin_unlock_bh(&caifd->flow_lock);
 
+       if (dtor)
+               dtor(skb);
+
        if (send_xoff)
                caifd->layer.up->
                        ctrlcmd(caifd->layer.up,
@@ -210,8 +222,10 @@ static int transmit(struct cflayer *layer, struct cfpkt *pkt)
                        netif_queue_stopped(caifd->netdev),
                        qlen, high);
        caifd->xoff = 1;
+       caifd->xoff_skb = skb;
+       caifd->xoff_skb_dtor = skb->destructor;
+       skb->destructor = caif_flow_cb;
        spin_unlock_bh(&caifd->flow_lock);
-       skb_orphan(skb);
 
        caifd->layer.up->ctrlcmd(caifd->layer.up,
                                        _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
@@ -420,6 +434,24 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
                caifd->layer.up->ctrlcmd(caifd->layer.up,
                                         _CAIF_CTRLCMD_PHYIF_DOWN_IND,
                                         caifd->layer.id);
+
+               spin_lock_bh(&caifd->flow_lock);
+
+               /*
+                * Replace our xoff-destructor with original destructor.
+                * We trust that skb->destructor *always* is called before
+                * the skb reference is invalid. The hijacked SKB destructor
+                * takes the flow_lock so manipulating the skb->destructor here
+                * should be safe.
+               */
+               if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
+                       caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
+
+               caifd->xoff = 0;
+               caifd->xoff_skb_dtor = NULL;
+               caifd->xoff_skb = NULL;
+
+               spin_unlock_bh(&caifd->flow_lock);
                caifd_put(caifd);
                break;