diff options
Diffstat (limited to 'net/nfc')
| -rw-r--r-- | net/nfc/core.c | 32 | ||||
| -rw-r--r-- | net/nfc/nci/core.c | 30 | 
2 files changed, 42 insertions, 20 deletions
| diff --git a/net/nfc/core.c b/net/nfc/core.c index 3c645c1d99c9..dc7a2404efdf 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -94,13 +94,13 @@ int nfc_dev_up(struct nfc_dev *dev)  	device_lock(&dev->dev); -	if (dev->rfkill && rfkill_blocked(dev->rfkill)) { -		rc = -ERFKILL; +	if (!device_is_registered(&dev->dev)) { +		rc = -ENODEV;  		goto error;  	} -	if (!device_is_registered(&dev->dev)) { -		rc = -ENODEV; +	if (dev->rfkill && rfkill_blocked(dev->rfkill)) { +		rc = -ERFKILL;  		goto error;  	} @@ -1125,11 +1125,7 @@ int nfc_register_device(struct nfc_dev *dev)  	if (rc)  		pr_err("Could not register llcp device\n"); -	rc = nfc_genl_device_added(dev); -	if (rc) -		pr_debug("The userspace won't be notified that the device %s was added\n", -			 dev_name(&dev->dev)); - +	device_lock(&dev->dev);  	dev->rfkill = rfkill_alloc(dev_name(&dev->dev), &dev->dev,  				   RFKILL_TYPE_NFC, &nfc_rfkill_ops, dev);  	if (dev->rfkill) { @@ -1138,6 +1134,12 @@ int nfc_register_device(struct nfc_dev *dev)  			dev->rfkill = NULL;  		}  	} +	device_unlock(&dev->dev); + +	rc = nfc_genl_device_added(dev); +	if (rc) +		pr_debug("The userspace won't be notified that the device %s was added\n", +			 dev_name(&dev->dev));  	return 0;  } @@ -1154,10 +1156,17 @@ void nfc_unregister_device(struct nfc_dev *dev)  	pr_debug("dev_name=%s\n", dev_name(&dev->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)); + +	device_lock(&dev->dev);  	if (dev->rfkill) {  		rfkill_unregister(dev->rfkill);  		rfkill_destroy(dev->rfkill);  	} +	device_unlock(&dev->dev);  	if (dev->ops->check_presence) {  		device_lock(&dev->dev); @@ -1167,11 +1176,6 @@ void nfc_unregister_device(struct nfc_dev *dev)  		cancel_work_sync(&dev->check_pres_work);  	} -	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);  	mutex_lock(&nfc_devlist_mutex); diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6fd873aa86be..d2537383a3e8 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -144,12 +144,15 @@ inline int nci_request(struct nci_dev *ndev,  {  	int rc; -	if (!test_bit(NCI_UP, &ndev->flags)) -		return -ENETDOWN; -  	/* Serialize all requests */  	mutex_lock(&ndev->req_lock); -	rc = __nci_request(ndev, req, opt, timeout); +	/* check the state after obtaing the lock against any races +	 * from nci_close_device when the device gets removed. +	 */ +	if (test_bit(NCI_UP, &ndev->flags)) +		rc = __nci_request(ndev, req, opt, timeout); +	else +		rc = -ENETDOWN;  	mutex_unlock(&ndev->req_lock);  	return rc; @@ -473,6 +476,11 @@ static int nci_open_device(struct nci_dev *ndev)  	mutex_lock(&ndev->req_lock); +	if (test_bit(NCI_UNREG, &ndev->flags)) { +		rc = -ENODEV; +		goto done; +	} +  	if (test_bit(NCI_UP, &ndev->flags)) {  		rc = -EALREADY;  		goto done; @@ -545,6 +553,10 @@ done:  static int nci_close_device(struct nci_dev *ndev)  {  	nci_req_cancel(ndev, ENODEV); + +	/* This mutex needs to be held as a barrier for +	 * caller nci_unregister_device +	 */  	mutex_lock(&ndev->req_lock);  	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { @@ -582,8 +594,8 @@ static int nci_close_device(struct nci_dev *ndev)  	del_timer_sync(&ndev->cmd_timer); -	/* Clear flags */ -	ndev->flags = 0; +	/* Clear flags except NCI_UNREG */ +	ndev->flags &= BIT(NCI_UNREG);  	mutex_unlock(&ndev->req_lock); @@ -1266,6 +1278,12 @@ void nci_unregister_device(struct nci_dev *ndev)  {  	struct nci_conn_info *conn_info, *n; +	/* This set_bit is not protected with specialized barrier, +	 * However, it is fine because the mutex_lock(&ndev->req_lock); +	 * in nci_close_device() will help to emit one. +	 */ +	set_bit(NCI_UNREG, &ndev->flags); +  	nci_close_device(ndev);  	destroy_workqueue(ndev->cmd_wq); |