block: remove delayed holder registration
[linux-block.git] / block / holder.c
CommitLineData
c66fd019 1// SPDX-License-Identifier: GPL-2.0-only
322cbb50 2#include <linux/blkdev.h>
b81e0c23 3#include <linux/slab.h>
c66fd019
CH
4
5struct bd_holder_disk {
6 struct list_head list;
0dbcfe24 7 struct block_device *bdev;
c66fd019
CH
8 int refcnt;
9};
10
11static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
12 struct gendisk *disk)
13{
14 struct bd_holder_disk *holder;
15
0dbcfe24
CH
16 list_for_each_entry(holder, &disk->slave_bdevs, list)
17 if (holder->bdev == bdev)
c66fd019
CH
18 return holder;
19 return NULL;
20}
21
22static int add_symlink(struct kobject *from, struct kobject *to)
23{
24 return sysfs_create_link(from, to, kobject_name(to));
25}
26
27static void del_symlink(struct kobject *from, struct kobject *to)
28{
29 sysfs_remove_link(from, kobject_name(to));
30}
31
32/**
33 * bd_link_disk_holder - create symlinks between holding disk and slave bdev
34 * @bdev: the claimed slave bdev
35 * @disk: the holding disk
36 *
37 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
38 *
39 * This functions creates the following sysfs symlinks.
40 *
41 * - from "slaves" directory of the holder @disk to the claimed @bdev
42 * - from "holders" directory of the @bdev to the holder @disk
43 *
44 * For example, if /dev/dm-0 maps to /dev/sda and disk for dm-0 is
45 * passed to bd_link_disk_holder(), then:
46 *
47 * /sys/block/dm-0/slaves/sda --> /sys/block/sda
48 * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0
49 *
50 * The caller must have claimed @bdev before calling this function and
51 * ensure that both @bdev and @disk are valid during the creation and
52 * lifetime of these symlinks.
53 *
54 * CONTEXT:
55 * Might sleep.
56 *
57 * RETURNS:
58 * 0 on success, -errno on failure.
59 */
60int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk)
61{
62 struct bd_holder_disk *holder;
63 int ret = 0;
64
7abc0777
CH
65 if (WARN_ON_ONCE(!disk->slave_dir))
66 return -EINVAL;
67
0dbcfe24 68 mutex_lock(&disk->open_mutex);
c66fd019
CH
69
70 WARN_ON_ONCE(!bdev->bd_holder);
71
c66fd019
CH
72 holder = bd_find_holder_disk(bdev, disk);
73 if (holder) {
74 holder->refcnt++;
75 goto out_unlock;
76 }
77
78 holder = kzalloc(sizeof(*holder), GFP_KERNEL);
79 if (!holder) {
80 ret = -ENOMEM;
81 goto out_unlock;
82 }
83
84 INIT_LIST_HEAD(&holder->list);
0dbcfe24 85 holder->bdev = bdev;
c66fd019 86 holder->refcnt = 1;
7abc0777
CH
87 ret = add_symlink(disk->slave_dir, bdev_kobj(bdev));
88 if (ret)
89 goto out_free_holder;
90 ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
91 if (ret)
92 goto out_del_symlink;
0dbcfe24 93 list_add(&holder->list, &disk->slave_bdevs);
7abc0777 94
759e0fd4
CH
95 /*
96 * del_gendisk drops the initial reference to bd_holder_dir, so we need
97 * to keep our own here to allow for cleanup past that point.
98 */
99 kobject_get(bdev->bd_holder_dir);
7abc0777
CH
100 mutex_unlock(&disk->open_mutex);
101 return 0;
759e0fd4 102
7abc0777
CH
103out_del_symlink:
104 del_symlink(disk->slave_dir, bdev_kobj(bdev));
105out_free_holder:
106 kfree(holder);
c66fd019 107out_unlock:
0dbcfe24 108 mutex_unlock(&disk->open_mutex);
c66fd019
CH
109 return ret;
110}
111EXPORT_SYMBOL_GPL(bd_link_disk_holder);
112
113/**
114 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder()
115 * @bdev: the calimed slave bdev
116 * @disk: the holding disk
117 *
118 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
119 *
120 * CONTEXT:
121 * Might sleep.
122 */
123void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
124{
125 struct bd_holder_disk *holder;
126
7abc0777
CH
127 if (WARN_ON_ONCE(!disk->slave_dir))
128 return;
129
0dbcfe24 130 mutex_lock(&disk->open_mutex);
c66fd019
CH
131 holder = bd_find_holder_disk(bdev, disk);
132 if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) {
7abc0777
CH
133 del_symlink(disk->slave_dir, bdev_kobj(bdev));
134 del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
759e0fd4 135 kobject_put(bdev->bd_holder_dir);
c66fd019
CH
136 list_del_init(&holder->list);
137 kfree(holder);
138 }
0dbcfe24 139 mutex_unlock(&disk->open_mutex);
c66fd019
CH
140}
141EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);