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