282 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * Copyright 2022-23 IBM Corp.
 | 
						|
 */
 | 
						|
 | 
						|
#define pr_fmt(fmt) "vas: " fmt
 | 
						|
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/miscdevice.h>
 | 
						|
#include <linux/kobject.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/mm.h>
 | 
						|
 | 
						|
#include "vas.h"
 | 
						|
 | 
						|
#ifdef CONFIG_SYSFS
 | 
						|
static struct kobject *pseries_vas_kobj;
 | 
						|
static struct kobject *gzip_caps_kobj;
 | 
						|
 | 
						|
struct vas_caps_entry {
 | 
						|
	struct kobject kobj;
 | 
						|
	struct vas_cop_feat_caps *caps;
 | 
						|
};
 | 
						|
 | 
						|
#define to_caps_entry(entry) container_of(entry, struct vas_caps_entry, kobj)
 | 
						|
 | 
						|
/*
 | 
						|
 * This function is used to get the notification from the drmgr when
 | 
						|
 * QoS credits are changed.
 | 
						|
 */
 | 
						|
static ssize_t update_total_credits_store(struct vas_cop_feat_caps *caps,
 | 
						|
						const char *buf, size_t count)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
	u16 creds;
 | 
						|
 | 
						|
	err = kstrtou16(buf, 0, &creds);
 | 
						|
	/*
 | 
						|
	 * The user space interface from the management console
 | 
						|
	 * notifies OS with the new QoS credits and then the
 | 
						|
	 * hypervisor. So OS has to use this new credits value
 | 
						|
	 * and reconfigure VAS windows (close or reopen depends
 | 
						|
	 * on the credits available) instead of depending on VAS
 | 
						|
	 * QoS capabilities from the hypervisor.
 | 
						|
	 */
 | 
						|
	if (!err)
 | 
						|
		err = vas_reconfig_capabilties(caps->win_type, creds);
 | 
						|
 | 
						|
	if (err)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	pr_info("Set QoS total credits %u\n", creds);
 | 
						|
 | 
						|
	return count;
 | 
						|
}
 | 
						|
 | 
						|
#define sysfs_caps_entry_read(_name)					\
 | 
						|
static ssize_t _name##_show(struct vas_cop_feat_caps *caps, char *buf) 	\
 | 
						|
{									\
 | 
						|
	return sprintf(buf, "%d\n", atomic_read(&caps->_name));	\
 | 
						|
}
 | 
						|
 | 
						|
struct vas_sysfs_entry {
 | 
						|
	struct attribute attr;
 | 
						|
	ssize_t (*show)(struct vas_cop_feat_caps *, char *);
 | 
						|
	ssize_t (*store)(struct vas_cop_feat_caps *, const char *, size_t);
 | 
						|
};
 | 
						|
 | 
						|
#define VAS_ATTR_RO(_name)	\
 | 
						|
	sysfs_caps_entry_read(_name);		\
 | 
						|
	static struct vas_sysfs_entry _name##_attribute = __ATTR(_name,	\
 | 
						|
				0444, _name##_show, NULL);
 | 
						|
 | 
						|
/*
 | 
						|
 * Create sysfs interface:
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities
 | 
						|
 *	This directory contains the following VAS GZIP capabilities
 | 
						|
 *	for the default credit type.
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_total_credits
 | 
						|
 *	Total number of default credits assigned to the LPAR which
 | 
						|
 *	can be changed with DLPAR operation.
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/default_capabilities/nr_used_credits
 | 
						|
 *	Number of credits used by the user space. One credit will
 | 
						|
 *	be assigned for each window open.
 | 
						|
 *
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities
 | 
						|
 *	This directory contains the following VAS GZIP capabilities
 | 
						|
 *	for the Quality of Service (QoS) credit type.
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_total_credits
 | 
						|
 *	Total number of QoS credits assigned to the LPAR. The user
 | 
						|
 *	has to define this value using HMC interface. It can be
 | 
						|
 *	changed dynamically by the user.
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/nr_used_credits
 | 
						|
 *	Number of credits used by the user space.
 | 
						|
 * /sys/devices/virtual/misc/vas/vas0/gzip/qos_capabilities/update_total_credits
 | 
						|
 *	Update total QoS credits dynamically
 | 
						|
 */
 | 
						|
 | 
						|
