block: make the block holder code optional
[linux-block.git] / block / holder.c
CommitLineData
c66fd019
CH
1// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/genhd.h>
3
4struct bd_holder_disk {
5 struct list_head list;
6 struct gendisk *disk;
7 int refcnt;
8};
9
10static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev,
11 struct gendisk *disk)
12{
13 struct bd_holder_disk *holder;
14
15 list_for_each_entry(holder, &bdev->bd_holder_disks, list)
16 if (holder->disk == disk)
17 return holder;
18 return NULL;
19}
20
21static int add_symlink(struct kobject *from, struct kobject *to)
22{
23 return sysfs_create_link(from, to, kobject_name(to));
24}
25
26static void del_symlink(struct kobject *from, struct kobject *to)
27{
28 sysfs_remove_link(from, kobject_name(to));
29}
30
31/**
32 * bd_link_disk_holder - create symlinks between holding disk and slave bdev
33 * @bdev: the claimed slave bdev
34 * @disk: the holding disk
35 *
36 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
37 *
38 * This functions creates the following sysfs symlinks.
39 *
40 * - from "slaves" directory of the holder @disk to the claimed @bdev
41 * - from "holders" directory of the @bdev to the holder @disk
42 *
43 * For example, if /dev/dm-0 maps to /dev/sda and disk for dm-0 is
44 * passed to bd_link_disk_holder(), then:
45 *
46 * /sys/block/dm-0/slaves/sda --> /sys/block/sda
47 * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0
48 *
49 * The caller must have claimed @bdev before calling this function and
50 * ensure that both @bdev and @disk are valid during the creation and
51 * lifetime of these symlinks.
52 *
53 * CONTEXT:
54 * Might sleep.
55 *
56 * RETURNS:
57 * 0 on success, -errno on failure.
58 */
59int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk)
60{
61 struct bd_holder_disk *holder;
62 int ret = 0;
63
64 mutex_lock(&bdev->bd_disk->open_mutex);
65
66 WARN_ON_ONCE(!bdev->bd_holder);
67
68 /* FIXME: remove the following once add_disk() handles errors */
69 if (WARN_ON(!disk->slave_dir || !bdev->bd_holder_dir))
70 goto out_unlock;
71
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);
85 holder->disk = disk;
86 holder->refcnt = 1;
87
88 ret = add_symlink(disk->slave_dir, bdev_kobj(bdev));
89 if (ret)
90 goto out_free;
91
92 ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
93 if (ret)
94 goto out_del;
95 /*
96 * bdev could be deleted beneath us which would implicitly destroy
97 * the holder directory. Hold on to it.
98 */
99 kobject_get(bdev->bd_holder_dir);
100
101 list_add(&holder->list, &bdev->bd_holder_disks);
102 goto out_unlock;
103
104out_del:
105 del_symlink(disk->slave_dir, bdev_kobj(bdev));
106out_free:
107 kfree(holder);
108out_unlock:
109 mutex_unlock(&bdev->bd_disk->open_mutex);
110 return ret;
111}
112EXPORT_SYMBOL_GPL(bd_link_disk_holder);
113
114/**
115 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder()
116 * @bdev: the calimed slave bdev
117 * @disk: the holding disk
118 *
119 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
120 *
121 * CONTEXT:
122 * Might sleep.
123 */
124void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
125{
126 struct bd_holder_disk *holder;
127
128 mutex_lock(&bdev->bd_disk->open_mutex);
129 holder = bd_find_holder_disk(bdev, disk);
130 if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) {
131 del_symlink(disk->slave_dir, bdev_kobj(bdev));
132 del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
133 kobject_put(bdev->bd_holder_dir);
134 list_del_init(&holder->list);
135 kfree(holder);
136 }
137 mutex_unlock(&bdev->bd_disk->open_mutex);
138}
139EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);