[SCSI] scsi_sysfs: restore prep_fn when ULD is removed
authorJames Bottomley <James.Bottomley@HansenPartnership.com>
Wed, 2 Jan 2008 17:14:30 +0000 (11:14 -0600)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Wed, 2 Jan 2008 19:08:00 +0000 (13:08 -0600)
A recent bug report:

http://bugzilla.kernel.org/show_bug.cgi?id=9674

Was caused because the ULDs now set their own prep functions, but
don't necessarily reset the prep function back to the SCSI default
when they are removed.  This leads to panics if commands are sent to
the device after the module is removed because the prep_fn is still
pointing to the old module code.  The fix for this is to implement a
bus remove method that resets the prep_fn pointer correctly before
calling the ULD specific driver remove method.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_sysfs.c

index 0e81e4cf8876766a92cb43333d6883baadefdf03..a9ac5b1b1667f339d5db22760e308b18ac9a3a71 100644 (file)
@@ -1332,7 +1332,7 @@ int scsi_prep_return(struct request_queue *q, struct request *req, int ret)
 }
 EXPORT_SYMBOL(scsi_prep_return);
 
-static int scsi_prep_fn(struct request_queue *q, struct request *req)
+int scsi_prep_fn(struct request_queue *q, struct request *req)
 {
        struct scsi_device *sdev = q->queuedata;
        int ret = BLKPREP_KILL;
index eff0059518954b26c62b116cd737f9e11e78da6a..3f34e9376b0aa148c8dafc58ee0c52c65849b1e1 100644 (file)
@@ -74,6 +74,9 @@ extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
 extern void scsi_free_queue(struct request_queue *q);
 extern int scsi_init_queue(void);
 extern void scsi_exit_queue(void);
+struct request_queue;
+struct request;
+extern int scsi_prep_fn(struct request_queue *, struct request *);
 
 /* scsi_proc.c */
 #ifdef CONFIG_SCSI_PROC_FS
index f374fdcb6815981d3055f171c01e26fee89e0820..00b3866773923c001c44958237020629b6c95027 100644 (file)
@@ -373,12 +373,29 @@ static int scsi_bus_resume(struct device * dev)
        return err;
 }
 
+static int scsi_bus_remove(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+       struct scsi_device *sdev = to_scsi_device(dev);
+       int err = 0;
+
+       /* reset the prep_fn back to the default since the
+        * driver may have altered it and it's being removed */
+       blk_queue_prep_rq(sdev->request_queue, scsi_prep_fn);
+
+       if (drv && drv->remove)
+               err = drv->remove(dev);
+
+       return 0;
+}
+
 struct bus_type scsi_bus_type = {
         .name          = "scsi",
         .match         = scsi_bus_match,
        .uevent         = scsi_bus_uevent,
        .suspend        = scsi_bus_suspend,
        .resume         = scsi_bus_resume,
+       .remove         = scsi_bus_remove,
 };
 
 int scsi_sysfs_register(void)