From 7d3113042823344dcc175b0ea00a83d0273c98a4 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Sun, 4 Dec 2011 11:22:55 +0000 Subject: caif: Stash away hijacked skb destructor and call it later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: David S. Miller --- net/caif/caif_dev.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'net/caif') diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 74c12734db7..9b298c14028 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -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; -- cgit v1.2.3