Commit 5885ddb0 authored by Mikael Pettersson's avatar Mikael Pettersson Committed by James Bottomley

fix ide-cd/ide-scsi oopses after module unload

In 2.5 (the bug's been there since 2.5.42), rmmod:ing a
modular IDE subdriver like ide-cd or ide-scsi and then
rebooting causes an oops in device_shutdown(). This is because
the IDE layer doesn't reset the drive->gendev.driver pointer
that it previously set up to point to data structures in the
subdriver module. device_shutdown() sees a non-NULL ->driver,
dereferences it, and oopses.

The patch below for 2.5.54 fixes two generic bugs related to
unloading of modular IDE subdrivers, and one specific to ide-scsi:

1. ata_attach() needs to set drive->gendev.driver = NULL
   when no specific driver claims the drive. This prevents a
   drive previously owned by a subdriver module from keeping
   its ->gendev.driver pointing into that module.

2. ide_unregister_driver() needs to unregister &driver->gen_driver;
   this is to balance the corresponding register call in
   ide_register_driver(). [This part of the patch is originally
   by Patrick Mochel.]

3. ide-scsi.c abuses ide_driver_t's busy field as a counter
   while the field in fact is a single-bit boolean. This causes
   the busyness of the driver to be incorrect while the driver
   is active. (From my recent patch for 2.4.20-ac2/2.4.21-pre2.)
parent 6de983fa
......@@ -1346,6 +1346,7 @@ int ata_attach(ide_drive_t *drive)
spin_unlock(&drivers_lock);
spin_lock(&drives_lock);
list_add_tail(&drive->list, &ata_unused);
drive->gendev.driver = NULL;
spin_unlock(&drives_lock);
return 1;
}
......@@ -2308,6 +2309,8 @@ void ide_unregister_driver(ide_driver_t *driver)
list_del(&driver->drivers);
spin_unlock(&drivers_lock);
driver_unregister(&driver->gen_driver);
while(!list_empty(&driver->drives)) {
drive = list_entry(driver->drives.next, ide_drive_t, list);
if (driver->cleanup(drive)) {
......
......@@ -590,6 +590,7 @@ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi, int id)
set_bit(IDESCSI_LOG_CMD, &scsi->log);
#endif /* IDESCSI_DEBUG_LOG */
idescsi_add_settings(drive);
DRIVER(drive)->busy--;
}
static int idescsi_cleanup (ide_drive_t *drive)
......@@ -995,7 +996,7 @@ static void __exit exit_idescsi_module(void)
for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
drive = idescsi_drives[id];
if (drive)
DRIVER(drive)->busy--;
DRIVER(drive)->busy = 0;
}
scsi_unregister (idescsi_host);
device_unregister(&idescsi_primary);
......
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