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/genhd.h> 17#include <linux/list.h> 18#include <linux/moduleparam.h> 19#include <linux/device.h> 20 21#include "rnbd-srv.h" 22 23static struct device *rnbd_dev; 24static struct class *rnbd_dev_class; 25static struct kobject *rnbd_devs_kobj; 26 27static void rnbd_srv_dev_release(struct kobject *kobj) 28{ 29 struct rnbd_srv_dev *dev; 30 31 dev = container_of(kobj, struct rnbd_srv_dev, dev_kobj); 32 33 kfree(dev); 34} 35 36static struct kobj_type dev_ktype = { 37 .sysfs_ops = &kobj_sysfs_ops, 38 .release = rnbd_srv_dev_release 39}; 40 41int rnbd_srv_create_dev_sysfs(struct rnbd_srv_dev *dev, 42 struct block_device *bdev, 43 const char *dev_name) 44{ 45 struct kobject *bdev_kobj; 46 int ret; 47 48 ret = kobject_init_and_add(&dev->dev_kobj, &dev_ktype, 49 rnbd_devs_kobj, dev_name); 50 if (ret) 51 return ret; 52 53 dev->dev_sessions_kobj = kobject_create_and_add("sessions", 54 &dev->dev_kobj); 55 if (!dev->dev_sessions_kobj) 56 goto put_dev_kobj; 57 58 bdev_kobj = &disk_to_dev(bdev->bd_disk)->kobj; 59 ret = sysfs_create_link(&dev->dev_kobj, bdev_kobj, "block_dev"); 60 if (ret) 61 goto put_sess_kobj; 62 63 return 0; 64 65put_sess_kobj: 66 kobject_put(dev->dev_sessions_kobj); 67put_dev_kobj: 68 kobject_put(&dev->dev_kobj); 69 return ret; 70} 71 72void rnbd_srv_destroy_dev_sysfs(struct rnbd_srv_dev *dev) 73{ 74 sysfs_remove_link(&dev->dev_kobj, "block_dev"); 75 kobject_del(dev->dev_sessions_kobj); 76 kobject_put(dev->dev_sessions_kobj); 77 kobject_del(&dev->dev_kobj); 78 kobject_put(&dev->dev_kobj); 79} 80 81static ssize_t read_only_show(struct kobject *kobj, struct kobj_attribute *attr, 82 char *page) 83{ 84 struct rnbd_srv_sess_dev *sess_dev; 85 86 sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj); 87 88 return scnprintf(page, PAGE_SIZE, "%d\n", 89 !(sess_dev->open_flags & FMODE_WRITE)); 90} 91 92static struct kobj_attribute rnbd_srv_dev_session_ro_attr = 93 __ATTR_RO(read_only); 94 95static ssize_t access_mode_show(struct kobject *kobj, 96 struct kobj_attribute *attr, 97 char *page) 98{ 99 struct rnbd_srv_sess_dev *sess_dev; 100 101 sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj); 102 103 return scnprintf(page, PAGE_SIZE, "%s\n", 104 rnbd_access_mode_str(sess_dev->access_mode)); 105} 106 107static struct kobj_attribute rnbd_srv_dev_session_access_mode_attr = 108 __ATTR_RO(access_mode); 109 110static ssize_t mapping_path_show(struct kobject *kobj, 111 struct kobj_attribute *attr, char *page) 112{ 113 struct rnbd_srv_sess_dev *sess_dev; 114 115 sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj); 116 117 return scnprintf(page, PAGE_SIZE, "%s\n", sess_dev->pathname); 118} 119 120static struct kobj_attribute rnbd_srv_dev_session_mapping_path_attr = 121 __ATTR_RO(mapping_path); 122 123static struct attribute *rnbd_srv_default_dev_sessions_attrs[] = { 124 &rnbd_srv_dev_session_access_mode_attr.attr, 125 &rnbd_srv_dev_session_ro_attr.attr, 126 &rnbd_srv_dev_session_mapping_path_attr.attr, 127 NULL, 128}; 129 130static struct attribute_group rnbd_srv_default_dev_session_attr_group = { 131 .attrs = rnbd_srv_default_dev_sessions_attrs, 132}; 133 134void rnbd_srv_destroy_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev) 135{ 136 sysfs_remove_group(&sess_dev->kobj, 137 &rnbd_srv_default_dev_session_attr_group); 138 139 kobject_del(&sess_dev->kobj); 140 kobject_put(&sess_dev->kobj); 141} 142 143static void rnbd_srv_sess_dev_release(struct kobject *kobj) 144{ 145 struct rnbd_srv_sess_dev *sess_dev; 146 147 sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj); 148 rnbd_destroy_sess_dev(sess_dev); 149} 150 151static struct kobj_type rnbd_srv_sess_dev_ktype = { 152 .sysfs_ops = &kobj_sysfs_ops, 153 .release = rnbd_srv_sess_dev_release, 154}; 155 156int rnbd_srv_create_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev) 157{ 158 int ret; 159 160 ret = kobject_init_and_add(&sess_dev->kobj, &rnbd_srv_sess_dev_ktype, 161 sess_dev->dev->dev_sessions_kobj, "%s", 162 sess_dev->sess->sessname); 163 if (ret) 164 return ret; 165 166 ret = sysfs_create_group(&sess_dev->kobj, 167 &rnbd_srv_default_dev_session_attr_group); 168 if (ret) 169 goto err; 170 171 return 0; 172 173err: 174 kobject_put(&sess_dev->kobj); 175 176 return ret; 177} 178 179int rnbd_srv_create_sysfs_files(void) 180{ 181 int err; 182 183 rnbd_dev_class = class_create(THIS_MODULE, "rnbd-server"); 184 if (IS_ERR(rnbd_dev_class)) 185 return PTR_ERR(rnbd_dev_class); 186 187 rnbd_dev = device_create(rnbd_dev_class, NULL, 188 MKDEV(0, 0), NULL, "ctl"); 189 if (IS_ERR(rnbd_dev)) { 190 err = PTR_ERR(rnbd_dev); 191 goto cls_destroy; 192 } 193 rnbd_devs_kobj = kobject_create_and_add("devices", &rnbd_dev->kobj); 194 if (!rnbd_devs_kobj) { 195 err = -ENOMEM; 196 goto dev_destroy; 197 } 198 199 return 0; 200 201dev_destroy: 202 device_destroy(rnbd_dev_class, MKDEV(0, 0)); 203cls_destroy: 204 class_destroy(rnbd_dev_class); 205 206 return err; 207} 208 209void rnbd_srv_destroy_sysfs_files(void) 210{ 211 kobject_del(rnbd_devs_kobj); 212 kobject_put(rnbd_devs_kobj); 213 device_destroy(rnbd_dev_class, MKDEV(0, 0)); 214 class_destroy(rnbd_dev_class); 215} 216