301 lines · 7.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * File attributes for Mediated devices * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. *     Author: Neo Jia <cjia@nvidia.com> *             Kirti Wankhede <kwankhede@nvidia.com> */ #include <linux/sysfs.h>#include <linux/ctype.h>#include <linux/slab.h>#include <linux/mdev.h> #include "mdev_private.h" struct mdev_type_attribute {	struct attribute attr;	ssize_t (*show)(struct mdev_type *mtype,			struct mdev_type_attribute *attr, char *buf);	ssize_t (*store)(struct mdev_type *mtype,			 struct mdev_type_attribute *attr, const char *buf,			 size_t count);}; #define MDEV_TYPE_ATTR_RO(_name) \	struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RO(_name)#define MDEV_TYPE_ATTR_WO(_name) \	struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_WO(_name) static ssize_t mdev_type_attr_show(struct kobject *kobj,				     struct attribute *__attr, char *buf){	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);	struct mdev_type *type = to_mdev_type(kobj);	ssize_t ret = -EIO; 	if (attr->show)		ret = attr->show(type, attr, buf);	return ret;} static ssize_t mdev_type_attr_store(struct kobject *kobj,				      struct attribute *__attr,				      const char *buf, size_t count){	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);	struct mdev_type *type = to_mdev_type(kobj);	ssize_t ret = -EIO; 	if (attr->store)		ret = attr->store(type, attr, buf, count);	return ret;} static const struct sysfs_ops mdev_type_sysfs_ops = {	.show = mdev_type_attr_show,	.store = mdev_type_attr_store,}; static ssize_t create_store(struct mdev_type *mtype,			    struct mdev_type_attribute *attr, const char *buf,			    size_t count){	char *str;	guid_t uuid;	int ret; 	if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))		return -EINVAL; 	str = kstrndup(buf, count, GFP_KERNEL);	if (!str)		return -ENOMEM; 	ret = guid_parse(str, &uuid);	kfree(str);	if (ret)		return ret; 	ret = mdev_device_create(mtype, &uuid);	if (ret)		return ret; 	return count;}static MDEV_TYPE_ATTR_WO(create); static ssize_t device_api_show(struct mdev_type *mtype,			       struct mdev_type_attribute *attr, char *buf){	return sysfs_emit(buf, "%s\n", mtype->parent->mdev_driver->device_api);}static MDEV_TYPE_ATTR_RO(device_api); static ssize_t name_show(struct mdev_type *mtype,			 struct mdev_type_attribute *attr, char *buf){	return sprintf(buf, "%s\n",		mtype->pretty_name ? mtype->pretty_name : mtype->sysfs_name);} static MDEV_TYPE_ATTR_RO(name); static ssize_t available_instances_show(struct mdev_type *mtype,					struct mdev_type_attribute *attr,					char *buf){	struct mdev_driver *drv = mtype->parent->mdev_driver; 	if (drv->get_available)		return sysfs_emit(buf, "%u\n", drv->get_available(mtype));	return sysfs_emit(buf, "%u\n",			  atomic_read(&mtype->parent->available_instances));}static MDEV_TYPE_ATTR_RO(available_instances); static ssize_t description_show(struct mdev_type *mtype,				struct mdev_type_attribute *attr,				char *buf){	return mtype->parent->mdev_driver->show_description(mtype, buf);}static MDEV_TYPE_ATTR_RO(description); static struct attribute *mdev_types_core_attrs[] = {	&mdev_type_attr_create.attr,	&mdev_type_attr_device_api.attr,	&mdev_type_attr_name.attr,	&mdev_type_attr_available_instances.attr,	&mdev_type_attr_description.attr,	NULL,}; static umode_t mdev_types_core_is_visible(struct kobject *kobj,					  struct attribute *attr, int n){	if (attr == &mdev_type_attr_description.attr &&	    !to_mdev_type(kobj)->parent->mdev_driver->show_description)		return 0;	return attr->mode;} static struct attribute_group mdev_type_core_group = {	.attrs = mdev_types_core_attrs,	.is_visible = mdev_types_core_is_visible,}; static const struct attribute_group *mdev_type_groups[] = {	&mdev_type_core_group,	NULL,}; static void mdev_type_release(struct kobject *kobj){	struct mdev_type *type = to_mdev_type(kobj); 	pr_debug("Releasing group %s\n", kobj->name);	/* Pairs with the get in add_mdev_supported_type() */	put_device(type->parent->dev);} static struct kobj_type mdev_type_ktype = {	.sysfs_ops	= &mdev_type_sysfs_ops,	.release	= mdev_type_release,	.default_groups	= mdev_type_groups,}; static int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type){	int ret; 	type->kobj.kset = parent->mdev_types_kset;	type->parent = parent;	/* Pairs with the put in mdev_type_release() */	get_device(parent->dev); 	ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,				   "%s-%s", dev_driver_string(parent->dev),				   type->sysfs_name);	if (ret) {		kobject_put(&type->kobj);		return ret;	} 	type->devices_kobj = kobject_create_and_add("devices", &type->kobj);	if (!type->devices_kobj) {		ret = -ENOMEM;		goto attr_devices_failed;	} 	return 0; attr_devices_failed:	kobject_del(&type->kobj);	kobject_put(&type->kobj);	return ret;} static void mdev_type_remove(struct mdev_type *type){	kobject_put(type->devices_kobj);	kobject_del(&type->kobj);	kobject_put(&type->kobj);} /* mdev sysfs functions */void parent_remove_sysfs_files(struct mdev_parent *parent){	int i; 	for (i = 0; i < parent->nr_types; i++)		mdev_type_remove(parent->types[i]);	kset_unregister(parent->mdev_types_kset);} int parent_create_sysfs_files(struct mdev_parent *parent){	int ret, i; 	parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",					       NULL, &parent->dev->kobj);	if (!parent->mdev_types_kset)		return -ENOMEM; 	for (i = 0; i < parent->nr_types; i++) {		ret = mdev_type_add(parent, parent->types[i]);		if (ret)			goto out_err;	}	return 0; out_err:	while (--i >= 0)		mdev_type_remove(parent->types[i]);	return 0;} static ssize_t remove_store(struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct mdev_device *mdev = to_mdev_device(dev);	unsigned long val; 	if (kstrtoul(buf, 0, &val) < 0)		return -EINVAL; 	if (val && device_remove_file_self(dev, attr)) {		int ret; 		ret = mdev_device_remove(mdev);		if (ret)			return ret;	} 	return count;} static DEVICE_ATTR_WO(remove); static struct attribute *mdev_device_attrs[] = {	&dev_attr_remove.attr,	NULL,}; static const struct attribute_group mdev_device_group = {	.attrs = mdev_device_attrs,}; const struct attribute_group *mdev_device_groups[] = {	&mdev_device_group,	NULL}; int mdev_create_sysfs_files(struct mdev_device *mdev){	struct mdev_type *type = mdev->type;	struct kobject *kobj = &mdev->dev.kobj;	int ret; 	ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev));	if (ret)		return ret; 	ret = sysfs_create_link(kobj, &type->kobj, "mdev_type");	if (ret)		goto type_link_failed;	return ret; type_link_failed:	sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));	return ret;} void mdev_remove_sysfs_files(struct mdev_device *mdev){	struct kobject *kobj = &mdev->dev.kobj; 	sysfs_remove_link(kobj, "mdev_type");	sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));}