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