linux/drivers/block/rnbd/rnbd-srv-sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * RDMA Network Block Driver
   4 *
   5 * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
   6 * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
   7 * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
   8 */
   9#undef pr_fmt
  10#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
  11
  12#include <uapi/linux/limits.h>
  13#include <linux/kobject.h>
  14#include <linux/sysfs.h>
  15#include <linux/stat.h>
  16#include <linux/list.h>
  17#include <linux/moduleparam.h>
  18#include <linux/device.h>
  19
  20#include "rnbd-srv.h"
  21
  22static struct device *rnbd_dev;
  23static struct class *rnbd_dev_class;
  24static struct kobject *rnbd_devs_kobj;
  25
  26static void rnbd_srv_dev_release(struct kobject *kobj)
  27{
  28        struct rnbd_srv_dev *dev;
  29
  30        dev = container_of(kobj, struct rnbd_srv_dev, dev_kobj);
  31
  32        kfree(dev);
  33}
  34
  35static struct kobj_type dev_ktype = {
  36        .sysfs_ops = &kobj_sysfs_ops,
  37        .release = rnbd_srv_dev_release
  38};
  39
  40int rnbd_srv_create_dev_sysfs(struct rnbd_srv_dev *dev,
  41                               struct block_device *bdev,
  42                               const char *dev_name)
  43{
  44        struct kobject *bdev_kobj;
  45        int ret;
  46
  47        ret = kobject_init_and_add(&dev->dev_kobj, &dev_ktype,
  48                                   rnbd_devs_kobj, dev_name);
  49        if (ret) {
  50                kobject_put(&dev->dev_kobj);
  51                return ret;
  52        }
  53
  54        dev->dev_sessions_kobj = kobject_create_and_add("sessions",
  55                                                        &dev->dev_kobj);
  56        if (!dev->dev_sessions_kobj) {
  57                ret = -ENOMEM;
  58                goto free_dev_kobj;
  59        }
  60
  61        bdev_kobj = &disk_to_dev(bdev->bd_disk)->kobj;
  62        ret = sysfs_create_link(&dev->dev_kobj, bdev_kobj, "block_dev");
  63        if (ret)
  64                goto put_sess_kobj;
  65
  66        return 0;
  67
  68put_sess_kobj:
  69        kobject_put(dev->dev_sessions_kobj);
  70free_dev_kobj:
  71        kobject_del(&dev->dev_kobj);
  72        kobject_put(&dev->dev_kobj);
  73        return ret;
  74}
  75
  76void rnbd_srv_destroy_dev_sysfs(struct rnbd_srv_dev *dev)
  77{
  78        sysfs_remove_link(&dev->dev_kobj, "block_dev");
  79        kobject_del(dev->dev_sessions_kobj);
  80        kobject_put(dev->dev_sessions_kobj);
  81        kobject_del(&dev->dev_kobj);
  82        kobject_put(&dev->dev_kobj);
  83}
  84
  85static ssize_t read_only_show(struct kobject *kobj, struct kobj_attribute *attr,
  86                              char *page)
  87{
  88        struct rnbd_srv_sess_dev *sess_dev;
  89
  90        sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
  91
  92        return sysfs_emit(page, "%d\n",
  93                          !(sess_dev->open_flags & FMODE_WRITE));
  94}
  95
  96static struct kobj_attribute rnbd_srv_dev_session_ro_attr =
  97        __ATTR_RO(read_only);
  98
  99static ssize_t access_mode_show(struct kobject *kobj,
 100                                struct kobj_attribute *attr,
 101                                char *page)
 102{
 103        struct rnbd_srv_sess_dev *sess_dev;
 104
 105        sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
 106
 107        return sysfs_emit(page, "%s\n",
 108                          rnbd_access_mode_str(sess_dev->access_mode));
 109}
 110
 111static struct kobj_attribute rnbd_srv_dev_session_access_mode_attr =
 112        __ATTR_RO(access_mode);
 113
 114static ssize_t mapping_path_show(struct kobject *kobj,
 115                                 struct kobj_attribute *attr, char *page)
 116{
 117        struct rnbd_srv_sess_dev *sess_dev;
 118
 119        sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
 120
 121        return sysfs_emit(page, "%s\n", sess_dev->pathname);
 122}
 123
 124static struct kobj_attribute rnbd_srv_dev_session_mapping_path_attr =
 125        __ATTR_RO(mapping_path);
 126
 127static ssize_t rnbd_srv_dev_session_force_close_show(struct kobject *kobj,
 128                                        struct kobj_attribute *attr, char *page)
 129{
 130        return sysfs_emit(page, "Usage: echo 1 > %s\n",
 131                          attr->attr.name);
 132}
 133
 134static ssize_t rnbd_srv_dev_session_force_close_store(struct kobject *kobj,
 135                                        struct kobj_attribute *attr,
 136                                        const char *buf, size_t count)
 137{
 138        struct rnbd_srv_sess_dev *sess_dev;
 139
 140        sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
 141
 142        if (!sysfs_streq(buf, "1")) {
 143                rnbd_srv_err(sess_dev, "%s: invalid value: '%s'\n",
 144                              attr->attr.name, buf);
 145                return -EINVAL;
 146        }
 147
 148        rnbd_srv_info(sess_dev, "force close requested\n");
 149        rnbd_srv_sess_dev_force_close(sess_dev, attr);
 150
 151        return count;
 152}
 153
 154static struct kobj_attribute rnbd_srv_dev_session_force_close_attr =
 155        __ATTR(force_close, 0644,
 156               rnbd_srv_dev_session_force_close_show,
 157               rnbd_srv_dev_session_force_close_store);
 158
 159static struct attribute *rnbd_srv_default_dev_sessions_attrs[] = {
 160        &rnbd_srv_dev_session_access_mode_attr.attr,
 161        &rnbd_srv_dev_session_ro_attr.attr,
 162        &rnbd_srv_dev_session_mapping_path_attr.attr,
 163        &rnbd_srv_dev_session_force_close_attr.attr,
 164        NULL,
 165};
 166
 167static struct attribute_group rnbd_srv_default_dev_session_attr_group = {
 168        .attrs = rnbd_srv_default_dev_sessions_attrs,
 169};
 170
 171void rnbd_srv_destroy_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev)
 172{
 173        sysfs_remove_group(&sess_dev->kobj,
 174                           &rnbd_srv_default_dev_session_attr_group);
 175
 176        kobject_del(&sess_dev->kobj);
 177        kobject_put(&sess_dev->kobj);
 178}
 179
 180static void rnbd_srv_sess_dev_release(struct kobject *kobj)
 181{
 182        struct rnbd_srv_sess_dev *sess_dev;
 183
 184        sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
 185        rnbd_destroy_sess_dev(sess_dev, sess_dev->keep_id);
 186}
 187
 188static struct kobj_type rnbd_srv_sess_dev_ktype = {
 189        .sysfs_ops      = &kobj_sysfs_ops,
 190        .release        = rnbd_srv_sess_dev_release,
 191};
 192
 193int rnbd_srv_create_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev)
 194{
 195        int ret;
 196
 197        ret = kobject_init_and_add(&sess_dev->kobj, &rnbd_srv_sess_dev_ktype,
 198                                   sess_dev->dev->dev_sessions_kobj, "%s",
 199                                   sess_dev->sess->sessname);
 200        if (ret) {
 201                kobject_put(&sess_dev->kobj);
 202                return ret;
 203        }
 204
 205        ret = sysfs_create_group(&sess_dev->kobj,
 206                                 &rnbd_srv_default_dev_session_attr_group);
 207        if (ret) {
 208                kobject_del(&sess_dev->kobj);
 209                kobject_put(&sess_dev->kobj);
 210        }
 211
 212        return ret;
 213}
 214
 215int rnbd_srv_create_sysfs_files(void)
 216{
 217        int err;
 218
 219        rnbd_dev_class = class_create(THIS_MODULE, "rnbd-server");
 220        if (IS_ERR(rnbd_dev_class))
 221                return PTR_ERR(rnbd_dev_class);
 222
 223        rnbd_dev = device_create(rnbd_dev_class, NULL,
 224                                  MKDEV(0, 0), NULL, "ctl");
 225        if (IS_ERR(rnbd_dev)) {
 226                err = PTR_ERR(rnbd_dev);
 227                goto cls_destroy;
 228        }
 229        rnbd_devs_kobj = kobject_create_and_add("devices", &rnbd_dev->kobj);
 230        if (!rnbd_devs_kobj) {
 231                err = -ENOMEM;
 232                goto dev_destroy;
 233        }
 234
 235        return 0;
 236
 237dev_destroy:
 238        device_destroy(rnbd_dev_class, MKDEV(0, 0));
 239cls_destroy:
 240        class_destroy(rnbd_dev_class);
 241
 242        return err;
 243}
 244
 245void rnbd_srv_destroy_sysfs_files(void)
 246{
 247        kobject_del(rnbd_devs_kobj);
 248        kobject_put(rnbd_devs_kobj);
 249        device_destroy(rnbd_dev_class, MKDEV(0, 0));
 250        class_destroy(rnbd_dev_class);
 251}
 252