diff options
author | Joe Lawrence <Joe.Lawrence@stratus.com> | 2013-03-05 10:57:56 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-04-06 06:14:20 -0400 |
commit | 2b5bebccd282f2527d17ac90cf3ad0d486dd89d8 (patch) | |
tree | 6fdef4e36e3e4d4cfef58d37c06bcfd86ff63557 /drivers/scsi | |
parent | 95c9f4d4da6c4e445a9dd58c8382356520ea91a0 (diff) |
[SCSI] st: Take additional queue ref in st_probe
This patch fixes a reference count bug in the SCSI tape driver which can be
reproduced with the following:
* Boot with slub_debug=FZPU, tape drive attached
* echo 1 > /sys/devices/... tape device pci path .../remove
* Wait for device removal
* echo 1 > /sys/kernel/slab/blkdev_queue/validate
* Slub debug complains about corrupted poison pattern
In commit 523e1d39 (block: make gendisk hold a reference to its queue)
add_disk() and disk_release() were modified to get/put an additional
reference on a disk queue to fix a reference counting discrepency
between bdev release and SCSI device removal. The ST driver never
calls add_disk(), so this commit introduced an extra kref put when the
ST driver frees its struct gendisk.
Attempts were made to fix this bug at the block level [1] but later
abandoned due to floppy driver issues [2].
[1] https://lkml.org/lkml/2012/8/27/354
[2] https://lkml.org/lkml/2012/9/22/113
Signed-off-by: Joe Lawrence <joe.lawrence@stratus.com>
Tested-by: Ewan D. Milne <emilne@redhat.com>
Acked-by: Kai Mäkisara <Kai.Makisara@kolumbus.fi>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/st.c | 8 |
1 files changed, 7 insertions, 1 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 86974471af68..2a32036a9404 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c | |||
@@ -4112,6 +4112,10 @@ static int st_probe(struct device *dev) | |||
4112 | tpnt->disk = disk; | 4112 | tpnt->disk = disk; |
4113 | disk->private_data = &tpnt->driver; | 4113 | disk->private_data = &tpnt->driver; |
4114 | disk->queue = SDp->request_queue; | 4114 | disk->queue = SDp->request_queue; |
4115 | /* SCSI tape doesn't register this gendisk via add_disk(). Manually | ||
4116 | * take queue reference that release_disk() expects. */ | ||
4117 | if (!blk_get_queue(disk->queue)) | ||
4118 | goto out_put_disk; | ||
4115 | tpnt->driver = &st_template; | 4119 | tpnt->driver = &st_template; |
4116 | 4120 | ||
4117 | tpnt->device = SDp; | 4121 | tpnt->device = SDp; |
@@ -4185,7 +4189,7 @@ static int st_probe(struct device *dev) | |||
4185 | idr_preload_end(); | 4189 | idr_preload_end(); |
4186 | if (error < 0) { | 4190 | if (error < 0) { |
4187 | pr_warn("st: idr allocation failed: %d\n", error); | 4191 | pr_warn("st: idr allocation failed: %d\n", error); |
4188 | goto out_put_disk; | 4192 | goto out_put_queue; |
4189 | } | 4193 | } |
4190 | tpnt->index = error; | 4194 | tpnt->index = error; |
4191 | sprintf(disk->disk_name, "st%d", tpnt->index); | 4195 | sprintf(disk->disk_name, "st%d", tpnt->index); |
@@ -4211,6 +4215,8 @@ out_remove_devs: | |||
4211 | spin_lock(&st_index_lock); | 4215 | spin_lock(&st_index_lock); |
4212 | idr_remove(&st_index_idr, tpnt->index); | 4216 | idr_remove(&st_index_idr, tpnt->index); |
4213 | spin_unlock(&st_index_lock); | 4217 | spin_unlock(&st_index_lock); |
4218 | out_put_queue: | ||
4219 | blk_put_queue(disk->queue); | ||
4214 | out_put_disk: | 4220 | out_put_disk: |
4215 | put_disk(disk); | 4221 | put_disk(disk); |
4216 | kfree(tpnt); | 4222 | kfree(tpnt); |