diff --git a/scst/include/scst.h b/scst/include/scst.h index ba525e062..a7ccc82ba 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2712,6 +2712,14 @@ struct scst_device { /* Set, if a strictly serialized cmd is waiting blocked */ unsigned int strictly_serialized_cmd_waiting:1; + /* + * Set, if this device is being unregistered. Useful to let sysfs + * attributes know when they should exit immediately to prevent + * possible deadlocks with their device unregistration waiting for + * their kobj last put. + */ + unsigned int dev_unregistering:1; + /* * Set if ext blocking is pending. It is just shortcut for * !list_empty(&dev->ext_blockers_list) to save a cache miss. diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 39c4e9971..2e0fc397b 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -8463,17 +8463,6 @@ static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, return pos; } -static bool scst_dev_being_unregistered(struct scst_device *dev) -{ - bool res; - - mutex_lock(&scst_mutex); - res = list_empty(&dev->dev_list_entry); - mutex_unlock(&scst_mutex); - - return res; -} - static int vdev_sysfs_process_get_filename(struct scst_sysfs_work_item *work) { int res = 0; @@ -8491,7 +8480,7 @@ static int vdev_sysfs_process_get_filename(struct scst_sysfs_work_item *work) * under scst_vdisk_mutex. */ while (!mutex_trylock(&scst_vdisk_mutex)) { - if (scst_dev_being_unregistered(dev)) { + if (dev->dev_unregistering) { TRACE_MGMT_DBG("Skipping being unregistered dev %s", dev->virt_name); res = -ENOENT; @@ -8502,6 +8491,18 @@ static int vdev_sysfs_process_get_filename(struct scst_sysfs_work_item *work) goto out_put; } msleep(100); + /* + * We need to reread dev_unregistering from memory, hence + * prevent compiler from putting it in a register. Generally, + * it shouldn't happen, because the compiler isn't allowed to do + * such a transformation if any functions that can cause side + * effects are called between successive accesses, but let's be + * on the safe side. We can't cast dev_unregistering to + * volatile, because it has no effect we need, and can't cast + * it to *(volatile bool*)&, because it isn't possible to get + * address of a bit field. + */ + barrier(); } virt_dev = dev->dh_priv; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 5a96e46e9..ce0db2f75 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1195,6 +1195,8 @@ static void scst_unregister_device(struct scsi_device *scsidp) goto out_unlock; } + dev->dev_unregistering = 1; + list_del_init(&dev->dev_list_entry); #ifdef CONFIG_SCST_FORWARD_MODE_PASS_THROUGH @@ -1418,7 +1420,8 @@ out: return res; out_unreg: - list_del_init(&dev->dev_list_entry); + dev->dev_unregistering = 1; + list_del(&dev->dev_list_entry); scst_assign_dev_handler(dev, &scst_null_devtype); goto out_pr_clear_dev; @@ -1471,6 +1474,8 @@ void scst_unregister_virtual_device(int id, goto out_unlock; } + dev->dev_unregistering = 1; + scst_cm_on_dev_unregister(dev); list_del_init(&dev->dev_list_entry);