Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e978d06 authored by Fenghua Yu's avatar Fenghua Yu Committed by Thomas Gleixner
Browse files

x86/intel_rdt: Add "info" files to resctrl file system



For the convenience of applications we make the decoded values of some
of the CPUID values available in read-only (0444) files.

Signed-off-by: default avatarFenghua Yu <fenghua.yu@intel.com>
Cc: "Ravi V Shankar" <ravi.v.shankar@intel.com>
Cc: "Tony Luck" <tony.luck@intel.com>
Cc: "Shaohua Li" <shli@fb.com>
Cc: "Sai Prakhya" <sai.praneeth.prakhya@intel.com>
Cc: "Peter Zijlstra" <peterz@infradead.org>
Cc: "Stephane Eranian" <eranian@google.com>
Cc: "Dave Hansen" <dave.hansen@intel.com>
Cc: "David Carrillo-Cisneros" <davidcc@google.com>
Cc: "Nilay Vaish" <nilayvaish@gmail.com>
Cc: "Vikas Shivappa" <vikas.shivappa@linux.intel.com>
Cc: "Ingo Molnar" <mingo@elte.hu>
Cc: "Borislav Petkov" <bp@suse.de>
Cc: "H. Peter Anvin" <h.peter.anvin@intel.com>
Link: http://lkml.kernel.org/r/1477692289-37412-5-git-send-email-fenghua.yu@intel.com


Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 5ff193fb
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -26,6 +26,30 @@ extern struct list_head rdt_all_groups;

int __init rdtgroup_init(void);

/**
 * struct rftype - describe each file in the resctrl file system
 * @name: file name
 * @mode: access mode
 * @kf_ops: operations
 * @seq_show: show content of the file
 * @write: write to the file
 */
struct rftype {
	char			*name;
	umode_t			mode;
	struct kernfs_ops	*kf_ops;

	int (*seq_show)(struct kernfs_open_file *of,
			struct seq_file *sf, void *v);
	/*
	 * write() is the generic write callback which maps directly to
	 * kernfs write operation and overrides all other operations.
	 * Maximum write size is determined by ->max_write_len.
	 */
	ssize_t (*write)(struct kernfs_open_file *of,
			 char *buf, size_t nbytes, loff_t off);
};

/**
 * struct rdt_resource - attributes of an RDT resource
 * @enabled:			Is this feature enabled on this machine
+185 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/kernfs.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
#include <linux/slab.h>

#include <uapi/linux/magic.h>
@@ -34,6 +36,176 @@ struct kernfs_root *rdt_root;
struct rdtgroup rdtgroup_default;
LIST_HEAD(rdt_all_groups);

/* Kernel fs node for "info" directory under root */
static struct kernfs_node *kn_info;

/* set uid and gid of rdtgroup dirs and files to that of the creator */
static int rdtgroup_kn_set_ugid(struct kernfs_node *kn)
{
	struct iattr iattr = { .ia_valid = ATTR_UID | ATTR_GID,
				.ia_uid = current_fsuid(),
				.ia_gid = current_fsgid(), };

	if (uid_eq(iattr.ia_uid, GLOBAL_ROOT_UID) &&
	    gid_eq(iattr.ia_gid, GLOBAL_ROOT_GID))
		return 0;

	return kernfs_setattr(kn, &iattr);
}

static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft)
{
	struct kernfs_node *kn;
	int ret;

	kn = __kernfs_create_file(parent_kn, rft->name, rft->mode,
				  0, rft->kf_ops, rft, NULL, NULL);
	if (IS_ERR(kn))
		return PTR_ERR(kn);

	ret = rdtgroup_kn_set_ugid(kn);
	if (ret) {
		kernfs_remove(kn);
		return ret;
	}

	return 0;
}

static int rdtgroup_add_files(struct kernfs_node *kn, struct rftype *rfts,
			      int len)
{
	struct rftype *rft;
	int ret;

	lockdep_assert_held(&rdtgroup_mutex);

	for (rft = rfts; rft < rfts + len; rft++) {
		ret = rdtgroup_add_file(kn, rft);
		if (ret)
			goto error;
	}

	return 0;
error:
	pr_warn("Failed to add %s, err=%d\n", rft->name, ret);
	while (--rft >= rfts)
		kernfs_remove_by_name(kn, rft->name);
	return ret;
}

