Merge branch 'drm-armada-fixes' of git://git.armlinux.org.uk/~rmk/linux-arm into...
[sfrench/cifs-2.6.git] / drivers / vfio / mdev / mdev_sysfs.c
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         uuid_le 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 = uuid_le_to_bin(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 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_link(&dev->kobj, "mdev_type");
284         sysfs_remove_link(type->devices_kobj, dev_name(dev));
285         sysfs_remove_files(&dev->kobj, mdev_device_attrs);
286 }