1b903737bSHaren Myneni // SPDX-License-Identifier: GPL-2.0-or-later
2b903737bSHaren Myneni /*
3b903737bSHaren Myneni  * Copyright 2022-23 IBM Corp.
4b903737bSHaren Myneni  */
5b903737bSHaren Myneni 
6b903737bSHaren Myneni #define pr_fmt(fmt) "vas: " fmt
7b903737bSHaren Myneni 
8b903737bSHaren Myneni #include <linux/module.h>
9b903737bSHaren Myneni #include <linux/kernel.h>
10b903737bSHaren Myneni #include <linux/miscdevice.h>
11b903737bSHaren Myneni #include <linux/kobject.h>
12b903737bSHaren Myneni #include <linux/slab.h>
13b903737bSHaren Myneni #include <linux/mm.h>
14b903737bSHaren Myneni 
15b903737bSHaren Myneni #include "vas.h"
16b903737bSHaren Myneni 
17b903737bSHaren Myneni #ifdef CONFIG_SYSFS
18b903737bSHaren Myneni static struct kobject *pseries_vas_kobj;
19b903737bSHaren Myneni static struct kobject *gzip_caps_kobj;
20b903737bSHaren Myneni 
21b903737bSHaren Myneni struct vas_caps_entry {
22b903737bSHaren Myneni 	struct kobject kobj;
23b903737bSHaren Myneni 	struct vas_cop_feat_caps *caps;
24b903737bSHaren Myneni };
25b903737bSHaren Myneni 
26b903737bSHaren Myneni #define to_caps_entry(entry) container_of(entry, struct vas_caps_entry, kobj)
27b903737bSHaren Myneni 
2845f06eacSHaren Myneni /*
2945f06eacSHaren Myneni  * This function is used to get the notification from the drmgr when
3057831bfbSHaren Myneni  * QoS credits are changed.
3145f06eacSHaren Myneni  */
update_total_credits_store(struct vas_cop_feat_caps * caps,const char * buf,size_t count)3257831bfbSHaren Myneni static ssize_t update_total_credits_store(struct vas_cop_feat_caps *caps,
3345f06eacSHaren Myneni 						const char *buf, size_t count)
3445f06eacSHaren Myneni {
3545f06eacSHaren Myneni 	int err;
3645f06eacSHaren Myneni 	u16 creds;
3745f06eacSHaren Myneni 
3845f06eacSHaren Myneni 	err = kstrtou16(buf, 0, &creds);
3957831bfbSHaren Myneni 	/*
4057831bfbSHaren Myneni 	 * The user space interface from the management console
4157831bfbSHaren Myneni 	 * notifies OS with the new QoS credits and then the
4257831bfbSHaren Myneni 	 * hypervisor. So OS has to use this new credits value
4357831bfbSHaren Myneni 	 * and reconfigure VAS windows (close or reopen depends
4457831bfbSHaren Myneni 	 * on the credits available) instead of depending on VAS
4557831bfbSHaren Myneni 	 * QoS capabilities from the hypervisor.
4657831bfbSHaren Myneni 	 */
4745f06eacSHaren Myneni 	if (!err)
4857831bfbSHaren Myneni 		err = vas_reconfig_capabilties(caps->win_type, creds);
4945f06eacSHaren Myneni 
5045f06eacSHaren Myneni 	if (err)
5145f06eacSHaren Myneni 		return -EINVAL;
5245f06eacSHaren Myneni 
5357831bfbSHaren Myneni 	pr_info("Set QoS total credits %u\n", creds);
5457831bfbSHaren Myneni 
5545f06eacSHaren Myneni 	return count;
5645f06eacSHaren Myneni }
5745f06eacSHaren Myneni 
58b903737bSHaren Myneni #define sysfs_caps_entry_read(_name)					\
59b903737bSHaren Myneni static ssize_t _name##_show(struct vas_cop_feat_caps *caps, char *buf) 	\
60b903737bSHaren Myneni {									\
61b903737bSHaren Myneni 	return sprintf(buf, "%d\n", atomic_read(&caps->_name));	\
62b903737bSHaren Myneni }
63b903737bSHaren Myneni 
64b903737bSHaren Myneni struct vas_sysfs_entry {
65b903737bSHaren Myneni 	struct attribute attr;
66b903737bSHaren Myneni 	ssize_t (*show)(struct vas_cop_feat_caps *, char *);
67b903737bSHaren Myneni 	ssize_t (*store)(struct vas_cop_feat_caps *, const char *, size_t);
68b903737bSHaren Myneni };
69b903737bSHaren Myneni 
70b903737bSHaren Myneni #define VAS_ATTR_RO(_name)	\
71b903737bSHaren Myneni 	sysfs_caps_entry_read(_name);		\
72b903737bSHaren Myneni 	static struct vas_sysfs_entry _name##_attribute = __ATTR(_name,	\
73b903737bSHaren Myneni 				0444, _name##_show, NULL);
74b903737bSHaren Myneni 
75b903737bSHaren Myneni /*
76b903737bSHaren Myneni  * Create sysfs interface:
77657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities
78b903737bSHaren Myneni  *	This directory contains the following VAS GZIP capabilities
791fd02f66SJulia Lawall  *	for the default credit type.
80657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_total_credits
81b903737bSHaren Myneni  *	Total number of default credits assigned to the LPAR which
82b903737bSHaren Myneni  *	can be changed with DLPAR operation.
83657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_used_credits
84b903737bSHaren Myneni  *	Number of credits used by the user space. One credit will
85b903737bSHaren Myneni  *	be assigned for each window open.
86b903737bSHaren Myneni  *
87657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities
88b903737bSHaren Myneni  *	This directory contains the following VAS GZIP capabilities
89b903737bSHaren Myneni  *	for the Quality of Service (QoS) credit type.
90657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_total_credits
91b903737bSHaren Myneni  *	Total number of QoS credits assigned to the LPAR. The user
92b903737bSHaren Myneni  *	has to define this value using HMC interface. It can be
93b903737bSHaren Myneni  *	changed dynamically by the user.
94657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_used_credits
95b903737bSHaren Myneni  *	Number of credits used by the user space.
96657ac633SHaren Myneni  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/update_total_credits
9745f06eacSHaren Myneni  *	Update total QoS credits dynamically
98b903737bSHaren Myneni  */
99b903737bSHaren Myneni 
100b903737bSHaren Myneni VAS_ATTR_RO(nr_total_credits);
101b903737bSHaren Myneni VAS_ATTR_RO(nr_used_credits);
102b903737bSHaren Myneni 
10345f06eacSHaren Myneni static struct vas_sysfs_entry update_total_credits_attribute =
10457831bfbSHaren Myneni 	__ATTR(update_total_credits, 0200, NULL, update_total_credits_store);
10545f06eacSHaren Myneni 
10645f06eacSHaren Myneni static struct attribute *vas_def_capab_attrs[] = {
107b903737bSHaren Myneni 	&nr_total_credits_attribute.attr,
108b903737bSHaren Myneni 	&nr_used_credits_attribute.attr,
109b903737bSHaren Myneni 	NULL,
110b903737bSHaren Myneni };
111c31bc046SGreg Kroah-Hartman ATTRIBUTE_GROUPS(vas_def_capab);
112b903737bSHaren Myneni 
11345f06eacSHaren Myneni static struct attribute *vas_qos_capab_attrs[] = {
11445f06eacSHaren Myneni 	&nr_total_credits_attribute.attr,
11545f06eacSHaren Myneni 	&nr_used_credits_attribute.attr,
11645f06eacSHaren Myneni 	&update_total_credits_attribute.attr,
11745f06eacSHaren Myneni 	NULL,
11845f06eacSHaren Myneni };
119c31bc046SGreg Kroah-Hartman ATTRIBUTE_GROUPS(vas_qos_capab);
12045f06eacSHaren Myneni 
vas_type_show(struct kobject * kobj,struct attribute * attr,char * buf)121b903737bSHaren Myneni static ssize_t vas_type_show(struct kobject *kobj, struct attribute *attr,
122b903737bSHaren Myneni 			     char *buf)
123b903737bSHaren Myneni {
124b903737bSHaren Myneni 	struct vas_caps_entry *centry;
125b903737bSHaren Myneni 	struct vas_cop_feat_caps *caps;
126b903737bSHaren Myneni 	struct vas_sysfs_entry *entry;
127b903737bSHaren Myneni 
128b903737bSHaren Myneni 	centry = to_caps_entry(kobj);
129b903737bSHaren Myneni 	caps = centry->caps;
130b903737bSHaren Myneni 	entry = container_of(attr, struct vas_sysfs_entry, attr);
131b903737bSHaren Myneni 
132b903737bSHaren Myneni 	if (!entry->show)
133b903737bSHaren Myneni 		return -EIO;
134b903737bSHaren Myneni 
135b903737bSHaren Myneni 	return entry->show(caps, buf);
136b903737bSHaren Myneni }
137b903737bSHaren Myneni 
vas_type_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)138b903737bSHaren Myneni static ssize_t vas_type_store(struct kobject *kobj, struct attribute *attr,
139b903737bSHaren Myneni 			      const char *buf, size_t count)
140b903737bSHaren Myneni {
141b903737bSHaren Myneni 	struct vas_caps_entry *centry;
142b903737bSHaren Myneni 	struct vas_cop_feat_caps *caps;
143b903737bSHaren Myneni 	struct vas_sysfs_entry *entry;
144b903737bSHaren Myneni 
145b903737bSHaren Myneni 	centry = to_caps_entry(kobj);
146b903737bSHaren Myneni 	caps = centry->caps;
147b903737bSHaren Myneni 	entry = container_of(attr, struct vas_sysfs_entry, attr);
148b903737bSHaren Myneni 	if (!entry->store)
149b903737bSHaren Myneni 		return -EIO;
150b903737bSHaren Myneni 
151b903737bSHaren Myneni 	return entry->store(caps, buf, count);
152b903737bSHaren Myneni }
153b903737bSHaren Myneni 
vas_type_release(struct kobject * kobj)154b903737bSHaren Myneni static void vas_type_release(struct kobject *kobj)
155b903737bSHaren Myneni {
156b903737bSHaren Myneni 	struct vas_caps_entry *centry = to_caps_entry(kobj);
157b903737bSHaren Myneni 	kfree(centry);
158b903737bSHaren Myneni }
159b903737bSHaren Myneni 
160b903737bSHaren Myneni static const struct sysfs_ops vas_sysfs_ops = {
161b903737bSHaren Myneni 	.show	=	vas_type_show,
162b903737bSHaren Myneni 	.store	=	vas_type_store,
163b903737bSHaren Myneni };
164b903737bSHaren Myneni 
16545f06eacSHaren Myneni static struct kobj_type vas_def_attr_type = {
166b903737bSHaren Myneni 		.release	=	vas_type_release,
167b903737bSHaren Myneni 		.sysfs_ops      =       &vas_sysfs_ops,
168c31bc046SGreg Kroah-Hartman 		.default_groups	=	vas_def_capab_groups,
169b903737bSHaren Myneni };
170b903737bSHaren Myneni 
17145f06eacSHaren Myneni static struct kobj_type vas_qos_attr_type = {
17245f06eacSHaren Myneni 		.release	=	vas_type_release,
17345f06eacSHaren Myneni 		.sysfs_ops	=	&vas_sysfs_ops,
174c31bc046SGreg Kroah-Hartman 		.default_groups	=	vas_qos_capab_groups,
17545f06eacSHaren Myneni };
17645f06eacSHaren Myneni 
vas_caps_kobj_name(struct vas_caps_entry * centry,struct kobject ** kobj)17745f06eacSHaren Myneni static char *vas_caps_kobj_name(struct vas_caps_entry *centry,
178b903737bSHaren Myneni 				struct kobject **kobj)
179b903737bSHaren Myneni {
18045f06eacSHaren Myneni 	struct vas_cop_feat_caps *caps = centry->caps;
18145f06eacSHaren Myneni 
182b903737bSHaren Myneni 	if (caps->descriptor == VAS_GZIP_QOS_CAPABILITIES) {
18345f06eacSHaren Myneni 		kobject_init(&centry->kobj, &vas_qos_attr_type);
184b903737bSHaren Myneni 		*kobj = gzip_caps_kobj;
185b903737bSHaren Myneni 		return "qos_capabilities";
186b903737bSHaren Myneni 	} else if (caps->descriptor == VAS_GZIP_DEFAULT_CAPABILITIES) {
18745f06eacSHaren Myneni 		kobject_init(&centry->kobj, &vas_def_attr_type);
188b903737bSHaren Myneni 		*kobj = gzip_caps_kobj;
189b903737bSHaren Myneni 		return "default_capabilities";
190b903737bSHaren Myneni 	} else
191b903737bSHaren Myneni 		return "Unknown";
192b903737bSHaren Myneni }
193b903737bSHaren Myneni 
194b903737bSHaren Myneni /*
195b903737bSHaren Myneni  * Add feature specific capability dir entry.
196b903737bSHaren Myneni  * Ex: VDefGzip or VQosGzip
197b903737bSHaren Myneni  */
sysfs_add_vas_caps(struct vas_cop_feat_caps * caps)198b903737bSHaren Myneni int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
199b903737bSHaren Myneni {
200b903737bSHaren Myneni 	struct vas_caps_entry *centry;
201b903737bSHaren Myneni 	struct kobject *kobj = NULL;
202b903737bSHaren Myneni 	int ret = 0;
203b903737bSHaren Myneni 	char *name;
204b903737bSHaren Myneni 
205b903737bSHaren Myneni 	centry = kzalloc(sizeof(*centry), GFP_KERNEL);
206b903737bSHaren Myneni 	if (!centry)
207b903737bSHaren Myneni 		return -ENOMEM;
208b903737bSHaren Myneni 
209b903737bSHaren Myneni 	centry->caps = caps;
21045f06eacSHaren Myneni 	name  = vas_caps_kobj_name(centry, &kobj);
211b903737bSHaren Myneni 
212b903737bSHaren Myneni 	if (kobj) {
213b903737bSHaren Myneni 		ret = kobject_add(&centry->kobj, kobj, "%s", name);
214b903737bSHaren Myneni 
215b903737bSHaren Myneni 		if (ret) {
216b903737bSHaren Myneni 			pr_err("VAS: sysfs kobject add / event failed %d\n",
217b903737bSHaren Myneni 					ret);
218b903737bSHaren Myneni 			kobject_put(&centry->kobj);
219b903737bSHaren Myneni 		}
220b903737bSHaren Myneni 	}
221b903737bSHaren Myneni 
222b903737bSHaren Myneni 	return ret;
223b903737bSHaren Myneni }
224b903737bSHaren Myneni 
225b903737bSHaren Myneni static struct miscdevice vas_miscdev = {
226b903737bSHaren Myneni 	.minor = MISC_DYNAMIC_MINOR,
227b903737bSHaren Myneni 	.name = "vas",
228b903737bSHaren Myneni };
229b903737bSHaren Myneni 
230b903737bSHaren Myneni /*
231b903737bSHaren Myneni  * Add VAS and VasCaps (overall capabilities) dir entries.
232b903737bSHaren Myneni  */
sysfs_pseries_vas_init(struct vas_all_caps * vas_caps)233b903737bSHaren Myneni int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
234b903737bSHaren Myneni {
235b903737bSHaren Myneni 	int ret;
236b903737bSHaren Myneni 
237b903737bSHaren Myneni 	ret = misc_register(&vas_miscdev);
238b903737bSHaren Myneni 	if (ret < 0) {
239b903737bSHaren Myneni 		pr_err("%s: register vas misc device failed\n", __func__);
240b903737bSHaren Myneni 		return ret;
241b903737bSHaren Myneni 	}
242b903737bSHaren Myneni 
243b903737bSHaren Myneni 	/*
244b903737bSHaren Myneni 	 * The hypervisor does not expose multiple VAS instances, but can
245b903737bSHaren Myneni 	 * see multiple VAS instances on PowerNV. So create 'vas0' directory
246b903737bSHaren Myneni 	 * on pseries.
247b903737bSHaren Myneni 	 */
248b903737bSHaren Myneni 	pseries_vas_kobj = kobject_create_and_add("vas0",
249b903737bSHaren Myneni 					&vas_miscdev.this_device->kobj);
250b903737bSHaren Myneni 	if (!pseries_vas_kobj) {
251*426e5805SZheng Bin 		misc_deregister(&vas_miscdev);
252b903737bSHaren Myneni 		pr_err("Failed to create VAS sysfs entry\n");
253b903737bSHaren Myneni 		return -ENOMEM;
254b903737bSHaren Myneni 	}
255b903737bSHaren Myneni 
256b903737bSHaren Myneni 	if ((vas_caps->feat_type & VAS_GZIP_QOS_FEAT_BIT) ||
257b903737bSHaren Myneni 		(vas_caps->feat_type & VAS_GZIP_DEF_FEAT_BIT)) {
258b903737bSHaren Myneni 		gzip_caps_kobj = kobject_create_and_add("gzip",
259b903737bSHaren Myneni 						       pseries_vas_kobj);
260b903737bSHaren Myneni 		if (!gzip_caps_kobj) {
261b903737bSHaren Myneni 			pr_err("Failed to create VAS GZIP capability entry\n");
262b903737bSHaren Myneni 			kobject_put(pseries_vas_kobj);
263*426e5805SZheng Bin 			misc_deregister(&vas_miscdev);
264b903737bSHaren Myneni 			return -ENOMEM;
265b903737bSHaren Myneni 		}
266b903737bSHaren Myneni 	}
267b903737bSHaren Myneni 
268b903737bSHaren Myneni 	return 0;
269b903737bSHaren Myneni }
270b903737bSHaren Myneni 
271b903737bSHaren Myneni #else
sysfs_add_vas_caps(struct vas_cop_feat_caps * caps)272b903737bSHaren Myneni int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
273b903737bSHaren Myneni {
274b903737bSHaren Myneni 	return 0;
275b903737bSHaren Myneni }
276b903737bSHaren Myneni 
sysfs_pseries_vas_init(struct vas_all_caps * vas_caps)277b903737bSHaren Myneni int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
278b903737bSHaren Myneni {
279b903737bSHaren Myneni 	return 0;
280b903737bSHaren Myneni }
281b903737bSHaren Myneni #endif
282