diff mbox

[PATCHv2,net-next,4/4] caif: Stash away hijacked skb destructor and call it later

Message ID 1322993450-8802-5-git-send-email-sjur.brandeland@stericsson.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

sjur.brandeland@stericsson.com Dec. 4, 2011, 10:10 a.m. UTC
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>
---
This functionality is added after V1 of this patchset.

 net/caif/caif_dev.c |   34 +++++++++++++++++++++++++++++++++-
 1 files changed, 33 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 1980404..a82d89f 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -37,6 +37,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;
 };
 
@@ -134,6 +136,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);
@@ -146,8 +149,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,
@@ -211,8 +223,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;