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 103 static struct attribute *vas_qos_capab_attrs[] = { 104 &nr_total_credits_attribute.attr, 105 &nr_used_credits_attribute.attr, 106 &update_total_credits_attribute.attr, 107 NULL, 108 }; 109 110 static ssize_t vas_type_show(struct kobject *kobj, struct attribute *attr, 111 char *buf) 112 { 113 struct vas_caps_entry *centry; 114 struct vas_cop_feat_caps *caps; 115 struct vas_sysfs_entry *entry; 116 117 centry = to_caps_entry(kobj); 118 caps = centry->caps; 119 entry = container_of(attr, struct vas_sysfs_entry, attr); 120 121 if (!entry->show) 122 return -EIO; 123 124 return entry->show(caps, buf); 125 } 126 127 static ssize_t vas_type_store(struct kobject *kobj, struct attribute *attr, 128 const char *buf, size_t count) 129 { 130 struct vas_caps_entry *centry; 131 struct vas_cop_feat_caps *caps; 132 struct vas_sysfs_entry *entry; 133 134 centry = to_caps_entry(kobj); 135 caps = centry->caps; 136 entry = container_of(attr, struct vas_sysfs_entry, attr); 137 if (!entry->store) 138 return -EIO; 139 140 return entry->store(caps, buf, count); 141 } 142 143 static void vas_type_release(struct kobject *kobj) 144 { 145 struct vas_caps_entry *centry = to_caps_entry(kobj); 146 kfree(centry); 147 } 148 149 static const struct sysfs_ops vas_sysfs_ops = { 150 .show = vas_type_show, 151 .store = vas_type_store, 152 }; 153 154 static struct kobj_type vas_def_attr_type = { 155 .release = vas_type_release, 156 .sysfs_ops = &vas_sysfs_ops, 157 .default_attrs = vas_def_capab_attrs, 158 }; 159 160 static struct kobj_type vas_qos_attr_type = { 161 .release = vas_type_release, 162 .sysfs_ops = &vas_sysfs_ops, 163 .default_attrs = vas_qos_capab_attrs, 164 }; 165 166 static char *vas_caps_kobj_name(struct vas_caps_entry *centry, 167 struct kobject **kobj) 168 { 169 struct vas_cop_feat_caps *caps = centry->caps; 170 171 if (caps->descriptor == VAS_GZIP_QOS_CAPABILITIES) { 172 kobject_init(¢ry->kobj, &vas_qos_attr_type); 173 *kobj = gzip_caps_kobj; 174 return "qos_capabilities"; 175 } else if (caps->descriptor == VAS_GZIP_DEFAULT_CAPABILITIES) { 176 kobject_init(¢ry->kobj, &vas_def_attr_type); 177 *kobj = gzip_caps_kobj; 178 return "default_capabilities"; 179 } else 180 return "Unknown"; 181 } 182 183 /* 184 * Add feature specific capability dir entry. 185 * Ex: VDefGzip or VQosGzip 186 */ 187 int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps) 188 { 189 struct vas_caps_entry *centry; 190 struct kobject *kobj = NULL; 191 int ret = 0; 192 char *name; 193 194 centry = kzalloc(sizeof(*centry), GFP_KERNEL); 195 if (!centry) 196 return -ENOMEM; 197 198 centry->caps = caps; 199 name = vas_caps_kobj_name(centry, &kobj); 200 201 if (kobj) { 202 ret = kobject_add(¢ry->kobj, kobj, "%s", name); 203 204 if (ret) { 205 pr_err("VAS: sysfs kobject add / event failed %d\n", 206 ret); 207 kobject_put(¢ry->kobj); 208 } 209 } 210 211 return ret; 212 } 213 214 static struct miscdevice vas_miscdev = { 215 .minor = MISC_DYNAMIC_MINOR, 216 .name = "vas", 217 }; 218 219 /* 220 * Add VAS and VasCaps (overall capabilities) dir entries. 221 */ 222 int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps) 223 { 224 int ret; 225 226 ret = misc_register(&vas_miscdev); 227 if (ret < 0) { 228 pr_err("%s: register vas misc device failed\n", __func__); 229 return ret; 230 } 231 232 /* 233 * The hypervisor does not expose multiple VAS instances, but can 234 * see multiple VAS instances on PowerNV. So create 'vas0' directory 235 * on pseries. 236 */ 237 pseries_vas_kobj = kobject_create_and_add("vas0", 238 &vas_miscdev.this_device->kobj); 239 if (!pseries_vas_kobj) { 240 pr_err("Failed to create VAS sysfs entry\n"); 241 return -ENOMEM; 242 } 243 244 if ((vas_caps->feat_type & VAS_GZIP_QOS_FEAT_BIT) || 245 (vas_caps->feat_type & VAS_GZIP_DEF_FEAT_BIT)) { 246 gzip_caps_kobj = kobject_create_and_add("gzip", 247 pseries_vas_kobj); 248 if (!gzip_caps_kobj) { 249 pr_err("Failed to create VAS GZIP capability entry\n"); 250 kobject_put(pseries_vas_kobj); 251 return -ENOMEM; 252 } 253 } 254 255 return 0; 256 } 257 258 #else 259 int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps) 260 { 261 return 0; 262 } 263 264 int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps) 265 { 266 return 0; 267 } 268 #endif 269