1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/genhd.h> 3 #include <linux/slab.h> 4 5 struct bd_holder_disk { 6 struct list_head list; 7 struct block_device *bdev; 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->bdev == bdev) 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 static int __link_disk_holder(struct block_device *bdev, struct gendisk *disk) 33 { 34 int ret; 35 36 ret = add_symlink(disk->slave_dir, bdev_kobj(bdev)); 37 if (ret) 38 return ret; 39 ret = add_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj); 40 if (ret) 41 del_symlink(disk->slave_dir, bdev_kobj(bdev)); 42 return ret; 43 } 44 45 /** 46 * bd_link_disk_holder - create symlinks between holding disk and slave bdev 47 * @bdev: the claimed slave bdev 48 * @disk: the holding disk 49 * 50 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. 51 * 52 * This functions creates the following sysfs symlinks. 53 * 54 * - from "slaves" directory of the holder @disk to the claimed @bdev 55 * - from "holders" directory of the @bdev to the holder @disk 56 * 57 * For example, if /dev/dm-0 maps to /dev/sda and disk for dm-0 is 58 * passed to bd_link_disk_holder(), then: 59 * 60 * /sys/block/dm-0/slaves/sda --> /sys/block/sda 61 * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 62 * 63 * The caller must have claimed @bdev before calling this function and 64 * ensure that both @bdev and @disk are valid during the creation and 65 * lifetime of these symlinks. 66 * 67 * CONTEXT: 68 * Might sleep. 69 * 70 * RETURNS: 71 * 0 on success, -errno on failure. 72 */ 73 int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) 74 { 75 struct bd_holder_disk *holder; 76 int ret = 0; 77 78 mutex_lock(&disk->open_mutex); 79 80 WARN_ON_ONCE(!bdev->bd_holder); 81 82 /* FIXME: remove the following once add_disk() handles errors */ 83 if (WARN_ON(!bdev->bd_holder_dir)) 84 goto out_unlock; 85 86 holder = bd_find_holder_disk(bdev, disk); 87 if (holder) { 88 holder->refcnt++; 89 goto out_unlock; 90 } 91 92 holder = kzalloc(sizeof(*holder), GFP_KERNEL); 93 if (!holder) { 94 ret = -ENOMEM; 95 goto out_unlock; 96 } 97 98 INIT_LIST_HEAD(&holder->list); 99 holder->bdev = bdev; 100 holder->refcnt = 1; 101 if (disk->slave_dir) { 102 ret = __link_disk_holder(bdev, disk); 103 if (ret) { 104 kfree(holder); 105 goto out_unlock; 106 } 107 } 108 109 list_add(&holder->list, &disk->slave_bdevs); 110 /* 111 * del_gendisk drops the initial reference to bd_holder_dir, so we need 112 * to keep our own here to allow for cleanup past that point. 113 */ 114 kobject_get(bdev->bd_holder_dir); 115 116 out_unlock: 117 mutex_unlock(&disk->open_mutex); 118 return ret; 119 } 120 EXPORT_SYMBOL_GPL(bd_link_disk_holder); 121 122 static void __unlink_disk_holder(struct block_device *bdev, 123 struct gendisk *disk) 124 { 125 del_symlink(disk->slave_dir, bdev_kobj(bdev)); 126 del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj); 127 } 128 129 /** 130 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder() 131 * @bdev: the calimed slave bdev 132 * @disk: the holding disk 133 * 134 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. 135 * 136 * CONTEXT: 137 * Might sleep. 138 */ 139 void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk) 140 { 141 struct bd_holder_disk *holder; 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 if (disk->slave_dir) 147 __unlink_disk_holder(bdev, disk); 148 kobject_put(bdev->bd_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 156 int bd_register_pending_holders(struct gendisk *disk) 157 { 158 struct bd_holder_disk *holder; 159 int ret; 160 161 mutex_lock(&disk->open_mutex); 162 list_for_each_entry(holder, &disk->slave_bdevs, list) { 163 ret = __link_disk_holder(holder->bdev, disk); 164 if (ret) 165 goto out_undo; 166 } 167 mutex_unlock(&disk->open_mutex); 168 return 0; 169 170 out_undo: 171 list_for_each_entry_continue_reverse(holder, &disk->slave_bdevs, list) 172 __unlink_disk_holder(holder->bdev, disk); 173 mutex_unlock(&disk->open_mutex); 174 return ret; 175 } 176