1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * File attributes for Mediated devices 4 * 5 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 6 * Author: Neo Jia <cjia@nvidia.com> 7 * Kirti Wankhede <kwankhede@nvidia.com> 8 */ 9 10 #include <linux/sysfs.h> 11 #include <linux/ctype.h> 12 #include <linux/device.h> 13 #include <linux/slab.h> 14 #include <linux/uuid.h> 15 #include <linux/mdev.h> 16 17 #include "mdev_private.h" 18 19 /* Static functions */ 20 21 static ssize_t mdev_type_attr_show(struct kobject *kobj, 22 struct attribute *__attr, char *buf) 23 { 24 struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); 25 struct mdev_type *type = to_mdev_type(kobj); 26 ssize_t ret = -EIO; 27 28 if (attr->show) 29 ret = attr->show(type, attr, buf); 30 return ret; 31 } 32 33 static ssize_t mdev_type_attr_store(struct kobject *kobj, 34 struct attribute *__attr, 35 const char *buf, size_t count) 36 { 37 struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); 38 struct mdev_type *type = to_mdev_type(kobj); 39 ssize_t ret = -EIO; 40 41 if (attr->store) 42 ret = attr->store(type, attr, buf, count); 43 return ret; 44 } 45 46 static const struct sysfs_ops mdev_type_sysfs_ops = { 47 .show = mdev_type_attr_show, 48 .store = mdev_type_attr_store, 49 }; 50 51 static ssize_t create_store(struct mdev_type *mtype, 52 struct mdev_type_attribute *attr, const char *buf, 53 size_t count) 54 { 55 char *str; 56 guid_t uuid; 57 int ret; 58 59 if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) 60 return -EINVAL; 61 62 str = kstrndup(buf, count, GFP_KERNEL); 63 if (!str) 64 return -ENOMEM; 65 66 ret = guid_parse(str, &uuid); 67 kfree(str); 68 if (ret) 69 return ret; 70 71 ret = mdev_device_create(mtype, &uuid); 72 if (ret) 73 return ret; 74 75 return count; 76 } 77 78 static MDEV_TYPE_ATTR_WO(create); 79 80 static void mdev_type_release(struct kobject *kobj) 81 { 82 struct mdev_type *type = to_mdev_type(kobj); 83 84 pr_debug("Releasing group %s\n", kobj->name); 85 /* Pairs with the get in add_mdev_supported_type() */ 86 mdev_put_parent(type->parent); 87 kfree(type); 88 } 89 90 static struct kobj_type mdev_type_ktype = { 91 .sysfs_ops = &mdev_type_sysfs_ops, 92 .release = mdev_type_release, 93 }; 94 95 static struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent, 96 unsigned int type_group_id) 97 { 98 struct mdev_type *type; 99 struct attribute_group *group = 100 parent->mdev_driver->supported_type_groups[type_group_id]; 101 int ret; 102 103 if (!group->name) { 104 pr_err("%s: Type name empty!\n", __func__); 105 return ERR_PTR(-EINVAL); 106 } 107 108 type = kzalloc(sizeof(*type), GFP_KERNEL); 109 if (!type) 110 return ERR_PTR(-ENOMEM); 111 112 type->kobj.kset = parent->mdev_types_kset; 113 type->parent = parent; 114 /* Pairs with the put in mdev_type_release() */ 115 mdev_get_parent(parent); 116 type->type_group_id = type_group_id; 117 118 ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, 119 "%s-%s", dev_driver_string(parent->dev), 120 group->name); 121 if (ret) { 122 kobject_put(&type->kobj); 123 return ERR_PTR(ret); 124 } 125 126 ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr); 127 if (ret) 128 goto attr_create_failed; 129 130 type->devices_kobj = kobject_create_and_add("devices", &type->kobj); 131 if (!type->devices_kobj) { 132 ret = -ENOMEM; 133 goto attr_devices_failed; 134 } 135 136 ret = sysfs_create_files(&type->kobj, 137 (const struct attribute **)group->attrs); 138 if (ret) { 139 ret = -ENOMEM; 140 goto attrs_failed; 141 } 142 return type; 143 144 attrs_failed: 145 kobject_put(type->devices_kobj); 146 attr_devices_failed: 147 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 148 attr_create_failed: 149 kobject_del(&type->kobj); 150 kobject_put(&type->kobj); 151 return ERR_PTR(ret); 152 } 153 154 static void remove_mdev_supported_type(struct mdev_type *type) 155 { 156 struct attribute_group *group = 157 type->parent->mdev_driver->supported_type_groups[type->type_group_id]; 158 159 sysfs_remove_files(&type->kobj, 160 (const struct attribute **)group->attrs); 161 kobject_put(type->devices_kobj); 162 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 163 kobject_del(&type->kobj); 164 kobject_put(&type->kobj); 165 } 166 167 static int add_mdev_supported_type_groups(struct mdev_parent *parent) 168 { 169 int i; 170 171 for (i = 0; parent->mdev_driver->supported_type_groups[i]; i++) { 172 struct mdev_type *type; 173 174 type = add_mdev_supported_type(parent, i); 175 if (IS_ERR(type)) { 176 struct mdev_type *ltype, *tmp; 177 178 list_for_each_entry_safe(ltype, tmp, &parent->type_list, 179 next) { 180 list_del(<ype->next); 181 remove_mdev_supported_type(ltype); 182 } 183 return PTR_ERR(type); 184 } 185 list_add(&type->next, &parent->type_list); 186 } 187 return 0; 188 } 189 190 /* mdev sysfs functions */ 191 void parent_remove_sysfs_files(struct mdev_parent *parent) 192 { 193 struct mdev_type *type, *tmp; 194 195 list_for_each_entry_safe(type, tmp, &parent->type_list, next) { 196 list_del(&type->next); 197 remove_mdev_supported_type(type); 198 } 199 200 kset_unregister(parent->mdev_types_kset); 201 } 202 203 int parent_create_sysfs_files(struct mdev_parent *parent) 204 { 205 int ret; 206 207 parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 208 NULL, &parent->dev->kobj); 209 210 if (!parent->mdev_types_kset) 211 return -ENOMEM; 212 213 INIT_LIST_HEAD(&parent->type_list); 214 215 ret = add_mdev_supported_type_groups(parent); 216 if (ret) 217 goto create_err; 218 return 0; 219 220 create_err: 221 kset_unregister(parent->mdev_types_kset); 222 return ret; 223 } 224 225 static ssize_t remove_store(struct device *dev, struct device_attribute *attr, 226 const char *buf, size_t count) 227 { 228 struct mdev_device *mdev = to_mdev_device(dev); 229 unsigned long val; 230 231 if (kstrtoul(buf, 0, &val) < 0) 232 return -EINVAL; 233 234 if (val && device_remove_file_self(dev, attr)) { 235 int ret; 236 237 ret = mdev_device_remove(mdev); 238 if (ret) 239 return ret; 240 } 241 242 return count; 243 } 244 245 static DEVICE_ATTR_WO(remove); 246 247 static struct attribute *mdev_device_attrs[] = { 248 &dev_attr_remove.attr, 249 NULL, 250 }; 251 252 static const struct attribute_group mdev_device_group = { 253 .attrs = mdev_device_attrs, 254 }; 255 256 const struct attribute_group *mdev_device_groups[] = { 257 &mdev_device_group, 258 NULL 259 }; 260 261 int mdev_create_sysfs_files(struct mdev_device *mdev) 262 { 263 struct mdev_type *type = mdev->type; 264 struct kobject *kobj = &mdev->dev.kobj; 265 int ret; 266 267 ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev)); 268 if (ret) 269 return ret; 270 271 ret = sysfs_create_link(kobj, &type->kobj, "mdev_type"); 272 if (ret) 273 goto type_link_failed; 274 return ret; 275 276 type_link_failed: 277 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 278 return ret; 279 } 280 281 void mdev_remove_sysfs_files(struct mdev_device *mdev) 282 { 283 struct kobject *kobj = &mdev->dev.kobj; 284 285 sysfs_remove_link(kobj, "mdev_type"); 286 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 287 } 288