1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27b96953bSKirti Wankhede /*
37b96953bSKirti Wankhede * File attributes for Mediated devices
47b96953bSKirti Wankhede *
57b96953bSKirti Wankhede * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
67b96953bSKirti Wankhede * Author: Neo Jia <cjia@nvidia.com>
77b96953bSKirti Wankhede * Kirti Wankhede <kwankhede@nvidia.com>
87b96953bSKirti Wankhede */
97b96953bSKirti Wankhede
107b96953bSKirti Wankhede #include <linux/sysfs.h>
117b96953bSKirti Wankhede #include <linux/ctype.h>
127b96953bSKirti Wankhede #include <linux/slab.h>
137b96953bSKirti Wankhede #include <linux/mdev.h>
147b96953bSKirti Wankhede
157b96953bSKirti Wankhede #include "mdev_private.h"
167b96953bSKirti Wankhede
17685a1537SChristoph Hellwig struct mdev_type_attribute {
18685a1537SChristoph Hellwig struct attribute attr;
19685a1537SChristoph Hellwig ssize_t (*show)(struct mdev_type *mtype,
20685a1537SChristoph Hellwig struct mdev_type_attribute *attr, char *buf);
21685a1537SChristoph Hellwig ssize_t (*store)(struct mdev_type *mtype,
22685a1537SChristoph Hellwig struct mdev_type_attribute *attr, const char *buf,
23685a1537SChristoph Hellwig size_t count);
24685a1537SChristoph Hellwig };
25685a1537SChristoph Hellwig
26685a1537SChristoph Hellwig #define MDEV_TYPE_ATTR_RO(_name) \
27685a1537SChristoph Hellwig struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RO(_name)
28685a1537SChristoph Hellwig #define MDEV_TYPE_ATTR_WO(_name) \
29685a1537SChristoph Hellwig struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_WO(_name)
307b96953bSKirti Wankhede
mdev_type_attr_show(struct kobject * kobj,struct attribute * __attr,char * buf)317b96953bSKirti Wankhede static ssize_t mdev_type_attr_show(struct kobject *kobj,
327b96953bSKirti Wankhede struct attribute *__attr, char *buf)
337b96953bSKirti Wankhede {
347b96953bSKirti Wankhede struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
357b96953bSKirti Wankhede struct mdev_type *type = to_mdev_type(kobj);
367b96953bSKirti Wankhede ssize_t ret = -EIO;
377b96953bSKirti Wankhede
387b96953bSKirti Wankhede if (attr->show)
399169cff1SJason Gunthorpe ret = attr->show(type, attr, buf);
407b96953bSKirti Wankhede return ret;
417b96953bSKirti Wankhede }
427b96953bSKirti Wankhede
mdev_type_attr_store(struct kobject * kobj,struct attribute * __attr,const char * buf,size_t count)437b96953bSKirti Wankhede static ssize_t mdev_type_attr_store(struct kobject *kobj,
447b96953bSKirti Wankhede struct attribute *__attr,
457b96953bSKirti Wankhede const char *buf, size_t count)
467b96953bSKirti Wankhede {
477b96953bSKirti Wankhede struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
487b96953bSKirti Wankhede struct mdev_type *type = to_mdev_type(kobj);
497b96953bSKirti Wankhede ssize_t ret = -EIO;
507b96953bSKirti Wankhede
517b96953bSKirti Wankhede if (attr->store)
529169cff1SJason Gunthorpe ret = attr->store(type, attr, buf, count);
537b96953bSKirti Wankhede return ret;
547b96953bSKirti Wankhede }
557b96953bSKirti Wankhede
567b96953bSKirti Wankhede static const struct sysfs_ops mdev_type_sysfs_ops = {
577b96953bSKirti Wankhede .show = mdev_type_attr_show,
587b96953bSKirti Wankhede .store = mdev_type_attr_store,
597b96953bSKirti Wankhede };
607b96953bSKirti Wankhede
create_store(struct mdev_type * mtype,struct mdev_type_attribute * attr,const char * buf,size_t count)619169cff1SJason Gunthorpe static ssize_t create_store(struct mdev_type *mtype,
629169cff1SJason Gunthorpe struct mdev_type_attribute *attr, const char *buf,
639169cff1SJason Gunthorpe size_t count)
647b96953bSKirti Wankhede {
657b96953bSKirti Wankhede char *str;
66278bca7fSAndy Shevchenko guid_t uuid;
677b96953bSKirti Wankhede int ret;
687b96953bSKirti Wankhede
697b96953bSKirti Wankhede if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))
707b96953bSKirti Wankhede return -EINVAL;
717b96953bSKirti Wankhede
727b96953bSKirti Wankhede str = kstrndup(buf, count, GFP_KERNEL);
737b96953bSKirti Wankhede if (!str)
747b96953bSKirti Wankhede return -ENOMEM;
757b96953bSKirti Wankhede
76278bca7fSAndy Shevchenko ret = guid_parse(str, &uuid);
777b96953bSKirti Wankhede kfree(str);
787b96953bSKirti Wankhede if (ret)
797b96953bSKirti Wankhede return ret;
807b96953bSKirti Wankhede
819169cff1SJason Gunthorpe ret = mdev_device_create(mtype, &uuid);
827b96953bSKirti Wankhede if (ret)
837b96953bSKirti Wankhede return ret;
847b96953bSKirti Wankhede
857b96953bSKirti Wankhede return count;
867b96953bSKirti Wankhede }
87e10b4f6cSBen Dooks (Codethink) static MDEV_TYPE_ATTR_WO(create);
887b96953bSKirti Wankhede
device_api_show(struct mdev_type * mtype,struct mdev_type_attribute * attr,char * buf)89290aac5dSJason Gunthorpe static ssize_t device_api_show(struct mdev_type *mtype,
90290aac5dSJason Gunthorpe struct mdev_type_attribute *attr, char *buf)
91290aac5dSJason Gunthorpe {
92290aac5dSJason Gunthorpe return sysfs_emit(buf, "%s\n", mtype->parent->mdev_driver->device_api);
93290aac5dSJason Gunthorpe }
94290aac5dSJason Gunthorpe static MDEV_TYPE_ATTR_RO(device_api);
95290aac5dSJason Gunthorpe
name_show(struct mdev_type * mtype,struct mdev_type_attribute * attr,char * buf)960bc79069SChristoph Hellwig static ssize_t name_show(struct mdev_type *mtype,
970bc79069SChristoph Hellwig struct mdev_type_attribute *attr, char *buf)
980bc79069SChristoph Hellwig {
99038ef0a4SBo Liu return sysfs_emit(buf, "%s\n",
1000bc79069SChristoph Hellwig mtype->pretty_name ? mtype->pretty_name : mtype->sysfs_name);
1010bc79069SChristoph Hellwig }
1020bc79069SChristoph Hellwig
1030bc79069SChristoph Hellwig static MDEV_TYPE_ATTR_RO(name);
1040bc79069SChristoph Hellwig
available_instances_show(struct mdev_type * mtype,struct mdev_type_attribute * attr,char * buf)105f2fbc72eSChristoph Hellwig static ssize_t available_instances_show(struct mdev_type *mtype,
106f2fbc72eSChristoph Hellwig struct mdev_type_attribute *attr,
107f2fbc72eSChristoph Hellwig char *buf)
108f2fbc72eSChristoph Hellwig {
109f2fbc72eSChristoph Hellwig struct mdev_driver *drv = mtype->parent->mdev_driver;
110f2fbc72eSChristoph Hellwig
1119c799c22SJason Gunthorpe if (drv->get_available)
112f2fbc72eSChristoph Hellwig return sysfs_emit(buf, "%u\n", drv->get_available(mtype));
1139c799c22SJason Gunthorpe return sysfs_emit(buf, "%u\n",
1149c799c22SJason Gunthorpe atomic_read(&mtype->parent->available_instances));
115f2fbc72eSChristoph Hellwig }
116f2fbc72eSChristoph Hellwig static MDEV_TYPE_ATTR_RO(available_instances);
117f2fbc72eSChristoph Hellwig
description_show(struct mdev_type * mtype,struct mdev_type_attribute * attr,char * buf)118685a1537SChristoph Hellwig static ssize_t description_show(struct mdev_type *mtype,
119685a1537SChristoph Hellwig struct mdev_type_attribute *attr,
120685a1537SChristoph Hellwig char *buf)
121685a1537SChristoph Hellwig {
122685a1537SChristoph Hellwig return mtype->parent->mdev_driver->show_description(mtype, buf);
123685a1537SChristoph Hellwig }
124685a1537SChristoph Hellwig static MDEV_TYPE_ATTR_RO(description);
125685a1537SChristoph Hellwig
126290aac5dSJason Gunthorpe static struct attribute *mdev_types_core_attrs[] = {
127290aac5dSJason Gunthorpe &mdev_type_attr_create.attr,
128290aac5dSJason Gunthorpe &mdev_type_attr_device_api.attr,
1290bc79069SChristoph Hellwig &mdev_type_attr_name.attr,
130f2fbc72eSChristoph Hellwig &mdev_type_attr_available_instances.attr,
131685a1537SChristoph Hellwig &mdev_type_attr_description.attr,
132290aac5dSJason Gunthorpe NULL,
133290aac5dSJason Gunthorpe };
134290aac5dSJason Gunthorpe
mdev_types_core_is_visible(struct kobject * kobj,struct attribute * attr,int n)135685a1537SChristoph Hellwig static umode_t mdev_types_core_is_visible(struct kobject *kobj,
136685a1537SChristoph Hellwig struct attribute *attr, int n)
137685a1537SChristoph Hellwig {
138685a1537SChristoph Hellwig if (attr == &mdev_type_attr_description.attr &&
139685a1537SChristoph Hellwig !to_mdev_type(kobj)->parent->mdev_driver->show_description)
140685a1537SChristoph Hellwig return 0;
141685a1537SChristoph Hellwig return attr->mode;
142685a1537SChristoph Hellwig }
143685a1537SChristoph Hellwig
144290aac5dSJason Gunthorpe static struct attribute_group mdev_type_core_group = {
145290aac5dSJason Gunthorpe .attrs = mdev_types_core_attrs,
146685a1537SChristoph Hellwig .is_visible = mdev_types_core_is_visible,
147290aac5dSJason Gunthorpe };
148290aac5dSJason Gunthorpe
149290aac5dSJason Gunthorpe static const struct attribute_group *mdev_type_groups[] = {
150290aac5dSJason Gunthorpe &mdev_type_core_group,
151290aac5dSJason Gunthorpe NULL,
152290aac5dSJason Gunthorpe };
153290aac5dSJason Gunthorpe
mdev_type_release(struct kobject * kobj)1547b96953bSKirti Wankhede static void mdev_type_release(struct kobject *kobj)
1557b96953bSKirti Wankhede {
1567b96953bSKirti Wankhede struct mdev_type *type = to_mdev_type(kobj);
1577b96953bSKirti Wankhede
1587b96953bSKirti Wankhede pr_debug("Releasing group %s\n", kobj->name);
1599a302449SJason Gunthorpe /* Pairs with the get in add_mdev_supported_type() */
16089345d51SChristoph Hellwig put_device(type->parent->dev);
1617b96953bSKirti Wankhede }
1627b96953bSKirti Wankhede
1637b96953bSKirti Wankhede static struct kobj_type mdev_type_ktype = {
1647b96953bSKirti Wankhede .sysfs_ops = &mdev_type_sysfs_ops,
1657b96953bSKirti Wankhede .release = mdev_type_release,
166290aac5dSJason Gunthorpe .default_groups = mdev_type_groups,
1677b96953bSKirti Wankhede };
1687b96953bSKirti Wankhede
mdev_type_add(struct mdev_parent * parent,struct mdev_type * type)169da44c340SChristoph Hellwig static int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type)
1707b96953bSKirti Wankhede {
1717b96953bSKirti Wankhede int ret;
1727b96953bSKirti Wankhede
1737b96953bSKirti Wankhede type->kobj.kset = parent->mdev_types_kset;
174b5a1f892SJason Gunthorpe type->parent = parent;
1759a302449SJason Gunthorpe /* Pairs with the put in mdev_type_release() */
17689345d51SChristoph Hellwig get_device(parent->dev);
1777b96953bSKirti Wankhede
1787b96953bSKirti Wankhede ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,
1797b96953bSKirti Wankhede "%s-%s", dev_driver_string(parent->dev),
180da44c340SChristoph Hellwig type->sysfs_name);
1817b96953bSKirti Wankhede if (ret) {
182aa8ba13cSQiushi Wu kobject_put(&type->kobj);
183da44c340SChristoph Hellwig return ret;
1847b96953bSKirti Wankhede }
1857b96953bSKirti Wankhede
1867b96953bSKirti Wankhede type->devices_kobj = kobject_create_and_add("devices", &type->kobj);
1877b96953bSKirti Wankhede if (!type->devices_kobj) {
1887b96953bSKirti Wankhede ret = -ENOMEM;
1897b96953bSKirti Wankhede goto attr_devices_failed;
1907b96953bSKirti Wankhede }
1917b96953bSKirti Wankhede
192da44c340SChristoph Hellwig return 0;
1937b96953bSKirti Wankhede
1947b96953bSKirti Wankhede attr_devices_failed:
1957b96953bSKirti Wankhede kobject_del(&type->kobj);
1967b96953bSKirti Wankhede kobject_put(&type->kobj);
197da44c340SChristoph Hellwig return ret;
1987b96953bSKirti Wankhede }
1997b96953bSKirti Wankhede
mdev_type_remove(struct mdev_type * type)200da44c340SChristoph Hellwig static void mdev_type_remove(struct mdev_type *type)
2017b96953bSKirti Wankhede {
2027b96953bSKirti Wankhede kobject_put(type->devices_kobj);
2037b96953bSKirti Wankhede kobject_del(&type->kobj);
2047b96953bSKirti Wankhede kobject_put(&type->kobj);
2057b96953bSKirti Wankhede }
2067b96953bSKirti Wankhede
2077b96953bSKirti Wankhede /* mdev sysfs functions */
parent_remove_sysfs_files(struct mdev_parent * parent)20842930553SAlex Williamson void parent_remove_sysfs_files(struct mdev_parent *parent)
2097b96953bSKirti Wankhede {
210da44c340SChristoph Hellwig int i;
2117b96953bSKirti Wankhede
212da44c340SChristoph Hellwig for (i = 0; i < parent->nr_types; i++)
213da44c340SChristoph Hellwig mdev_type_remove(parent->types[i]);
2147b96953bSKirti Wankhede kset_unregister(parent->mdev_types_kset);
2157b96953bSKirti Wankhede }
2167b96953bSKirti Wankhede
parent_create_sysfs_files(struct mdev_parent * parent)21742930553SAlex Williamson int parent_create_sysfs_files(struct mdev_parent *parent)
2187b96953bSKirti Wankhede {
219da44c340SChristoph Hellwig int ret, i;
2207b96953bSKirti Wankhede
2217b96953bSKirti Wankhede parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",
2227b96953bSKirti Wankhede NULL, &parent->dev->kobj);
2237b96953bSKirti Wankhede if (!parent->mdev_types_kset)
2247b96953bSKirti Wankhede return -ENOMEM;
2257b96953bSKirti Wankhede
226da44c340SChristoph Hellwig for (i = 0; i < parent->nr_types; i++) {
227da44c340SChristoph Hellwig ret = mdev_type_add(parent, parent->types[i]);
2287b96953bSKirti Wankhede if (ret)
229da44c340SChristoph Hellwig goto out_err;
230da44c340SChristoph Hellwig }
231e6486939SJason Gunthorpe return 0;
2327b96953bSKirti Wankhede
233da44c340SChristoph Hellwig out_err:
234da44c340SChristoph Hellwig while (--i >= 0)
235da44c340SChristoph Hellwig mdev_type_remove(parent->types[i]);
236*c777b11dSJinjie Ruan kset_unregister(parent->mdev_types_kset);
237*c777b11dSJinjie Ruan return ret;
2387b96953bSKirti Wankhede }
2397b96953bSKirti Wankhede
remove_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2407b96953bSKirti Wankhede static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
2417b96953bSKirti Wankhede const char *buf, size_t count)
2427b96953bSKirti Wankhede {
2432a3d15f2SJason Gunthorpe struct mdev_device *mdev = to_mdev_device(dev);
2447b96953bSKirti Wankhede unsigned long val;
2457b96953bSKirti Wankhede
2467b96953bSKirti Wankhede if (kstrtoul(buf, 0, &val) < 0)
2477b96953bSKirti Wankhede return -EINVAL;
2487b96953bSKirti Wankhede
2497b96953bSKirti Wankhede if (val && device_remove_file_self(dev, attr)) {
2507b96953bSKirti Wankhede int ret;
2517b96953bSKirti Wankhede
2522a3d15f2SJason Gunthorpe ret = mdev_device_remove(mdev);
25326c9e398SParav Pandit if (ret)
2547b96953bSKirti Wankhede return ret;
2557b96953bSKirti Wankhede }
2567b96953bSKirti Wankhede
2577b96953bSKirti Wankhede return count;
2587b96953bSKirti Wankhede }
2597b96953bSKirti Wankhede
2607b96953bSKirti Wankhede static DEVICE_ATTR_WO(remove);
2617b96953bSKirti Wankhede
2622aa72ec9SJason Gunthorpe static struct attribute *mdev_device_attrs[] = {
2637b96953bSKirti Wankhede &dev_attr_remove.attr,
2647b96953bSKirti Wankhede NULL,
2657b96953bSKirti Wankhede };
2667b96953bSKirti Wankhede
2672aa72ec9SJason Gunthorpe static const struct attribute_group mdev_device_group = {
2682aa72ec9SJason Gunthorpe .attrs = mdev_device_attrs,
2692aa72ec9SJason Gunthorpe };
2702aa72ec9SJason Gunthorpe
2712aa72ec9SJason Gunthorpe const struct attribute_group *mdev_device_groups[] = {
2722aa72ec9SJason Gunthorpe &mdev_device_group,
2732aa72ec9SJason Gunthorpe NULL
2742aa72ec9SJason Gunthorpe };
2752aa72ec9SJason Gunthorpe
mdev_create_sysfs_files(struct mdev_device * mdev)276417fd5bfSJason Gunthorpe int mdev_create_sysfs_files(struct mdev_device *mdev)
2777b96953bSKirti Wankhede {
278417fd5bfSJason Gunthorpe struct mdev_type *type = mdev->type;
2792a3d15f2SJason Gunthorpe struct kobject *kobj = &mdev->dev.kobj;
2807b96953bSKirti Wankhede int ret;
2817b96953bSKirti Wankhede
2822a3d15f2SJason Gunthorpe ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev));
2837b96953bSKirti Wankhede if (ret)
2846a62c1dfSAlex Williamson return ret;
2857b96953bSKirti Wankhede
2862a3d15f2SJason Gunthorpe ret = sysfs_create_link(kobj, &type->kobj, "mdev_type");
2877b96953bSKirti Wankhede if (ret)
2887b96953bSKirti Wankhede goto type_link_failed;
2897b96953bSKirti Wankhede return ret;
2907b96953bSKirti Wankhede
2917b96953bSKirti Wankhede type_link_failed:
292417fd5bfSJason Gunthorpe sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
2937b96953bSKirti Wankhede return ret;
2947b96953bSKirti Wankhede }
2957b96953bSKirti Wankhede
mdev_remove_sysfs_files(struct mdev_device * mdev)296417fd5bfSJason Gunthorpe void mdev_remove_sysfs_files(struct mdev_device *mdev)
2977b96953bSKirti Wankhede {
2982a3d15f2SJason Gunthorpe struct kobject *kobj = &mdev->dev.kobj;
2992a3d15f2SJason Gunthorpe
3002a3d15f2SJason Gunthorpe sysfs_remove_link(kobj, "mdev_type");
301417fd5bfSJason Gunthorpe sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
3027b96953bSKirti Wankhede }
303