Commit 1a0db774 authored by Zenghui Yu's avatar Zenghui Yu Committed by Martin K. Petersen

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: ead09dd3 ("scsi: bsg: Simplify device registration")
Reviewed-by: default avatarJohan Hovold <johan@kernel.org>
Signed-off-by: default avatarZenghui Yu <yuzenghui@huawei.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4521428c
...@@ -165,13 +165,20 @@ static const struct file_operations bsg_fops = { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
......
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