scsi: bsg: Fix device unregistration
device_initialize() is used to take a refcount on the device. However,
put_device() is not called during device teardown. This leads to a
leak of private data of the driver core, dev_name(), etc. This is
reported by kmemleak at boot time if we compile kernel with
DEBUG_TEST_DRIVER_REMOVE.
Fix memory leaks during unregistration and implement a release
function.
Link: https://lore.kernel.org/r/20210911105306.1511-1-yuzenghui@huawei.com
Fixes: ead09dd3ae
("scsi: bsg: Simplify device registration")
Reviewed-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Zenghui Yu <yuzenghui@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
4521428c48
commit
1a0db7744e
23
block/bsg.c
23
block/bsg.c
|
@ -165,13 +165,20 @@ static const struct file_operations bsg_fops = {
|
||||||
.llseek = default_llseek,
|
.llseek = default_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void bsg_device_release(struct device *dev)
|
||||||
|
{
|
||||||
|
struct bsg_device *bd = container_of(dev, struct bsg_device, device);
|
||||||
|
|
||||||
|
ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt));
|
||||||
|
kfree(bd);
|
||||||
|
}
|
||||||
|
|
||||||
void bsg_unregister_queue(struct bsg_device *bd)
|
void bsg_unregister_queue(struct bsg_device *bd)
|
||||||
{
|
{
|
||||||
if (bd->queue->kobj.sd)
|
if (bd->queue->kobj.sd)
|
||||||
sysfs_remove_link(&bd->queue->kobj, "bsg");
|
sysfs_remove_link(&bd->queue->kobj, "bsg");
|
||||||
cdev_device_del(&bd->cdev, &bd->device);
|
cdev_device_del(&bd->cdev, &bd->device);
|
||||||
ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt));
|
put_device(&bd->device);
|
||||||
kfree(bd);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bsg_unregister_queue);
|
EXPORT_SYMBOL_GPL(bsg_unregister_queue);
|
||||||
|
|
||||||
|
@ -193,11 +200,13 @@ struct bsg_device *bsg_register_queue(struct request_queue *q,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -ENOSPC)
|
if (ret == -ENOSPC)
|
||||||
dev_err(parent, "bsg: too many bsg devices\n");
|
dev_err(parent, "bsg: too many bsg devices\n");
|
||||||
goto out_kfree;
|
kfree(bd);
|
||||||
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
bd->device.devt = MKDEV(bsg_major, ret);
|
bd->device.devt = MKDEV(bsg_major, ret);
|
||||||
bd->device.class = bsg_class;
|
bd->device.class = bsg_class;
|
||||||
bd->device.parent = parent;
|
bd->device.parent = parent;
|
||||||
|
bd->device.release = bsg_device_release;
|
||||||
dev_set_name(&bd->device, "%s", name);
|
dev_set_name(&bd->device, "%s", name);
|
||||||
device_initialize(&bd->device);
|
device_initialize(&bd->device);
|
||||||
|
|
||||||
|
@ -205,7 +214,7 @@ struct bsg_device *bsg_register_queue(struct request_queue *q,
|
||||||
bd->cdev.owner = THIS_MODULE;
|
bd->cdev.owner = THIS_MODULE;
|
||||||
ret = cdev_device_add(&bd->cdev, &bd->device);
|
ret = cdev_device_add(&bd->cdev, &bd->device);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_ida_remove;
|
goto out_put_device;
|
||||||
|
|
||||||
if (q->kobj.sd) {
|
if (q->kobj.sd) {
|
||||||
ret = sysfs_create_link(&q->kobj, &bd->device.kobj, "bsg");
|
ret = sysfs_create_link(&q->kobj, &bd->device.kobj, "bsg");
|
||||||
|
@ -217,10 +226,8 @@ struct bsg_device *bsg_register_queue(struct request_queue *q,
|
||||||
|
|
||||||
out_device_del:
|
out_device_del:
|
||||||
cdev_device_del(&bd->cdev, &bd->device);
|
cdev_device_del(&bd->cdev, &bd->device);
|
||||||
out_ida_remove:
|
out_put_device:
|
||||||
ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt));
|
put_device(&bd->device);
|
||||||
out_kfree:
|
|
||||||
kfree(bd);
|
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bsg_register_queue);
|
EXPORT_SYMBOL_GPL(bsg_register_queue);
|
||||||
|
|
Loading…
Reference in New Issue