1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2022-23 IBM Corp.
4  */
5 
6 #define pr_fmt(fmt) "vas: " fmt
7 
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/miscdevice.h>
11 #include <linux/kobject.h>
12 #include <linux/slab.h>
13 #include <linux/mm.h>
14 
15 #include "vas.h"
16 
17 #ifdef CONFIG_SYSFS
18 static struct kobject *pseries_vas_kobj;
19 static struct kobject *gzip_caps_kobj;
20 
21 struct vas_caps_entry {
22 	struct kobject kobj;
23 	struct vas_cop_feat_caps *caps;
24 };
25 
26 #define to_caps_entry(entry) container_of(entry, struct vas_caps_entry, kobj)
27 
28 /*
29  * This function is used to get the notification from the drmgr when
30  * QoS credits are changed.
31  */
32 static ssize_t update_total_credits_store(struct vas_cop_feat_caps *caps,
33 						const char *buf, size_t count)
34 {
35 	int err;
36 	u16 creds;
37 
38 	err = kstrtou16(buf, 0, &creds);
39 	/*
40 	 * The user space interface from the management console
41 	 * notifies OS with the new QoS credits and then the
42 	 * hypervisor. So OS has to use this new credits value
43 	 * and reconfigure VAS windows (close or reopen depends
44 	 * on the credits available) instead of depending on VAS
45 	 * QoS capabilities from the hypervisor.
46 	 */
47 	if (!err)
48 		err = vas_reconfig_capabilties(caps->win_type, creds);
49 
50 	if (err)
51 		return -EINVAL;
52 
53 	pr_info("Set QoS total credits %u\n", creds);
54 
55 	return count;
56 }
57 
58 #define sysfs_caps_entry_read(_name)					\
59 static ssize_t _name##_show(struct vas_cop_feat_caps *caps, char *buf) 	\
60 {									\
61 	return sprintf(buf, "%d\n", atomic_read(&caps->_name));	\
62 }
63 
64 struct vas_sysfs_entry {
65 	struct attribute attr;
66 	ssize_t (*show)(struct vas_cop_feat_caps *, char *);
67 	ssize_t (*store)(struct vas_cop_feat_caps *, const char *, size_t);
68 };
69 
70 #define VAS_ATTR_RO(_name)	\
71 	sysfs_caps_entry_read(_name);		\
72 	static struct vas_sysfs_entry _name##_attribute = __ATTR(_name,	\
73 				0444, _name##_show, NULL);
74 
75 /*
76  * Create sysfs interface:
77  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities
78  *	This directory contains the following VAS GZIP capabilities
79  *	for the default credit type.
80  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_total_credits
81  *	Total number of default credits assigned to the LPAR which
82  *	can be changed with DLPAR operation.
83  * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_used_credits
84  *	Number of credits used by the user space. One credit will
85  *	be assigned for each window open.
86  *
87  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities
88  *	This directory contains the following VAS GZIP capabilities
89  *	for the Quality of Service (QoS) credit type.
90  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_total_credits
91  *	Total number of QoS credits assigned to the LPAR. The user
92  *	has to define this value using HMC interface. It can be
93  *	changed dynamically by the user.
94  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_used_credits
95  *	Number of credits used by the user space.
96  * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/update_total_credits
97  *	Update total QoS credits dynamically
98  */
99 
100 VAS_ATTR_RO(nr_total_credits);
101 VAS_ATTR_RO(nr_used_credits);
102 
103 static struct vas_sysfs_entry update_total_credits_attribute =
104 	__ATTR(update_total_credits, 0200, NULL, update_total_credits_store);
105 
106 static struct attribute *vas_def_capab_attrs[] = {
107 	&nr_total_credits_attribute.attr,
108 	&nr_used_credits_attribute.attr,
109 	NULL,
110 };
111 ATTRIBUTE_GROUPS(vas_def_capab);
112 
113 static struct attribute *vas_qos_capab_attrs[] = {
114 	&nr_total_credits_attribute.attr,
115 	&nr_used_credits_attribute.attr,
116 	&update_total_credits_attribute.attr,
117 	NULL,
118 };
119 ATTRIBUTE_GROUPS(vas_qos_capab);
120 
121 static ssize_t vas_type_show(struct kobject *kobj, struct attribute *attr,
122 			     char *buf)
123 {
124 	struct vas_caps_entry *centry;
125 	struct vas_cop_feat_caps *caps;
126 	struct vas_sysfs_entry *entry;
127 
128 	centry = to_caps_entry(kobj);
129 	caps = centry->caps;
130 	entry = container_of(attr, struct vas_sysfs_entry, attr);
131 
132 	if (!entry->show)
133 		return -EIO;
134 
135 	return entry->show(caps, buf);
136 }
137 
138 static ssize_t vas_type_store(struct kobject *kobj, struct attribute *attr,
139 			      const char *buf, size_t count)
140 {
141 	struct vas_caps_entry *centry;
142 	struct vas_cop_feat_caps *caps;
143 	struct vas_sysfs_entry *entry;
144 
145 	centry = to_caps_entry(kobj);
146 	caps = centry->caps;
147 	entry = container_of(attr, struct vas_sysfs_entry, attr);
148 	if (!entry->store)
149 		return -EIO;
150 
151 	return entry->store(caps, buf, count);
152 }
153 
154 static void vas_type_release(struct kobject *kobj)
155 {
156 	struct vas_caps_entry *centry = to_caps_entry(kobj);
157 	kfree(centry);
158 }
159 
160 static const struct sysfs_ops vas_sysfs_ops = {
161 	.show	=	vas_type_show,
162 	.store	=	vas_type_store,
163 };
164 
165 static struct kobj_type vas_def_attr_type = {
166 		.release	=	vas_type_release,
167 		.sysfs_ops      =       &vas_sysfs_ops,
168 		.default_groups	=	vas_def_capab_groups,
169 };
170 
171 static struct kobj_type vas_qos_attr_type = {
172 		.release	=	vas_type_release,
173 		.sysfs_ops	=	&vas_sysfs_ops,
174 		.default_groups	=	vas_qos_capab_groups,
175 };
176 
177 static char *vas_caps_kobj_name(struct vas_caps_entry *centry,
178 				struct kobject **kobj)
179 {
180 	struct vas_cop_feat_caps *caps = centry->caps;
181 
182 	if (caps->descriptor == VAS_GZIP_QOS_CAPABILITIES) {
183 		kobject_init(&centry->kobj, &vas_qos_attr_type);
184 		*kobj = gzip_caps_kobj;
185 		return "qos_capabilities";
186 	} else if (caps->descriptor == VAS_GZIP_DEFAULT_CAPABILITIES) {
187 		kobject_init(&centry->kobj, &vas_def_attr_type);
188 		*kobj = gzip_caps_kobj;
189 		return "default_capabilities";
190 	} else
191 		return "Unknown";
192 }
193 
194 /*
195  * Add feature specific capability dir entry.
196  * Ex: VDefGzip or VQosGzip
197  */
198 int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
199 {
200 	struct vas_caps_entry *centry;
201 	struct kobject *kobj = NULL;
202 	int ret = 0;
203 	char *name;
204 
205 	centry = kzalloc(sizeof(*centry), GFP_KERNEL);
206 	if (!centry)
207 		return -ENOMEM;
208 
209 	centry->caps = caps;
210 	name  = vas_caps_kobj_name(centry, &kobj);
211 
212 	if (kobj) {
213 		ret = kobject_add(&centry->kobj, kobj, "%s", name);
214 
215 		if (ret) {
216 			pr_err("VAS: sysfs kobject add / event failed %d\n",
217 					ret);
218 			kobject_put(&centry->kobj);
219 		}
220 	}
221 
222 	return ret;
223 }
224 
225 static struct miscdevice vas_miscdev = {
226 	.minor = MISC_DYNAMIC_MINOR,
227 	.name = "vas",
228 };
229 
230 /*
231  * Add VAS and VasCaps (overall capabilities) dir entries.
232  */
233 int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
234 {
235 	int ret;
236 
237 	ret = misc_register(&vas_miscdev);
238 	if (ret < 0) {
239 		pr_err("%s: register vas misc device failed\n", __func__);
240 		return ret;
241 	}
242 
243 	/*
244 	 * The hypervisor does not expose multiple VAS instances, but can
245 	 * see multiple VAS instances on PowerNV. So create 'vas0' directory
246 	 * on pseries.
247 	 */
248 	pseries_vas_kobj = kobject_create_and_add("vas0",
249 					&vas_miscdev.this_device->kobj);
250 	if (!pseries_vas_kobj) {
251 		misc_deregister(&vas_miscdev);
252 		pr_err("Failed to create VAS sysfs entry\n");
253 		return -ENOMEM;
254 	}
255 
256 	if ((vas_caps->feat_type & VAS_GZIP_QOS_FEAT_BIT) ||
257 		(vas_caps->feat_type & VAS_GZIP_DEF_FEAT_BIT)) {
258 		gzip_caps_kobj = kobject_create_and_add("gzip",
259 						       pseries_vas_kobj);
260 		if (!gzip_caps_kobj) {
261 			pr_err("Failed to create VAS GZIP capability entry\n");
262 			kobject_put(pseries_vas_kobj);
263 			misc_deregister(&vas_miscdev);
264 			return -ENOMEM;
265 		}
266 	}
267 
268 	return 0;
269 }
270 
271 #else
272 int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
273 {
274 	return 0;
275 }
276 
277 int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
278 {
279 	return 0;
280 }
281 #endif
282