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 23 static struct device *rnbd_dev; 24 static struct class *rnbd_dev_class; 25 static struct kobject *rnbd_devs_kobj; 26 27 static 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 36 static struct kobj_type dev_ktype = { 37 .sysfs_ops = &kobj_sysfs_ops, 38 .release = rnbd_srv_dev_release 39 }; 40 41 int 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 65 put_sess_kobj: 66 kobject_put(dev->dev_sessions_kobj); 67 put_dev_kobj: 68 kobject_put(&dev->dev_kobj); 69 return ret; 70 } 71 72 void 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 81 static 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 92 static struct kobj_attribute rnbd_srv_dev_session_ro_attr = 93 __ATTR_RO(read_only); 94 95 static 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 107 static struct kobj_attribute rnbd_srv_dev_session_access_mode_attr = 108 __ATTR_RO(access_mode); 109 110 static 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 120 static struct kobj_attribute rnbd_srv_dev_session_mapping_path_attr = 121 __ATTR_RO(mapping_path); 122 123 static 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 130 static struct attribute_group rnbd_srv_default_dev_session_attr_group = { 131 .attrs = rnbd_srv_default_dev_sessions_attrs, 132 }; 133 134 void 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 143 static 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 151 static struct kobj_type rnbd_srv_sess_dev_ktype = { 152 .sysfs_ops = &kobj_sysfs_ops, 153 .release = rnbd_srv_sess_dev_release, 154 }; 155 156 int 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 173 err: 174 kobject_put(&sess_dev->kobj); 175 176 return ret; 177 } 178 179 int 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 201 dev_destroy: 202 device_destroy(rnbd_dev_class, MKDEV(0, 0)); 203 cls_destroy: 204 class_destroy(rnbd_dev_class); 205 206 return err; 207 } 208 209 void 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