linux/block/holder.c
<<
>>
Prefs
   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 kobject          *holder_dir;
   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->holder_dir == bdev->bd_holder_dir)
  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
  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 */
  60int 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
 114out_del_symlink:
 115        del_symlink(disk->slave_dir, bdev_kobj(bdev));
 116out_free_holder:
 117        kfree(holder);
 118out_unlock:
 119        mutex_unlock(&disk->open_mutex);
 120        if (ret)
 121                kobject_put(bdev->bd_holder_dir);
 122        return ret;
 123}
 124EXPORT_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 */
 136void 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}
 154EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);
 155