2.5.54 fix ide-cd/ide-scsi oopses after module unload
PATCH 1/1 2.5.54 fix ide-cd/ide-scsi oopses after module unload
Mikael Pettersson posted the following to the linux-kernel mailing list. His patch includes a fix that Willem Riede spotted as well (no driver_unregister() in ide.c). Willem also had other changes in ide-scsi.c which are still pending. See attached file.
Doug Gilbert
Mikael Pettersson wrote on lkml:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
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.)
With these three fixes modular ide-cd and ide-scsi work quite reliably for me in 2.5.54.
/Mikael
drivers/ide/ide.c +3 -0
@@ -1346,6 +1346,7 @@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 @@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)) {
drivers/scsi/ide-scsi.c +2 -1
@@ -590,6 +590,7 @@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 @@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);
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
drivers/scsi/ide-scsi.c2553wr2 renamed from drivers/scsi/ide-scsi.c +3 -1
@@ -289,6 +289,7 @@ pc->timeout = jiffies + WAIT_READY; /* NOTE! Save the failed packet command in "rq->buffer" */ rq->buffer = (void *) failed_command->special; + pc->scsi_cmd = ((idescsi_pc_t *) failed_command->special)->scsi_cmd; if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { printk ("ide-scsi: %s: queue cmd = ", drive->name); hexdump(pc->c, 6);
@@ -876,7 +877,8 @@ /* is cmd active? * need to lock so this stuff doesn't change under us */ spin_lock_irqsave(&ide_lock, flags); - if (scsi->pc && scsi->pc->scsi_cmd->serial_number == cmd->serial_number) { + if (scsi->pc && scsi->pc->scsi_cmd && + scsi->pc->scsi_cmd->serial_number == cmd->serial_number) { /* yep - let's give it some more time - * we can do that, we're in _our_ error kernel thread */ spin_unlock_irqrestore(&ide_lock, flags);