summary refs log tree commit diff
path: root/net
diff options
context:
space:
mode:
authorEric Lapuyade <eric.lapuyade@linux.intel.com>2012-11-26 18:06:27 +0100
committerSamuel Ortiz <sameo@linux.intel.com>2013-01-10 00:51:48 +0100
commitf0c9103813b3045bd5b43220b6a78c9908a45d24 (patch)
tree78422096c0728f84a43c057bcf6ffa185bd13190 /net
parent5f4d6214ef5e9b1ff6a72ddfa387c1d72adfac98 (diff)
downloadlinux-f0c9103813b3045bd5b43220b6a78c9908a45d24.tar.gz
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>
Diffstat (limited to 'net')
-rw-r--r--net/nfc/core.c47
-rw-r--r--net/nfc/hci/core.c31
-rw-r--r--net/nfc/hci/hcp.c7
3 files changed, 53 insertions, 32 deletions
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)
 		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);
 
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)
 	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);
 
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,
 	}
 
 	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);