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 22 static struct device *rnbd_dev; 23 static struct class *rnbd_dev_class; 24 static struct kobject *rnbd_devs_kobj; 25 26 static 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 35 static struct kobj_type dev_ktype = { 36 .sysfs_ops = &kobj_sysfs_ops, 37 .release = rnbd_srv_dev_release 38 }; 39 40 int 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 68 put_sess_kobj: 69 kobject_put(dev->dev_sessions_kobj); 70 free_dev_kobj: 71 kobject_del(&dev->dev_kobj); 72 kobject_put(&dev->dev_kobj); 73 return ret; 74 } 75 76 void 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 85 static 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 96 static struct kobj_attribute rnbd_srv_dev_session_ro_attr = 97 __ATTR_RO(read_only); 98 99 static 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 111 static struct kobj_attribute rnbd_srv_dev_session_access_mode_attr = 112 __ATTR_RO(access_mode); 113 114 static 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 124 static struct kobj_attribute rnbd_srv_dev_session_mapping_path_attr = 125 __ATTR_RO(mapping_path); 126 127 static 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 134 static 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 154 static 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 159 static 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 167 static struct attribute_group rnbd_srv_default_dev_session_attr_group = { 168 .attrs = rnbd_srv_default_dev_sessions_attrs, 169 }; 170 171 void 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 180 static 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 188 static struct kobj_type rnbd_srv_sess_dev_ktype = { 189 .sysfs_ops = &kobj_sysfs_ops, 190 .release = rnbd_srv_sess_dev_release, 191 }; 192 193 int 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 215 int 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 237 dev_destroy: 238 device_destroy(rnbd_dev_class, MKDEV(0, 0)); 239 cls_destroy: 240 class_destroy(rnbd_dev_class); 241 242 return err; 243 } 244 245 void 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