holder.c (4177B)
1// SPDX-License-Identifier: GPL-2.0-only 2#include <linux/blkdev.h> 3#include <linux/slab.h> 4 5struct bd_holder_disk { 6 struct list_head list; 7 struct block_device *bdev; 8 int refcnt; 9}; 10 11static 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 22static int add_symlink(struct kobject *from, struct kobject *to) 23{ 24 return sysfs_create_link(from, to, kobject_name(to)); 25} 26 27static void del_symlink(struct kobject *from, struct kobject *to) 28{ 29 sysfs_remove_link(from, kobject_name(to)); 30} 31 32static 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 */ 73int 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 holder = bd_find_holder_disk(bdev, disk); 83 if (holder) { 84 holder->refcnt++; 85 goto out_unlock; 86 } 87 88 holder = kzalloc(sizeof(*holder), GFP_KERNEL); 89 if (!holder) { 90 ret = -ENOMEM; 91 goto out_unlock; 92 } 93 94 INIT_LIST_HEAD(&holder->list); 95 holder->bdev = bdev; 96 holder->refcnt = 1; 97 if (disk->slave_dir) { 98 ret = __link_disk_holder(bdev, disk); 99 if (ret) { 100 kfree(holder); 101 goto out_unlock; 102 } 103 } 104 105 list_add(&holder->list, &disk->slave_bdevs); 106 /* 107 * del_gendisk drops the initial reference to bd_holder_dir, so we need 108 * to keep our own here to allow for cleanup past that point. 109 */ 110 kobject_get(bdev->bd_holder_dir); 111 112out_unlock: 113 mutex_unlock(&disk->open_mutex); 114 return ret; 115} 116EXPORT_SYMBOL_GPL(bd_link_disk_holder); 117 118static void __unlink_disk_holder(struct block_device *bdev, 119 struct gendisk *disk) 120{ 121 del_symlink(disk->slave_dir, bdev_kobj(bdev)); 122 del_symlink(bdev->bd_holder_dir, &disk_to_dev(disk)->kobj); 123} 124 125/** 126 * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder() 127 * @bdev: the calimed slave bdev 128 * @disk: the holding disk 129 * 130 * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. 131 * 132 * CONTEXT: 133 * Might sleep. 134 */ 135void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk) 136{ 137 struct bd_holder_disk *holder; 138 139 mutex_lock(&disk->open_mutex); 140 holder = bd_find_holder_disk(bdev, disk); 141 if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) { 142 if (disk->slave_dir) 143 __unlink_disk_holder(bdev, disk); 144 kobject_put(bdev->bd_holder_dir); 145 list_del_init(&holder->list); 146 kfree(holder); 147 } 148 mutex_unlock(&disk->open_mutex); 149} 150EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); 151 152int bd_register_pending_holders(struct gendisk *disk) 153{ 154 struct bd_holder_disk *holder; 155 int ret; 156 157 mutex_lock(&disk->open_mutex); 158 list_for_each_entry(holder, &disk->slave_bdevs, list) { 159 ret = __link_disk_holder(holder->bdev, disk); 160 if (ret) 161 goto out_undo; 162 } 163 mutex_unlock(&disk->open_mutex); 164 return 0; 165 166out_undo: 167 list_for_each_entry_continue_reverse(holder, &disk->slave_bdevs, list) 168 __unlink_disk_holder(holder->bdev, disk); 169 mutex_unlock(&disk->open_mutex); 170 return ret; 171}