gendisk instance has to be released after request queue is cleaned up because bdi is referred from gendisk since commit edb0872f44ec ("block: move the bdi from the request_queue to the gendisk"). For sd and sr, gendisk can be removed in the release handler(sd_remove/ sr_remove) of sdev->sdev_gendev, which is triggered in device_del(sdev->sdev_gendev) in __scsi_remove_device(), when the request queue isn't cleaned up yet. So kernel oops could be triggered when referring bdi via gendisk. Fix the issue by moving blk_cleanup_queue() into sd_remove() and sr_remove(). Fixes: edb0872f44ec ("block: move the bdi from the request_queue to the gendisk") Cc: Christoph Hellwig <hch@xxxxxx> Cc: Sven Schnelle <svens@xxxxxxxxxxxxx> Signed-off-by: Ming Lei <ming.lei@xxxxxxxxxx> --- drivers/scsi/scsi_priv.h | 6 ++++++ drivers/scsi/scsi_sysfs.c | 3 ++- drivers/scsi/sd.c | 2 ++ drivers/scsi/sr.c | 4 +++- include/scsi/scsi_device.h | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 6d9152031a40..6aef88d772a3 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -145,6 +145,12 @@ extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; extern const struct attribute_group *scsi_sysfs_shost_attr_groups[]; +static inline void scsi_sysfs_cleanup_sdev(struct scsi_device *sdev) +{ + sdev->request_queue_cleaned = true; + blk_cleanup_queue(sdev->request_queue); +} + /* scsi_netlink.c */ #ifdef CONFIG_SCSI_NETLINK extern void scsi_netlink_init(void); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 86793259e541..9d54fc9c89ea 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1463,7 +1463,8 @@ void __scsi_remove_device(struct scsi_device *sdev) scsi_device_set_state(sdev, SDEV_DEL); mutex_unlock(&sdev->state_mutex); - blk_cleanup_queue(sdev->request_queue); + if (!sdev->request_queue_cleaned) + blk_cleanup_queue(sdev->request_queue); cancel_work_sync(&sdev->requeue_work); if (sdev->host->hostt->slave_destroy) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index cbd9999f93a6..8132f9833488 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3498,6 +3498,8 @@ static int sd_remove(struct device *dev) del_gendisk(sdkp->disk); sd_shutdown(dev); + scsi_sysfs_cleanup_sdev(to_scsi_device(dev)); + free_opal_dev(sdkp->opal_dev); mutex_lock(&sd_ref_mutex); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 8b17b35283aa..ef85940f3168 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -64,7 +64,7 @@ #include "scsi_logging.h" #include "sr.h" - +#include "scsi_priv.h" MODULE_DESCRIPTION("SCSI cdrom (sr) driver"); MODULE_LICENSE("GPL"); @@ -1045,6 +1045,8 @@ static int sr_remove(struct device *dev) del_gendisk(cd->disk); dev_set_drvdata(dev, NULL); + scsi_sysfs_cleanup_sdev(to_scsi_device(dev)); + mutex_lock(&sr_ref_mutex); kref_put(&cd->kref, sr_kref_release); mutex_unlock(&sr_ref_mutex); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 09a17f6e93a7..d102bc2c423b 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -207,6 +207,7 @@ struct scsi_device { unsigned rpm_autosuspend:1; /* Enable runtime autosuspend at device * creation time */ unsigned ignore_media_change:1; /* Ignore MEDIA CHANGE on resume */ + unsigned request_queue_cleaned:1; /* Request queue has been cleaned up */ bool offline_already; /* Device offline message logged */ -- 2.31.1