162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * File attributes for Mediated devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
662306a36Sopenharmony_ci *     Author: Neo Jia <cjia@nvidia.com>
762306a36Sopenharmony_ci *             Kirti Wankhede <kwankhede@nvidia.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/sysfs.h>
1162306a36Sopenharmony_ci#include <linux/ctype.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/mdev.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "mdev_private.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct mdev_type_attribute {
1862306a36Sopenharmony_ci	struct attribute attr;
1962306a36Sopenharmony_ci	ssize_t (*show)(struct mdev_type *mtype,
2062306a36Sopenharmony_ci			struct mdev_type_attribute *attr, char *buf);
2162306a36Sopenharmony_ci	ssize_t (*store)(struct mdev_type *mtype,
2262306a36Sopenharmony_ci			 struct mdev_type_attribute *attr, const char *buf,
2362306a36Sopenharmony_ci			 size_t count);
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define MDEV_TYPE_ATTR_RO(_name) \
2762306a36Sopenharmony_ci	struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_RO(_name)
2862306a36Sopenharmony_ci#define MDEV_TYPE_ATTR_WO(_name) \
2962306a36Sopenharmony_ci	struct mdev_type_attribute mdev_type_attr_##_name = __ATTR_WO(_name)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic ssize_t mdev_type_attr_show(struct kobject *kobj,
3262306a36Sopenharmony_ci				     struct attribute *__attr, char *buf)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
3562306a36Sopenharmony_ci	struct mdev_type *type = to_mdev_type(kobj);
3662306a36Sopenharmony_ci	ssize_t ret = -EIO;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (attr->show)
3962306a36Sopenharmony_ci		ret = attr->show(type, attr, buf);
4062306a36Sopenharmony_ci	return ret;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic ssize_t mdev_type_attr_store(struct kobject *kobj,
4462306a36Sopenharmony_ci				      struct attribute *__attr,
4562306a36Sopenharmony_ci				      const char *buf, size_t count)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct mdev_type_attribute *attr = to_mdev_type_attr(__attr);
4862306a36Sopenharmony_ci	struct mdev_type *type = to_mdev_type(kobj);
4962306a36Sopenharmony_ci	ssize_t ret = -EIO;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (attr->store)
5262306a36Sopenharmony_ci		ret = attr->store(type, attr, buf, count);
5362306a36Sopenharmony_ci	return ret;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct sysfs_ops mdev_type_sysfs_ops = {
5762306a36Sopenharmony_ci	.show = mdev_type_attr_show,
5862306a36Sopenharmony_ci	.store = mdev_type_attr_store,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic ssize_t create_store(struct mdev_type *mtype,
6262306a36Sopenharmony_ci			    struct mdev_type_attribute *attr, const char *buf,
6362306a36Sopenharmony_ci			    size_t count)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	char *str;
6662306a36Sopenharmony_ci	guid_t uuid;
6762306a36Sopenharmony_ci	int ret;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if ((count < UUID_STRING_LEN) || (count > UUID_STRING_LEN + 1))
7062306a36Sopenharmony_ci		return -EINVAL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	str = kstrndup(buf, count, GFP_KERNEL);
7362306a36Sopenharmony_ci	if (!str)
7462306a36Sopenharmony_ci		return -ENOMEM;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ret = guid_parse(str, &uuid);
7762306a36Sopenharmony_ci	kfree(str);
7862306a36Sopenharmony_ci	if (ret)
7962306a36Sopenharmony_ci		return ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ret = mdev_device_create(mtype, &uuid);
8262306a36Sopenharmony_ci	if (ret)
8362306a36Sopenharmony_ci		return ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return count;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_cistatic MDEV_TYPE_ATTR_WO(create);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic ssize_t device_api_show(struct mdev_type *mtype,
9062306a36Sopenharmony_ci			       struct mdev_type_attribute *attr, char *buf)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", mtype->parent->mdev_driver->device_api);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(device_api);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic ssize_t name_show(struct mdev_type *mtype,
9762306a36Sopenharmony_ci			 struct mdev_type_attribute *attr, char *buf)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n",
10062306a36Sopenharmony_ci		mtype->pretty_name ? mtype->pretty_name : mtype->sysfs_name);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(name);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic ssize_t available_instances_show(struct mdev_type *mtype,
10662306a36Sopenharmony_ci					struct mdev_type_attribute *attr,
10762306a36Sopenharmony_ci					char *buf)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct mdev_driver *drv = mtype->parent->mdev_driver;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (drv->get_available)
11262306a36Sopenharmony_ci		return sysfs_emit(buf, "%u\n", drv->get_available(mtype));
11362306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n",
11462306a36Sopenharmony_ci			  atomic_read(&mtype->parent->available_instances));
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(available_instances);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic ssize_t description_show(struct mdev_type *mtype,
11962306a36Sopenharmony_ci				struct mdev_type_attribute *attr,
12062306a36Sopenharmony_ci				char *buf)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	return mtype->parent->mdev_driver->show_description(mtype, buf);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_cistatic MDEV_TYPE_ATTR_RO(description);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct attribute *mdev_types_core_attrs[] = {
12762306a36Sopenharmony_ci	&mdev_type_attr_create.attr,
12862306a36Sopenharmony_ci	&mdev_type_attr_device_api.attr,
12962306a36Sopenharmony_ci	&mdev_type_attr_name.attr,
13062306a36Sopenharmony_ci	&mdev_type_attr_available_instances.attr,
13162306a36Sopenharmony_ci	&mdev_type_attr_description.attr,
13262306a36Sopenharmony_ci	NULL,
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic umode_t mdev_types_core_is_visible(struct kobject *kobj,
13662306a36Sopenharmony_ci					  struct attribute *attr, int n)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	if (attr == &mdev_type_attr_description.attr &&
13962306a36Sopenharmony_ci	    !to_mdev_type(kobj)->parent->mdev_driver->show_description)
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci	return attr->mode;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic struct attribute_group mdev_type_core_group = {
14562306a36Sopenharmony_ci	.attrs = mdev_types_core_attrs,
14662306a36Sopenharmony_ci	.is_visible = mdev_types_core_is_visible,
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic const struct attribute_group *mdev_type_groups[] = {
15062306a36Sopenharmony_ci	&mdev_type_core_group,
15162306a36Sopenharmony_ci	NULL,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void mdev_type_release(struct kobject *kobj)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct mdev_type *type = to_mdev_type(kobj);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	pr_debug("Releasing group %s\n", kobj->name);
15962306a36Sopenharmony_ci	/* Pairs with the get in add_mdev_supported_type() */
16062306a36Sopenharmony_ci	put_device(type->parent->dev);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic struct kobj_type mdev_type_ktype = {
16462306a36Sopenharmony_ci	.sysfs_ops	= &mdev_type_sysfs_ops,
16562306a36Sopenharmony_ci	.release	= mdev_type_release,
16662306a36Sopenharmony_ci	.default_groups	= mdev_type_groups,
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int mdev_type_add(struct mdev_parent *parent, struct mdev_type *type)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	type->kobj.kset = parent->mdev_types_kset;
17462306a36Sopenharmony_ci	type->parent = parent;
17562306a36Sopenharmony_ci	/* Pairs with the put in mdev_type_release() */
17662306a36Sopenharmony_ci	get_device(parent->dev);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = kobject_init_and_add(&type->kobj, &mdev_type_ktype, NULL,
17962306a36Sopenharmony_ci				   "%s-%s", dev_driver_string(parent->dev),
18062306a36Sopenharmony_ci				   type->sysfs_name);
18162306a36Sopenharmony_ci	if (ret) {
18262306a36Sopenharmony_ci		kobject_put(&type->kobj);
18362306a36Sopenharmony_ci		return ret;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	type->devices_kobj = kobject_create_and_add("devices", &type->kobj);
18762306a36Sopenharmony_ci	if (!type->devices_kobj) {
18862306a36Sopenharmony_ci		ret = -ENOMEM;
18962306a36Sopenharmony_ci		goto attr_devices_failed;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciattr_devices_failed:
19562306a36Sopenharmony_ci	kobject_del(&type->kobj);
19662306a36Sopenharmony_ci	kobject_put(&type->kobj);
19762306a36Sopenharmony_ci	return ret;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void mdev_type_remove(struct mdev_type *type)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	kobject_put(type->devices_kobj);
20362306a36Sopenharmony_ci	kobject_del(&type->kobj);
20462306a36Sopenharmony_ci	kobject_put(&type->kobj);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/* mdev sysfs functions */
20862306a36Sopenharmony_civoid parent_remove_sysfs_files(struct mdev_parent *parent)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int i;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	for (i = 0; i < parent->nr_types; i++)
21362306a36Sopenharmony_ci		mdev_type_remove(parent->types[i]);
21462306a36Sopenharmony_ci	kset_unregister(parent->mdev_types_kset);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ciint parent_create_sysfs_files(struct mdev_parent *parent)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	int ret, i;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	parent->mdev_types_kset = kset_create_and_add("mdev_supported_types",
22262306a36Sopenharmony_ci					       NULL, &parent->dev->kobj);
22362306a36Sopenharmony_ci	if (!parent->mdev_types_kset)
22462306a36Sopenharmony_ci		return -ENOMEM;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (i = 0; i < parent->nr_types; i++) {
22762306a36Sopenharmony_ci		ret = mdev_type_add(parent, parent->types[i]);
22862306a36Sopenharmony_ci		if (ret)
22962306a36Sopenharmony_ci			goto out_err;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciout_err:
23462306a36Sopenharmony_ci	while (--i >= 0)
23562306a36Sopenharmony_ci		mdev_type_remove(parent->types[i]);
23662306a36Sopenharmony_ci	kset_unregister(parent->mdev_types_kset);
23762306a36Sopenharmony_ci	return ret;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic ssize_t remove_store(struct device *dev, struct device_attribute *attr,
24162306a36Sopenharmony_ci			    const char *buf, size_t count)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct mdev_device *mdev = to_mdev_device(dev);
24462306a36Sopenharmony_ci	unsigned long val;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (kstrtoul(buf, 0, &val) < 0)
24762306a36Sopenharmony_ci		return -EINVAL;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (val && device_remove_file_self(dev, attr)) {
25062306a36Sopenharmony_ci		int ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		ret = mdev_device_remove(mdev);
25362306a36Sopenharmony_ci		if (ret)
25462306a36Sopenharmony_ci			return ret;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return count;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(remove);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic struct attribute *mdev_device_attrs[] = {
26362306a36Sopenharmony_ci	&dev_attr_remove.attr,
26462306a36Sopenharmony_ci	NULL,
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const struct attribute_group mdev_device_group = {
26862306a36Sopenharmony_ci	.attrs = mdev_device_attrs,
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ciconst struct attribute_group *mdev_device_groups[] = {
27262306a36Sopenharmony_ci	&mdev_device_group,
27362306a36Sopenharmony_ci	NULL
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ciint mdev_create_sysfs_files(struct mdev_device *mdev)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct mdev_type *type = mdev->type;
27962306a36Sopenharmony_ci	struct kobject *kobj = &mdev->dev.kobj;
28062306a36Sopenharmony_ci	int ret;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ret = sysfs_create_link(type->devices_kobj, kobj, dev_name(&mdev->dev));
28362306a36Sopenharmony_ci	if (ret)
28462306a36Sopenharmony_ci		return ret;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ret = sysfs_create_link(kobj, &type->kobj, "mdev_type");
28762306a36Sopenharmony_ci	if (ret)
28862306a36Sopenharmony_ci		goto type_link_failed;
28962306a36Sopenharmony_ci	return ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_citype_link_failed:
29262306a36Sopenharmony_ci	sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
29362306a36Sopenharmony_ci	return ret;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_civoid mdev_remove_sysfs_files(struct mdev_device *mdev)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct kobject *kobj = &mdev->dev.kobj;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	sysfs_remove_link(kobj, "mdev_type");
30162306a36Sopenharmony_ci	sysfs_remove_link(mdev->type->devices_kobj, dev_name(&mdev->dev));
30262306a36Sopenharmony_ci}
303