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