Commit 4352438e authored by James Bottomley's avatar James Bottomley Committed by Linus Torvalds

[PATCH] Fix removable USB drive oops

The actual problem reported was because there wasn't a corresponding
check on transport_classdev.class in the unregister.

However, on closer inspection I also turned up a nasty thinko in the
reference counting.  For reasons best known to the class code authors,
class devices have to obtain their own references to the devices they're
attached to which they release again in their .release routines, so you
have to remember to do a get_device() in the correct place after the
class_device_add().  I put comments in the code so that, hopefully, we
can avoid the problem in future.
parent b4324bc3
...@@ -367,15 +367,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) ...@@ -367,15 +367,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
printk(KERN_INFO "error 2\n"); printk(KERN_INFO "error 2\n");
goto clean_device; goto clean_device;
} }
/* take a reference for the sdev_classdev; this is
* released by the sdev_class .release */
get_device(&sdev->sdev_gendev);
if (sdev->transport_classdev.class) { if (sdev->transport_classdev.class) {
error = class_device_add(&sdev->transport_classdev); error = class_device_add(&sdev->transport_classdev);
if (error) if (error)
goto clean_device2; goto clean_device2;
/* take a reference for the transport_classdev; this
* is released by the transport_class .release */
get_device(&sdev->sdev_gendev);
} }
get_device(&sdev->sdev_gendev);
if (sdev->host->hostt->sdev_attrs) { if (sdev->host->hostt->sdev_attrs) {
for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
error = attr_add(&sdev->sdev_gendev, error = attr_add(&sdev->sdev_gendev,
...@@ -434,7 +439,8 @@ void scsi_remove_device(struct scsi_device *sdev) ...@@ -434,7 +439,8 @@ void scsi_remove_device(struct scsi_device *sdev)
if (sdev->sdev_state == SDEV_RUNNING || sdev->sdev_state == SDEV_CANCEL) { if (sdev->sdev_state == SDEV_RUNNING || sdev->sdev_state == SDEV_CANCEL) {
scsi_device_set_state(sdev, SDEV_DEL); scsi_device_set_state(sdev, SDEV_DEL);
class_device_unregister(&sdev->sdev_classdev); class_device_unregister(&sdev->sdev_classdev);
class_device_unregister(&sdev->transport_classdev); if(sdev->transport_classdev.class)
class_device_unregister(&sdev->transport_classdev);
device_del(&sdev->sdev_gendev); device_del(&sdev->sdev_gendev);
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
......
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