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(kobj, type->parent->dev, 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->kobj, type->parent->dev, 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 kobject *kobj, struct device *dev, 52 const char *buf, size_t count) 53 { 54 char *str; 55 guid_t uuid; 56 int ret; 57 58 if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) 59 return -EINVAL; 60 61 str = kstrndup(buf, count, GFP_KERNEL); 62 if (!str) 63 return -ENOMEM; 64 65 ret = guid_parse(str, &uuid); 66 kfree(str); 67 if (ret) 68 return ret; 69 70 ret = mdev_device_create(kobj, dev, &uuid); 71 if (ret) 72 return ret; 73 74 return count; 75 } 76 77 MDEV_TYPE_ATTR_WO(create); 78 79 static void mdev_type_release(struct kobject *kobj) 80 { 81 struct mdev_type *type = to_mdev_type(kobj); 82 83 pr_debug("Releasing group %s\n", kobj->name); 84 kfree(type); 85 } 86 87 static struct kobj_type mdev_type_ktype = { 88 .sysfs_ops = &mdev_type_sysfs_ops, 89 .release = mdev_type_release, 90 }; 91 92 static struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent, 93 struct attribute_group *group) 94 { 95 struct mdev_type *type; 96 int ret; 97 98 if (!group->name) { 99 pr_err("%s: Type name empty!\n", __func__); 100 return ERR_PTR(-EINVAL); 101 } 102 103 type = kzalloc(sizeof(*type), GFP_KERNEL); 104 if (!type) 105 return ERR_PTR(-ENOMEM); 106 107 type->kobj.kset = parent->mdev_types_kset; 108 109 ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, 110 "%s-%s", dev_driver_string(parent->dev), 111 group->name); 112 if (ret) { 113 kfree(type); 114 return ERR_PTR(ret); 115 } 116 117 ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr); 118 if (ret) 119 goto attr_create_failed; 120 121 type->devices_kobj = kobject_create_and_add("devices", &type->kobj); 122 if (!type->devices_kobj) { 123 ret = -ENOMEM; 124 goto attr_devices_failed; 125 } 126 127 ret = sysfs_create_files(&type->kobj, 128 (const struct attribute **)group->attrs); 129 if (ret) { 130 ret = -ENOMEM; 131 goto attrs_failed; 132 } 133 134 type->group = group; 135 type->parent = parent; 136 return type; 137 138 attrs_failed: 139 kobject_put(type->devices_kobj); 140 attr_devices_failed: 141 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 142 attr_create_failed: 143 kobject_del(&type->kobj); 144 kobject_put(&type->kobj); 145 return ERR_PTR(ret); 146 } 147 148 static void remove_mdev_supported_type(struct mdev_type *type) 149 { 150 sysfs_remove_files(&type->kobj, 151 (const struct attribute **)type->group->attrs); 152 kobject_put(type->devices_kobj); 153 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 154 kobject_del(&type->kobj); 155 kobject_put(&type->kobj); 156 } 157 158 static int add_mdev_supported_type_groups(struct mdev_parent *parent) 159 { 160 int i; 161 162 for (i = 0; parent->ops->supported_type_groups[i]; i++) { 163 struct mdev_type *type; 164 165 type = add_mdev_supported_type(parent, 166 parent->ops->supported_type_groups[i]); 167 if (IS_ERR(type)) { 168 struct mdev_type *ltype, *tmp; 169 170 list_for_each_entry_safe(ltype, tmp, &parent->type_list, 171 next) { 172 list_del(<ype->next); 173 remove_mdev_supported_type(ltype); 174 } 175 return PTR_ERR(type); 176 } 177 list_add(&type->next, &parent->type_list); 178 } 179 return 0; 180 } 181 182 /* mdev sysfs functions */ 183 void parent_remove_sysfs_files(struct mdev_parent *parent) 184 { 185 struct mdev_type *type, *tmp; 186 187 list_for_each_entry_safe(type, tmp, &parent->type_list, next) { 188 list_del(&type->next); 189 remove_mdev_supported_type(type); 190 } 191 192 sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups); 193 kset_unregister(parent->mdev_types_kset); 194 } 195 196 int parent_create_sysfs_files(struct mdev_parent *parent) 197 { 198 int ret; 199 200 parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 201 NULL, &parent->dev->kobj); 202 203 if (!parent->mdev_types_kset) 204 return -ENOMEM; 205 206 INIT_LIST_HEAD(&parent->type_list); 207 208 ret = sysfs_create_groups(&parent->dev->kobj, 209 parent->ops->dev_attr_groups); 210 if (ret) 211 goto create_err; 212 213 ret = add_mdev_supported_type_groups(parent); 214 if (ret) 215 sysfs_remove_groups(&parent->dev->kobj, 216 parent->ops->dev_attr_groups); 217 else 218 return ret; 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 unsigned long val; 229 230 if (kstrtoul(buf, 0, &val) < 0) 231 return -EINVAL; 232 233 if (val && device_remove_file_self(dev, attr)) { 234 int ret; 235 236 ret = mdev_device_remove(dev); 237 if (ret) 238 return ret; 239 } 240 241 return count; 242 } 243 244 static DEVICE_ATTR_WO(remove); 245 246 static const struct attribute *mdev_device_attrs[] = { 247 &dev_attr_remove.attr, 248 NULL, 249 }; 250 251 int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type) 252 { 253 int ret; 254 255 ret = sysfs_create_link(type->devices_kobj, &dev->kobj, dev_name(dev)); 256 if (ret) 257 return ret; 258 259 ret = sysfs_create_link(&dev->kobj, &type->kobj, "mdev_type"); 260 if (ret) 261 goto type_link_failed; 262 263 ret = sysfs_create_files(&dev->kobj, mdev_device_attrs); 264 if (ret) 265 goto create_files_failed; 266 267 return ret; 268 269 create_files_failed: 270 sysfs_remove_link(&dev->kobj, "mdev_type"); 271 type_link_failed: 272 sysfs_remove_link(type->devices_kobj, dev_name(dev)); 273 return ret; 274 } 275 276 void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type) 277 { 278 sysfs_remove_files(&dev->kobj, mdev_device_attrs); 279 sysfs_remove_link(&dev->kobj, "mdev_type"); 280 sysfs_remove_link(type->devices_kobj, dev_name(dev)); 281 } 282