NFC: Queue a copy of the transmitted LLCP skb
authorSamuel Ortiz <sameo@linux.intel.com>
Thu, 1 Nov 2012 22:36:07 +0000 (23:36 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 19 Nov 2012 22:57:01 +0000 (23:57 +0100)
Drivers are allowed to modify the sent skb and thus we need to make a copy
of it before passing it to the driver. Without this fix, LLCP Tx skbs were
not queued properly as the ptype check was failing due to e.g. the pn533
driver skb_pushing the Tx skb.

Reported-by: Thierry Escande <thierry.escande@linux.intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
net/nfc/llcp/llcp.c

index f6804532047a2467b85b7ec252459362a1f2e16d..66733335345fb74c4b1df30a73ac32a5ee8e11ef 100644 (file)
@@ -656,6 +656,8 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
                        nfc_llcp_send_symm(local->dev);
                } else {
+                       struct sk_buff *copy_skb = NULL;
+                       u8 ptype = nfc_llcp_ptype(skb);
                        int ret;
 
                        pr_debug("Sending pending skb\n");
@@ -663,22 +665,29 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       if (ptype == LLCP_PDU_I)
+                               copy_skb = skb_copy(skb, GFP_ATOMIC);
+
                        nfc_llcp_send_to_raw_sock(local, skb,
                                                  NFC_LLCP_DIRECTION_TX);
 
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
 
-                       if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
-                               skb = skb_get(skb);
-                               skb_queue_tail(&llcp_sock->tx_pending_queue,
-                                              skb);
+                       if (ret) {
+                               kfree_skb(copy_skb);
+                               goto out;
                        }
+
+                       if (ptype == LLCP_PDU_I && copy_skb)
+                               skb_queue_tail(&llcp_sock->tx_pending_queue,
+                                              copy_skb);
                }
        } else {
                nfc_llcp_send_symm(local->dev);
        }
 
+out:
        mod_timer(&local->link_timer,
                  jiffies + msecs_to_jiffies(2 * local->remote_lto));
 }