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