18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * File attributes for Mediated devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Neo Jia <cjia@nvidia.com> 78c2ecf20Sopenharmony_ci * Kirti Wankhede <kwankhede@nvidia.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 118c2ecf20Sopenharmony_ci#include <linux/ctype.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/uuid.h> 158c2ecf20Sopenharmony_ci#include <linux/mdev.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "mdev_private.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Static functions */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic ssize_t mdev_type_attr_show(struct kobject *kobj, 228c2ecf20Sopenharmony_ci struct attribute *__attr, char *buf) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); 258c2ecf20Sopenharmony_ci struct mdev_type *type = to_mdev_type(kobj); 268c2ecf20Sopenharmony_ci ssize_t ret = -EIO; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (attr->show) 298c2ecf20Sopenharmony_ci ret = attr->show(kobj, type->parent->dev, buf); 308c2ecf20Sopenharmony_ci return ret; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t mdev_type_attr_store(struct kobject *kobj, 348c2ecf20Sopenharmony_ci struct attribute *__attr, 358c2ecf20Sopenharmony_ci const char *buf, size_t count) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct mdev_type_attribute *attr = to_mdev_type_attr(__attr); 388c2ecf20Sopenharmony_ci struct mdev_type *type = to_mdev_type(kobj); 398c2ecf20Sopenharmony_ci ssize_t ret = -EIO; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (attr->store) 428c2ecf20Sopenharmony_ci ret = attr->store(&type->kobj, type->parent->dev, buf, count); 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const struct sysfs_ops mdev_type_sysfs_ops = { 478c2ecf20Sopenharmony_ci .show = mdev_type_attr_show, 488c2ecf20Sopenharmony_ci .store = mdev_type_attr_store, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic ssize_t create_store(struct kobject *kobj, struct device *dev, 528c2ecf20Sopenharmony_ci const char *buf, size_t count) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci char *str; 558c2ecf20Sopenharmony_ci guid_t uuid; 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1)) 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci str = kstrndup(buf, count, GFP_KERNEL); 628c2ecf20Sopenharmony_ci if (!str) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = guid_parse(str, &uuid); 668c2ecf20Sopenharmony_ci kfree(str); 678c2ecf20Sopenharmony_ci if (ret) 688c2ecf20Sopenharmony_ci return ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = mdev_device_create(kobj, dev, &uuid); 718c2ecf20Sopenharmony_ci if (ret) 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return count; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic MDEV_TYPE_ATTR_WO(create); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void mdev_type_release(struct kobject *kobj) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct mdev_type *type = to_mdev_type(kobj); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci pr_debug("Releasing group %s\n", kobj->name); 848c2ecf20Sopenharmony_ci kfree(type); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic struct kobj_type mdev_type_ktype = { 888c2ecf20Sopenharmony_ci .sysfs_ops = &mdev_type_sysfs_ops, 898c2ecf20Sopenharmony_ci .release = mdev_type_release, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent, 938c2ecf20Sopenharmony_ci struct attribute_group *group) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct mdev_type *type; 968c2ecf20Sopenharmony_ci int ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!group->name) { 998c2ecf20Sopenharmony_ci pr_err("%s: Type name empty!\n", __func__); 1008c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci type = kzalloc(sizeof(*type), GFP_KERNEL); 1048c2ecf20Sopenharmony_ci if (!type) 1058c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci type->kobj.kset = parent->mdev_types_kset; 1088c2ecf20Sopenharmony_ci type->parent = parent; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL, 1118c2ecf20Sopenharmony_ci "%s-%s", dev_driver_string(parent->dev), 1128c2ecf20Sopenharmony_ci group->name); 1138c2ecf20Sopenharmony_ci if (ret) { 1148c2ecf20Sopenharmony_ci kobject_put(&type->kobj); 1158c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = sysfs_create_file(&type->kobj, &mdev_type_attr_create.attr); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci goto attr_create_failed; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci type->devices_kobj = kobject_create_and_add("devices", &type->kobj); 1238c2ecf20Sopenharmony_ci if (!type->devices_kobj) { 1248c2ecf20Sopenharmony_ci ret = -ENOMEM; 1258c2ecf20Sopenharmony_ci goto attr_devices_failed; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ret = sysfs_create_files(&type->kobj, 1298c2ecf20Sopenharmony_ci (const struct attribute **)group->attrs); 1308c2ecf20Sopenharmony_ci if (ret) { 1318c2ecf20Sopenharmony_ci ret = -ENOMEM; 1328c2ecf20Sopenharmony_ci goto attrs_failed; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci type->group = group; 1368c2ecf20Sopenharmony_ci return type; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciattrs_failed: 1398c2ecf20Sopenharmony_ci kobject_put(type->devices_kobj); 1408c2ecf20Sopenharmony_ciattr_devices_failed: 1418c2ecf20Sopenharmony_ci sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 1428c2ecf20Sopenharmony_ciattr_create_failed: 1438c2ecf20Sopenharmony_ci kobject_del(&type->kobj); 1448c2ecf20Sopenharmony_ci kobject_put(&type->kobj); 1458c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void remove_mdev_supported_type(struct mdev_type *type) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci sysfs_remove_files(&type->kobj, 1518c2ecf20Sopenharmony_ci (const struct attribute **)type->group->attrs); 1528c2ecf20Sopenharmony_ci kobject_put(type->devices_kobj); 1538c2ecf20Sopenharmony_ci sysfs_remove_file(&type->kobj, &mdev_type_attr_create.attr); 1548c2ecf20Sopenharmony_ci kobject_del(&type->kobj); 1558c2ecf20Sopenharmony_ci kobject_put(&type->kobj); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int add_mdev_supported_type_groups(struct mdev_parent *parent) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (i = 0; parent->ops->supported_type_groups[i]; i++) { 1638c2ecf20Sopenharmony_ci struct mdev_type *type; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci type = add_mdev_supported_type(parent, 1668c2ecf20Sopenharmony_ci parent->ops->supported_type_groups[i]); 1678c2ecf20Sopenharmony_ci if (IS_ERR(type)) { 1688c2ecf20Sopenharmony_ci struct mdev_type *ltype, *tmp; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci list_for_each_entry_safe(ltype, tmp, &parent->type_list, 1718c2ecf20Sopenharmony_ci next) { 1728c2ecf20Sopenharmony_ci list_del(<ype->next); 1738c2ecf20Sopenharmony_ci remove_mdev_supported_type(ltype); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci return PTR_ERR(type); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci list_add(&type->next, &parent->type_list); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* mdev sysfs functions */ 1838c2ecf20Sopenharmony_civoid parent_remove_sysfs_files(struct mdev_parent *parent) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct mdev_type *type, *tmp; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci list_for_each_entry_safe(type, tmp, &parent->type_list, next) { 1888c2ecf20Sopenharmony_ci list_del(&type->next); 1898c2ecf20Sopenharmony_ci remove_mdev_supported_type(type); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci sysfs_remove_groups(&parent->dev->kobj, parent->ops->dev_attr_groups); 1938c2ecf20Sopenharmony_ci kset_unregister(parent->mdev_types_kset); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ciint parent_create_sysfs_files(struct mdev_parent *parent) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci parent->mdev_types_kset = kset_create_and_add("mdev_supported_types", 2018c2ecf20Sopenharmony_ci NULL, &parent->dev->kobj); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!parent->mdev_types_kset) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&parent->type_list); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = sysfs_create_groups(&parent->dev->kobj, 2098c2ecf20Sopenharmony_ci parent->ops->dev_attr_groups); 2108c2ecf20Sopenharmony_ci if (ret) 2118c2ecf20Sopenharmony_ci goto create_err; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = add_mdev_supported_type_groups(parent); 2148c2ecf20Sopenharmony_ci if (ret) 2158c2ecf20Sopenharmony_ci sysfs_remove_groups(&parent->dev->kobj, 2168c2ecf20Sopenharmony_ci parent->ops->dev_attr_groups); 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cicreate_err: 2218c2ecf20Sopenharmony_ci kset_unregister(parent->mdev_types_kset); 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic ssize_t remove_store(struct device *dev, struct device_attribute *attr, 2268c2ecf20Sopenharmony_ci const char *buf, size_t count) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci unsigned long val; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (val && device_remove_file_self(dev, attr)) { 2348c2ecf20Sopenharmony_ci int ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = mdev_device_remove(dev); 2378c2ecf20Sopenharmony_ci if (ret) 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return count; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(remove); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic const struct attribute *mdev_device_attrs[] = { 2478c2ecf20Sopenharmony_ci &dev_attr_remove.attr, 2488c2ecf20Sopenharmony_ci NULL, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciint mdev_create_sysfs_files(struct device *dev, struct mdev_type *type) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = sysfs_create_link(type->devices_kobj, &dev->kobj, dev_name(dev)); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = sysfs_create_link(&dev->kobj, &type->kobj, "mdev_type"); 2608c2ecf20Sopenharmony_ci if (ret) 2618c2ecf20Sopenharmony_ci goto type_link_failed; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = sysfs_create_files(&dev->kobj, mdev_device_attrs); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci goto create_files_failed; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cicreate_files_failed: 2708c2ecf20Sopenharmony_ci sysfs_remove_link(&dev->kobj, "mdev_type"); 2718c2ecf20Sopenharmony_citype_link_failed: 2728c2ecf20Sopenharmony_ci sysfs_remove_link(type->devices_kobj, dev_name(dev)); 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_civoid mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci sysfs_remove_files(&dev->kobj, mdev_device_attrs); 2798c2ecf20Sopenharmony_ci sysfs_remove_link(&dev->kobj, "mdev_type"); 2808c2ecf20Sopenharmony_ci sysfs_remove_link(type->devices_kobj, dev_name(dev)); 2818c2ecf20Sopenharmony_ci} 282