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