static int rdtgroup_seqfile_show(struct seq_file *m, void *arg)
{
	struct kernfs_open_file *of = m->private;
	struct rftype *rft = of->kn->priv;

	if (rft->seq_show)
		return rft->seq_show(of, m, arg);
	return 0;
}

static ssize_t rdtgroup_file_write(struct kernfs_open_file *of, char *buf,
				   size_t nbytes, loff_t off)
{
	struct rftype *rft = of->kn->priv;

	if (rft->write)
		return rft->write(of, buf, nbytes, off);

	return -EINVAL;
}

static struct kernfs_ops rdtgroup_kf_single_ops = {
	.atomic_write_len	= PAGE_SIZE,
	.write			= rdtgroup_file_write,
	.seq_show		= rdtgroup_seqfile_show,
};

static int rdt_num_closids_show(struct kernfs_open_file *of,
				struct seq_file *seq, void *v)
{
	struct rdt_resource *r = of->kn->parent->priv;

	seq_printf(seq, "%d\n", r->num_closid);

	return 0;
}

static int rdt_cbm_mask_show(struct kernfs_open_file *of,
			     struct seq_file *seq, void *v)
{
	struct rdt_resource *r = of->kn->parent->priv;

	seq_printf(seq, "%x\n", r->max_cbm);

	return 0;
}

/* rdtgroup information files for one cache resource. */
static struct rftype res_info_files[] = {
	{
		.name		= "num_closids",
		.mode		= 0444,
		.kf_ops		= &rdtgroup_kf_single_ops,
		.seq_show	= rdt_num_closids_show,
	},
	{
		.name		= "cbm_mask",
		.mode		= 0444,
		.kf_ops		= &rdtgroup_kf_single_ops,
		.seq_show	= rdt_cbm_mask_show,
	},
};

static int rdtgroup_create_info_dir(struct kernfs_node *parent_kn)
{
	struct kernfs_node *kn_subdir;
	struct rdt_resource *r;
	int ret;

	/* create the directory */
	kn_info = kernfs_create_dir(parent_kn, "info", parent_kn->mode, NULL);
	if (IS_ERR(kn_info))
		return PTR_ERR(kn_info);
	kernfs_get(kn_info);

	for_each_enabled_rdt_resource(r) {
		kn_subdir = kernfs_create_dir(kn_info, r->name,
					      kn_info->mode, r);
		if (IS_ERR(kn_subdir)) {
			ret = PTR_ERR(kn_subdir);
			goto out_destroy;
		}
		kernfs_get(kn_subdir);
		ret = rdtgroup_kn_set_ugid(kn_subdir);
		if (ret)
			goto out_destroy;
		ret = rdtgroup_add_files(kn_subdir, res_info_files,
					 ARRAY_SIZE(res_info_files));
		if (ret)
			goto out_destroy;
		kernfs_activate(kn_subdir);
	}

	/*
	 * This extra ref will be put in kernfs_remove() and guarantees
	 * that @rdtgrp->kn is always accessible.
	 */
	kernfs_get(kn_info);

	ret = rdtgroup_kn_set_ugid(kn_info);
	if (ret)
		goto out_destroy;

	kernfs_activate(kn_info);

	return 0;

out_destroy:
	kernfs_remove(kn_info);
	return ret;
}

static void l3_qos_cfg_update(void *arg)
{
	bool *enable = arg;
@@ -137,6 +309,10 @@ static struct dentry *rdt_mount(struct file_system_type *fs_type,
		goto out_cdp;
	}

	ret = rdtgroup_create_info_dir(rdtgroup_default.kn);
	if (ret)
		goto out_cdp;

	dentry = kernfs_mount(fs_type, flags, rdt_root,
			      RDTGROUP_SUPER_MAGIC, NULL);
	if (IS_ERR(dentry))
@@ -191,6 +367,14 @@ static int reset_all_cbms(struct rdt_resource *r)
	return 0;
}

/*
 * Forcibly remove all of subdirectories under root.
 */
static void rmdir_all_sub(void)
{
	kernfs_remove(kn_info);
}

static void rdt_kill_sb(struct super_block *sb)
{
	struct rdt_resource *r;
@@ -201,6 +385,7 @@ static void rdt_kill_sb(struct super_block *sb)
	for_each_enabled_rdt_resource(r)
		reset_all_cbms(r);
	cdp_disable();
	rmdir_all_sub();
	static_branch_disable(&rdt_enable_key);
	kernfs_kill_sb(sb);
	mutex_unlock(&rdtgroup_mutex);