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>
This commit is contained in:
Eric Lapuyade 2012-11-26 18:06:27 +01:00 committed by Samuel Ortiz
parent 5f4d6214ef
commit f0c9103813
5 changed files with 58 additions and 33 deletions

View File

@ -87,6 +87,8 @@ struct nfc_hci_dev {
u32 max_data_link_payload; u32 max_data_link_payload;
bool shutting_down;
struct mutex msg_tx_mutex; struct mutex msg_tx_mutex;
struct list_head msg_tx_queue; struct list_head msg_tx_queue;

View File

@ -115,6 +115,8 @@ struct nfc_dev {
struct timer_list check_pres_timer; struct timer_list check_pres_timer;
struct work_struct check_pres_work; struct work_struct check_pres_work;
bool shutting_down;
struct nfc_ops *ops; struct nfc_ops *ops;
}; };
#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)

View File

@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
dev->active_target = target; dev->active_target = target;
dev->rf_mode = NFC_RF_INITIATOR; 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 + mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); 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, rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
cb_context); cb_context);
if (!rc && dev->ops->check_presence) if (!rc && dev->ops->check_presence && !dev->shutting_down)
mod_timer(&dev->check_pres_timer, jiffies + mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
} else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { } 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)); 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); nfc_genl_data_exit(&dev->genl_data);
kfree(dev->targets); kfree(dev->targets);
kfree(dev); 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); rc = dev->ops->check_presence(dev, dev->active_target);
if (rc == -EOPNOTSUPP) if (rc == -EOPNOTSUPP)
goto exit; goto exit;
if (!rc) { if (rc) {
mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
} else {
u32 active_target_idx = dev->active_target->idx; u32 active_target_idx = dev->active_target->idx;
device_unlock(&dev->dev); device_unlock(&dev->dev);
nfc_target_lost(dev, active_target_idx); nfc_target_lost(dev, active_target_idx);
return; return;
} }
if (!dev->shutting_down)
mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
} }
exit: exit:
@ -853,26 +849,27 @@ void nfc_unregister_device(struct nfc_dev *dev)
id = dev->idx; id = dev->idx;
mutex_lock(&nfc_devlist_mutex); if (dev->ops->check_presence) {
nfc_devlist_generation++; device_lock(&dev->dev);
dev->shutting_down = true;
/* lock to avoid unregistering a device while an operation device_unlock(&dev->dev);
is in progress */ del_timer_sync(&dev->check_pres_timer);
device_lock(&dev->dev); cancel_work_sync(&dev->check_pres_work);
device_del(&dev->dev); }
device_unlock(&dev->dev);
mutex_unlock(&nfc_devlist_mutex);
nfc_llcp_unregister_device(dev);
rc = nfc_genl_device_removed(dev); rc = nfc_genl_device_removed(dev);
if (rc) if (rc)
pr_debug("The userspace won't be notified that the device %s was removed\n", pr_debug("The userspace won't be notified that the device %s "
dev_name(&dev->dev)); "was removed\n", dev_name(&dev->dev));
nfc_llcp_unregister_device(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); ida_simple_remove(&nfc_index_ida, id);
} }
EXPORT_SYMBOL(nfc_unregister_device); EXPORT_SYMBOL(nfc_unregister_device);

View File

@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
int r = 0; int r = 0;
mutex_lock(&hdev->msg_tx_mutex); mutex_lock(&hdev->msg_tx_mutex);
if (hdev->shutting_down)
goto exit;
if (hdev->cmd_pending_msg) { if (hdev->cmd_pending_msg) {
if (timer_pending(&hdev->cmd_timer) == 0) { 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; 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->rx_hcp_frags);
skb_queue_purge(&hdev->msg_rx_queue); 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); skb_queue_purge(&msg->msg_frags);
kfree(msg); 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); EXPORT_SYMBOL(nfc_hci_unregister_device);

View File

@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
} }
mutex_lock(&hdev->msg_tx_mutex); 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); list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
mutex_unlock(&hdev->msg_tx_mutex); mutex_unlock(&hdev->msg_tx_mutex);