intel_rdt_ctrlmondata.c 6.5 KB
Newer Older
T
Tony Luck 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Resource Director Technology(RDT)
 * - Cache Allocation code.
 *
 * Copyright (C) 2016 Intel Corporation
 *
 * Authors:
 *    Fenghua Yu <fenghua.yu@intel.com>
 *    Tony Luck <tony.luck@intel.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * More information about RDT be found in the Intel (R) x86 Architecture
 * Software Developer Manual June 2016, volume 3, section 17.17.
 */

#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt

#include <linux/kernfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
29
#include "intel_rdt.h"
T
Tony Luck 已提交
30

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
/*
 * Check whether MBA bandwidth percentage value is correct. The value is
 * checked against the minimum and max bandwidth values specified by the
 * hardware. The allocated bandwidth percentage is rounded to the next
 * control step available on the hardware.
 */
static bool bw_validate(char *buf, unsigned long *data, struct rdt_resource *r)
{
	unsigned long bw;
	int ret;

	/*
	 * Only linear delay values is supported for current Intel SKUs.
	 */
	if (!r->membw.delay_linear)
		return false;

	ret = kstrtoul(buf, 10, &bw);
	if (ret)
		return false;

	if (bw < r->membw.min_bw || bw > r->default_ctrl)
		return false;

	*data = roundup(bw, (unsigned long)r->membw.bw_gran);
	return true;
}

int parse_bw(char *buf, struct rdt_resource *r, struct rdt_domain *d)
{
	unsigned long data;

	if (d->have_new_ctrl)
		return -EINVAL;

	if (!bw_validate(buf, &data, r))
		return -EINVAL;
	d->new_ctrl = data;
	d->have_new_ctrl = true;

	return 0;
}

T
Tony Luck 已提交
74 75 76 77 78 79
/*
 * Check whether a cache bit mask is valid. The SDM says:
 *	Please note that all (and only) contiguous '1' combinations
 *	are allowed (e.g. FFFFH, 0FF0H, 003CH, etc.).
 * Additionally Haswell requires at least two bits set.
 */
80
static bool cbm_validate(char *buf, unsigned long *data, struct rdt_resource *r)
T
Tony Luck 已提交
81
{
82
	unsigned long first_bit, zero_bit, val;
83
	unsigned int cbm_len = r->cache.cbm_len;
84 85 86 87 88
	int ret;

	ret = kstrtoul(buf, 16, &val);
	if (ret)
		return false;
T
Tony Luck 已提交
89

90
	if (val == 0 || val > r->default_ctrl)
T
Tony Luck 已提交
91 92
		return false;

93 94
	first_bit = find_first_bit(&val, cbm_len);
	zero_bit = find_next_zero_bit(&val, cbm_len, first_bit);
T
Tony Luck 已提交
95

96
	if (find_next_bit(&val, cbm_len, zero_bit) < cbm_len)
T
Tony Luck 已提交
97 98
		return false;

99
	if ((zero_bit - first_bit) < r->cache.min_cbm_bits)
T
Tony Luck 已提交
100
		return false;
101 102

	*data = val;
T
Tony Luck 已提交
103 104 105 106 107 108 109
	return true;
}

/*
 * Read one cache bit mask (hex). Check that it is valid for the current
 * resource type.
 */
110
int parse_cbm(char *buf, struct rdt_resource *r, struct rdt_domain *d)
T
Tony Luck 已提交
111 112 113
{
	unsigned long data;

114
	if (d->have_new_ctrl)
115 116
		return -EINVAL;

117
	if(!cbm_validate(buf, &data, r))
T
Tony Luck 已提交
118
		return -EINVAL;
119 120
	d->new_ctrl = data;
	d->have_new_ctrl = true;
T
Tony Luck 已提交
121 122 123 124 125 126 127

	return 0;
}

/*
 * For each domain in this resource we expect to find a series of:
 *	id=mask
128 129
 * separated by ";". The "id" is in decimal, and must match one of
 * the "id"s for this resource.
T
Tony Luck 已提交
130 131 132 133 134 135 136
 */
static int parse_line(char *line, struct rdt_resource *r)
{
	char *dom = NULL, *id;
	struct rdt_domain *d;
	unsigned long dom_id;

137 138 139 140 141 142 143
next:
	if (!line || line[0] == '\0')
		return 0;
	dom = strsep(&line, ";");
	id = strsep(&dom, "=");
	if (!dom || kstrtoul(id, 10, &dom_id))
		return -EINVAL;
144
	dom = strim(dom);
T
Tony Luck 已提交
145
	list_for_each_entry(d, &r->domains, list) {
146
		if (d->id == dom_id) {
147
			if (r->parse_ctrlval(dom, r, d))
148 149 150
				return -EINVAL;
			goto next;
		}
T
Tony Luck 已提交
151
	}
152
	return -EINVAL;
T
Tony Luck 已提交
153 154 155 156 157 158 159
}

