Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/dasd: fix panic during offline processing

A DASD device consists of the device itself and a discipline with a
corresponding private structure. These fields are set up during online
processing right after the device is created and before it is processed by
the state machine and made available for I/O.
During offline processing the discipline pointer and the private data gets
freed within the state machine and without protection of the existing
reference count. This might lead to a kernel panic because a function might
have taken a device reference and accesses the discipline pointer and/or
private data of the device while this is already freed.

Fix by freeing the discipline pointer and the private data after ensuring
that there is no reference to the device left.

Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Stefan Haberland and committed by
Martin Schwidefsky
c020d722 a9f6273f

+18 -10
+16 -10
drivers/s390/block/dasd.c
··· 212 212 { 213 213 /* Disable extended error reporting for this device. */ 214 214 dasd_eer_disable(device); 215 - /* Forget the discipline information. */ 216 - if (device->discipline) { 217 - if (device->discipline->uncheck_device) 218 - device->discipline->uncheck_device(device); 219 - module_put(device->discipline->owner); 220 - } 221 - device->discipline = NULL; 222 - if (device->base_discipline) 223 - module_put(device->base_discipline->owner); 224 - device->base_discipline = NULL; 225 215 device->state = DASD_STATE_NEW; 226 216 227 217 if (device->block) ··· 3366 3376 return 0; 3367 3377 } 3368 3378 EXPORT_SYMBOL_GPL(dasd_generic_probe); 3379 + 3380 + void dasd_generic_free_discipline(struct dasd_device *device) 3381 + { 3382 + /* Forget the discipline information. */ 3383 + if (device->discipline) { 3384 + if (device->discipline->uncheck_device) 3385 + device->discipline->uncheck_device(device); 3386 + module_put(device->discipline->owner); 3387 + device->discipline = NULL; 3388 + } 3389 + if (device->base_discipline) { 3390 + module_put(device->base_discipline->owner); 3391 + device->base_discipline = NULL; 3392 + } 3393 + } 3394 + EXPORT_SYMBOL_GPL(dasd_generic_free_discipline); 3369 3395 3370 3396 /* 3371 3397 * This will one day be called from a global not_oper handler.
+1
drivers/s390/block/dasd_devmap.c
··· 617 617 /* Wait for reference counter to drop to zero. */ 618 618 wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0); 619 619 620 + dasd_generic_free_discipline(device); 620 621 /* Disconnect dasd_device structure from ccw_device structure. */ 621 622 cdev = device->cdev; 622 623 device->cdev = NULL;
+1
drivers/s390/block/dasd_int.h
··· 725 725 int dasd_cancel_req(struct dasd_ccw_req *); 726 726 int dasd_flush_device_queue(struct dasd_device *); 727 727 int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *); 728 + void dasd_generic_free_discipline(struct dasd_device *); 728 729 void dasd_generic_remove (struct ccw_device *cdev); 729 730 int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); 730 731 int dasd_generic_set_offline (struct ccw_device *cdev);