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->ops->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->ops->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->ops->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 sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups); 201 kset_unregister(parent->mdev_types_kset); 202 } 203 204 int parent_create_sysfs_files(struct mdev_parent *parent) 205 { 206 int ret; 207 208 parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 209 NULL, &parent->dev->kobj); 210 211 if (!parent->mdev_types_kset) 212 return -ENOMEM; 213 214 INIT_LIST_HEAD(&parent->type_list); 215 216 ret = sysfs_create_groups(&parent->dev->kobj, 217 parent->ops->dev_attr_groups); 218 if (ret) 219 goto create_err; 220 221 ret = add_mdev_supported_type_groups(parent); 222 if (ret) 223 sysfs_remove_groups(&parent->dev->kobj, 224 parent->ops->dev_attr_groups); 225 else 226 return ret; 227 228 create_err: 229 kset_unregister(parent->mdev_types_kset); 230 return ret; 231 } 232 233 static ssize_t remove_store(struct device *dev, struct device_attribute *attr, 234 const char *buf, size_t count) 235 { 236 struct mdev_device *mdev = to_mdev_device(dev); 237 unsigned long val; 238 239 if (kstrtoul(buf, 0, &val) < 0) 240 return -EINVAL; 241 242 if (val && device_remove_file_self(dev, attr)) { 243 int ret; 244 245 ret = mdev_device_remove(mdev); 246 if (ret) 247 return ret; 248 } 249 250 return count; 251 } 252 253 static DEVICE_ATTR_WO(remove); 254 255 static const struct attribute *mdev_device_attrs[] = { 256 &dev_attr_remove.attr, 257 NULL, 258 }; 259 260 int mdev_create_sysfs_files(struct mdev_device *mdev) 261 { 262 struct mdev_type *type = mdev->type; 263 struct kobject *kobj = &mdev->dev.kobj; 264 int ret; 265 266 ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev)); 267 if (ret) 268 return ret; 269 270 ret = sysfs_create_link(kobj, &type->kobj, "mdev_type"); 271 if (ret) 272 goto type_link_failed; 273 274 ret = sysfs_create_files(kobj, mdev_device_attrs); 275 if (ret) 276 goto create_files_failed; 277 278 return ret; 279 280 create_files_failed: 281 sysfs_remove_link(kobj, "mdev_type"); 282 type_link_failed: 283 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 284 return ret; 285 } 286 287 void mdev_remove_sysfs_files(struct mdev_device *mdev) 288 { 289 struct kobject *kobj = &mdev->dev.kobj; 290 291 sysfs_remove_files(kobj, mdev_device_attrs); 292 sysfs_remove_link(kobj, "mdev_type"); 293 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 294 } 295