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