diff options
author | Eric Lapuyade <eric.lapuyade@linux.intel.com> | 2012-11-26 12:06:27 -0500 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-01-09 18:51:48 -0500 |
commit | f0c9103813b3045bd5b43220b6a78c9908a45d24 (patch) | |
tree | 78422096c0728f84a43c057bcf6ffa185bd13190 | |
parent | 5f4d6214ef5e9b1ff6a72ddfa387c1d72adfac98 (diff) |
NFC: Fixed nfc core and hci unregistration and cleanup
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>
-rw-r--r-- | include/net/nfc/hci.h | 2 | ||||
-rw-r--r-- | include/net/nfc/nfc.h | 2 | ||||
-rw-r--r-- | net/nfc/core.c | 47 | ||||
-rw-r--r-- | net/nfc/hci/core.c | 31 | ||||
-rw-r--r-- | net/nfc/hci/hcp.c | 7 |
5 files changed, 57 insertions, 32 deletions
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 671953e11575..e6224571e5e6 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h | |||
@@ -87,6 +87,8 @@ struct nfc_hci_dev { | |||
87 | 87 | ||
88 | u32 max_data_link_payload; | 88 | u32 max_data_link_payload; |
89 | 89 | ||
90 | bool shutting_down; | ||
91 | |||
90 | struct mutex msg_tx_mutex; | 92 | struct mutex msg_tx_mutex; |
91 | 93 | ||
92 | struct list_head msg_tx_queue; | 94 | struct list_head msg_tx_queue; |
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index fce80b2f9be7..1665674e86b2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h | |||
@@ -115,6 +115,8 @@ struct nfc_dev { | |||
115 | struct timer_list check_pres_timer; | 115 | struct timer_list check_pres_timer; |
116 | struct work_struct check_pres_work; | 116 | struct work_struct check_pres_work; |
117 | 117 | ||
118 | bool shutting_down; | ||
119 | |||
118 | struct nfc_ops *ops; | 120 | struct nfc_ops *ops; |
119 | }; | 121 | }; |
120 | #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) | 122 | #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) |
diff --git a/net/nfc/core.c b/net/nfc/core.c index aa64ea441676..7d7b4ee34015 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c | |||
@@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) | |||
338 | dev->active_target = target; | 338 | dev->active_target = target; |
339 | dev->rf_mode = NFC_RF_INITIATOR; | 339 | dev->rf_mode = NFC_RF_INITIATOR; |
340 | 340 | ||
341 | if (dev->ops->check_presence) | 341 | if (dev->ops->check_presence && !dev->shutting_down) |
342 | mod_timer(&dev->check_pres_timer, jiffies + | 342 | mod_timer(&dev->check_pres_timer, jiffies + |
343 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); | 343 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); |
344 | } | 344 | } |
@@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, | |||
429 | rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, | 429 | rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, |
430 | cb_context); | 430 | cb_context); |
431 | 431 | ||
432 | if (!rc && dev->ops->check_presence) | 432 | if (!rc && dev->ops->check_presence && !dev->shutting_down) |
433 | mod_timer(&dev->check_pres_timer, jiffies + | 433 | mod_timer(&dev->check_pres_timer, jiffies + |
434 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); | 434 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); |
435 | } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { | 435 | } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { |
@@ -684,11 +684,6 @@ static void nfc_release(struct device *d) | |||
684 | 684 | ||
685 | pr_debug("dev_name=%s\n", dev_name(&dev->dev)); | 685 | pr_debug("dev_name=%s\n", dev_name(&dev->dev)); |
686 | 686 | ||
687 | if (dev->ops->check_presence) { | ||
688 | del_timer_sync(&dev->check_pres_timer); | ||
689 | cancel_work_sync(&dev->check_pres_work); | ||
690 | } | ||
691 | |||
692 | nfc_genl_data_exit(&dev->genl_data); | 687 | nfc_genl_data_exit(&dev->genl_data); |
693 | kfree(dev->targets); | 688 | kfree(dev->targets); |
694 | kfree(dev); | 689 | kfree(dev); |
@@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work) | |||
706 | rc = dev->ops->check_presence(dev, dev->active_target); | 701 | rc = dev->ops->check_presence(dev, dev->active_target); |
707 | if (rc == -EOPNOTSUPP) | 702 | if (rc == -EOPNOTSUPP) |
708 | goto exit; | 703 | goto exit; |
709 | if (!rc) { | 704 | if (rc) { |
710 | mod_timer(&dev->check_pres_timer, jiffies + | ||
711 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); | ||
712 | } else { | ||
713 | u32 active_target_idx = dev->active_target->idx; | 705 | u32 active_target_idx = dev->active_target->idx; |
714 | device_unlock(&dev->dev); | 706 | device_unlock(&dev->dev); |
715 | nfc_target_lost(dev, active_target_idx); | 707 | nfc_target_lost(dev, active_target_idx); |
716 | return; | 708 | return; |
717 | } | 709 | } |
710 | |||
711 | if (!dev->shutting_down) | ||
712 | mod_timer(&dev->check_pres_timer, jiffies + | ||
713 | msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); | ||
718 | } | 714 | } |
719 | 715 | ||
720 | exit: | 716 | exit: |
@@ -853,26 +849,27 @@ void nfc_unregister_device(struct nfc_dev *dev) | |||
853 | 849 | ||
854 | id = dev->idx; | 850 | id = dev->idx; |
855 | 851 | ||
856 | mutex_lock(&nfc_devlist_mutex); | 852 | if (dev->ops->check_presence) { |
857 | nfc_devlist_generation++; | 853 | device_lock(&dev->dev); |
858 | 854 | dev->shutting_down = true; | |
859 | /* lock to avoid unregistering a device while an operation | 855 | device_unlock(&dev->dev); |
860 | is in progress */ | 856 | del_timer_sync(&dev->check_pres_timer); |
861 | device_lock(&dev->dev); | 857 | cancel_work_sync(&dev->check_pres_work); |
862 | device_del(&dev->dev); | 858 | } |
863 | device_unlock(&dev->dev); | ||
864 | 859 | ||
865 | mutex_unlock(&nfc_devlist_mutex); | 860 | rc = nfc_genl_device_removed(dev); |
861 | if (rc) | ||
862 | pr_debug("The userspace won't be notified that the device %s " | ||
863 | "was removed\n", dev_name(&dev->dev)); | ||
866 | 864 | ||
867 | nfc_llcp_unregister_device(dev); | 865 | nfc_llcp_unregister_device(dev); |
868 | 866 | ||
869 | rc = nfc_genl_device_removed(dev); | 867 | mutex_lock(&nfc_devlist_mutex); |
870 | if (rc) | 868 | nfc_devlist_generation++; |
871 | pr_debug("The userspace won't be notified that the device %s was removed\n", | 869 | device_del(&dev->dev); |
872 | dev_name(&dev->dev)); | 870 | mutex_unlock(&nfc_devlist_mutex); |
873 | 871 | ||
874 | ida_simple_remove(&nfc_index_ida, id); | 872 | ida_simple_remove(&nfc_index_ida, id); |
875 | |||
876 | } | 873 | } |
877 | EXPORT_SYMBOL(nfc_unregister_device); | 874 | EXPORT_SYMBOL(nfc_unregister_device); |
878 | 875 | ||
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 7bea574d5934..b4b84268653d 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c | |||
@@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) | |||
57 | int r = 0; | 57 | int r = 0; |
58 | 58 | ||
59 | mutex_lock(&hdev->msg_tx_mutex); | 59 | mutex_lock(&hdev->msg_tx_mutex); |
60 | if (hdev->shutting_down) | ||
61 | goto exit; | ||
60 | 62 | ||
61 | if (hdev->cmd_pending_msg) { | 63 | if (hdev->cmd_pending_msg) { |
62 | if (timer_pending(&hdev->cmd_timer) == 0) { | 64 | if (timer_pending(&hdev->cmd_timer) == 0) { |
@@ -868,6 +870,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) | |||
868 | { | 870 | { |
869 | struct hci_msg *msg, *n; | 871 | struct hci_msg *msg, *n; |
870 | 872 | ||
873 | mutex_lock(&hdev->msg_tx_mutex); | ||
874 | |||
875 | if (hdev->cmd_pending_msg) { | ||
876 | if (hdev->cmd_pending_msg->cb) | ||
877 | hdev->cmd_pending_msg->cb( | ||
878 | hdev->cmd_pending_msg->cb_context, | ||
879 | NULL, -ESHUTDOWN); | ||
880 | kfree(hdev->cmd_pending_msg); | ||
881 | hdev->cmd_pending_msg = NULL; | ||
882 | } | ||
883 | |||
884 | hdev->shutting_down = true; | ||
885 | |||
886 | mutex_unlock(&hdev->msg_tx_mutex); | ||
887 | |||
888 | del_timer_sync(&hdev->cmd_timer); | ||
889 | cancel_work_sync(&hdev->msg_tx_work); | ||
890 | |||
891 | cancel_work_sync(&hdev->msg_rx_work); | ||
892 | |||
893 | nfc_unregister_device(hdev->ndev); | ||
894 | |||
871 | skb_queue_purge(&hdev->rx_hcp_frags); | 895 | skb_queue_purge(&hdev->rx_hcp_frags); |
872 | skb_queue_purge(&hdev->msg_rx_queue); | 896 | skb_queue_purge(&hdev->msg_rx_queue); |
873 | 897 | ||
@@ -876,13 +900,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) | |||
876 | skb_queue_purge(&msg->msg_frags); | 900 | skb_queue_purge(&msg->msg_frags); |
877 | kfree(msg); | 901 | kfree(msg); |
878 | } | 902 | } |
879 | |||
880 | del_timer_sync(&hdev->cmd_timer); | ||
881 | |||
882 | nfc_unregister_device(hdev->ndev); | ||
883 | |||
884 | cancel_work_sync(&hdev->msg_tx_work); | ||
885 | cancel_work_sync(&hdev->msg_rx_work); | ||
886 | } | 903 | } |
887 | EXPORT_SYMBOL(nfc_hci_unregister_device); | 904 | EXPORT_SYMBOL(nfc_hci_unregister_device); |
888 | 905 | ||
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index bc308a7ca609..b6b4109f2343 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c | |||
@@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, | |||
105 | } | 105 | } |
106 | 106 | ||
107 | mutex_lock(&hdev->msg_tx_mutex); | 107 | mutex_lock(&hdev->msg_tx_mutex); |
108 | |||
109 | if (hdev->shutting_down) { | ||
110 | err = -ESHUTDOWN; | ||
111 | mutex_unlock(&hdev->msg_tx_mutex); | ||
112 | goto out_skb_err; | ||
113 | } | ||
114 | |||
108 | list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); | 115 | list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); |
109 | mutex_unlock(&hdev->msg_tx_mutex); | 116 | mutex_unlock(&hdev->msg_tx_mutex); |
110 | 117 | ||