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