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 static MDEV_TYPE_ATTR_WO(create); 76 77 static ssize_t device_api_show(struct mdev_type *mtype, 78 struct mdev_type_attribute *attr, char *buf) 79 { 80 return sysfs_emit(buf, "%s\n", mtype->parent->mdev_driver->device_api); 81 } 82 static MDEV_TYPE_ATTR_RO(device_api); 83 84 static ssize_t name_show(struct mdev_type *mtype, 85 struct mdev_type_attribute *attr, char *buf) 86 { 87 return sprintf(buf, "%s\n", 88 mtype->pretty_name ? mtype->pretty_name : mtype->sysfs_name); 89 } 90 91 static MDEV_TYPE_ATTR_RO(name); 92 93 static struct attribute *mdev_types_core_attrs[] = { 94 &mdev_type_attr_create.attr, 95 &mdev_type_attr_device_api.attr, 96 &mdev_type_attr_name.attr, 97 NULL, 98 }; 99 100 static struct attribute_group mdev_type_core_group = { 101 .attrs = mdev_types_core_attrs, 102 }; 103 104 static const struct attribute_group *mdev_type_groups[] = { 105 &mdev_type_core_group, 106 NULL, 107 }; 108 109 static void mdev_type_release(struct kobject *kobj) 110 { 111 struct mdev_type *type = to_mdev_type(kobj); 112 113 pr_debug("Releasing group %s\n", kobj->name); 114 /* Pairs with the get in add_mdev_supported_type() */ 115 put_device(type->parent->dev); 116 } 117 118 static struct kobj_type mdev_type_ktype = { 119 .sysfs_ops = &mdev_type_sysfs_ops, 120 .release = mdev_type_release, 121 .default_groups = mdev_type_groups, 122 }; 123 124 static int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type) 125 { 126 int ret; 127 128 type->kobj.kset = parent->mdev_types_kset; 129 type->parent = parent; 130 /* Pairs with the put in mdev_type_release() */ 131 get_device(parent->dev); 132 133 ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, 134 "%s-%s", dev_driver_string(parent->dev), 135 type->sysfs_name); 136 if (ret) { 137 kobject_put(&type->kobj); 138 return ret; 139 } 140 141 type->devices_kobj = kobject_create_and_add("devices", &type->kobj); 142 if (!type->devices_kobj) { 143 ret = -ENOMEM; 144 goto attr_devices_failed; 145 } 146 147 ret = sysfs_create_files(&type->kobj, parent->mdev_driver->types_attrs); 148 if (ret) 149 goto attrs_failed; 150 return 0; 151 152 attrs_failed: 153 kobject_put(type->devices_kobj); 154 attr_devices_failed: 155 kobject_del(&type->kobj); 156 kobject_put(&type->kobj); 157 return ret; 158 } 159 160 static void mdev_type_remove(struct mdev_type *type) 161 { 162 sysfs_remove_files(&type->kobj, type->parent->mdev_driver->types_attrs); 163 164 kobject_put(type->devices_kobj); 165 kobject_del(&type->kobj); 166 kobject_put(&type->kobj); 167 } 168 169 /* mdev sysfs functions */ 170 void parent_remove_sysfs_files(struct mdev_parent *parent) 171 { 172 int i; 173 174 for (i = 0; i < parent->nr_types; i++) 175 mdev_type_remove(parent->types[i]); 176 kset_unregister(parent->mdev_types_kset); 177 } 178 179 int parent_create_sysfs_files(struct mdev_parent *parent) 180 { 181 int ret, i; 182 183 parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 184 NULL, &parent->dev->kobj); 185 if (!parent->mdev_types_kset) 186 return -ENOMEM; 187 188 for (i = 0; i < parent->nr_types; i++) { 189 ret = mdev_type_add(parent, parent->types[i]); 190 if (ret) 191 goto out_err; 192 } 193 return 0; 194 195 out_err: 196 while (--i >= 0) 197 mdev_type_remove(parent->types[i]); 198 return 0; 199 } 200 201 static ssize_t remove_store(struct device *dev, struct device_attribute *attr, 202 const char *buf, size_t count) 203 { 204 struct mdev_device *mdev = to_mdev_device(dev); 205 unsigned long val; 206 207 if (kstrtoul(buf, 0, &val) < 0) 208 return -EINVAL; 209 210 if (val && device_remove_file_self(dev, attr)) { 211 int ret; 212 213 ret = mdev_device_remove(mdev); 214 if (ret) 215 return ret; 216 } 217 218 return count; 219 } 220 221 static DEVICE_ATTR_WO(remove); 222 223 static struct attribute *mdev_device_attrs[] = { 224 &dev_attr_remove.attr, 225 NULL, 226 }; 227 228 static const struct attribute_group mdev_device_group = { 229 .attrs = mdev_device_attrs, 230 }; 231 232 const struct attribute_group *mdev_device_groups[] = { 233 &mdev_device_group, 234 NULL 235 }; 236 237 int mdev_create_sysfs_files(struct mdev_device *mdev) 238 { 239 struct mdev_type *type = mdev->type; 240 struct kobject *kobj = &mdev->dev.kobj; 241 int ret; 242 243 ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev)); 244 if (ret) 245 return ret; 246 247 ret = sysfs_create_link(kobj, &type->kobj, "mdev_type"); 248 if (ret) 249 goto type_link_failed; 250 return ret; 251 252 type_link_failed: 253 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 254 return ret; 255 } 256 257 void mdev_remove_sysfs_files(struct mdev_device *mdev) 258 { 259 struct kobject *kobj = &mdev->dev.kobj; 260 261 sysfs_remove_link(kobj, "mdev_type"); 262 sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); 263 } 264