ACPI: APEI: Fix integer overflow in ghes_estatus_pool_init()
[sfrench/cifs-2.6.git] / drivers / vfio / mdev / mdev_sysfs.c
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/device.h>
13 #include <linux/slab.h>
14 #include <linux/uuid.h>
15 #include <linux/mdev.h>
16
17 #include "mdev_private.h"
18
19 /* Static functions */
20
21 static ssize_t mdev_type_attr_show(struct kobject *kobj,
22                                      struct attribute *__attr, char *buf)
23 {
24         struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
25         struct mdev_type *type = to_mdev_type(kobj);
26         ssize_t ret = -EIO;
27
28         if (attr->show)
29                 ret = attr->show(type, attr, buf);
30         return ret;
31 }
32
33 static ssize_t mdev_type_attr_store(struct kobject *kobj,
34                                       struct attribute *__attr,
35                                       const char *buf, size_t count)
36 {
37         struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
38         struct mdev_type *type = to_mdev_type(kobj);
39         ssize_t ret = -EIO;
40
41         if (attr->store)
42                 ret = attr->store(type, attr, buf, count);
43         return ret;
44 }
45
46 static const struct sysfs_ops mdev_type_sysfs_ops = {
47         .show = mdev_type_attr_show,
48         .store = mdev_type_attr_store,
49 };
50
51 static ssize_t create_store(struct mdev_type *mtype,
52                             struct mdev_type_attribute *attr, const char *buf,
53                             size_t count)
54 {
55         char *str;
56         guid_t uuid;
57         int ret;
58
59         if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))
60                 return -EINVAL;
61
62         str = kstrndup(buf, count, GFP_KERNEL);
63         if (!str)
64                 return -ENOMEM;
65
66         ret = guid_parse(str, &uuid);
67         kfree(str);
68         if (ret)
69                 return ret;
70
71         ret = mdev_device_create(mtype, &uuid);
72         if (ret)
73                 return ret;
74
75         return count;
76 }
77
78 static MDEV_TYPE_ATTR_WO(create);
79
80 static void mdev_type_release(struct kobject *kobj)
81 {
82         struct mdev_type *type = to_mdev_type(kobj);
83
84         pr_debug("Releasing group %s\n", kobj->name);
85         /* Pairs with the get in add_mdev_supported_type() */
86         mdev_put_parent(type->parent);
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                                                  unsigned int type_group_id)
97 {
98         struct mdev_type *type;
99         struct attribute_group *group =
100                 parent->mdev_driver->supported_type_groups[type_group_id];
101         int ret;
102
103         if (!group->name) {
104                 pr_err("%s: Type name empty!\n", __func__);
105                 return ERR_PTR(-EINVAL);
106         }
107
108         type = kzalloc(sizeof(*type), GFP_KERNEL);
109         if (!type)
110                 return ERR_PTR(-ENOMEM);
111
112         type->kobj.kset = parent->mdev_types_kset;
113         type->parent = parent;
114         /* Pairs with the put in mdev_type_release() */
115         mdev_get_parent(parent);
116         type->type_group_id = type_group_id;
117
118         ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,
119                                    "%s-%s", dev_driver_string(parent->dev),
120                                    group->name);
121         if (ret) {
122                 kobject_put(&type->kobj);
123                 return ERR_PTR(ret);
124         }
125
126         ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr);
127         if (ret)
128                 goto attr_create_failed;
129
130         type->devices_kobj = kobject_create_and_add("devices", &type->kobj);
131         if (!type->devices_kobj) {
132                 ret = -ENOMEM;
133                 goto attr_devices_failed;
134         }
135
136         ret = sysfs_create_files(&type->kobj,
137                                  (const struct attribute **)group->attrs);
138         if (ret) {
139                 ret = -ENOMEM;
140                 goto attrs_failed;
141         }
142         return type;
143
144 attrs_failed:
145         kobject_put(type->devices_kobj);
146 attr_devices_failed:
147         sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
148 attr_create_failed:
149         kobject_del(&type->kobj);
150         kobject_put(&type->kobj);
151         return ERR_PTR(ret);
152 }
153
154 static void remove_mdev_supported_type(struct mdev_type *type)
155 {
156         struct attribute_group *group =
157                 type->parent->mdev_driver->supported_type_groups[type->type_group_id];
158
159         sysfs_remove_files(&type->kobj,
160                            (const struct attribute **)group->attrs);
161         kobject_put(type->devices_kobj);
162         sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr);
163         kobject_del(&type->kobj);
164         kobject_put(&type->kobj);
165 }
166
167 static int add_mdev_supported_type_groups(struct mdev_parent *parent)
168 {
169         int i;
170
171         for (i = 0; parent->mdev_driver->supported_type_groups[i]; i++) {
172                 struct mdev_type *type;
173
174                 type = add_mdev_supported_type(parent, i);
175                 if (IS_ERR(type)) {
176                         struct mdev_type *ltype, *tmp;
177
178                         list_for_each_entry_safe(ltype, tmp, &parent->type_list,
179                                                   next) {
180                                 list_del(&ltype->next);
181                                 remove_mdev_supported_type(ltype);
182                         }
183                         return PTR_ERR(type);
184                 }
185                 list_add(&type->next, &parent->type_list);
186         }
187         return 0;
188 }
189
190 /* mdev sysfs functions */
191 void parent_remove_sysfs_files(struct mdev_parent *parent)
192 {
193         struct mdev_type *type, *tmp;
194
195         list_for_each_entry_safe(type, tmp, &parent->type_list, next) {
196                 list_del(&type->next);
197                 remove_mdev_supported_type(type);
198         }
199
200         kset_unregister(parent->mdev_types_kset);
201 }
202
203 int parent_create_sysfs_files(struct mdev_parent *parent)
204 {
205         int ret;
206
207         parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",
208                                                NULL, &parent->dev->kobj);
209
210         if (!parent->mdev_types_kset)
211                 return -ENOMEM;
212
213         INIT_LIST_HEAD(&parent->type_list);
214
215         ret = add_mdev_supported_type_groups(parent);
216         if (ret)
217                 goto create_err;
218         return 0;
219
220 create_err:
221         kset_unregister(parent->mdev_types_kset);
222         return ret;
223 }
224
225 static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
226                             const char *buf, size_t count)
227 {
228         struct mdev_device *mdev = to_mdev_device(dev);
229         unsigned long val;
230
231         if (kstrtoul(buf, 0, &val) < 0)
232                 return -EINVAL;
233
234         if (val && device_remove_file_self(dev, attr)) {
235                 int ret;
236
237                 ret = mdev_device_remove(mdev);
238                 if (ret)
239                         return ret;
240         }
241
242         return count;
243 }
244
245 static DEVICE_ATTR_WO(remove);
246
247 static struct attribute *mdev_device_attrs[] = {
248         &dev_attr_remove.attr,
249         NULL,
250 };
251
252 static const struct attribute_group mdev_device_group = {
253         .attrs = mdev_device_attrs,
254 };
255
256 const struct attribute_group *mdev_device_groups[] = {
257         &mdev_device_group,
258         NULL
259 };
260
261 int mdev_create_sysfs_files(struct mdev_device *mdev)
262 {
263         struct mdev_type *type = mdev->type;
264         struct kobject *kobj = &mdev->dev.kobj;
265         int ret;
266
267         ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev));
268         if (ret)
269                 return ret;
270
271         ret = sysfs_create_link(kobj, &type->kobj, "mdev_type");
272         if (ret)
273                 goto type_link_failed;
274         return ret;
275
276 type_link_failed:
277         sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
278         return ret;
279 }
280
281 void mdev_remove_sysfs_files(struct mdev_device *mdev)
282 {
283         struct kobject *kobj = &mdev->dev.kobj;
284
285         sysfs_remove_link(kobj, "mdev_type");
286         sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
287 }