VAS_ATTR_RO(nr_total_credits);
 | 
						|
VAS_ATTR_RO(nr_used_credits);
 | 
						|
 | 
						|
static struct vas_sysfs_entry update_total_credits_attribute =
 | 
						|
	__ATTR(update_total_credits, 0200, NULL, update_total_credits_store);
 | 
						|
 | 
						|
static struct attribute *vas_def_capab_attrs[] = {
 | 
						|
	&nr_total_credits_attribute.attr,
 | 
						|
	&nr_used_credits_attribute.attr,
 | 
						|
	NULL,
 | 
						|
};
 | 
						|
ATTRIBUTE_GROUPS(vas_def_capab);
 | 
						|
 | 
						|
static struct attribute *vas_qos_capab_attrs[] = {
 | 
						|
	&nr_total_credits_attribute.attr,
 | 
						|
	&nr_used_credits_attribute.attr,
 | 
						|
	&update_total_credits_attribute.attr,
 | 
						|
	NULL,
 | 
						|
};
 | 
						|
ATTRIBUTE_GROUPS(vas_qos_capab);
 | 
						|
 | 
						|
static ssize_t vas_type_show(struct kobject *kobj, struct attribute *attr,
 | 
						|
			     char *buf)
 | 
						|
{
 | 
						|
	struct vas_caps_entry *centry;
 | 
						|
	struct vas_cop_feat_caps *caps;
 | 
						|
	struct vas_sysfs_entry *entry;
 | 
						|
 | 
						|
	centry = to_caps_entry(kobj);
 | 
						|
	caps = centry->caps;
 | 
						|
	entry = container_of(attr, struct vas_sysfs_entry, attr);
 | 
						|
 | 
						|
	if (!entry->show)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	return entry->show(caps, buf);
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t vas_type_store(struct kobject *kobj, struct attribute *attr,
 | 
						|
			      const char *buf, size_t count)
 | 
						|
{
 | 
						|
	struct vas_caps_entry *centry;
 | 
						|
	struct vas_cop_feat_caps *caps;
 | 
						|
	struct vas_sysfs_entry *entry;
 | 
						|
 | 
						|
	centry = to_caps_entry(kobj);
 | 
						|
	caps = centry->caps;
 | 
						|
	entry = container_of(attr, struct vas_sysfs_entry, attr);
 | 
						|
	if (!entry->store)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	return entry->store(caps, buf, count);
 | 
						|
}
 | 
						|
 | 
						|
static void vas_type_release(struct kobject *kobj)
 | 
						|
{
 | 
						|
	struct vas_caps_entry *centry = to_caps_entry(kobj);
 | 
						|
	kfree(centry);
 | 
						|
}
 | 
						|
 | 
						|
static const struct sysfs_ops vas_sysfs_ops = {
 | 
						|
	.show	=	vas_type_show,
 | 
						|
	.store	=	vas_type_store,
 | 
						|
};
 | 
						|
 | 
						|
static struct kobj_type vas_def_attr_type = {
 | 
						|
		.release	=	vas_type_release,
 | 
						|
		.sysfs_ops      =       &vas_sysfs_ops,
 | 
						|
		.default_groups	=	vas_def_capab_groups,
 | 
						|
};
 | 
						|
 | 
						|
static struct kobj_type vas_qos_attr_type = {
 | 
						|
		.release	=	vas_type_release,
 | 
						|
		.sysfs_ops	=	&vas_sysfs_ops,
 | 
						|
		.default_groups	=	vas_qos_capab_groups,
 | 
						|
};
 | 
						|
 | 
						|
static char *vas_caps_kobj_name(struct vas_caps_entry *centry,
 | 
						|
				struct kobject **kobj)
 | 
						|
{
 | 
						|
	struct vas_cop_feat_caps *caps = centry->caps;
 | 
						|
 | 
						|
	if (caps->descriptor == VAS_GZIP_QOS_CAPABILITIES) {
 | 
						|
		kobject_init(¢ry->kobj, &vas_qos_attr_type);
 | 
						|
		*kobj = gzip_caps_kobj;
 | 
						|
		return "qos_capabilities";
 | 
						|
	} else if (caps->descriptor == VAS_GZIP_DEFAULT_CAPABILITIES) {
 | 
						|
		kobject_init(¢ry->kobj, &vas_def_attr_type);
 | 
						|
		*kobj = gzip_caps_kobj;
 | 
						|
		return "default_capabilities";
 | 
						|
	} else
 | 
						|
		return "Unknown";
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Add feature specific capability dir entry.
 | 
						|
 * Ex: VDefGzip or VQosGzip
 | 
						|
 */
 | 
						|
int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
 | 
						|
{
 | 
						|
	struct vas_caps_entry *centry;
 | 
						|
	struct kobject *kobj = NULL;
 | 
						|
	int ret = 0;
 | 
						|
	char *name;
 | 
						|
 | 
						|
	centry = kzalloc(sizeof(*centry), GFP_KERNEL);
 | 
						|
	if (!centry)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	centry->caps = caps;
 | 
						|
	name  = vas_caps_kobj_name(centry, &kobj);
 | 
						|
 | 
						|
	if (kobj) {
 | 
						|
		ret = kobject_add(¢ry->kobj, kobj, "%s", name);
 | 
						|
 | 
						|
		if (ret) {
 | 
						|
			pr_err("VAS: sysfs kobject add / event failed %d\n",
 | 
						|
					ret);
 | 
						|
			kobject_put(¢ry->kobj);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static struct miscdevice vas_miscdev = {
 | 
						|
	.minor = MISC_DYNAMIC_MINOR,
 | 
						|
	.name = "vas",
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Add VAS and VasCaps (overall capabilities) dir entries.
 | 
						|
 */
 | 
						|
int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = misc_register(&vas_miscdev);
 | 
						|
	if (ret < 0) {
 | 
						|
		pr_err("%s: register vas misc device failed\n", __func__);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * The hypervisor does not expose multiple VAS instances, but can
 | 
						|
	 * see multiple VAS instances on PowerNV. So create 'vas0' directory
 | 
						|
	 * on pseries.
 | 
						|
	 */
 | 
						|
	pseries_vas_kobj = kobject_create_and_add("vas0",
 | 
						|
					&vas_miscdev.this_device->kobj);
 | 
						|
	if (!pseries_vas_kobj) {
 | 
						|
		misc_deregister(&vas_miscdev);
 | 
						|
		pr_err("Failed to create VAS sysfs entry\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((vas_caps->feat_type & VAS_GZIP_QOS_FEAT_BIT) ||
 | 
						|
		(vas_caps->feat_type & VAS_GZIP_DEF_FEAT_BIT)) {
 | 
						|
		gzip_caps_kobj = kobject_create_and_add("gzip",
 | 
						|
						       pseries_vas_kobj);
 | 
						|
		if (!gzip_caps_kobj) {
 | 
						|
			pr_err("Failed to create VAS GZIP capability entry\n");
 | 
						|
			kobject_put(pseries_vas_kobj);
 | 
						|
			misc_deregister(&vas_miscdev);
 | 
						|
			return -ENOMEM;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
int sysfs_add_vas_caps(struct vas_cop_feat_caps *caps)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int __init sysfs_pseries_vas_init(struct vas_all_caps *vas_caps)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
#endif
 |