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