NFC: pn533: Fix socket deadlock
A deadlock can occur when the NFC raw socket is closed while the driver is processing a command. Following is the call graph of the affected situation: send data via raw_sock: ------------- rawsock_tx_work sock_hold => socket refcnt++ nfc_data_exchange => cb = rawsock_data_exchange_complete ops->im_transceive = pn533_transceive => arg->cb = db = rawsock_data_exchange_complete pn533_send_data_async => cb = pn533_data_exchange_complete __pn533_send_async => cmd->complete_cb = cb = pn533_data_exchange_complete if_ops->send_frame_async response: -------- pn533_recv_response queue_work(priv->wq, &priv->cmd_complete_work) pn533_wq_cmd_complete pn533_send_async_complete cmd->complete_cb() = pn533_data_exchange_complete() arg->cb() = rawsock_data_exchange_complete() sock_put => socket refcnt-- => If the corresponding socket gets closed in the meantime socket will be destructed sk_free __sk_free sk->sk_destruct = rawsock_destruct nfc_deactivate_target ops->deactivate_target = pn533_deactivate_target pn533_send_cmd_sync pn533_send_cmd_async __pn533_send_async list_add_tail(&cmd->queue,&dev->cmd_queue) => add to command list because a command is currently processed wait_for_completion => the workqueue thread waits here because it is the one processing the commands => deadlock To fix the deadlock pn533_deactivate_target is changed to issue the PN533_CMD_IN_RELEASE command in async mode. This way nothing blocks and the release command is executed after the current command. Signed-off-by: Michael Thalmeier <michael.thalmeier@hale.at> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Родитель
e997ebbe46
Коммит
37f895d7e8
|
@ -2263,12 +2263,35 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pn533_deactivate_target_complete(struct pn533 *dev, void *arg,
|
||||
struct sk_buff *resp)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
dev_dbg(&dev->interface->dev, "%s\n", __func__);
|
||||
|
||||
if (IS_ERR(resp)) {
|
||||
rc = PTR_ERR(resp);
|
||||
|
||||
nfc_err(&dev->interface->dev, "Target release error %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = resp->data[0] & PN533_CMD_RET_MASK;
|
||||
if (rc != PN533_CMD_RET_SUCCESS)
|
||||
nfc_err(&dev->interface->dev,
|
||||
"Error 0x%x when releasing the target\n", rc);
|
||||
|
||||
dev_kfree_skb(resp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
|
||||
struct nfc_target *target, u8 mode)
|
||||
{
|
||||
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
|
||||
struct sk_buff *skb;
|
||||
struct sk_buff *resp;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&dev->interface->dev, "%s\n", __func__);
|
||||
|
@ -2287,16 +2310,13 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev,
|
|||
|
||||
*skb_put(skb, 1) = 1; /* TG*/
|
||||
|
||||
resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb);
|
||||
if (IS_ERR(resp))
|
||||
return;
|
||||
rc = pn533_send_cmd_async(dev, PN533_CMD_IN_RELEASE, skb,
|
||||
pn533_deactivate_target_complete, NULL);
|
||||
if (rc < 0) {
|
||||
dev_kfree_skb(skb);
|
||||
nfc_err(&dev->interface->dev, "Target release error %d\n", rc);
|
||||
}
|
||||
|
||||
rc = resp->data[0] & PN533_CMD_RET_MASK;
|
||||
if (rc != PN533_CMD_RET_SUCCESS)
|
||||
nfc_err(&dev->interface->dev,
|
||||
"Error 0x%x when releasing the target\n", rc);
|
||||
|
||||
dev_kfree_skb(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче