NFC: Fixed nfc core and hci unregistration and cleanup
authorEric Lapuyade <eric.lapuyade@linux.intel.com>
Mon, 26 Nov 2012 17:06:27 +0000 (18:06 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Wed, 9 Jan 2013 23:51:48 +0000 (00:51 +0100)
When an adapter is removed, it will unregister itself from hci and/or
nfc core. In order to do that safely, work tasks must first be canceled
and prevented to be scheduled again, before the hci or nfc device can be
destroyed.

Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
include/net/nfc/hci.h
include/net/nfc/nfc.h
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/hci/hcp.c

index 671953e11575449b30a10081d5b7aa7b26c0fa0a..e6224571e5e69ca1e7bce2a45621e1b1f8b8e035 100644 (file)
@@ -87,6 +87,8 @@ struct nfc_hci_dev {
 
        u32 max_data_link_payload;
 
+       bool shutting_down;
+
        struct mutex msg_tx_mutex;
 
        struct list_head msg_tx_queue;
index fce80b2f9be7e5eaf50e5fc5ccf55fde0440f793..1665674e86b28d6158f561045ad8cabc31b63935 100644 (file)
@@ -115,6 +115,8 @@ struct nfc_dev {
        struct timer_list check_pres_timer;
        struct work_struct check_pres_work;
 
+       bool shutting_down;
+
        struct nfc_ops *ops;
 };
 #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
index aa64ea441676a1ce9fc8c2417cc4487eaecf2cec..7d7b4ee34015ba04c356b7a449537265aed72db8 100644 (file)
@@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
                dev->active_target = target;
                dev->rf_mode = NFC_RF_INITIATOR;
 
-               if (dev->ops->check_presence)
+               if (dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
@@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
                                             cb_context);
 
-               if (!rc && dev->ops->check_presence)
+               if (!rc && dev->ops->check_presence && !dev->shutting_down)
                        mod_timer(&dev->check_pres_timer, jiffies +
                                  msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
@@ -684,11 +684,6 @@ static void nfc_release(struct device *d)
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
-       if (dev->ops->check_presence) {
-               del_timer_sync(&dev->check_pres_timer);
-               cancel_work_sync(&dev->check_pres_work);
-       }
-
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
        kfree(dev);
@@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work)
                rc = dev->ops->check_presence(dev, dev->active_target);
                if (rc == -EOPNOTSUPP)
                        goto exit;
-               if (!rc) {
-                       mod_timer(&dev->check_pres_timer, jiffies +
-                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
-               } else {
+               if (rc) {
                        u32 active_target_idx = dev->active_target->idx;
                        device_unlock(&dev->dev);
                        nfc_target_lost(dev, active_target_idx);
                        return;
                }
+
+               if (!dev->shutting_down)
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
        }
 
 exit:
@@ -853,26 +849,27 @@ void nfc_unregister_device(struct nfc_dev *dev)
 
        id = dev->idx;
 
-       mutex_lock(&nfc_devlist_mutex);
-       nfc_devlist_generation++;
-
-       /* lock to avoid unregistering a device while an operation
-          is in progress */
-       device_lock(&dev->dev);
-       device_del(&dev->dev);
-       device_unlock(&dev->dev);
+       if (dev->ops->check_presence) {
+               device_lock(&dev->dev);
+               dev->shutting_down = true;
+               device_unlock(&dev->dev);
+               del_timer_sync(&dev->check_pres_timer);
+               cancel_work_sync(&dev->check_pres_work);
+       }
 
-       mutex_unlock(&nfc_devlist_mutex);
+       rc = nfc_genl_device_removed(dev);
+       if (rc)
+               pr_debug("The userspace won't be notified that the device %s "
+                        "was removed\n", dev_name(&dev->dev));
 
        nfc_llcp_unregister_device(dev);
 
-       rc = nfc_genl_device_removed(dev);
-       if (rc)
-               pr_debug("The userspace won't be notified that the device %s was removed\n",
-                        dev_name(&dev->dev));
+       mutex_lock(&nfc_devlist_mutex);
+       nfc_devlist_generation++;
+       device_del(&dev->dev);
+       mutex_unlock(&nfc_devlist_mutex);
 
        ida_simple_remove(&nfc_index_ida, id);
-
 }
 EXPORT_SYMBOL(nfc_unregister_device);
 
index 7bea574d59344f754ad74a1a390a8d742843549b..b4b84268653d6039dd6a8fc5d180d65c6bd584e6 100644 (file)
@@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
        int r = 0;
 
        mutex_lock(&hdev->msg_tx_mutex);
+       if (hdev->shutting_down)
+               goto exit;
 
        if (hdev->cmd_pending_msg) {
                if (timer_pending(&hdev->cmd_timer) == 0) {
@@ -868,6 +870,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
 {
        struct hci_msg *msg, *n;
 
+       mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->cmd_pending_msg) {
+               if (hdev->cmd_pending_msg->cb)
+                       hdev->cmd_pending_msg->cb(
+                                            hdev->cmd_pending_msg->cb_context,
+                                            NULL, -ESHUTDOWN);
+               kfree(hdev->cmd_pending_msg);
+               hdev->cmd_pending_msg = NULL;
+       }
+
+       hdev->shutting_down = true;
+
+       mutex_unlock(&hdev->msg_tx_mutex);
+
+       del_timer_sync(&hdev->cmd_timer);
+       cancel_work_sync(&hdev->msg_tx_work);
+
+       cancel_work_sync(&hdev->msg_rx_work);
+
+       nfc_unregister_device(hdev->ndev);
+
        skb_queue_purge(&hdev->rx_hcp_frags);
        skb_queue_purge(&hdev->msg_rx_queue);
 
@@ -876,13 +900,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
                skb_queue_purge(&msg->msg_frags);
                kfree(msg);
        }
-
-       del_timer_sync(&hdev->cmd_timer);
-
-       nfc_unregister_device(hdev->ndev);
-
-       cancel_work_sync(&hdev->msg_tx_work);
-       cancel_work_sync(&hdev->msg_rx_work);
 }
 EXPORT_SYMBOL(nfc_hci_unregister_device);
 
index bc308a7ca6093f2857905beb3eecfe86d612671f..b6b4109f2343eb9cc9628c1c0ba07342d4f6c6f0 100644 (file)
@@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        }
 
        mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->shutting_down) {
+               err = -ESHUTDOWN;
+               mutex_unlock(&hdev->msg_tx_mutex);
+               goto out_skb_err;
+       }
+
        list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
        mutex_unlock(&hdev->msg_tx_mutex);