aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com>2011-12-04 11:22:55 +0000
committerDavid S. Miller <davem@davemloft.net>2011-12-05 18:27:56 -0500
commit7d3113042823344dcc175b0ea00a83d0273c98a4 (patch)
tree7557851b0c2a3da774a36448cd78fedcc0958e6d
parent0e4c7d85d5e522d5839bdc5745235428faf38d44 (diff)
caif: Stash away hijacked skb destructor and call it later
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>
-rw-r--r--net/caif/caif_dev.c34
1 files changed, 33 insertions, 1 deletions
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;