static int update_domains(struct rdt_resource *r, int closid)
{
	struct msr_param msr_param;
	cpumask_var_t cpu_mask;
	struct rdt_domain *d;
160
	int cpu;
T
Tony Luck 已提交
161 162 163 164 165 166 167 168 169

	if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL))
		return -ENOMEM;

	msr_param.low = closid;
	msr_param.high = msr_param.low + 1;
	msr_param.res = r;

	list_for_each_entry(d, &r->domains, list) {
170
		if (d->have_new_ctrl && d->new_ctrl != d->ctrl_val[closid]) {
171
			cpumask_set_cpu(cpumask_any(&d->cpu_mask), cpu_mask);
172
			d->ctrl_val[closid] = d->new_ctrl;
173
		}
T
Tony Luck 已提交
174
	}
175 176
	if (cpumask_empty(cpu_mask))
		goto done;
T
Tony Luck 已提交
177 178 179
	cpu = get_cpu();
	/* Update CBM on this cpu if it's in cpu_mask. */
	if (cpumask_test_cpu(cpu, cpu_mask))
180
		rdt_ctrl_update(&msr_param);
T
Tony Luck 已提交
181
	/* Update CBM on other cpus. */
182
	smp_call_function_many(cpu_mask, rdt_ctrl_update, &msr_param, 1);
T
Tony Luck 已提交
183 184
	put_cpu();

185
done:
T
Tony Luck 已提交
186 187 188 189 190
	free_cpumask_var(cpu_mask);

	return 0;
}

191 192 193 194
static int rdtgroup_parse_resource(char *resname, char *tok, int closid)
{
	struct rdt_resource *r;

195
	for_each_alloc_enabled_rdt_resource(r) {
196 197 198 199 200 201
		if (!strcmp(resname, r->name) && closid < r->num_closid)
			return parse_line(tok, r);
	}
	return -EINVAL;
}

T
Tony Luck 已提交
202 203 204 205
ssize_t rdtgroup_schemata_write(struct kernfs_open_file *of,
				char *buf, size_t nbytes, loff_t off)
{
	struct rdtgroup *rdtgrp;
206
	struct rdt_domain *dom;
T
Tony Luck 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	struct rdt_resource *r;
	char *tok, *resname;
	int closid, ret = 0;

	/* Valid input requires a trailing newline */
	if (nbytes == 0 || buf[nbytes - 1] != '\n')
		return -EINVAL;
	buf[nbytes - 1] = '\0';

	rdtgrp = rdtgroup_kn_lock_live(of->kn);
	if (!rdtgrp) {
		rdtgroup_kn_unlock(of->kn);
		return -ENOENT;
	}

	closid = rdtgrp->closid;

224
	for_each_alloc_enabled_rdt_resource(r) {
225
		list_for_each_entry(dom, &r->domains, list)
226
			dom->have_new_ctrl = false;
227
	}
T
Tony Luck 已提交
228 229

	while ((tok = strsep(&buf, "\n")) != NULL) {
230
		resname = strim(strsep(&tok, ":"));
T
Tony Luck 已提交
231 232 233 234
		if (!tok) {
			ret = -EINVAL;
			goto out;
		}
235 236
		ret = rdtgroup_parse_resource(resname, tok, closid);
		if (ret)
T
Tony Luck 已提交
237 238 239
			goto out;
	}

240
	for_each_alloc_enabled_rdt_resource(r) {
T
Tony Luck 已提交
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		ret = update_domains(r, closid);
		if (ret)
			goto out;
	}

out:
	rdtgroup_kn_unlock(of->kn);
	return ret ?: nbytes;
}

static void show_doms(struct seq_file *s, struct rdt_resource *r, int closid)
{
	struct rdt_domain *dom;
	bool sep = false;

256
	seq_printf(s, "%*s:", max_name_width, r->name);
T
Tony Luck 已提交
257 258 259
	list_for_each_entry(dom, &r->domains, list) {
		if (sep)
			seq_puts(s, ";");
260
		seq_printf(s, r->format_str, dom->id, max_data_width,
261
			   dom->ctrl_val[closid]);
T
Tony Luck 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
		sep = true;
	}
	seq_puts(s, "\n");
}

int rdtgroup_schemata_show(struct kernfs_open_file *of,
			   struct seq_file *s, void *v)
{
	struct rdtgroup *rdtgrp;
	struct rdt_resource *r;
	int closid, ret = 0;

	rdtgrp = rdtgroup_kn_lock_live(of->kn);
	if (rdtgrp) {
		closid = rdtgrp->closid;
277
		for_each_alloc_enabled_rdt_resource(r) {
T
Tony Luck 已提交
278 279 280 281 282 283 284 285 286
			if (closid < r->num_closid)
				show_doms(s, r, closid);
		}
	} else {
		ret = -ENOENT;
	}
	rdtgroup_kn_unlock(of->kn);
	return ret;
}