Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7b96953b KW |
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> | |
7b96953b KW |
8 | */ |
9 | ||
10 | #include <linux/sysfs.h> | |
11 | #include <linux/ctype.h> | |
7b96953b | 12 | #include <linux/slab.h> |
7b96953b KW |
13 | #include <linux/mdev.h> |
14 | ||
15 | #include "mdev_private.h" | |
16 | ||
685a1537 CH |
17 | struct mdev_type_attribute { |
18 | struct attribute attr; | |
19 | ssize_t (*show)(struct mdev_type *mtype, | |
20 | struct mdev_type_attribute *attr, char *buf); | |
21 | ssize_t (*store)(struct mdev_type *mtype, | |
22 | struct mdev_type_attribute *attr, const char *buf, | |
23 | size_t count); | |
24 | }; | |
25 | ||
26 | #define MDEV_TYPE_ATTR_RO(_name) \ | |
27 | struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RO(_name) | |
28 | #define MDEV_TYPE_ATTR_WO(_name) \ | |
29 | struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_WO(_name) | |
7b96953b KW |
30 | |
31 | static ssize_t mdev_type_attr_show(struct kobject *kobj, | |
32 | struct attribute *__attr, char *buf) | |
33 | { | |
34 | struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); | |
35 | struct mdev_type *type = to_mdev_type(kobj); | |
36 | ssize_t ret = -EIO; | |
37 | ||
38 | if (attr->show) | |
9169cff1 | 39 | ret = attr->show(type, attr, buf); |
7b96953b KW |
40 | return ret; |
41 | } | |
42 | ||
43 | static ssize_t mdev_type_attr_store(struct kobject *kobj, | |
44 | struct attribute *__attr, | |
45 | const char *buf, size_t count) | |
46 | { | |
47 | struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); | |
48 | struct mdev_type *type = to_mdev_type(kobj); | |
49 | ssize_t ret = -EIO; | |
50 | ||
51 | if (attr->store) | |
9169cff1 | 52 | ret = attr->store(type, attr, buf, count); |
7b96953b KW |
53 | return ret; |
54 | } | |
55 | ||
56 | static const struct sysfs_ops mdev_type_sysfs_ops = { | |
57 | .show = mdev_type_attr_show, | |
58 | .store = mdev_type_attr_store, | |
59 | }; | |
60 | ||
9169cff1 JG |
61 | static ssize_t create_store(struct mdev_type *mtype, |
62 | struct mdev_type_attribute *attr, const char *buf, | |
63 | size_t count) | |
7b96953b KW |
64 | { |
65 | char *str; | |
278bca7f | 66 | guid_t uuid; |
7b96953b KW |
67 | int ret; |
68 | ||
69 | if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) | |
70 | return -EINVAL; | |
71 | ||
72 | str = kstrndup(buf, count, GFP_KERNEL); | |
73 | if (!str) | |
74 | return -ENOMEM; | |
75 | ||
278bca7f | 76 | ret = guid_parse(str, &uuid); |
7b96953b KW |
77 | kfree(str); |
78 | if (ret) | |
79 | return ret; | |
80 | ||
9169cff1 | 81 | ret = mdev_device_create(mtype, &uuid); |
7b96953b KW |
82 | if (ret) |
83 | return ret; | |
84 | ||
85 | return count; | |
86 | } | |
e10b4f6c | 87 | static MDEV_TYPE_ATTR_WO(create); |
7b96953b | 88 | |
290aac5d JG |
89 | static ssize_t device_api_show(struct mdev_type *mtype, |
90 | struct mdev_type_attribute *attr, char *buf) | |
91 | { | |
92 | return sysfs_emit(buf, "%s\n", mtype->parent->mdev_driver->device_api); | |
93 | } | |
94 | static MDEV_TYPE_ATTR_RO(device_api); | |
95 | ||
0bc79069 CH |
96 | static ssize_t name_show(struct mdev_type *mtype, |
97 | struct mdev_type_attribute *attr, char *buf) | |
98 | { | |
99 | return sprintf(buf, "%s\n", | |
100 | mtype->pretty_name ? mtype->pretty_name : mtype->sysfs_name); | |
101 | } | |
102 | ||
103 | static MDEV_TYPE_ATTR_RO(name); | |
104 | ||
f2fbc72e CH |
105 | static ssize_t available_instances_show(struct mdev_type *mtype, |
106 | struct mdev_type_attribute *attr, | |
107 | char *buf) | |
108 | { | |
109 | struct mdev_driver *drv = mtype->parent->mdev_driver; | |
110 | ||
111 | return sysfs_emit(buf, "%u\n", drv->get_available(mtype)); | |
112 | } | |
113 | static MDEV_TYPE_ATTR_RO(available_instances); | |
114 | ||
685a1537 CH |
115 | static ssize_t description_show(struct mdev_type *mtype, |
116 | struct mdev_type_attribute *attr, | |
117 | char *buf) | |
118 | { | |
119 | return mtype->parent->mdev_driver->show_description(mtype, buf); | |
120 | } | |
121 | static MDEV_TYPE_ATTR_RO(description); | |
122 | ||
290aac5d JG |
123 | static struct attribute *mdev_types_core_attrs[] = { |
124 | &mdev_type_attr_create.attr, | |
125 | &mdev_type_attr_device_api.attr, | |
0bc79069 | 126 | &mdev_type_attr_name.attr, |
f2fbc72e | 127 | &mdev_type_attr_available_instances.attr, |
685a1537 | 128 | &mdev_type_attr_description.attr, |
290aac5d JG |
129 | NULL, |
130 | }; | |
131 | ||
685a1537 CH |
132 | static umode_t mdev_types_core_is_visible(struct kobject *kobj, |
133 | struct attribute *attr, int n) | |
134 | { | |
135 | if (attr == &mdev_type_attr_description.attr && | |
136 | !to_mdev_type(kobj)->parent->mdev_driver->show_description) | |
137 | return 0; | |
138 | return attr->mode; | |
139 | } | |
140 | ||
290aac5d JG |
141 | static struct attribute_group mdev_type_core_group = { |
142 | .attrs = mdev_types_core_attrs, | |
685a1537 | 143 | .is_visible = mdev_types_core_is_visible, |
290aac5d JG |
144 | }; |
145 | ||
146 | static const struct attribute_group *mdev_type_groups[] = { | |
147 | &mdev_type_core_group, | |
148 | NULL, | |
149 | }; | |
150 | ||
7b96953b KW |
151 | static void mdev_type_release(struct kobject *kobj) |
152 | { | |
153 | struct mdev_type *type = to_mdev_type(kobj); | |
154 | ||
155 | pr_debug("Releasing group %s\n", kobj->name); | |
9a302449 | 156 | /* Pairs with the get in add_mdev_supported_type() */ |
89345d51 | 157 | put_device(type->parent->dev); |
7b96953b KW |
158 | } |
159 | ||
160 | static struct kobj_type mdev_type_ktype = { | |
290aac5d JG |
161 | .sysfs_ops = &mdev_type_sysfs_ops, |
162 | .release = mdev_type_release, | |
163 | .default_groups = mdev_type_groups, | |
7b96953b KW |
164 | }; |
165 | ||
da44c340 | 166 | static int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type) |
7b96953b | 167 | { |
7b96953b KW |
168 | int ret; |
169 | ||
7b96953b | 170 | type->kobj.kset = parent->mdev_types_kset; |
b5a1f892 | 171 | type->parent = parent; |
9a302449 | 172 | /* Pairs with the put in mdev_type_release() */ |
89345d51 | 173 | get_device(parent->dev); |
7b96953b KW |
174 | |
175 | ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, | |
176 | "%s-%s", dev_driver_string(parent->dev), | |
da44c340 | 177 | type->sysfs_name); |
7b96953b | 178 | if (ret) { |
aa8ba13c | 179 | kobject_put(&type->kobj); |
da44c340 | 180 | return ret; |
7b96953b KW |
181 | } |
182 | ||
7b96953b KW |
183 | type->devices_kobj = kobject_create_and_add("devices", &type->kobj); |
184 | if (!type->devices_kobj) { | |
185 | ret = -ENOMEM; | |
186 | goto attr_devices_failed; | |
187 | } | |
188 | ||
da44c340 | 189 | return 0; |
7b96953b | 190 | |
7b96953b | 191 | attr_devices_failed: |
7b96953b KW |
192 | kobject_del(&type->kobj); |
193 | kobject_put(&type->kobj); | |
da44c340 | 194 | return ret; |
7b96953b KW |
195 | } |
196 | ||
da44c340 | 197 | static void mdev_type_remove(struct mdev_type *type) |
7b96953b | 198 | { |
7b96953b | 199 | kobject_put(type->devices_kobj); |
7b96953b KW |
200 | kobject_del(&type->kobj); |
201 | kobject_put(&type->kobj); | |
202 | } | |
203 | ||
7b96953b | 204 | /* mdev sysfs functions */ |
42930553 | 205 | void parent_remove_sysfs_files(struct mdev_parent *parent) |
7b96953b | 206 | { |
da44c340 | 207 | int i; |
7b96953b | 208 | |
da44c340 CH |
209 | for (i = 0; i < parent->nr_types; i++) |
210 | mdev_type_remove(parent->types[i]); | |
7b96953b KW |
211 | kset_unregister(parent->mdev_types_kset); |
212 | } | |
213 | ||
42930553 | 214 | int parent_create_sysfs_files(struct mdev_parent *parent) |
7b96953b | 215 | { |
da44c340 | 216 | int ret, i; |
7b96953b KW |
217 | |
218 | parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", | |
219 | NULL, &parent->dev->kobj); | |
7b96953b KW |
220 | if (!parent->mdev_types_kset) |
221 | return -ENOMEM; | |
222 | ||
da44c340 CH |
223 | for (i = 0; i < parent->nr_types; i++) { |
224 | ret = mdev_type_add(parent, parent->types[i]); | |
225 | if (ret) | |
226 | goto out_err; | |
227 | } | |
e6486939 | 228 | return 0; |
7b96953b | 229 | |
da44c340 CH |
230 | out_err: |
231 | while (--i >= 0) | |
232 | mdev_type_remove(parent->types[i]); | |
233 | return 0; | |
7b96953b KW |
234 | } |
235 | ||
236 | static ssize_t remove_store(struct device *dev, struct device_attribute *attr, | |
237 | const char *buf, size_t count) | |
238 | { | |
2a3d15f2 | 239 | struct mdev_device *mdev = to_mdev_device(dev); |
7b96953b KW |
240 | unsigned long val; |
241 | ||
242 | if (kstrtoul(buf, 0, &val) < 0) | |
243 | return -EINVAL; | |
244 | ||
245 | if (val && device_remove_file_self(dev, attr)) { | |
246 | int ret; | |
247 | ||
2a3d15f2 | 248 | ret = mdev_device_remove(mdev); |
26c9e398 | 249 | if (ret) |
7b96953b | 250 | return ret; |
7b96953b KW |
251 | } |
252 | ||
253 | return count; | |
254 | } | |
255 | ||
256 | static DEVICE_ATTR_WO(remove); | |
257 | ||
2aa72ec9 | 258 | static struct attribute *mdev_device_attrs[] = { |
7b96953b KW |
259 | &dev_attr_remove.attr, |
260 | NULL, | |
261 | }; | |
262 | ||
2aa72ec9 JG |
263 | static const struct attribute_group mdev_device_group = { |
264 | .attrs = mdev_device_attrs, | |
265 | }; | |
266 | ||
267 | const struct attribute_group *mdev_device_groups[] = { | |
268 | &mdev_device_group, | |
269 | NULL | |
270 | }; | |
271 | ||
417fd5bf | 272 | int mdev_create_sysfs_files(struct mdev_device *mdev) |
7b96953b | 273 | { |
417fd5bf | 274 | struct mdev_type *type = mdev->type; |
2a3d15f2 | 275 | struct kobject *kobj = &mdev->dev.kobj; |
7b96953b KW |
276 | int ret; |
277 | ||
2a3d15f2 | 278 | ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev)); |
7b96953b | 279 | if (ret) |
6a62c1df | 280 | return ret; |
7b96953b | 281 | |
2a3d15f2 | 282 | ret = sysfs_create_link(kobj, &type->kobj, "mdev_type"); |
7b96953b KW |
283 | if (ret) |
284 | goto type_link_failed; | |
7b96953b KW |
285 | return ret; |
286 | ||
287 | type_link_failed: | |
417fd5bf | 288 | sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); |
7b96953b KW |
289 | return ret; |
290 | } | |
291 | ||
417fd5bf | 292 | void mdev_remove_sysfs_files(struct mdev_device *mdev) |
7b96953b | 293 | { |
2a3d15f2 JG |
294 | struct kobject *kobj = &mdev->dev.kobj; |
295 | ||
2a3d15f2 | 296 | sysfs_remove_link(kobj, "mdev_type"); |
417fd5bf | 297 | sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev)); |
7b96953b | 298 | } |