Commit 1378a683 authored by Gerald Schaefer's avatar Gerald Schaefer Committed by Martin Schwidefsky

s390/dcssblk: fix possible deadlock in remove vs. per-device attributes

dcssblk_remove_store() holds the dcssblk_devices_sem semaphore while
calling device_unregister(), which in turn tries to acquire the kernfs
kn->dev_map rwsem for the device sysfs subtree. The same rwsem is also
acquired when using the per-device sysfs attributes in the device sub-tree,
and the attribute handlers then also acquire the dcssblk_devices_sem.

This can lead to a deadlock when removing a DCSS while concurrently
reading from / writing to one of its sysfs attributes. The following
lockdep warning hinted towards the issue (CPU0 = dcssblk_remove_store,
CPU1 = dcssblk_shared_store):

[   76.496047]  Possible unsafe locking scenario:

[   76.496054]        CPU0                    CPU1
[   76.496059]        ----                    ----
[   76.496087]   lock(&dcssblk_devices_sem);
[   76.496090]                                lock(s_active#175);
[   76.496106]                                lock(&dcssblk_devices_sem);
[   76.496110]   lock(s_active#175);
[   76.496115]
 *** DEADLOCK ***

Fix this by releasing the dcssblk_devices_sem semaphore, which only
protects internal DCSS data, before calling device_unregister().
Signed-off-by: default avatarGerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 4f375903
...@@ -756,15 +756,16 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch ...@@ -756,15 +756,16 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
blk_cleanup_queue(dev_info->dcssblk_queue); blk_cleanup_queue(dev_info->dcssblk_queue);
dev_info->gd->queue = NULL; dev_info->gd->queue = NULL;
put_disk(dev_info->gd); put_disk(dev_info->gd);
device_unregister(&dev_info->dev);
/* unload all related segments */ /* unload all related segments */
list_for_each_entry(entry, &dev_info->seg_list, lh) list_for_each_entry(entry, &dev_info->seg_list, lh)
segment_unload(entry->segment_name); segment_unload(entry->segment_name);
put_device(&dev_info->dev);
up_write(&dcssblk_devices_sem); up_write(&dcssblk_devices_sem);
device_unregister(&dev_info->dev);
put_device(&dev_info->dev);
rc = count; rc = count;
out_buf: out_buf:
kfree(local_buf); kfree(local_buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment