1 // SPDX-License-Identifier: GPL-2.0-only 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 struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev, 12 struct gendisk *disk) 13 { 14 struct bd_holder_disk *holder; 15 16 list_for_each_entry(holder, &disk->slave_bdevs, list) 17 if (holder->holder_dir == bdev->bd_holder_dir) 18 return holder; 19 return NULL; 20 } 21 22 static int add_symlink(struct kobject *from, struct kobject *to) 23 { 24 return sysfs_create_link(from, to, kobject_name(to)); 25 } 26 27 static 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 */ 60 int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) 61 { 62 struct bd_holder_disk *holder; 63 int ret = 0; 64 65 if (WARN_ON_ONCE(!disk->slave_dir)) 66 return -EINVAL; 67 68 if (bdev->bd_disk == disk) 69 return -EINVAL; 70 71 /* 72 * del_gendisk drops the initial reference to bd_holder_dir, so we 73 * need to keep our own here to allow for cleanup past that point. 74 */ 75 mutex_lock(&bdev->bd_disk->open_mutex); 76 if (!disk_live(bdev->bd_disk)) { 77 mutex_unlock(&bdev->bd_disk->open_mutex); 78 return -ENODEV; 79 } 80 kobject_get(bdev->bd_holder_dir); 81 mutex_unlock(&bdev->bd_disk->open_mutex); 82 83 mutex_lock(&disk->open_mutex); 84 WARN_ON_ONCE(!bdev->bd_holder); 85 86 holder = bd_find_holder_disk(bdev, disk); 87 if (holder) { 88 kobject_put(bdev->bd_holder_dir); 89 holder->refcnt++; 90 goto out_unlock; 91 } 92 93 holder = kzalloc(sizeof(*holder), GFP_KERNEL); 94 if (!holder) { 95 ret = -ENOMEM; 96 goto out_unlock; 97 } 98 99 INIT_LIST_HEAD(&holder->list); 100 holder->refcnt = 1; 101 holder->holder_dir = bdev->bd_holder_dir; 102 103 ret = add_symlink(disk->slave_dir, bdev_kobj(bdev)); 104 if (ret) 105 goto out_free_holder; 106 ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj); 107 if (ret) 108 goto out_del_symlink; 109 list_add(&holder->list, &disk->slave_bdevs); 110 111 mutex_unlock(&disk->open_mutex); 112 return 0; 113 114 out_del_symlink: 115 del_symlink(disk->slave_dir, bdev_kobj(bdev)); 116 out_free_holder: 117 kfree(holder); 118 out_unlock: 119 mutex_unlock(&disk->open_mutex); 120 if (ret) 121 kobject_put(bdev->bd_holder_dir); 122 return ret; 123 } 124 EXPORT_SYMBOL_GPL(bd_link_disk_holder); 125 126 /** 127 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder() 128 * @bdev: the calimed slave bdev 129 * @disk: the holding disk 130 * 131 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. 132 * 133 * CONTEXT: 134 * Might sleep. 135 */ 136 void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk) 137 { 138 struct bd_holder_disk *holder; 139 140 if (WARN_ON_ONCE(!disk->slave_dir)) 141 return; 142 143 mutex_lock(&disk->open_mutex); 144 holder = bd_find_holder_disk(bdev, disk); 145 if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) { 146 del_symlink(disk->slave_dir, bdev_kobj(bdev)); 147 del_symlink(holder->holder_dir, &disk_to_dev(disk)->kobj); 148 kobject_put(holder->holder_dir); 149 list_del_init(&holder->list); 150 kfree(holder); 151 } 152 mutex_unlock(&disk->open_mutex); 153 } 154 EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); 155