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(¢ry->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(¢ry->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(¢ry->kobj, kobj, "%s", name); 214 215 if (ret) { 216 pr_err("VAS: sysfs kobject add / event failed %d\n", 217 ret); 218 kobject_put(¢ry->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