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 <[email protected]> 7 * Kirti Wankhede <[email protected]> 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 } 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 int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type) 93 { 94 int ret; 95 96 type->kobj.kset = parent->mdev_types_kset; 97 type->parent = parent; 98 /* Pairs with the put in mdev_type_release() */ 99 get_device(parent->dev); 100 101 ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, 102 "%s-%s", dev_driver_string(parent->dev), 103 type->sysfs_name); 104 if (ret) { 105 kobject_put(&type->kobj); 106 return ret; 107 } 108 109 ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr); 110 if (ret) 111 goto attr_create_failed; 112 113 type->devices_kobj = kobject_create_and_add("devices", &type->kobj); 114 if (!type->devices_kobj) { 115 ret = -ENOMEM; 116 goto attr_devices_failed; 117 } 118 119 ret = sysfs_create_files(&type->kobj, parent->mdev_driver->types_attrs); 120 if (ret) 121 goto attrs_failed; 122 return 0; 123 124 attrs_failed: 125 kobject_put(type->devices_kobj); 126 attr_devices_failed: 127 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 128 attr_create_failed: 129 kobject_del(&type->kobj); 130 kobject_put(&type->kobj); 131 return ret; 132 } 133 134 static void mdev_type_remove(struct mdev_type *type) 135 { 136 sysfs_remove_files(&type->kobj, type->parent->mdev_driver->types_attrs); 137 138 kobject_put(type->devices_kobj); 139 sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 140 kobject_del(&type->kobj); 141 kobject_put(&type->kobj); 142 } 143 144 /* mdev sysfs functions */ 145 void parent_remove_sysfs_files(struct mdev_parent *parent) 146 { 147 int i; 148 149 for (i = 0; i < parent->nr_types; i++) 150 mdev_type_remove(parent->types[i]); 151 kset_unregister(parent->mdev_types_kset); 152 } 153 154 int parent_create_sysfs_files(struct mdev_parent *parent) 155 { 156 int ret, i; 157 158 parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 159 NULL, &parent->dev->kobj); 160 if (!parent->mdev_types_kset) 161 return -ENOMEM; 162 163 for (i = 0; i < parent->nr_types; i++) { 164 ret = mdev_type_add(parent, parent->types[i]); 165 if (ret) 166 goto out_err; 167 } 168 return 0; 169 170 out_err: 171 while (--i >= 0) 172 mdev_type_remove(parent->types[i]); 173 return 0; 174 } 175 176 static ssize_t remove_store(struct device *dev, struct device_attribute *attr, 177 const char *buf, size_t count) 178 { 179 struct mdev_device *mdev = to_mdev_device(dev); 180 unsigned long val; 181 182 if (kstrtoul(buf, 0, &val) < 0) 183 return -EINVAL; 184 185 if (val && device_remove_file_self(dev, attr)) { 186 int ret; 187 188 ret = mdev_device_remove(mdev); 189 if (ret) 190 return ret; 191 } 192 193 return count; 194 } 195 196 static DEVICE_ATTR_WO(remove); 197 198 static struct attribute *mdev_device_attrs[] = { 199 &dev_attr_remove.attr, 200 NULL, 201 }; 202 203 static const struct attribute_group mdev_device_group = { 204 .attrs = mdev_device_attrs, 205 }; 206 207 const struct attribute_group *mdev_device_groups[] = { 208 &mdev_device_group, 209 NULL 210 }; 211 212 int mdev_create_sysfs_files(struct mdev_device *mdev) 213 { 214 struct mdev_type *type = mdev->type; 215 struct kobject *kobj = &mdev->dev.kobj; 216 int ret; 217 218 ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev)); 219 if (ret) 220 return ret; 221 222 ret = sysfs_create_link(kobj, &type->kobj, "mdev_type"); 223 if (ret) 224 goto type_link_failed; 225 return ret; 226 227 type_link_failed: 228 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 229 return ret; 230 } 231 232 void mdev_remove_sysfs_files(struct mdev_device *mdev) 233 { 234 struct kobject *kobj = &mdev->dev.kobj; 235 236 sysfs_remove_link(kobj, "mdev_type"); 237 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 238 } 239