[PATCH] s390: dasd reference counting
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Tue, 21 Feb 2006 02:28:13 +0000 (18:28 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Tue, 21 Feb 2006 04:00:12 +0000 (20:00 -0800)
When using the dasd diag discipline, the base discipline module (eckd or fba)
can be unloaded, even though the dasd driver requires both discipline modules
(base and diag) to work correctly.

Implement reference counting for both base and diag discipline modules in
order to fix this.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_int.h

index 08c88fcd896339eeb8c9700535c5aa0f620d313b..06bb992a4c6c9a9c6890f97bc7581fb2877aa463 100644 (file)
@@ -156,7 +156,12 @@ dasd_state_known_to_new(struct dasd_device * device)
        /* disable extended error reporting for this device */
        dasd_disable_eer(device);
        /* Forget the discipline information. */
+       if (device->discipline)
+               module_put(device->discipline->owner);
        device->discipline = NULL;
+       if (device->base_discipline)
+               module_put(device->base_discipline->owner);
+       device->base_discipline = NULL;
        device->state = DASD_STATE_NEW;
 
        dasd_free_queue(device);
@@ -1880,9 +1885,10 @@ dasd_generic_remove (struct ccw_device *cdev)
  */
 int
 dasd_generic_set_online (struct ccw_device *cdev,
-                        struct dasd_discipline *discipline)
+                        struct dasd_discipline *base_discipline)
 
 {
+       struct dasd_discipline *discipline;
        struct dasd_device *device;
        int rc;
 
@@ -1890,6 +1896,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
        if (IS_ERR(device))
                return PTR_ERR(device);
 
+       discipline = base_discipline;
        if (device->features & DASD_FEATURE_USEDIAG) {
                if (!dasd_diag_discipline_pointer) {
                        printk (KERN_WARNING
@@ -1901,6 +1908,16 @@ dasd_generic_set_online (struct ccw_device *cdev,
                }
                discipline = dasd_diag_discipline_pointer;
        }
+       if (!try_module_get(base_discipline->owner)) {
+               dasd_delete_device(device);
+               return -EINVAL;
+       }
+       if (!try_module_get(discipline->owner)) {
+               module_put(base_discipline->owner);
+               dasd_delete_device(device);
+               return -EINVAL;
+       }
+       device->base_discipline = base_discipline;
        device->discipline = discipline;
 
        rc = discipline->check_device(device);
@@ -1909,6 +1926,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
                        "dasd_generic couldn't online device %s "
                        "with discipline %s rc=%i\n",
                        cdev->dev.bus_id, discipline->name, rc);
+               module_put(discipline->owner);
+               module_put(base_discipline->owner);
                dasd_delete_device(device);
                return rc;
        }
index d1b08fa13fd224184a9387625c218302b1724eed..5efac1b97ecedc27b3e5c024ebc508781a24b604 100644 (file)
@@ -321,6 +321,7 @@ struct dasd_device {
 
        /* Device discipline stuff. */
        struct dasd_discipline *discipline;
+       struct dasd_discipline *base_discipline;
        char *private;
 
        /* Device state and target state. */