mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
block: fix kobject double initialization in add_disk
Device-mapper can call add_disk() multiple times for the same gendisk
due to its two-phase creation process (dm create + dm load). This leads
to kobject double initialization errors when the underlying iSCSI devices
become temporarily unavailable and then reappear.
However, if the first add_disk() call fails and is retried, the queue_kobj
gets initialized twice, causing:
kobject: kobject (ffff88810c27bb90): tried to init an initialized object,
something is seriously wrong.
Call Trace:
<TASK>
dump_stack_lvl+0x5b/0x80
kobject_init.cold+0x43/0x51
blk_register_queue+0x46/0x280
add_disk_fwnode+0xb5/0x280
dm_setup_md_queue+0x194/0x1c0
table_load+0x297/0x2d0
ctl_ioctl+0x2a2/0x480
dm_ctl_ioctl+0xe/0x20
__x64_sys_ioctl+0xc7/0x110
do_syscall_64+0x72/0x390
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fix this by separating kobject initialization from sysfs registration:
- Initialize queue_kobj early during gendisk allocation
- add_disk() only adds the already-initialized kobject to sysfs
- del_gendisk() removes from sysfs but doesn't destroy the kobject
- Final cleanup happens when the disk is released
Fixes: 2bd85221a6
("block: untangle request_queue refcounting from sysfs")
Reported-by: Li Lingfeng <lilingfeng3@huawei.com>
Closes: https://lore.kernel.org/all/83591d0b-2467-433c-bce0-5581298eb161@huawei.com/
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Yu Kuai <yukuai3@huawei.com>
Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
Link: https://lore.kernel.org/r/20250808053609.3237836-1-zhengqixing@huaweicloud.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
196447c712
commit
343dc5423b
@ -847,7 +847,7 @@ static void blk_queue_release(struct kobject *kobj)
|
|||||||
/* nothing to do here, all data is associated with the parent gendisk */
|
/* nothing to do here, all data is associated with the parent gendisk */
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct kobj_type blk_queue_ktype = {
|
const struct kobj_type blk_queue_ktype = {
|
||||||
.default_groups = blk_queue_attr_groups,
|
.default_groups = blk_queue_attr_groups,
|
||||||
.sysfs_ops = &queue_sysfs_ops,
|
.sysfs_ops = &queue_sysfs_ops,
|
||||||
.release = blk_queue_release,
|
.release = blk_queue_release,
|
||||||
@ -875,15 +875,14 @@ int blk_register_queue(struct gendisk *disk)
|
|||||||
struct request_queue *q = disk->queue;
|
struct request_queue *q = disk->queue;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
kobject_init(&disk->queue_kobj, &blk_queue_ktype);
|
|
||||||
ret = kobject_add(&disk->queue_kobj, &disk_to_dev(disk)->kobj, "queue");
|
ret = kobject_add(&disk->queue_kobj, &disk_to_dev(disk)->kobj, "queue");
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_put_queue_kobj;
|
return ret;
|
||||||
|
|
||||||
if (queue_is_mq(q)) {
|
if (queue_is_mq(q)) {
|
||||||
ret = blk_mq_sysfs_register(disk);
|
ret = blk_mq_sysfs_register(disk);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_put_queue_kobj;
|
goto out_del_queue_kobj;
|
||||||
}
|
}
|
||||||
mutex_lock(&q->sysfs_lock);
|
mutex_lock(&q->sysfs_lock);
|
||||||
|
|
||||||
@ -934,8 +933,8 @@ out_debugfs_remove:
|
|||||||
mutex_unlock(&q->sysfs_lock);
|
mutex_unlock(&q->sysfs_lock);
|
||||||
if (queue_is_mq(q))
|
if (queue_is_mq(q))
|
||||||
blk_mq_sysfs_unregister(disk);
|
blk_mq_sysfs_unregister(disk);
|
||||||
out_put_queue_kobj:
|
out_del_queue_kobj:
|
||||||
kobject_put(&disk->queue_kobj);
|
kobject_del(&disk->queue_kobj);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,5 +985,4 @@ void blk_unregister_queue(struct gendisk *disk)
|
|||||||
elevator_set_none(q);
|
elevator_set_none(q);
|
||||||
|
|
||||||
blk_debugfs_remove(disk);
|
blk_debugfs_remove(disk);
|
||||||
kobject_put(&disk->queue_kobj);
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ struct elevator_tags;
|
|||||||
/* Max future timer expiry for timeouts */
|
/* Max future timer expiry for timeouts */
|
||||||
#define BLK_MAX_TIMEOUT (5 * HZ)
|
#define BLK_MAX_TIMEOUT (5 * HZ)
|
||||||
|
|
||||||
|
extern const struct kobj_type blk_queue_ktype;
|
||||||
extern struct dentry *blk_debugfs_root;
|
extern struct dentry *blk_debugfs_root;
|
||||||
|
|
||||||
struct blk_flush_queue {
|
struct blk_flush_queue {
|
||||||
|
@ -1303,6 +1303,7 @@ static void disk_release(struct device *dev)
|
|||||||
disk_free_zone_resources(disk);
|
disk_free_zone_resources(disk);
|
||||||
xa_destroy(&disk->part_tbl);
|
xa_destroy(&disk->part_tbl);
|
||||||
|
|
||||||
|
kobject_put(&disk->queue_kobj);
|
||||||
disk->queue->disk = NULL;
|
disk->queue->disk = NULL;
|
||||||
blk_put_queue(disk->queue);
|
blk_put_queue(disk->queue);
|
||||||
|
|
||||||
@ -1486,6 +1487,7 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,
|
|||||||
INIT_LIST_HEAD(&disk->slave_bdevs);
|
INIT_LIST_HEAD(&disk->slave_bdevs);
|
||||||
#endif
|
#endif
|
||||||
mutex_init(&disk->rqos_state_mutex);
|
mutex_init(&disk->rqos_state_mutex);
|
||||||
|
kobject_init(&disk->queue_kobj, &blk_queue_ktype);
|
||||||
return disk;
|
return disk;
|
||||||
|
|
||||||
out_erase_part0:
|
out_erase_part0:
|
||||||
|
Loading…
Reference in New Issue
Block a user