xref: /openbmc/linux/drivers/vfio/mdev/mdev_sysfs.c (revision 151f4e2b)
1 /*
2  * File attributes for Mediated devices
3  *
4  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
5  *     Author: Neo Jia <cjia@nvidia.com>
6  *             Kirti Wankhede <kwankhede@nvidia.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12 
13 #include <linux/sysfs.h>
14 #include <linux/ctype.h>
15 #include <linux/device.h>
16 #include <linux/slab.h>
17 #include <linux/uuid.h>
18 #include <linux/mdev.h>
19 
20 #include "mdev_private.h"
21 
22 /* Static functions */
23 
24 static ssize_t mdev_type_attr_show(struct kobject *kobj,
25 				     struct attribute *__attr, char *buf)
26 {
27 	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
28 	struct mdev_type *type = to_mdev_type(kobj);
29 	ssize_t ret = -EIO;
30 
31 	if (attr->show)
32 		ret = attr->show(kobj, type->parent->dev, buf);
33 	return ret;
34 }
35 
36 static ssize_t mdev_type_attr_store(struct kobject *kobj,
37 				      struct attribute *__attr,
38 				      const char *buf, size_t count)
39 {
40 	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
41 	struct mdev_type *type = to_mdev_type(kobj);
42 	ssize_t ret = -EIO;
43 
44 	if (attr->store)
45 		ret = attr->store(&type->kobj, type->parent->dev, buf, count);
46 	return ret;
47 }
48 
49 static const struct sysfs_ops mdev_type_sysfs_ops = {
50 	.show = mdev_type_attr_show,
51 	.store = mdev_type_attr_store,
52 };
53 
54 static ssize_t create_store(struct kobject *kobj, struct device *dev,
55 			    const char *buf, size_t count)
56 {
57 	char *str;
58 	guid_t uuid;
59 	int ret;
60 
61 	if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))
62 		return -EINVAL;
63 
64 	str = kstrndup(buf, count, GFP_KERNEL);
65 	if (!str)
66 		return -ENOMEM;
67 
68 	ret = guid_parse(str, &uuid);
69 	kfree(str);
70 	if (ret)
71 		return ret;
72 
73 	ret = mdev_device_create(kobj, dev, &uuid);
74 	if (ret)
75 		return ret;
76 
77 	return count;
78 }
79 
80 MDEV_TYPE_ATTR_WO(create);
81 
82 static void mdev_type_release(struct kobject *kobj)
83 {
84 	struct mdev_type *type = to_mdev_type(kobj);
85 
86 	pr_debug("Releasing group %s\n", kobj->name);
87 	kfree(type);
88 }
89 
90 static struct kobj_type mdev_type_ktype = {
91 	.sysfs_ops = &mdev_type_sysfs_ops,
92 	.release = mdev_type_release,
93 };
94 
95 static struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent,
96 						 struct attribute_group *group)
97 {
98 	struct mdev_type *type;
99 	int ret;
100 
101 	if (!group->name) {
102 		pr_err("%s: Type name empty!\n", __func__);
103 		return ERR_PTR(-EINVAL);
104 	}
105 
106 	type = kzalloc(sizeof(*type), GFP_KERNEL);
107 	if (!type)
108 		return ERR_PTR(-ENOMEM);
109 
110 	type->kobj.kset = parent->mdev_types_kset;
111 
112 	ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,
113 				   "%s-%s", dev_driver_string(parent->dev),
114 				   group->name);
115 	if (ret) {
116 		kfree(type);
117 		return ERR_PTR(ret);
118 	}
119 
120 	ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr);
121 	if (ret)
122 		goto attr_create_failed;
123 
124 	type->devices_kobj = kobject_create_and_add("devices", &type->kobj);
125 	if (!type->devices_kobj) {
126 		ret = -ENOMEM;
127 		goto attr_devices_failed;
128 	}
129 
130 	ret = sysfs_create_files(&type->kobj,
131 				 (const struct attribute **)group->attrs);
132 	if (ret) {
133 		ret = -ENOMEM;
134 		goto attrs_failed;
135 	}
136 
137 	type->group = group;
138 	type->parent = parent;
139 	return type;
140 
141 attrs_failed:
142 	kobject_put(type->devices_kobj);
143 attr_devices_failed:
144 	sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
145 attr_create_failed:
146 	kobject_del(&type->kobj);
147 	kobject_put(&type->kobj);
148 	return ERR_PTR(ret);
149 }
150 
151 static void remove_mdev_supported_type(struct mdev_type *type)
152 {
153 	sysfs_remove_files(&type->kobj,
154 			   (const struct attribute **)type->group->attrs);
155 	kobject_put(type->devices_kobj);
156 	sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
157 	kobject_del(&type->kobj);
158 	kobject_put(&type->kobj);
159 }
160 
161 static int add_mdev_supported_type_groups(struct mdev_parent *parent)
162 {
163 	int i;
164 
165 	for (i = 0; parent->ops->supported_type_groups[i]; i++) {
166 		struct mdev_type *type;
167 
168 		type = add_mdev_supported_type(parent,
169 					parent->ops->supported_type_groups[i]);
170 		if (IS_ERR(type)) {
171 			struct mdev_type *ltype, *tmp;
172 
173 			list_for_each_entry_safe(ltype, tmp, &parent->type_list,
174 						  next) {
175 				list_del(&ltype->next);
176 				remove_mdev_supported_type(ltype);
177 			}
178 			return PTR_ERR(type);
179 		}
180 		list_add(&type->next, &parent->type_list);
181 	}
182 	return 0;
183 }
184 
185 /* mdev sysfs functions */
186 void parent_remove_sysfs_files(struct mdev_parent *parent)
187 {
188 	struct mdev_type *type, *tmp;
189 
190 	list_for_each_entry_safe(type, tmp, &parent->type_list, next) {
191 		list_del(&type->next);
192 		remove_mdev_supported_type(type);
193 	}
194 
195 	sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups);
196 	kset_unregister(parent->mdev_types_kset);
197 }
198 
199 int parent_create_sysfs_files(struct mdev_parent *parent)
200 {
201 	int ret;
202 
203 	parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",
204 					       NULL, &parent->dev->kobj);
205 
206 	if (!parent->mdev_types_kset)
207 		return -ENOMEM;
208 
209 	INIT_LIST_HEAD(&parent->type_list);
210 
211 	ret = sysfs_create_groups(&parent->dev->kobj,
212 				  parent->ops->dev_attr_groups);
213 	if (ret)
214 		goto create_err;
215 
216 	ret = add_mdev_supported_type_groups(parent);
217 	if (ret)
218 		sysfs_remove_groups(&parent->dev->kobj,
219 				    parent->ops->dev_attr_groups);
220 	else
221 		return ret;
222 
223 create_err:
224 	kset_unregister(parent->mdev_types_kset);
225 	return ret;
226 }
227 
228 static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
229 			    const char *buf, size_t count)
230 {
231 	unsigned long val;
232 
233 	if (kstrtoul(buf, 0, &val) < 0)
234 		return -EINVAL;
235 
236 	if (val && device_remove_file_self(dev, attr)) {
237 		int ret;
238 
239 		ret = mdev_device_remove(dev, false);
240 		if (ret) {
241 			device_create_file(dev, attr);
242 			return ret;
243 		}
244 	}
245 
246 	return count;
247 }
248 
249 static DEVICE_ATTR_WO(remove);
250 
251 static const struct attribute *mdev_device_attrs[] = {
252 	&dev_attr_remove.attr,
253 	NULL,
254 };
255 
256 int  mdev_create_sysfs_files(struct device *dev, struct mdev_type *type)
257 {
258 	int ret;
259 
260 	ret = sysfs_create_link(type->devices_kobj, &dev->kobj, dev_name(dev));
261 	if (ret)
262 		return ret;
263 
264 	ret = sysfs_create_link(&dev->kobj, &type->kobj, "mdev_type");
265 	if (ret)
266 		goto type_link_failed;
267 
268 	ret = sysfs_create_files(&dev->kobj, mdev_device_attrs);
269 	if (ret)
270 		goto create_files_failed;
271 
272 	return ret;
273 
274 create_files_failed:
275 	sysfs_remove_link(&dev->kobj, "mdev_type");
276 type_link_failed:
277 	sysfs_remove_link(type->devices_kobj, dev_name(dev));
278 	return ret;
279 }
280 
281 void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type)
282 {
283 	sysfs_remove_files(&dev->kobj, mdev_device_attrs);
284 	sysfs_remove_link(&dev->kobj, "mdev_type");
285 	sysfs_remove_link(type->devices_kobj, dev_name(dev));
286 }
287