Skip to content

Commit

Permalink
block: support delayed holder registration
Browse files Browse the repository at this point in the history
device mapper needs to register holders before it is ready to do I/O.
Currently it does so by registering the disk early, which can leave
the disk and queue in a weird half state where the queue is registered
with the disk, except for sysfs and the elevator.  And this state has
been a bit promlematic before, and will get more so when sorting out
the responsibilities between the queue and the disk.

Support registering holders on an initialized but not registered disk
instead by delaying the sysfs registration until the disk is registered.

Signed-off-by: Christoph Hellwig <[email protected]>
Reviewed-by: Mike Snitzer <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
Christoph Hellwig authored and axboe committed Aug 9, 2021
1 parent 0dbcfe2 commit d626338
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 17 deletions.
10 changes: 10 additions & 0 deletions block/genhd.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,16 @@ static void register_disk(struct device *parent, struct gendisk *disk,
kobject_create_and_add("holders", &ddev->kobj);
disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj);

/*
* XXX: this is a mess, can't wait for real error handling in add_disk.
* Make sure ->slave_dir is NULL if we failed some of the registration
* so that the cleanup in bd_unlink_disk_holder works properly.
*/
if (bd_register_pending_holders(disk) < 0) {
kobject_put(disk->slave_dir);
disk->slave_dir = NULL;
}

if (disk->flags & GENHD_FL_HIDDEN)
return;

Expand Down
68 changes: 51 additions & 17 deletions block/holder.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ static void del_symlink(struct kobject *from, struct kobject *to)
sysfs_remove_link(from, kobject_name(to));
}

static int __link_disk_holder(struct block_device *bdev, struct gendisk *disk)
{
int ret;

ret = add_symlink(disk->slave_dir, bdev_kobj(bdev));
if (ret)
return ret;
ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
if (ret)
del_symlink(disk->slave_dir, bdev_kobj(bdev));
return ret;
}

/**
* bd_link_disk_holder - create symlinks between holding disk and slave bdev
* @bdev: the claimed slave bdev
Expand Down Expand Up @@ -66,7 +79,7 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk)
WARN_ON_ONCE(!bdev->bd_holder);

/* FIXME: remove the following once add_disk() handles errors */
if (WARN_ON(!disk->slave_dir || !bdev->bd_holder_dir))
if (WARN_ON(!bdev->bd_holder_dir))
goto out_unlock;

holder = bd_find_holder_disk(bdev, disk);
Expand All @@ -84,28 +97,28 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk)
INIT_LIST_HEAD(&holder->list);
holder->bdev = bdev;
holder->refcnt = 1;

ret = add_symlink(disk->slave_dir, bdev_kobj(bdev));
if (ret)
goto out_free;

ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
if (ret)
goto out_del;
if (disk->slave_dir) {
ret = __link_disk_holder(bdev, disk);
if (ret) {
kfree(holder);
goto out_unlock;
}
}

list_add(&holder->list, &disk->slave_bdevs);
goto out_unlock;

out_del:
del_symlink(disk->slave_dir, bdev_kobj(bdev));
out_free:
kfree(holder);
out_unlock:
mutex_unlock(&disk->open_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(bd_link_disk_holder);

static void __unlink_disk_holder(struct block_device *bdev,
struct gendisk *disk)
{
del_symlink(disk->slave_dir, bdev_kobj(bdev));
del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
}

/**
* bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder()
* @bdev: the calimed slave bdev
Expand All @@ -123,11 +136,32 @@ void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
mutex_lock(&disk->open_mutex);
holder = bd_find_holder_disk(bdev, disk);
if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) {
del_symlink(disk->slave_dir, bdev_kobj(bdev));
del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
if (disk->slave_dir)
__unlink_disk_holder(bdev, disk);
list_del_init(&holder->list);
kfree(holder);
}
mutex_unlock(&disk->open_mutex);
}
EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);

int bd_register_pending_holders(struct gendisk *disk)
{
struct bd_holder_disk *holder;
int ret;

mutex_lock(&disk->open_mutex);
list_for_each_entry(holder, &disk->slave_bdevs, list) {
ret = __link_disk_holder(holder->bdev, disk);
if (ret)
goto out_undo;
}
mutex_unlock(&disk->open_mutex);
return 0;

out_undo:
list_for_each_entry_continue_reverse(holder, &disk->slave_bdevs, list)
__unlink_disk_holder(holder->bdev, disk);
mutex_unlock(&disk->open_mutex);
return ret;
}
5 changes: 5 additions & 0 deletions include/linux/genhd.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
#ifdef CONFIG_BLOCK_HOLDER_DEPRECATED
int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk);
void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk);
int bd_register_pending_holders(struct gendisk *disk);
#else
static inline int bd_link_disk_holder(struct block_device *bdev,
struct gendisk *disk)
Expand All @@ -333,6 +334,10 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
struct gendisk *disk)
{
}
static inline int bd_register_pending_holders(struct gendisk *disk)
{
return 0;
}
#endif /* CONFIG_BLOCK_HOLDER_DEPRECATED */

dev_t part_devt(struct gendisk *disk, u8 partno);
Expand Down

0 comments on commit d626338

Please sign in to comment.