diff --git a/scst/include/scst.h b/scst/include/scst.h index 570f05a26..d792e655a 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2224,6 +2224,14 @@ struct scst_device { /* Set, if a strictly serialized cmd is waiting blocked */ unsigned short strictly_serialized_cmd_waiting:1; + /* + * Set, if this device is being unregistered. Useful to let sysfs + * attributes know when they should exit immediatelly to prevent + * possible deadlocks with their device unregistration waiting for + * their kobj last put. + */ + unsigned short dev_unregistering:1; + /**************************************************************/ /************************************************************* diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 1c1eec304..ff4a25c74 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef INSIDE_KERNEL_TREE #include #endif @@ -4416,9 +4417,24 @@ static int vdev_sysfs_process_get_filename(struct scst_sysfs_work_item *work) dev = work->dev; - if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { - res = -EINTR; - goto out_put; + /* + * Since we have a get() on dev->dev_kobj, we can not simply mutex_lock + * scst_vdisk_mutex, because otherwise we can fall in a deadlock with + * vdisk_del_device(), which is waiting for the last ref to dev_kobj + * under scst_vdisk_mutex. + */ + while (!mutex_trylock(&scst_vdisk_mutex)) { + if ((volatile bool)(dev->dev_unregistering)) { + TRACE_MGMT_DBG("Skipping being unregistered dev %s", + dev->virt_name); + res = -ENOENT; + goto out_put; + } + if (signal_pending(current)) { + res = -EINTR; + goto out_put; + } + msleep(100); } virt_dev = dev->dh_priv; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 01d938c71..48ec6c96c 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1006,6 +1006,8 @@ static void scst_unregister_device(struct scsi_device *scsidp) goto out_unlock; } + dev->dev_unregistering = 1; + list_del(&dev->dev_list_entry); scst_dg_dev_remove_by_dev(dev); @@ -1254,6 +1256,8 @@ void scst_unregister_virtual_device(int id) goto out_unlock; } + dev->dev_unregistering = 1; + list_del(&dev->dev_list_entry); scst_pr_clear_dev(dev); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index eff55e1ce..0e6d3ae13 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -522,9 +522,9 @@ int scst_sysfs_queue_wait_work(struct scst_sysfs_work_item *work) * for the last put during some object unregistration and at the same * time another queued work is having reference on that object taken and * waiting for attention from the sysfs thread. Generally, all sysfs - * function calling kobject_get() and then queuing sysfs thread job. For - * instance. This is especially dangerous in read only cases, like - * vdev_sysfs_filename_show(). + * functions calling kobject_get() and then queuing sysfs thread job + * affected by this. This is especially dangerous in read only cases, + * like vdev_sysfs_filename_show(). * * So, to eliminate that deadlock we will create an extra sysfs thread * for each queued sysfs work. This thread will quit as soon as it will @@ -548,8 +548,7 @@ int scst_sysfs_queue_wait_work(struct scst_sysfs_work_item *work) timeout = 5*HZ; continue; } - TRACE_MGMT_DBG("Time out waiting for work %p", - work); + TRACE_MGMT_DBG("Time out waiting for work %p", work); res = -EAGAIN; goto out_put; } else if (rc < 0) {