blob: 11e65d99a9fb1654039cd67e9ffe9acf81e069a0 [file] [log] [blame]
Christoph Hellwigc66fd012021-08-04 11:41:40 +02001// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/genhd.h>
3
4struct bd_holder_disk {
5 struct list_head list;
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +02006 struct block_device *bdev;
Christoph Hellwigc66fd012021-08-04 11:41:40 +02007 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
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +020015 list_for_each_entry(holder, &disk->slave_bdevs, list)
16 if (holder->bdev == bdev)
Christoph Hellwigc66fd012021-08-04 11:41:40 +020017 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
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +020064 mutex_lock(&disk->open_mutex);
Christoph Hellwigc66fd012021-08-04 11:41:40 +020065
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);
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +020085 holder->bdev = bdev;
Christoph Hellwigc66fd012021-08-04 11:41:40 +020086 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;
Christoph Hellwigc66fd012021-08-04 11:41:40 +020095
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +020096 list_add(&holder->list, &disk->slave_bdevs);
Christoph Hellwigc66fd012021-08-04 11:41:40 +020097 goto out_unlock;
98
99out_del:
100 del_symlink(disk->slave_dir, bdev_kobj(bdev));
101out_free:
102 kfree(holder);
103out_unlock:
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +0200104 mutex_unlock(&disk->open_mutex);
Christoph Hellwigc66fd012021-08-04 11:41:40 +0200105 return ret;
106}
107EXPORT_SYMBOL_GPL(bd_link_disk_holder);
108
109/**
110 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder()
111 * @bdev: the calimed slave bdev
112 * @disk: the holding disk
113 *
114 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT.
115 *
116 * CONTEXT:
117 * Might sleep.
118 */
119void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk)
120{
121 struct bd_holder_disk *holder;
122
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +0200123 mutex_lock(&disk->open_mutex);
Christoph Hellwigc66fd012021-08-04 11:41:40 +0200124 holder = bd_find_holder_disk(bdev, disk);
125 if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) {
126 del_symlink(disk->slave_dir, bdev_kobj(bdev));
127 del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj);
Christoph Hellwigc66fd012021-08-04 11:41:40 +0200128 list_del_init(&holder->list);
129 kfree(holder);
130 }
Christoph Hellwig0dbcfe22021-08-04 11:41:42 +0200131 mutex_unlock(&disk->open_mutex);
Christoph Hellwigc66fd012021-08-04 11:41:40 +0200132}
133EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);