Commit 2b5bebcc authored by Joe Lawrence's avatar Joe Lawrence Committed by James Bottomley

[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/113Signed-off-by: default avatarJoe Lawrence <joe.lawrence@stratus.com>
Tested-by: default avatarEwan D. Milne <emilne@redhat.com>
Acked-by: default avatarKai Mäkisara <Kai.Makisara@kolumbus.fi>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 95c9f4d4
...@@ -4112,6 +4112,10 @@ static int st_probe(struct device *dev) ...@@ -4112,6 +4112,10 @@ static int st_probe(struct device *dev)
tpnt->disk = disk; tpnt->disk = disk;
disk->private_data = &tpnt->driver; disk->private_data = &tpnt->driver;
disk->queue = SDp->request_queue; disk->queue = SDp->request_queue;
/* SCSI tape doesn't register this gendisk via add_disk(). Manually
* take queue reference that release_disk() expects. */
if (!blk_get_queue(disk->queue))
goto out_put_disk;
tpnt->driver = &st_template; tpnt->driver = &st_template;
tpnt->device = SDp; tpnt->device = SDp;
...@@ -4185,7 +4189,7 @@ static int st_probe(struct device *dev) ...@@ -4185,7 +4189,7 @@ static int st_probe(struct device *dev)
idr_preload_end(); idr_preload_end();
if (error < 0) { if (error < 0) {
pr_warn("st: idr allocation failed: %d\n", error); pr_warn("st: idr allocation failed: %d\n", error);
goto out_put_disk; goto out_put_queue;
} }
tpnt->index = error; tpnt->index = error;
sprintf(disk->disk_name, "st%d", tpnt->index); sprintf(disk->disk_name, "st%d", tpnt->index);
...@@ -4211,6 +4215,8 @@ static int st_probe(struct device *dev) ...@@ -4211,6 +4215,8 @@ static int st_probe(struct device *dev)
spin_lock(&st_index_lock); spin_lock(&st_index_lock);
idr_remove(&st_index_idr, tpnt->index); idr_remove(&st_index_idr, tpnt->index);
spin_unlock(&st_index_lock); spin_unlock(&st_index_lock);
out_put_queue:
blk_put_queue(disk->queue);
out_put_disk: out_put_disk:
put_disk(disk); put_disk(disk);
kfree(tpnt); kfree(tpnt);
......
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