Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Joe Lawrence and committed by
James Bottomley
2b5bebcc 95c9f4d4

+7 -1
+7 -1
drivers/scsi/st.c
··· 4112 4112 tpnt->disk = disk; 4113 4113 disk->private_data = &tpnt->driver; 4114 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 4119 tpnt->driver = &st_template; 4116 4120 4117 4121 tpnt->device = SDp; ··· 4189 4185 idr_preload_end(); 4190 4186 if (error < 0) { 4191 4187 pr_warn("st: idr allocation failed: %d\n", error); 4192 - goto out_put_disk; 4188 + goto out_put_queue; 4193 4189 } 4194 4190 tpnt->index = error; 4195 4191 sprintf(disk->disk_name, "st%d", tpnt->index); ··· 4215 4211 spin_lock(&st_index_lock); 4216 4212 idr_remove(&st_index_idr, tpnt->index); 4217 4213 spin_unlock(&st_index_lock); 4214 + out_put_queue: 4215 + blk_put_queue(disk->queue); 4218 4216 out_put_disk: 4219 4217 put_disk(disk); 4220 4218 kfree(tpnt);