Skip to content

Commit

Permalink
NFC: Fixed nfc core and hci unregistration and cleanup
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Lapuyade authored and Samuel Ortiz committed Jan 9, 2013
1 parent 5f4d621 commit f0c9103
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 32 deletions.
2 changes: 2 additions & 0 deletions include/net/nfc/hci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions include/net/nfc/nfc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 22 additions & 25 deletions net/nfc/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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:
Expand Down Expand Up @@ -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);

Expand Down
31 changes: 24 additions & 7 deletions net/nfc/hci/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions net/nfc/hci/hcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit f0c9103

Please sign in to comment.