linux/arch/x86/kernel/cpu/intel_rdt_ctrlmondata.c
<<
>>
Prefs
   1/*
   2 * Resource Director Technology(RDT)
   3 * - Cache Allocation code.
   4 *
   5 * Copyright (C) 2016 Intel Corporation
   6 *
   7 * Authors:
   8 *    Fenghua Yu <fenghua.yu@intel.com>
   9 *    Tony Luck <tony.luck@intel.com>
  10 *
  11 * This program is free software; you can redistribute it and/or modify it
  12 * under the terms and conditions of the GNU General Public License,
  13 * version 2, as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope it will be useful, but WITHOUT
  16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  18 * more details.
  19 *
  20 * More information about RDT be found in the Intel (R) x86 Architecture
  21 * Software Developer Manual June 2016, volume 3, section 17.17.
  22 */
  23
  24#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
  25
  26#include <linux/kernfs.h>
  27#include <linux/seq_file.h>
  28#include <linux/slab.h>
  29#include "intel_rdt.h"
  30
  31/*
  32 * Check whether MBA bandwidth percentage value is correct. The value is
  33 * checked against the minimum and max bandwidth values specified by the
  34 * hardware. The allocated bandwidth percentage is rounded to the next
  35 * control step available on the hardware.
  36 */
  37static bool bw_validate(char *buf, unsigned long *data, struct rdt_resource *r)
  38{
  39        unsigned long bw;
  40        int ret;
  41
  42        /*
  43         * Only linear delay values is supported for current Intel SKUs.
  44         */
  45        if (!r->membw.delay_linear) {
  46                rdt_last_cmd_puts("No support for non-linear MB domains\n");
  47                return false;
  48        }
  49
  50        ret = kstrtoul(buf, 10, &bw);
  51        if (ret) {
  52                rdt_last_cmd_printf("Non-decimal digit in MB value %s\n", buf);
  53                return false;
  54        }
  55
  56        if ((bw < r->membw.min_bw || bw > r->default_ctrl) &&
  57            !is_mba_sc(r)) {
  58                rdt_last_cmd_printf("MB value %ld out of range [%d,%d]\n", bw,
  59                                    r->membw.min_bw, r->default_ctrl);
  60                return false;
  61        }
  62
  63        *data = roundup(bw, (unsigned long)r->membw.bw_gran);
  64        return true;
  65}
  66
  67int parse_bw(struct rdt_parse_data *data, struct rdt_resource *r,
  68             struct rdt_domain *d)
  69{
  70        unsigned long bw_val;
  71
  72        if (d->have_new_ctrl) {
  73                rdt_last_cmd_printf("duplicate domain %d\n", d->id);
  74                return -EINVAL;
  75        }
  76
  77        if (!bw_validate(data->buf, &bw_val, r))
  78                return -EINVAL;
  79        d->new_ctrl = bw_val;
  80        d->have_new_ctrl = true;
  81
  82        return 0;
  83}
  84
  85/*
  86 * Check whether a cache bit mask is valid. The SDM says:
  87 *      Please note that all (and only) contiguous '1' combinations
  88 *      are allowed (e.g. FFFFH, 0FF0H, 003CH, etc.).
  89 * Additionally Haswell requires at least two bits set.
  90 */
  91static bool cbm_validate(char *buf, u32 *data, struct rdt_resource *r)
  92{
  93        unsigned long first_bit, zero_bit, val;
  94        unsigned int cbm_len = r->cache.cbm_len;
  95        int ret;
  96
  97        ret = kstrtoul(buf, 16, &val);
  98        if (ret) {
  99                rdt_last_cmd_printf("non-hex character in mask %s\n", buf);
 100                return false;
 101        }
 102
 103        if (val == 0 || val > r->default_ctrl) {
 104                rdt_last_cmd_puts("mask out of range\n");
 105                return false;
 106        }
 107
 108        first_bit = find_first_bit(&val, cbm_len);
 109        zero_bit = find_next_zero_bit(&val, cbm_len, first_bit);
 110
 111        if (find_next_bit(&val, cbm_len, zero_bit) < cbm_len) {
 112                rdt_last_cmd_printf("mask %lx has non-consecutive 1-bits\n", val);
 113                return false;
 114        }
 115
 116        if ((zero_bit - first_bit) < r->cache.min_cbm_bits) {
 117                rdt_last_cmd_printf("Need at least %d bits in mask\n",
 118                                    r->cache.min_cbm_bits);
 119                return false;
 120        }
 121
 122        *data = val;
 123        return true;
 124}
 125
 126/*
 127 * Read one cache bit mask (hex). Check that it is valid for the current
 128 * resource type.
 129 */
 130int parse_cbm(struct rdt_parse_data *data, struct rdt_resource *r,
 131              struct rdt_domain *d)
 132{
 133        struct rdtgroup *rdtgrp = data->rdtgrp;
 134        u32 cbm_val;
 135
 136        if (d->have_new_ctrl) {
 137                rdt_last_cmd_printf("duplicate domain %d\n", d->id);
 138                return -EINVAL;
 139        }
 140
 141        /*
 142         * Cannot set up more than one pseudo-locked region in a cache
 143         * hierarchy.
 144         */
 145        if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
 146            rdtgroup_pseudo_locked_in_hierarchy(d)) {
 147                rdt_last_cmd_printf("pseudo-locked region in hierarchy\n");
 148                return -EINVAL;
 149        }
 150
 151        if (!cbm_validate(data->buf, &cbm_val, r))
 152                return -EINVAL;
 153
 154        if ((rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
 155             rdtgrp->mode == RDT_MODE_SHAREABLE) &&
 156            rdtgroup_cbm_overlaps_pseudo_locked(d, cbm_val)) {
 157                rdt_last_cmd_printf("CBM overlaps with pseudo-locked region\n");
 158                return -EINVAL;
 159        }
 160
 161        /*
 162         * The CBM may not overlap with the CBM of another closid if
 163         * either is exclusive.
 164         */
 165        if (rdtgroup_cbm_overlaps(r, d, cbm_val, rdtgrp->closid, true)) {
 166                rdt_last_cmd_printf("overlaps with exclusive group\n");
 167                return -EINVAL;
 168        }
 169
 170        if (rdtgroup_cbm_overlaps(r, d, cbm_val, rdtgrp->closid, false)) {
 171                if (rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
 172                    rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
 173                        rdt_last_cmd_printf("overlaps with other group\n");
 174                        return -EINVAL;
 175                }
 176        }
 177
 178        d->new_ctrl = cbm_val;
 179        d->have_new_ctrl = true;
 180
 181        return 0;
 182}
 183
 184/*
 185 * For each domain in this resource we expect to find a series of:
 186 *      id=mask
 187 * separated by ";". The "id" is in decimal, and must match one of
 188 * the "id"s for this resource.
 189 */
 190static int parse_line(char *line, struct rdt_resource *r,
 191                      struct rdtgroup *rdtgrp)
 192{
 193        struct rdt_parse_data data;
 194        char *dom = NULL, *id;
 195        struct rdt_domain *d;
 196        unsigned long dom_id;
 197
 198        if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
 199            r->rid == RDT_RESOURCE_MBA) {
 200                rdt_last_cmd_puts("Cannot pseudo-lock MBA resource\n");
 201                return -EINVAL;
 202        }
 203
 204next:
 205        if (!line || line[0] == '\0')
 206                return 0;
 207        dom = strsep(&line, ";");
 208        id = strsep(&dom, "=");
 209        if (!dom || kstrtoul(id, 10, &dom_id)) {
 210                rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
 211                return -EINVAL;
 212        }
 213        dom = strim(dom);
 214        list_for_each_entry(d, &r->domains, list) {
 215                if (d->id == dom_id) {
 216                        data.buf = dom;
 217                        data.rdtgrp = rdtgrp;
 218                        if (r->parse_ctrlval(&data, r, d))
 219                                return -EINVAL;
 220                        if (rdtgrp->mode ==  RDT_MODE_PSEUDO_LOCKSETUP) {
 221                                /*
 222                                 * In pseudo-locking setup mode and just
 223                                 * parsed a valid CBM that should be
 224                                 * pseudo-locked. Only one locked region per
 225                                 * resource group and domain so just do
 226                                 * the required initialization for single
 227                                 * region and return.
 228                                 */
 229                                rdtgrp->plr->r = r;
 230                                rdtgrp->plr->d = d;
 231                                rdtgrp->plr->cbm = d->new_ctrl;
 232                                d->plr = rdtgrp->plr;
 233                                return 0;
 234                        }
 235                        goto next;
 236                }
 237        }
 238        return -EINVAL;
 239}
 240
 241int update_domains(struct rdt_resource *r, int closid)
 242{
 243        struct msr_param msr_param;
 244        cpumask_var_t cpu_mask;
 245        struct rdt_domain *d;
 246        bool mba_sc;
 247        u32 *dc;
 248        int cpu;
 249
 250        if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL))
 251                return -ENOMEM;
 252
 253        msr_param.low = closid;
 254        msr_param.high = msr_param.low + 1;
 255        msr_param.res = r;
 256
 257        mba_sc = is_mba_sc(r);
 258        list_for_each_entry(d, &r->domains, list) {
 259                dc = !mba_sc ? d->ctrl_val : d->mbps_val;
 260                if (d->have_new_ctrl && d->new_ctrl != dc[closid]) {
 261                        cpumask_set_cpu(cpumask_any(&d->cpu_mask), cpu_mask);
 262                        dc[closid] = d->new_ctrl;
 263                }
 264        }
 265
 266        /*
 267         * Avoid writing the control msr with control values when
 268         * MBA software controller is enabled
 269         */
 270        if (cpumask_empty(cpu_mask) || mba_sc)
 271                goto done;
 272        cpu = get_cpu();
 273        /* Update CBM on this cpu if it's in cpu_mask. */
 274        if (cpumask_test_cpu(cpu, cpu_mask))
 275                rdt_ctrl_update(&msr_param);
 276        /* Update CBM on other cpus. */
 277        smp_call_function_many(cpu_mask, rdt_ctrl_update, &msr_param, 1);
 278        put_cpu();
 279
 280done:
 281        free_cpumask_var(cpu_mask);
 282
 283        return 0;
 284}
 285
 286static int rdtgroup_parse_resource(char *resname, char *tok,
 287                                   struct rdtgroup *rdtgrp)
 288{
 289        struct rdt_resource *r;
 290
 291        for_each_alloc_enabled_rdt_resource(r) {
 292                if (!strcmp(resname, r->name) && rdtgrp->closid < r->num_closid)
 293                        return parse_line(tok, r, rdtgrp);
 294        }
 295        rdt_last_cmd_printf("unknown/unsupported resource name '%s'\n", resname);
 296        return -EINVAL;
 297}
 298
 299ssize_t rdtgroup_schemata_write(struct kernfs_open_file *of,
 300                                char *buf, size_t nbytes, loff_t off)
 301{
 302        struct rdtgroup *rdtgrp;
 303        struct rdt_domain *dom;
 304        struct rdt_resource *r;
 305        char *tok, *resname;
 306        int ret = 0;
 307
 308        /* Valid input requires a trailing newline */
 309        if (nbytes == 0 || buf[nbytes - 1] != '\n')
 310                return -EINVAL;
 311        buf[nbytes - 1] = '\0';
 312
 313        rdtgrp = rdtgroup_kn_lock_live(of->kn);
 314        if (!rdtgrp) {
 315                rdtgroup_kn_unlock(of->kn);
 316                return -ENOENT;
 317        }
 318        rdt_last_cmd_clear();
 319
 320        /*
 321         * No changes to pseudo-locked region allowed. It has to be removed
 322         * and re-created instead.
 323         */
 324        if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
 325                ret = -EINVAL;
 326                rdt_last_cmd_puts("resource group is pseudo-locked\n");
 327                goto out;
 328        }
 329
 330        for_each_alloc_enabled_rdt_resource(r) {
 331                list_for_each_entry(dom, &r->domains, list)
 332                        dom->have_new_ctrl = false;
 333        }
 334
 335        while ((tok = strsep(&buf, "\n")) != NULL) {
 336                resname = strim(strsep(&tok, ":"));
 337                if (!tok) {
 338                        rdt_last_cmd_puts("Missing ':'\n");
 339                        ret = -EINVAL;
 340                        goto out;
 341                }
 342                if (tok[0] == '\0') {
 343                        rdt_last_cmd_printf("Missing '%s' value\n", resname);
 344                        ret = -EINVAL;
 345                        goto out;
 346                }
 347                ret = rdtgroup_parse_resource(resname, tok, rdtgrp);
 348                if (ret)
 349                        goto out;
 350        }
 351
 352        for_each_alloc_enabled_rdt_resource(r) {
 353                ret = update_domains(r, rdtgrp->closid);
 354                if (ret)
 355                        goto out;
 356        }
 357
 358        if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
 359                /*
 360                 * If pseudo-locking fails we keep the resource group in
 361                 * mode RDT_MODE_PSEUDO_LOCKSETUP with its class of service
 362                 * active and updated for just the domain the pseudo-locked
 363                 * region was requested for.
 364                 */
 365                ret = rdtgroup_pseudo_lock_create(rdtgrp);
 366        }
 367
 368out:
 369        rdtgroup_kn_unlock(of->kn);
 370        return ret ?: nbytes;
 371}
 372
 373static void show_doms(struct seq_file *s, struct rdt_resource *r, int closid)
 374{
 375        struct rdt_domain *dom;
 376        bool sep = false;
 377        u32 ctrl_val;
 378
 379        seq_printf(s, "%*s:", max_name_width, r->name);
 380        list_for_each_entry(dom, &r->domains, list) {
 381                if (sep)
 382                        seq_puts(s, ";");
 383
 384                ctrl_val = (!is_mba_sc(r) ? dom->ctrl_val[closid] :
 385                            dom->mbps_val[closid]);
 386                seq_printf(s, r->format_str, dom->id, max_data_width,
 387                           ctrl_val);
 388                sep = true;
 389        }
 390        seq_puts(s, "\n");
 391}
 392
 393int rdtgroup_schemata_show(struct kernfs_open_file *of,
 394                           struct seq_file *s, void *v)
 395{
 396        struct rdtgroup *rdtgrp;
 397        struct rdt_resource *r;
 398        int ret = 0;
 399        u32 closid;
 400
 401        rdtgrp = rdtgroup_kn_lock_live(of->kn);
 402        if (rdtgrp) {
 403                if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
 404                        for_each_alloc_enabled_rdt_resource(r)
 405                                seq_printf(s, "%s:uninitialized\n", r->name);
 406                } else if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
 407                        seq_printf(s, "%s:%d=%x\n", rdtgrp->plr->r->name,
 408                                   rdtgrp->plr->d->id, rdtgrp->plr->cbm);
 409                } else {
 410                        closid = rdtgrp->closid;
 411                        for_each_alloc_enabled_rdt_resource(r) {
 412                                if (closid < r->num_closid)
 413                                        show_doms(s, r, closid);
 414                        }
 415                }
 416        } else {
 417                ret = -ENOENT;
 418        }
 419        rdtgroup_kn_unlock(of->kn);
 420        return ret;
 421}
 422
 423void mon_event_read(struct rmid_read *rr, struct rdt_domain *d,
 424                    struct rdtgroup *rdtgrp, int evtid, int first)
 425{
 426        /*
 427         * setup the parameters to send to the IPI to read the data.
 428         */
 429        rr->rgrp = rdtgrp;
 430        rr->evtid = evtid;
 431        rr->d = d;
 432        rr->val = 0;
 433        rr->first = first;
 434
 435        smp_call_function_any(&d->cpu_mask, mon_event_count, rr, 1);
 436}
 437
 438int rdtgroup_mondata_show(struct seq_file *m, void *arg)
 439{
 440        struct kernfs_open_file *of = m->private;
 441        u32 resid, evtid, domid;
 442        struct rdtgroup *rdtgrp;
 443        struct rdt_resource *r;
 444        union mon_data_bits md;
 445        struct rdt_domain *d;
 446        struct rmid_read rr;
 447        int ret = 0;
 448
 449        rdtgrp = rdtgroup_kn_lock_live(of->kn);
 450
 451        md.priv = of->kn->priv;
 452        resid = md.u.rid;
 453        domid = md.u.domid;
 454        evtid = md.u.evtid;
 455
 456        r = &rdt_resources_all[resid];
 457        d = rdt_find_domain(r, domid, NULL);
 458        if (!d) {
 459                ret = -ENOENT;
 460                goto out;
 461        }
 462
 463        mon_event_read(&rr, d, rdtgrp, evtid, false);
 464
 465        if (rr.val & RMID_VAL_ERROR)
 466                seq_puts(m, "Error\n");
 467        else if (rr.val & RMID_VAL_UNAVAIL)
 468                seq_puts(m, "Unavailable\n");
 469        else
 470                seq_printf(m, "%llu\n", rr.val * r->mon_scale);
 471
 472out:
 473        rdtgroup_kn_unlock(of->kn);
 474        return ret;
 475}
 476