Skip to content

Commit 0c6924c

Browse files
vneethvhcahca
authored andcommitted
s390/cio: introduce locking for register/unregister functions
Unbinding an I/O subchannel with a child-CCW device in disconnected state sometimes causes a kernel-panic. The race condition was seen mostly during testing, when setting all the CHPIDs of a device to offline and at the same time, the unbinding the I/O subchannel driver. The kernel-panic occurs because of double delete, the I/O subchannel driver calls device_del on the CCW device while another device_del invocation for the same device is in-flight. For instance, disabling all the CHPIDs will trigger the ccw_device_remove function, which will call a ccw_device_unregister(), which ends up calling the device_del() which is asynchronous via cdev's todo workqueue. And unbinding the I/O subchannel driver calls io_subchannel_remove() function which calls the ccw_device_unregister() and device_del(). This double delete can be prevented by serializing all CCW device registration/unregistration calls into the driver core. This patch introduces a mutex which will be used for this purpose. Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com> Reported-by: Boris Fiuczynski <fiuczy@linux.ibm.com> Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
1 parent 0517899 commit 0c6924c

File tree

2 files changed

+11
-0
lines changed

2 files changed

+11
-0
lines changed

arch/s390/include/asm/ccwdev.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <asm/fcx.h>
1616
#include <asm/irq.h>
1717
#include <asm/schid.h>
18+
#include <linux/mutex.h>
1819

1920
/* structs from asm/cio.h */
2021
struct irb;
@@ -87,6 +88,7 @@ struct ccw_device {
8788
spinlock_t *ccwlock;
8889
/* private: */
8990
struct ccw_device_private *private; /* cio private information */
91+
struct mutex reg_mutex;
9092
/* public: */
9193
struct ccw_device_id id;
9294
struct ccw_driver *drv;

drivers/s390/cio/device.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,13 @@ int ccw_device_is_orphan(struct ccw_device *cdev)
244244

245245
static void ccw_device_unregister(struct ccw_device *cdev)
246246
{
247+
mutex_lock(&cdev->reg_mutex);
247248
if (device_is_registered(&cdev->dev)) {
248249
/* Undo device_add(). */
249250
device_del(&cdev->dev);
250251
}
252+
mutex_unlock(&cdev->reg_mutex);
253+
251254
if (cdev->private->flags.initialized) {
252255
cdev->private->flags.initialized = 0;
253256
/* Release reference from device_initialize(). */
@@ -653,11 +656,13 @@ static void ccw_device_do_unbind_bind(struct ccw_device *cdev)
653656
{
654657
int ret;
655658

659+
mutex_lock(&cdev->reg_mutex);
656660
if (device_is_registered(&cdev->dev)) {
657661
device_release_driver(&cdev->dev);
658662
ret = device_attach(&cdev->dev);
659663
WARN_ON(ret == -ENODEV);
660664
}
665+
mutex_unlock(&cdev->reg_mutex);
661666
}
662667

663668
static void
@@ -740,6 +745,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
740745
INIT_LIST_HEAD(&priv->cmb_list);
741746
init_waitqueue_head(&priv->wait_q);
742747
timer_setup(&priv->timer, ccw_device_timeout, 0);
748+
mutex_init(&cdev->reg_mutex);
743749

744750
atomic_set(&priv->onoff, 0);
745751
cdev->ccwlock = sch->lock;
@@ -825,6 +831,7 @@ static void io_subchannel_register(struct ccw_device *cdev)
825831
* be registered). We need to reprobe since we may now have sense id
826832
* information.
827833
*/
834+
mutex_lock(&cdev->reg_mutex);
828835
if (device_is_registered(&cdev->dev)) {
829836
if (!cdev->drv) {
830837
ret = device_reprobe(&cdev->dev);
@@ -847,12 +854,14 @@ static void io_subchannel_register(struct ccw_device *cdev)
847854
spin_lock_irqsave(sch->lock, flags);
848855
sch_set_cdev(sch, NULL);
849856
spin_unlock_irqrestore(sch->lock, flags);
857+
mutex_unlock(&cdev->reg_mutex);
850858
/* Release initial device reference. */
851859
put_device(&cdev->dev);
852860
goto out_err;
853861
}
854862
out:
855863
cdev->private->flags.recog_done = 1;
864+
mutex_unlock(&cdev->reg_mutex);
856865
wake_up(&cdev->private->wait_q);
857866
out_err:
858867
if (adjust_init_count && atomic_dec_and_test(&ccw_device_init_count))

0 commit comments

Comments
 (0)