Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/fs/resctrl/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 105 kB image not shown  

Quelle  rdtgroup.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * User interface for Resource Allocation in Resource Director Technology(RDT)
 *
 * Copyright (C) 2016 Intel Corporation
 *
 * Author: Fenghua Yu <fenghua.yu@intel.com>
 *
 * More information about RDT be found in the Intel (R) x86 Architecture
 * Software Developer Manual.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/cpu.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/fs_parser.h>
#include <linux/sysfs.h>
#include <linux/kernfs.h>
#include <linux/resctrl.h>
#include <linux/seq_buf.h>
#include <linux/seq_file.h>
#include <linux/sched/task.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>

#include <uapi/linux/magic.h>

#include "internal.h"

/* Mutex to protect rdtgroup access. */
DEFINE_MUTEX(rdtgroup_mutex);

static struct kernfs_root *rdt_root;

struct rdtgroup rdtgroup_default;

LIST_HEAD(rdt_all_groups);

/* list of entries for the schemata file */
LIST_HEAD(resctrl_schema_all);

/*
 * List of struct mon_data containing private data of event files for use by
 * rdtgroup_mondata_show(). Protected by rdtgroup_mutex.
 */

static LIST_HEAD(mon_data_kn_priv_list);

/* The filesystem can only be mounted once. */
bool resctrl_mounted;

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

/* Kernel fs node for "mon_groups" directory under root */
static struct kernfs_node *kn_mongrp;

/* Kernel fs node for "mon_data" directory under root */
static struct kernfs_node *kn_mondata;

/*
 * Used to store the max resource name width to display the schemata names in
 * a tabular format.
 */

int max_name_width;

static struct seq_buf last_cmd_status;

static char last_cmd_status_buf[512];

static int rdtgroup_setup_root(struct rdt_fs_context *ctx);

static void rdtgroup_destroy_root(void);

struct dentry *debugfs_resctrl;

/*
 * Memory bandwidth monitoring event to use for the default CTRL_MON group
 * and each new CTRL_MON group created by the user.  Only relevant when
 * the filesystem is mounted with the "mba_MBps" option so it does not
 * matter that it remains uninitialized on systems that do not support
 * the "mba_MBps" option.
 */

enum resctrl_event_id mba_mbps_default_event;

static bool resctrl_debug;

void rdt_last_cmd_clear(void)
{
 lockdep_assert_held(&rdtgroup_mutex);
 seq_buf_clear(&last_cmd_status);
}

void rdt_last_cmd_puts(const char *s)
{
 lockdep_assert_held(&rdtgroup_mutex);
 seq_buf_puts(&last_cmd_status, s);
}

void rdt_last_cmd_printf(const char *fmt, ...)
{
 va_list ap;

 va_start(ap, fmt);
 lockdep_assert_held(&rdtgroup_mutex);
 seq_buf_vprintf(&last_cmd_status, fmt, ap);
 va_end(ap);
}

void rdt_staged_configs_clear(void)
{
 struct rdt_ctrl_domain *dom;
 struct rdt_resource *r;

 lockdep_assert_held(&rdtgroup_mutex);

 for_each_alloc_capable_rdt_resource(r) {
  list_for_each_entry(dom, &r->ctrl_domains, hdr.list)
   memset(dom->staged_config, 0, sizeof(dom->staged_config));
 }
}

static bool resctrl_is_mbm_enabled(void)
{
 return (resctrl_arch_is_mbm_total_enabled() ||
  resctrl_arch_is_mbm_local_enabled());
}

static bool resctrl_is_mbm_event(int e)
{
 return (e >= QOS_L3_MBM_TOTAL_EVENT_ID &&
  e <= QOS_L3_MBM_LOCAL_EVENT_ID);
}

/*
 * Trivial allocator for CLOSIDs. Use BITMAP APIs to manipulate a bitmap
 * of free CLOSIDs.
 *
 * Using a global CLOSID across all resources has some advantages and
 * some drawbacks:
 * + We can simply set current's closid to assign a task to a resource
 *   group.
 * + Context switch code can avoid extra memory references deciding which
 *   CLOSID to load into the PQR_ASSOC MSR
 * - We give up some options in configuring resource groups across multi-socket
 *   systems.
 * - Our choices on how to configure each resource become progressively more
 *   limited as the number of resources grows.
 */

static unsigned long *closid_free_map;

static int closid_free_map_len;

int closids_supported(void)
{
 return closid_free_map_len;
}

static int closid_init(void)
{
 struct resctrl_schema *s;
 u32 rdt_min_closid = ~0;

 /* Monitor only platforms still call closid_init() */
 if (list_empty(&resctrl_schema_all))
  return 0;

 /* Compute rdt_min_closid across all resources */
 list_for_each_entry(s, &resctrl_schema_all, list)
  rdt_min_closid = min(rdt_min_closid, s->num_closid);

 closid_free_map = bitmap_alloc(rdt_min_closid, GFP_KERNEL);
 if (!closid_free_map)
  return -ENOMEM;
 bitmap_fill(closid_free_map, rdt_min_closid);

 /* RESCTRL_RESERVED_CLOSID is always reserved for the default group */
 __clear_bit(RESCTRL_RESERVED_CLOSID, closid_free_map);
 closid_free_map_len = rdt_min_closid;

 return 0;
}

static void closid_exit(void)
{
 bitmap_free(closid_free_map);
 closid_free_map = NULL;
}

static int closid_alloc(void)
{
 int cleanest_closid;
 u32 closid;

 lockdep_assert_held(&rdtgroup_mutex);

 if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID) &&
     resctrl_arch_is_llc_occupancy_enabled()) {
  cleanest_closid = resctrl_find_cleanest_closid();
  if (cleanest_closid < 0)
   return cleanest_closid;
  closid = cleanest_closid;
 } else {
  closid = find_first_bit(closid_free_map, closid_free_map_len);
  if (closid == closid_free_map_len)
   return -ENOSPC;
 }
 __clear_bit(closid, closid_free_map);

 return closid;
}

void closid_free(int closid)
{
 lockdep_assert_held(&rdtgroup_mutex);

 __set_bit(closid, closid_free_map);
}

/**
 * closid_allocated - test if provided closid is in use
 * @closid: closid to be tested
 *
 * Return: true if @closid is currently associated with a resource group,
 * false if @closid is free
 */

bool closid_allocated(unsigned int closid)
{
 lockdep_assert_held(&rdtgroup_mutex);

 return !test_bit(closid, closid_free_map);
}

/**
 * rdtgroup_mode_by_closid - Return mode of resource group with closid
 * @closid: closid if the resource group
 *
 * Each resource group is associated with a @closid. Here the mode
 * of a resource group can be queried by searching for it using its closid.
 *
 * Return: mode as &enum rdtgrp_mode of resource group with closid @closid
 */

enum rdtgrp_mode rdtgroup_mode_by_closid(int closid)
{
 struct rdtgroup *rdtgrp;

 list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) {
  if (rdtgrp->closid == closid)
   return rdtgrp->mode;
 }

 return RDT_NUM_MODES;
}

static const char * const rdt_mode_str[] = {
 [RDT_MODE_SHAREABLE]  = "shareable",
 [RDT_MODE_EXCLUSIVE]  = "exclusive",
 [RDT_MODE_PSEUDO_LOCKSETUP] = "pseudo-locksetup",
 [RDT_MODE_PSEUDO_LOCKED] = "pseudo-locked",
};

/**
 * rdtgroup_mode_str - Return the string representation of mode
 * @mode: the resource group mode as &enum rdtgroup_mode
 *
 * Return: string representation of valid mode, "unknown" otherwise
 */

static const char *rdtgroup_mode_str(enum rdtgrp_mode mode)
{
 if (mode < RDT_MODE_SHAREABLE || mode >= RDT_NUM_MODES)
  return "unknown";

 return rdt_mode_str[mode];
}

/* 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,
      GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
      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_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 const struct kernfs_ops rdtgroup_kf_single_ops = {
 .atomic_write_len = PAGE_SIZE,
 .write   = rdtgroup_file_write,
 .seq_show  = rdtgroup_seqfile_show,
};

static const struct kernfs_ops kf_mondata_ops = {
 .atomic_write_len = PAGE_SIZE,
 .seq_show  = rdtgroup_mondata_show,
};

static bool is_cpu_list(struct kernfs_open_file *of)
{
 struct rftype *rft = of->kn->priv;

 return rft->flags & RFTYPE_FLAGS_CPUS_LIST;
}

static int rdtgroup_cpus_show(struct kernfs_open_file *of,
         struct seq_file *s, void *v)
{
 struct rdtgroup *rdtgrp;
 struct cpumask *mask;
 int ret = 0;

 rdtgrp = rdtgroup_kn_lock_live(of->kn);

 if (rdtgrp) {
  if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
   if (!rdtgrp->plr->d) {
    rdt_last_cmd_clear();
    rdt_last_cmd_puts("Cache domain offline\n");
    ret = -ENODEV;
   } else {
    mask = &rdtgrp->plr->d->hdr.cpu_mask;
    seq_printf(s, is_cpu_list(of) ?
        "%*pbl\n" : "%*pb\n",
        cpumask_pr_args(mask));
   }
  } else {
   seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n",
       cpumask_pr_args(&rdtgrp->cpu_mask));
  }
 } else {
  ret = -ENOENT;
 }
 rdtgroup_kn_unlock(of->kn);

 return ret;
}

/*
 * Update the PGR_ASSOC MSR on all cpus in @cpu_mask,
 *
 * Per task closids/rmids must have been set up before calling this function.
 * @r may be NULL.
 */

static void
update_closid_rmid(const struct cpumask *cpu_mask, struct rdtgroup *r)
{
 struct resctrl_cpu_defaults defaults, *p = NULL;

 if (r) {
  defaults.closid = r->closid;
  defaults.rmid = r->mon.rmid;
  p = &defaults;
 }

 on_each_cpu_mask(cpu_mask, resctrl_arch_sync_cpu_closid_rmid, p, 1);
}

static int cpus_mon_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask,
     cpumask_var_t tmpmask)
{
 struct rdtgroup *prgrp = rdtgrp->mon.parent, *crgrp;
 struct list_head *head;

 /* Check whether cpus belong to parent ctrl group */
 cpumask_andnot(tmpmask, newmask, &prgrp->cpu_mask);
 if (!cpumask_empty(tmpmask)) {
  rdt_last_cmd_puts("Can only add CPUs to mongroup that belong to parent\n");
  return -EINVAL;
 }

 /* Check whether cpus are dropped from this group */
 cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask);
 if (!cpumask_empty(tmpmask)) {
  /* Give any dropped cpus to parent rdtgroup */
  cpumask_or(&prgrp->cpu_mask, &prgrp->cpu_mask, tmpmask);
  update_closid_rmid(tmpmask, prgrp);
 }

 /*
 * If we added cpus, remove them from previous group that owned them
 * and update per-cpu rmid
 */

 cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask);
 if (!cpumask_empty(tmpmask)) {
  head = &prgrp->mon.crdtgrp_list;
  list_for_each_entry(crgrp, head, mon.crdtgrp_list) {
   if (crgrp == rdtgrp)
    continue;
   cpumask_andnot(&crgrp->cpu_mask, &crgrp->cpu_mask,
           tmpmask);
  }
  update_closid_rmid(tmpmask, rdtgrp);
 }

 /* Done pushing/pulling - update this group with new mask */
 cpumask_copy(&rdtgrp->cpu_mask, newmask);

 return 0;
}

static void cpumask_rdtgrp_clear(struct rdtgroup *r, struct cpumask *m)
{
 struct rdtgroup *crgrp;

 cpumask_andnot(&r->cpu_mask, &r->cpu_mask, m);
 /* update the child mon group masks as well*/
 list_for_each_entry(crgrp, &r->mon.crdtgrp_list, mon.crdtgrp_list)
  cpumask_and(&crgrp->cpu_mask, &r->cpu_mask, &crgrp->cpu_mask);
}

static int cpus_ctrl_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask,
      cpumask_var_t tmpmask, cpumask_var_t tmpmask1)
{
 struct rdtgroup *r, *crgrp;
 struct list_head *head;

 /* Check whether cpus are dropped from this group */
 cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask);
 if (!cpumask_empty(tmpmask)) {
  /* Can't drop from default group */
  if (rdtgrp == &rdtgroup_default) {
   rdt_last_cmd_puts("Can't drop CPUs from default group\n");
   return -EINVAL;
  }

  /* Give any dropped cpus to rdtgroup_default */
  cpumask_or(&rdtgroup_default.cpu_mask,
      &rdtgroup_default.cpu_mask, tmpmask);
  update_closid_rmid(tmpmask, &rdtgroup_default);
 }

 /*
 * If we added cpus, remove them from previous group and
 * the prev group's child groups that owned them
 * and update per-cpu closid/rmid.
 */

 cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask);
 if (!cpumask_empty(tmpmask)) {
  list_for_each_entry(r, &rdt_all_groups, rdtgroup_list) {
   if (r == rdtgrp)
    continue;
   cpumask_and(tmpmask1, &r->cpu_mask, tmpmask);
   if (!cpumask_empty(tmpmask1))
    cpumask_rdtgrp_clear(r, tmpmask1);
  }
  update_closid_rmid(tmpmask, rdtgrp);
 }

 /* Done pushing/pulling - update this group with new mask */
 cpumask_copy(&rdtgrp->cpu_mask, newmask);

 /*
 * Clear child mon group masks since there is a new parent mask
 * now and update the rmid for the cpus the child lost.
 */

 head = &rdtgrp->mon.crdtgrp_list;
 list_for_each_entry(crgrp, head, mon.crdtgrp_list) {
  cpumask_and(tmpmask, &rdtgrp->cpu_mask, &crgrp->cpu_mask);
  update_closid_rmid(tmpmask, rdtgrp);
  cpumask_clear(&crgrp->cpu_mask);
 }

 return 0;
}

static ssize_t rdtgroup_cpus_write(struct kernfs_open_file *of,
       char *buf, size_t nbytes, loff_t off)
{
 cpumask_var_t tmpmask, newmask, tmpmask1;
 struct rdtgroup *rdtgrp;
 int ret;

 if (!buf)
  return -EINVAL;

 if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
  return -ENOMEM;
 if (!zalloc_cpumask_var(&newmask, GFP_KERNEL)) {
  free_cpumask_var(tmpmask);
  return -ENOMEM;
 }
 if (!zalloc_cpumask_var(&tmpmask1, GFP_KERNEL)) {
  free_cpumask_var(tmpmask);
  free_cpumask_var(newmask);
  return -ENOMEM;
 }

 rdtgrp = rdtgroup_kn_lock_live(of->kn);
 if (!rdtgrp) {
  ret = -ENOENT;
  goto unlock;
 }

 rdt_last_cmd_clear();

 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED ||
     rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
  ret = -EINVAL;
  rdt_last_cmd_puts("Pseudo-locking in progress\n");
  goto unlock;
 }

 if (is_cpu_list(of))
  ret = cpulist_parse(buf, newmask);
 else
  ret = cpumask_parse(buf, newmask);

 if (ret) {
  rdt_last_cmd_puts("Bad CPU list/mask\n");
  goto unlock;
 }

 /* check that user didn't specify any offline cpus */
 cpumask_andnot(tmpmask, newmask, cpu_online_mask);
 if (!cpumask_empty(tmpmask)) {
  ret = -EINVAL;
  rdt_last_cmd_puts("Can only assign online CPUs\n");
  goto unlock;
 }

 if (rdtgrp->type == RDTCTRL_GROUP)
  ret = cpus_ctrl_write(rdtgrp, newmask, tmpmask, tmpmask1);
 else if (rdtgrp->type == RDTMON_GROUP)
  ret = cpus_mon_write(rdtgrp, newmask, tmpmask);
 else
  ret = -EINVAL;

unlock:
 rdtgroup_kn_unlock(of->kn);
 free_cpumask_var(tmpmask);
 free_cpumask_var(newmask);
 free_cpumask_var(tmpmask1);

 return ret ?: nbytes;
}

/**
 * rdtgroup_remove - the helper to remove resource group safely
 * @rdtgrp: resource group to remove
 *
 * On resource group creation via a mkdir, an extra kernfs_node reference is
 * taken to ensure that the rdtgroup structure remains accessible for the
 * rdtgroup_kn_unlock() calls where it is removed.
 *
 * Drop the extra reference here, then free the rdtgroup structure.
 *
 * Return: void
 */

static void rdtgroup_remove(struct rdtgroup *rdtgrp)
{
 kernfs_put(rdtgrp->kn);
 kfree(rdtgrp);
}

static void _update_task_closid_rmid(void *task)
{
 /*
 * If the task is still current on this CPU, update PQR_ASSOC MSR.
 * Otherwise, the MSR is updated when the task is scheduled in.
 */

 if (task == current)
  resctrl_arch_sched_in(task);
}

static void update_task_closid_rmid(struct task_struct *t)
{
 if (IS_ENABLED(CONFIG_SMP) && task_curr(t))
  smp_call_function_single(task_cpu(t), _update_task_closid_rmid, t, 1);
 else
  _update_task_closid_rmid(t);
}

static bool task_in_rdtgroup(struct task_struct *tsk, struct rdtgroup *rdtgrp)
{
 u32 closid, rmid = rdtgrp->mon.rmid;

 if (rdtgrp->type == RDTCTRL_GROUP)
  closid = rdtgrp->closid;
 else if (rdtgrp->type == RDTMON_GROUP)
  closid = rdtgrp->mon.parent->closid;
 else
  return false;

 return resctrl_arch_match_closid(tsk, closid) &&
        resctrl_arch_match_rmid(tsk, closid, rmid);
}

static int __rdtgroup_move_task(struct task_struct *tsk,
    struct rdtgroup *rdtgrp)
{
 /* If the task is already in rdtgrp, no need to move the task. */
 if (task_in_rdtgroup(tsk, rdtgrp))
  return 0;

 /*
 * Set the task's closid/rmid before the PQR_ASSOC MSR can be
 * updated by them.
 *
 * For ctrl_mon groups, move both closid and rmid.
 * For monitor groups, can move the tasks only from
 * their parent CTRL group.
 */

 if (rdtgrp->type == RDTMON_GROUP &&
     !resctrl_arch_match_closid(tsk, rdtgrp->mon.parent->closid)) {
  rdt_last_cmd_puts("Can't move task to different control group\n");
  return -EINVAL;
 }

 if (rdtgrp->type == RDTMON_GROUP)
  resctrl_arch_set_closid_rmid(tsk, rdtgrp->mon.parent->closid,
          rdtgrp->mon.rmid);
 else
  resctrl_arch_set_closid_rmid(tsk, rdtgrp->closid,
          rdtgrp->mon.rmid);

 /*
 * Ensure the task's closid and rmid are written before determining if
 * the task is current that will decide if it will be interrupted.
 * This pairs with the full barrier between the rq->curr update and
 * resctrl_arch_sched_in() during context switch.
 */

 smp_mb();

 /*
 * By now, the task's closid and rmid are set. If the task is current
 * on a CPU, the PQR_ASSOC MSR needs to be updated to make the resource
 * group go into effect. If the task is not current, the MSR will be
 * updated when the task is scheduled in.
 */

 update_task_closid_rmid(tsk);

 return 0;
}

static bool is_closid_match(struct task_struct *t, struct rdtgroup *r)
{
 return (resctrl_arch_alloc_capable() && (r->type == RDTCTRL_GROUP) &&
  resctrl_arch_match_closid(t, r->closid));
}

static bool is_rmid_match(struct task_struct *t, struct rdtgroup *r)
{
 return (resctrl_arch_mon_capable() && (r->type == RDTMON_GROUP) &&
  resctrl_arch_match_rmid(t, r->mon.parent->closid,
     r->mon.rmid));
}

/**
 * rdtgroup_tasks_assigned - Test if tasks have been assigned to resource group
 * @r: Resource group
 *
 * Return: 1 if tasks have been assigned to @r, 0 otherwise
 */

int rdtgroup_tasks_assigned(struct rdtgroup *r)
{
 struct task_struct *p, *t;
 int ret = 0;

 lockdep_assert_held(&rdtgroup_mutex);

 rcu_read_lock();
 for_each_process_thread(p, t) {
  if (is_closid_match(t, r) || is_rmid_match(t, r)) {
   ret = 1;
   break;
  }
 }
 rcu_read_unlock();

 return ret;
}

static int rdtgroup_task_write_permission(struct task_struct *task,
       struct kernfs_open_file *of)
{
 const struct cred *tcred = get_task_cred(task);
 const struct cred *cred = current_cred();
 int ret = 0;

 /*
 * Even if we're attaching all tasks in the thread group, we only
 * need to check permissions on one of them.
 */

 if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
     !uid_eq(cred->euid, tcred->uid) &&
     !uid_eq(cred->euid, tcred->suid)) {
  rdt_last_cmd_printf("No permission to move task %d\n", task->pid);
  ret = -EPERM;
 }

 put_cred(tcred);
 return ret;
}

static int rdtgroup_move_task(pid_t pid, struct rdtgroup *rdtgrp,
         struct kernfs_open_file *of)
{
 struct task_struct *tsk;
 int ret;

 rcu_read_lock();
 if (pid) {
  tsk = find_task_by_vpid(pid);
  if (!tsk) {
   rcu_read_unlock();
   rdt_last_cmd_printf("No task %d\n", pid);
   return -ESRCH;
  }
 } else {
  tsk = current;
 }

 get_task_struct(tsk);
 rcu_read_unlock();

 ret = rdtgroup_task_write_permission(tsk, of);
 if (!ret)
  ret = __rdtgroup_move_task(tsk, rdtgrp);

 put_task_struct(tsk);
 return ret;
}

static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of,
        char *buf, size_t nbytes, loff_t off)
{
 struct rdtgroup *rdtgrp;
 char *pid_str;
 int ret = 0;
 pid_t pid;

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

 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED ||
     rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
  ret = -EINVAL;
  rdt_last_cmd_puts("Pseudo-locking in progress\n");
  goto unlock;
 }

 while (buf && buf[0] != '\0' && buf[0] != '\n') {
  pid_str = strim(strsep(&buf, ","));

  if (kstrtoint(pid_str, 0, &pid)) {
   rdt_last_cmd_printf("Task list parsing error pid %s\n", pid_str);
   ret = -EINVAL;
   break;
  }

  if (pid < 0) {
   rdt_last_cmd_printf("Invalid pid %d\n", pid);
   ret = -EINVAL;
   break;
  }

  ret = rdtgroup_move_task(pid, rdtgrp, of);
  if (ret) {
   rdt_last_cmd_printf("Error while processing task %d\n", pid);
   break;
  }
 }

unlock:
 rdtgroup_kn_unlock(of->kn);

 return ret ?: nbytes;
}

static void show_rdt_tasks(struct rdtgroup *r, struct seq_file *s)
{
 struct task_struct *p, *t;
 pid_t pid;

 rcu_read_lock();
 for_each_process_thread(p, t) {
  if (is_closid_match(t, r) || is_rmid_match(t, r)) {
   pid = task_pid_vnr(t);
   if (pid)
    seq_printf(s, "%d\n", pid);
  }
 }
 rcu_read_unlock();
}

static int rdtgroup_tasks_show(struct kernfs_open_file *of,
          struct seq_file *s, void *v)
{
 struct rdtgroup *rdtgrp;
 int ret = 0;

 rdtgrp = rdtgroup_kn_lock_live(of->kn);
 if (rdtgrp)
  show_rdt_tasks(rdtgrp, s);
 else
  ret = -ENOENT;
 rdtgroup_kn_unlock(of->kn);

 return ret;
}

static int rdtgroup_closid_show(struct kernfs_open_file *of,
    struct seq_file *s, void *v)
{
 struct rdtgroup *rdtgrp;
 int ret = 0;

 rdtgrp = rdtgroup_kn_lock_live(of->kn);
 if (rdtgrp)
  seq_printf(s, "%u\n", rdtgrp->closid);
 else
  ret = -ENOENT;
 rdtgroup_kn_unlock(of->kn);

 return ret;
}

static int rdtgroup_rmid_show(struct kernfs_open_file *of,
         struct seq_file *s, void *v)
{
 struct rdtgroup *rdtgrp;
 int ret = 0;

 rdtgrp = rdtgroup_kn_lock_live(of->kn);
 if (rdtgrp)
  seq_printf(s, "%u\n", rdtgrp->mon.rmid);
 else
  ret = -ENOENT;
 rdtgroup_kn_unlock(of->kn);

 return ret;
}

#ifdef CONFIG_PROC_CPU_RESCTRL
/*
 * A task can only be part of one resctrl control group and of one monitor
 * group which is associated to that control group.
 *
 * 1)   res:
 *      mon:
 *
 *    resctrl is not available.
 *
 * 2)   res:/
 *      mon:
 *
 *    Task is part of the root resctrl control group, and it is not associated
 *    to any monitor group.
 *
 * 3)  res:/
 *     mon:mon0
 *
 *    Task is part of the root resctrl control group and monitor group mon0.
 *
 * 4)  res:group0
 *     mon:
 *
 *    Task is part of resctrl control group group0, and it is not associated
 *    to any monitor group.
 *
 * 5) res:group0
 *    mon:mon1
 *
 *    Task is part of resctrl control group group0 and monitor group mon1.
 */

int proc_resctrl_show(struct seq_file *s, struct pid_namespace *ns,
        struct pid *pid, struct task_struct *tsk)
{
 struct rdtgroup *rdtg;
 int ret = 0;

 mutex_lock(&rdtgroup_mutex);

 /* Return empty if resctrl has not been mounted. */
 if (!resctrl_mounted) {
  seq_puts(s, "res:\nmon:\n");
  goto unlock;
 }

 list_for_each_entry(rdtg, &rdt_all_groups, rdtgroup_list) {
  struct rdtgroup *crg;

  /*
 * Task information is only relevant for shareable
 * and exclusive groups.
 */

  if (rdtg->mode != RDT_MODE_SHAREABLE &&
      rdtg->mode != RDT_MODE_EXCLUSIVE)
   continue;

  if (!resctrl_arch_match_closid(tsk, rdtg->closid))
   continue;

  seq_printf(s, "res:%s%s\n", (rdtg == &rdtgroup_default) ? "/" : "",
      rdt_kn_name(rdtg->kn));
  seq_puts(s, "mon:");
  list_for_each_entry(crg, &rdtg->mon.crdtgrp_list,
        mon.crdtgrp_list) {
   if (!resctrl_arch_match_rmid(tsk, crg->mon.parent->closid,
           crg->mon.rmid))
    continue;
   seq_printf(s, "%s", rdt_kn_name(crg->kn));
   break;
  }
  seq_putc(s, '\n');
  goto unlock;
 }
 /*
 * The above search should succeed. Otherwise return
 * with an error.
 */

 ret = -ENOENT;
unlock:
 mutex_unlock(&rdtgroup_mutex);

 return ret;
}
#endif

static int rdt_last_cmd_status_show(struct kernfs_open_file *of,
        struct seq_file *seq, void *v)
{
 int len;

 mutex_lock(&rdtgroup_mutex);
 len = seq_buf_used(&last_cmd_status);
 if (len)
  seq_printf(seq, "%.*s", len, last_cmd_status_buf);
 else
  seq_puts(seq, "ok\n");
 mutex_unlock(&rdtgroup_mutex);
 return 0;
}

static void *rdt_kn_parent_priv(struct kernfs_node *kn)
{
 /*
 * The parent pointer is only valid within RCU section since it can be
 * replaced.
 */

 guard(rcu)();
 return rcu_dereference(kn->__parent)->priv;
}

static int rdt_num_closids_show(struct kernfs_open_file *of,
    struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);

 seq_printf(seq, "%u\n", s->num_closid);
 return 0;
}

static int rdt_default_ctrl_show(struct kernfs_open_file *of,
     struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%x\n", resctrl_get_default_ctrl(r));
 return 0;
}

static int rdt_min_cbm_bits_show(struct kernfs_open_file *of,
     struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%u\n", r->cache.min_cbm_bits);
 return 0;
}

static int rdt_shareable_bits_show(struct kernfs_open_file *of,
       struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%x\n", r->cache.shareable_bits);
 return 0;
}

/*
 * rdt_bit_usage_show - Display current usage of resources
 *
 * A domain is a shared resource that can now be allocated differently. Here
 * we display the current regions of the domain as an annotated bitmask.
 * For each domain of this resource its allocation bitmask
 * is annotated as below to indicate the current usage of the corresponding bit:
 *   0 - currently unused
 *   X - currently available for sharing and used by software and hardware
 *   H - currently used by hardware only but available for software use
 *   S - currently used and shareable by software only
 *   E - currently used exclusively by one resource group
 *   P - currently pseudo-locked by one resource group
 */

static int rdt_bit_usage_show(struct kernfs_open_file *of,
         struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 /*
 * Use unsigned long even though only 32 bits are used to ensure
 * test_bit() is used safely.
 */

 unsigned long sw_shareable = 0, hw_shareable = 0;
 unsigned long exclusive = 0, pseudo_locked = 0;
 struct rdt_resource *r = s->res;
 struct rdt_ctrl_domain *dom;
 int i, hwb, swb, excl, psl;
 enum rdtgrp_mode mode;
 bool sep = false;
 u32 ctrl_val;

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);
 hw_shareable = r->cache.shareable_bits;
 list_for_each_entry(dom, &r->ctrl_domains, hdr.list) {
  if (sep)
   seq_putc(seq, ';');
  sw_shareable = 0;
  exclusive = 0;
  seq_printf(seq, "%d=", dom->hdr.id);
  for (i = 0; i < closids_supported(); i++) {
   if (!closid_allocated(i))
    continue;
   ctrl_val = resctrl_arch_get_config(r, dom, i,
          s->conf_type);
   mode = rdtgroup_mode_by_closid(i);
   switch (mode) {
   case RDT_MODE_SHAREABLE:
    sw_shareable |= ctrl_val;
    break;
   case RDT_MODE_EXCLUSIVE:
    exclusive |= ctrl_val;
    break;
   case RDT_MODE_PSEUDO_LOCKSETUP:
   /*
 * RDT_MODE_PSEUDO_LOCKSETUP is possible
 * here but not included since the CBM
 * associated with this CLOSID in this mode
 * is not initialized and no task or cpu can be
 * assigned this CLOSID.
 */

    break;
   case RDT_MODE_PSEUDO_LOCKED:
   case RDT_NUM_MODES:
    WARN(1,
         "invalid mode for closid %d\n", i);
    break;
   }
  }
  for (i = r->cache.cbm_len - 1; i >= 0; i--) {
   pseudo_locked = dom->plr ? dom->plr->cbm : 0;
   hwb = test_bit(i, &hw_shareable);
   swb = test_bit(i, &sw_shareable);
   excl = test_bit(i, &exclusive);
   psl = test_bit(i, &pseudo_locked);
   if (hwb && swb)
    seq_putc(seq, 'X');
   else if (hwb && !swb)
    seq_putc(seq, 'H');
   else if (!hwb && swb)
    seq_putc(seq, 'S');
   else if (excl)
    seq_putc(seq, 'E');
   else if (psl)
    seq_putc(seq, 'P');
   else /* Unused bits remain */
    seq_putc(seq, '0');
  }
  sep = true;
 }
 seq_putc(seq, '\n');
 mutex_unlock(&rdtgroup_mutex);
 cpus_read_unlock();
 return 0;
}

static int rdt_min_bw_show(struct kernfs_open_file *of,
      struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%u\n", r->membw.min_bw);
 return 0;
}

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

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

 return 0;
}

static int rdt_mon_features_show(struct kernfs_open_file *of,
     struct seq_file *seq, void *v)
{
 struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
 struct mon_evt *mevt;

 list_for_each_entry(mevt, &r->evt_list, list) {
  seq_printf(seq, "%s\n", mevt->name);
  if (mevt->configurable)
   seq_printf(seq, "%s_config\n", mevt->name);
 }

 return 0;
}

static int rdt_bw_gran_show(struct kernfs_open_file *of,
       struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%u\n", r->membw.bw_gran);
 return 0;
}

static int rdt_delay_linear_show(struct kernfs_open_file *of,
     struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%u\n", r->membw.delay_linear);
 return 0;
}

static int max_threshold_occ_show(struct kernfs_open_file *of,
      struct seq_file *seq, void *v)
{
 seq_printf(seq, "%u\n", resctrl_rmid_realloc_threshold);

 return 0;
}

static int rdt_thread_throttle_mode_show(struct kernfs_open_file *of,
      struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 switch (r->membw.throttle_mode) {
 case THREAD_THROTTLE_PER_THREAD:
  seq_puts(seq, "per-thread\n");
  return 0;
 case THREAD_THROTTLE_MAX:
  seq_puts(seq, "max\n");
  return 0;
 case THREAD_THROTTLE_UNDEFINED:
  seq_puts(seq, "undefined\n");
  return 0;
 }

 WARN_ON_ONCE(1);

 return 0;
}

static ssize_t max_threshold_occ_write(struct kernfs_open_file *of,
           char *buf, size_t nbytes, loff_t off)
{
 unsigned int bytes;
 int ret;

 ret = kstrtouint(buf, 0, &bytes);
 if (ret)
  return ret;

 if (bytes > resctrl_rmid_realloc_limit)
  return -EINVAL;

 resctrl_rmid_realloc_threshold = resctrl_arch_round_mon_val(bytes);

 return nbytes;
}

/*
 * rdtgroup_mode_show - Display mode of this resource group
 */

static int rdtgroup_mode_show(struct kernfs_open_file *of,
         struct seq_file *s, void *v)
{
 struct rdtgroup *rdtgrp;

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

 seq_printf(s, "%s\n", rdtgroup_mode_str(rdtgrp->mode));

 rdtgroup_kn_unlock(of->kn);
 return 0;
}

static enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
{
 switch (my_type) {
 case CDP_CODE:
  return CDP_DATA;
 case CDP_DATA:
  return CDP_CODE;
 default:
 case CDP_NONE:
  return CDP_NONE;
 }
}

static int rdt_has_sparse_bitmasks_show(struct kernfs_open_file *of,
     struct seq_file *seq, void *v)
{
 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
 struct rdt_resource *r = s->res;

 seq_printf(seq, "%u\n", r->cache.arch_has_sparse_bitmasks);

 return 0;
}

/**
 * __rdtgroup_cbm_overlaps - Does CBM for intended closid overlap with other
 * @r: Resource to which domain instance @d belongs.
 * @d: The domain instance for which @closid is being tested.
 * @cbm: Capacity bitmask being tested.
 * @closid: Intended closid for @cbm.
 * @type: CDP type of @r.
 * @exclusive: Only check if overlaps with exclusive resource groups
 *
 * Checks if provided @cbm intended to be used for @closid on domain
 * @d overlaps with any other closids or other hardware usage associated
 * with this domain. If @exclusive is true then only overlaps with
 * resource groups in exclusive mode will be considered. If @exclusive
 * is false then overlaps with any resource group or hardware entities
 * will be considered.
 *
 * @cbm is unsigned long, even if only 32 bits are used, to make the
 * bitmap functions work correctly.
 *
 * Return: false if CBM does not overlap, true if it does.
 */

static bool __rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_ctrl_domain *d,
        unsigned long cbm, int closid,
        enum resctrl_conf_type type, bool exclusive)
{
 enum rdtgrp_mode mode;
 unsigned long ctrl_b;
 int i;

 /* Check for any overlap with regions used by hardware directly */
 if (!exclusive) {
  ctrl_b = r->cache.shareable_bits;
  if (bitmap_intersects(&cbm, &ctrl_b, r->cache.cbm_len))
   return true;
 }

 /* Check for overlap with other resource groups */
 for (i = 0; i < closids_supported(); i++) {
  ctrl_b = resctrl_arch_get_config(r, d, i, type);
  mode = rdtgroup_mode_by_closid(i);
  if (closid_allocated(i) && i != closid &&
      mode != RDT_MODE_PSEUDO_LOCKSETUP) {
   if (bitmap_intersects(&cbm, &ctrl_b, r->cache.cbm_len)) {
    if (exclusive) {
     if (mode == RDT_MODE_EXCLUSIVE)
      return true;
     continue;
    }
    return true;
   }
  }
 }

 return false;
}

/**
 * rdtgroup_cbm_overlaps - Does CBM overlap with other use of hardware
 * @s: Schema for the resource to which domain instance @d belongs.
 * @d: The domain instance for which @closid is being tested.
 * @cbm: Capacity bitmask being tested.
 * @closid: Intended closid for @cbm.
 * @exclusive: Only check if overlaps with exclusive resource groups
 *
 * Resources that can be allocated using a CBM can use the CBM to control
 * the overlap of these allocations. rdtgroup_cmb_overlaps() is the test
 * for overlap. Overlap test is not limited to the specific resource for
 * which the CBM is intended though - when dealing with CDP resources that
 * share the underlying hardware the overlap check should be performed on
 * the CDP resource sharing the hardware also.
 *
 * Refer to description of __rdtgroup_cbm_overlaps() for the details of the
 * overlap test.
 *
 * Return: true if CBM overlap detected, false if there is no overlap
 */

bool rdtgroup_cbm_overlaps(struct resctrl_schema *s, struct rdt_ctrl_domain *d,
      unsigned long cbm, int closid, bool exclusive)
{
 enum resctrl_conf_type peer_type = resctrl_peer_type(s->conf_type);
 struct rdt_resource *r = s->res;

 if (__rdtgroup_cbm_overlaps(r, d, cbm, closid, s->conf_type,
        exclusive))
  return true;

 if (!resctrl_arch_get_cdp_enabled(r->rid))
  return false;
 return  __rdtgroup_cbm_overlaps(r, d, cbm, closid, peer_type, exclusive);
}

/**
 * rdtgroup_mode_test_exclusive - Test if this resource group can be exclusive
 * @rdtgrp: Resource group identified through its closid.
 *
 * An exclusive resource group implies that there should be no sharing of
 * its allocated resources. At the time this group is considered to be
 * exclusive this test can determine if its current schemata supports this
 * setting by testing for overlap with all other resource groups.
 *
 * Return: true if resource group can be exclusive, false if there is overlap
 * with allocations of other resource groups and thus this resource group
 * cannot be exclusive.
 */

static bool rdtgroup_mode_test_exclusive(struct rdtgroup *rdtgrp)
{
 int closid = rdtgrp->closid;
 struct rdt_ctrl_domain *d;
 struct resctrl_schema *s;
 struct rdt_resource *r;
 bool has_cache = false;
 u32 ctrl;

 /* Walking r->domains, ensure it can't race with cpuhp */
 lockdep_assert_cpus_held();

 list_for_each_entry(s, &resctrl_schema_all, list) {
  r = s->res;
  if (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_SMBA)
   continue;
  has_cache = true;
  list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
   ctrl = resctrl_arch_get_config(r, d, closid,
             s->conf_type);
   if (rdtgroup_cbm_overlaps(s, d, ctrl, closid, false)) {
    rdt_last_cmd_puts("Schemata overlaps\n");
    return false;
   }
  }
 }

 if (!has_cache) {
  rdt_last_cmd_puts("Cannot be exclusive without CAT/CDP\n");
  return false;
 }

 return true;
}

/*
 * rdtgroup_mode_write - Modify the resource group's mode
 */

static ssize_t rdtgroup_mode_write(struct kernfs_open_file *of,
       char *buf, size_t nbytes, loff_t off)
{
 struct rdtgroup *rdtgrp;
 enum rdtgrp_mode mode;
 int 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;
 }

 rdt_last_cmd_clear();

 mode = rdtgrp->mode;

 if ((!strcmp(buf, "shareable") && mode == RDT_MODE_SHAREABLE) ||
     (!strcmp(buf, "exclusive") && mode == RDT_MODE_EXCLUSIVE) ||
     (!strcmp(buf, "pseudo-locksetup") &&
      mode == RDT_MODE_PSEUDO_LOCKSETUP) ||
     (!strcmp(buf, "pseudo-locked") && mode == RDT_MODE_PSEUDO_LOCKED))
  goto out;

 if (mode == RDT_MODE_PSEUDO_LOCKED) {
  rdt_last_cmd_puts("Cannot change pseudo-locked group\n");
  ret = -EINVAL;
  goto out;
 }

 if (!strcmp(buf, "shareable")) {
  if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
   ret = rdtgroup_locksetup_exit(rdtgrp);
   if (ret)
    goto out;
  }
  rdtgrp->mode = RDT_MODE_SHAREABLE;
 } else if (!strcmp(buf, "exclusive")) {
  if (!rdtgroup_mode_test_exclusive(rdtgrp)) {
   ret = -EINVAL;
   goto out;
  }
  if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
   ret = rdtgroup_locksetup_exit(rdtgrp);
   if (ret)
    goto out;
  }
  rdtgrp->mode = RDT_MODE_EXCLUSIVE;
 } else if (IS_ENABLED(CONFIG_RESCTRL_FS_PSEUDO_LOCK) &&
     !strcmp(buf, "pseudo-locksetup")) {
  ret = rdtgroup_locksetup_enter(rdtgrp);
  if (ret)
   goto out;
  rdtgrp->mode = RDT_MODE_PSEUDO_LOCKSETUP;
 } else {
  rdt_last_cmd_puts("Unknown or unsupported mode\n");
  ret = -EINVAL;
 }

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

/**
 * rdtgroup_cbm_to_size - Translate CBM to size in bytes
 * @r: RDT resource to which @d belongs.
 * @d: RDT domain instance.
 * @cbm: bitmask for which the size should be computed.
 *
 * The bitmask provided associated with the RDT domain instance @d will be
 * translated into how many bytes it represents. The size in bytes is
 * computed by first dividing the total cache size by the CBM length to
 * determine how many bytes each bit in the bitmask represents. The result
 * is multiplied with the number of bits set in the bitmask.
 *
 * @cbm is unsigned long, even if only 32 bits are used to make the
 * bitmap functions work correctly.
 */

unsigned int rdtgroup_cbm_to_size(struct rdt_resource *r,
      struct rdt_ctrl_domain *d, unsigned long cbm)
{
 unsigned int size = 0;
 struct cacheinfo *ci;
 int num_b;

 if (WARN_ON_ONCE(r->ctrl_scope != RESCTRL_L2_CACHE && r->ctrl_scope != RESCTRL_L3_CACHE))
  return size;

 num_b = bitmap_weight(&cbm, r->cache.cbm_len);
 ci = get_cpu_cacheinfo_level(cpumask_any(&d->hdr.cpu_mask), r->ctrl_scope);
 if (ci)
  size = ci->size / r->cache.cbm_len * num_b;

 return size;
}

bool is_mba_sc(struct rdt_resource *r)
{
 if (!r)
  r = resctrl_arch_get_resource(RDT_RESOURCE_MBA);

 /*
 * The software controller support is only applicable to MBA resource.
 * Make sure to check for resource type.
 */

 if (r->rid != RDT_RESOURCE_MBA)
  return false;

 return r->membw.mba_sc;
}

/*
 * rdtgroup_size_show - Display size in bytes of allocated regions
 *
 * The "size" file mirrors the layout of the "schemata" file, printing the
 * size in bytes of each region instead of the capacity bitmask.
 */

static int rdtgroup_size_show(struct kernfs_open_file *of,
         struct seq_file *s, void *v)
{
 struct resctrl_schema *schema;
 enum resctrl_conf_type type;
 struct rdt_ctrl_domain *d;
 struct rdtgroup *rdtgrp;
 struct rdt_resource *r;
 unsigned int size;
 int ret = 0;
 u32 closid;
 bool sep;
 u32 ctrl;

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

 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
  if (!rdtgrp->plr->d) {
   rdt_last_cmd_clear();
   rdt_last_cmd_puts("Cache domain offline\n");
   ret = -ENODEV;
  } else {
   seq_printf(s, "%*s:", max_name_width,
       rdtgrp->plr->s->name);
   size = rdtgroup_cbm_to_size(rdtgrp->plr->s->res,
          rdtgrp->plr->d,
          rdtgrp->plr->cbm);
   seq_printf(s, "%d=%u\n", rdtgrp->plr->d->hdr.id, size);
  }
  goto out;
 }

 closid = rdtgrp->closid;

 list_for_each_entry(schema, &resctrl_schema_all, list) {
  r = schema->res;
  type = schema->conf_type;
  sep = false;
  seq_printf(s, "%*s:", max_name_width, schema->name);
  list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
   if (sep)
    seq_putc(s, ';');
   if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
    size = 0;
   } else {
    if (is_mba_sc(r))
     ctrl = d->mbps_val[closid];
    else
     ctrl = resctrl_arch_get_config(r, d,
               closid,
               type);
    if (r->rid == RDT_RESOURCE_MBA ||
        r->rid == RDT_RESOURCE_SMBA)
     size = ctrl;
    else
     size = rdtgroup_cbm_to_size(r, d, ctrl);
   }
   seq_printf(s, "%d=%u", d->hdr.id, size);
   sep = true;
  }
  seq_putc(s, '\n');
 }

out:
 rdtgroup_kn_unlock(of->kn);

 return ret;
}

static void mondata_config_read(struct resctrl_mon_config_info *mon_info)
{
 smp_call_function_any(&mon_info->d->hdr.cpu_mask,
         resctrl_arch_mon_event_config_read, mon_info, 1);
}

static int mbm_config_show(struct seq_file *s, struct rdt_resource *r, u32 evtid)
{
 struct resctrl_mon_config_info mon_info;
 struct rdt_mon_domain *dom;
 bool sep = false;

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);

 list_for_each_entry(dom, &r->mon_domains, hdr.list) {
  if (sep)
   seq_puts(s, ";");

  memset(&mon_info, 0, sizeof(struct resctrl_mon_config_info));
  mon_info.r = r;
  mon_info.d = dom;
  mon_info.evtid = evtid;
  mondata_config_read(&mon_info);

  seq_printf(s, "%d=0x%02x", dom->hdr.id, mon_info.mon_config);
  sep = true;
 }
 seq_puts(s, "\n");

 mutex_unlock(&rdtgroup_mutex);
 cpus_read_unlock();

 return 0;
}

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

 mbm_config_show(seq, r, QOS_L3_MBM_TOTAL_EVENT_ID);

 return 0;
}

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

 mbm_config_show(seq, r, QOS_L3_MBM_LOCAL_EVENT_ID);

 return 0;
}

static void mbm_config_write_domain(struct rdt_resource *r,
        struct rdt_mon_domain *d, u32 evtid, u32 val)
{
 struct resctrl_mon_config_info mon_info = {0};

 /*
 * Read the current config value first. If both are the same then
 * no need to write it again.
 */

 mon_info.r = r;
 mon_info.d = d;
 mon_info.evtid = evtid;
 mondata_config_read(&mon_info);
 if (mon_info.mon_config == val)
  return;

 mon_info.mon_config = val;

 /*
 * Update MSR_IA32_EVT_CFG_BASE MSR on one of the CPUs in the
 * domain. The MSRs offset from MSR MSR_IA32_EVT_CFG_BASE
 * are scoped at the domain level. Writing any of these MSRs
 * on one CPU is observed by all the CPUs in the domain.
 */

 smp_call_function_any(&d->hdr.cpu_mask, resctrl_arch_mon_event_config_write,
         &mon_info, 1);

 /*
 * When an Event Configuration is changed, the bandwidth counters
 * for all RMIDs and Events will be cleared by the hardware. The
 * hardware also sets MSR_IA32_QM_CTR.Unavailable (bit 62) for
 * every RMID on the next read to any event for every RMID.
 * Subsequent reads will have MSR_IA32_QM_CTR.Unavailable (bit 62)
 * cleared while it is tracked by the hardware. Clear the
 * mbm_local and mbm_total counts for all the RMIDs.
 */

 resctrl_arch_reset_rmid_all(r, d);
}

static int mon_config_write(struct rdt_resource *r, char *tok, u32 evtid)
{
 char *dom_str = NULL, *id_str;
 unsigned long dom_id, val;
 struct rdt_mon_domain *d;

 /* Walking r->domains, ensure it can't race with cpuhp */
 lockdep_assert_cpus_held();

next:
 if (!tok || tok[0] == '\0')
  return 0;

 /* Start processing the strings for each domain */
 dom_str = strim(strsep(&tok, ";"));
 id_str = strsep(&dom_str, "=");

 if (!id_str || kstrtoul(id_str, 10, &dom_id)) {
  rdt_last_cmd_puts("Missing '=' or non-numeric domain id\n");
  return -EINVAL;
 }

 if (!dom_str || kstrtoul(dom_str, 16, &val)) {
  rdt_last_cmd_puts("Non-numeric event configuration value\n");
  return -EINVAL;
 }

 /* Value from user cannot be more than the supported set of events */
 if ((val & r->mbm_cfg_mask) != val) {
  rdt_last_cmd_printf("Invalid event configuration: max valid mask is 0x%02x\n",
        r->mbm_cfg_mask);
  return -EINVAL;
 }

 list_for_each_entry(d, &r->mon_domains, hdr.list) {
  if (d->hdr.id == dom_id) {
   mbm_config_write_domain(r, d, evtid, val);
   goto next;
  }
 }

 return -EINVAL;
}

static ssize_t mbm_total_bytes_config_write(struct kernfs_open_file *of,
         char *buf, size_t nbytes,
         loff_t off)
{
 struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
 int ret;

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

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);

 rdt_last_cmd_clear();

 buf[nbytes - 1] = '\0';

 ret = mon_config_write(r, buf, QOS_L3_MBM_TOTAL_EVENT_ID);

 mutex_unlock(&rdtgroup_mutex);
 cpus_read_unlock();

 return ret ?: nbytes;
}

static ssize_t mbm_local_bytes_config_write(struct kernfs_open_file *of,
         char *buf, size_t nbytes,
         loff_t off)
{
 struct rdt_resource *r = rdt_kn_parent_priv(of->kn);
 int ret;

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

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);

 rdt_last_cmd_clear();

 buf[nbytes - 1] = '\0';

 ret = mon_config_write(r, buf, QOS_L3_MBM_LOCAL_EVENT_ID);

 mutex_unlock(&rdtgroup_mutex);
 cpus_read_unlock();

 return ret ?: nbytes;
}

/* rdtgroup information files for one cache resource. */
static struct rftype res_common_files[] = {
 {
  .name  = "last_cmd_status",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_last_cmd_status_show,
  .fflags  = RFTYPE_TOP_INFO,
 },
 {
  .name  = "num_closids",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_num_closids_show,
  .fflags  = RFTYPE_CTRL_INFO,
 },
 {
  .name  = "mon_features",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_mon_features_show,
  .fflags  = RFTYPE_MON_INFO,
 },
 {
  .name  = "num_rmids",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_num_rmids_show,
  .fflags  = RFTYPE_MON_INFO,
 },
 {
  .name  = "cbm_mask",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_default_ctrl_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "min_cbm_bits",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_min_cbm_bits_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "shareable_bits",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_shareable_bits_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "bit_usage",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_bit_usage_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "min_bandwidth",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_min_bw_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_MB,
 },
 {
  .name  = "bandwidth_gran",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_bw_gran_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_MB,
 },
 {
  .name  = "delay_linear",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_delay_linear_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_MB,
 },
 /*
 * Platform specific which (if any) capabilities are provided by
 * thread_throttle_mode. Defer "fflags" initialization to platform
 * discovery.
 */

 {
  .name  = "thread_throttle_mode",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_thread_throttle_mode_show,
 },
 {
  .name  = "max_threshold_occupancy",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = max_threshold_occ_write,
  .seq_show = max_threshold_occ_show,
  .fflags  = RFTYPE_MON_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "mbm_total_bytes_config",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = mbm_total_bytes_config_show,
  .write  = mbm_total_bytes_config_write,
 },
 {
  .name  = "mbm_local_bytes_config",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = mbm_local_bytes_config_show,
  .write  = mbm_local_bytes_config_write,
 },
 {
  .name  = "cpus",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_cpus_write,
  .seq_show = rdtgroup_cpus_show,
  .fflags  = RFTYPE_BASE,
 },
 {
  .name  = "cpus_list",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_cpus_write,
  .seq_show = rdtgroup_cpus_show,
  .flags  = RFTYPE_FLAGS_CPUS_LIST,
  .fflags  = RFTYPE_BASE,
 },
 {
  .name  = "tasks",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_tasks_write,
  .seq_show = rdtgroup_tasks_show,
  .fflags  = RFTYPE_BASE,
 },
 {
  .name  = "mon_hw_id",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdtgroup_rmid_show,
  .fflags  = RFTYPE_MON_BASE | RFTYPE_DEBUG,
 },
 {
  .name  = "schemata",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_schemata_write,
  .seq_show = rdtgroup_schemata_show,
  .fflags  = RFTYPE_CTRL_BASE,
 },
 {
  .name  = "mba_MBps_event",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_mba_mbps_event_write,
  .seq_show = rdtgroup_mba_mbps_event_show,
 },
 {
  .name  = "mode",
  .mode  = 0644,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .write  = rdtgroup_mode_write,
  .seq_show = rdtgroup_mode_show,
  .fflags  = RFTYPE_CTRL_BASE,
 },
 {
  .name  = "size",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdtgroup_size_show,
  .fflags  = RFTYPE_CTRL_BASE,
 },
 {
  .name  = "sparse_masks",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdt_has_sparse_bitmasks_show,
  .fflags  = RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE,
 },
 {
  .name  = "ctrl_hw_id",
  .mode  = 0444,
  .kf_ops  = &rdtgroup_kf_single_ops,
  .seq_show = rdtgroup_closid_show,
  .fflags  = RFTYPE_CTRL_BASE | RFTYPE_DEBUG,
 },
};

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

 rfts = res_common_files;
 len = ARRAY_SIZE(res_common_files);

 lockdep_assert_held(&rdtgroup_mutex);

 if (resctrl_debug)
  fflags |= RFTYPE_DEBUG;

 for (rft = rfts; rft < rfts + len; rft++) {
  if (rft->fflags && ((fflags & rft->fflags) == rft->fflags)) {
   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) {
  if ((fflags & rft->fflags) == rft->fflags)
   kernfs_remove_by_name(kn, rft->name);
 }
 return ret;
}

static struct rftype *rdtgroup_get_rftype_by_name(const char *name)
{
 struct rftype *rfts, *rft;
 int len;

 rfts = res_common_files;
 len = ARRAY_SIZE(res_common_files);

 for (rft = rfts; rft < rfts + len; rft++) {
  if (!strcmp(rft->name, name))
   return rft;
 }

 return NULL;
}

static void thread_throttle_mode_init(void)
{
 enum membw_throttle_mode throttle_mode = THREAD_THROTTLE_UNDEFINED;
 struct rdt_resource *r_mba, *r_smba;

 r_mba = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
 if (r_mba->alloc_capable &&
     r_mba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
  throttle_mode = r_mba->membw.throttle_mode;

 r_smba = resctrl_arch_get_resource(RDT_RESOURCE_SMBA);
 if (r_smba->alloc_capable &&
     r_smba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
  throttle_mode = r_smba->membw.throttle_mode;

 if (throttle_mode == THREAD_THROTTLE_UNDEFINED)
  return;

 resctrl_file_fflags_init("thread_throttle_mode",
     RFTYPE_CTRL_INFO | RFTYPE_RES_MB);
}

void resctrl_file_fflags_init(const char *config, unsigned long fflags)
{
 struct rftype *rft;

 rft = rdtgroup_get_rftype_by_name(config);
 if (rft)
  rft->fflags = fflags;
}

/**
 * rdtgroup_kn_mode_restrict - Restrict user access to named resctrl file
 * @r: The resource group with which the file is associated.
 * @name: Name of the file
 *
 * The permissions of named resctrl file, directory, or link are modified
 * to not allow read, write, or execute by any user.
 *
 * WARNING: This function is intended to communicate to the user that the
 * resctrl file has been locked down - that it is not relevant to the
 * particular state the system finds itself in. It should not be relied
 * on to protect from user access because after the file's permissions
 * are restricted the user can still change the permissions using chmod
 * from the command line.
 *
 * Return: 0 on success, <0 on failure.
 */

int rdtgroup_kn_mode_restrict(struct rdtgroup *r, const char *name)
{
 struct iattr iattr = {.ia_valid = ATTR_MODE,};
 struct kernfs_node *kn;
 int ret = 0;

 kn = kernfs_find_and_get_ns(r->kn, name, NULL);
 if (!kn)
  return -ENOENT;

 switch (kernfs_type(kn)) {
 case KERNFS_DIR:
  iattr.ia_mode = S_IFDIR;
  break;
 case KERNFS_FILE:
  iattr.ia_mode = S_IFREG;
  break;
 case KERNFS_LINK:
  iattr.ia_mode = S_IFLNK;
  break;
 }

 ret = kernfs_setattr(kn, &iattr);
 kernfs_put(kn);
 return ret;
}

/**
 * rdtgroup_kn_mode_restore - Restore user access to named resctrl file
 * @r: The resource group with which the file is associated.
 * @name: Name of the file
 * @mask: Mask of permissions that should be restored
 *
 * Restore the permissions of the named file. If @name is a directory the
 * permissions of its parent will be used.
 *
 * Return: 0 on success, <0 on failure.
 */

int rdtgroup_kn_mode_restore(struct rdtgroup *r, const char *name,
        umode_t mask)
{
 struct iattr iattr = {.ia_valid = ATTR_MODE,};
 struct kernfs_node *kn, *parent;
 struct rftype *rfts, *rft;
 int ret, len;

 rfts = res_common_files;
 len = ARRAY_SIZE(res_common_files);

 for (rft = rfts; rft < rfts + len; rft++) {
  if (!strcmp(rft->name, name))
   iattr.ia_mode = rft->mode & mask;
 }

 kn = kernfs_find_and_get_ns(r->kn, name, NULL);
 if (!kn)
  return -ENOENT;

 switch (kernfs_type(kn)) {
 case KERNFS_DIR:
  parent = kernfs_get_parent(kn);
  if (parent) {
   iattr.ia_mode |= parent->mode;
   kernfs_put(parent);
  }
  iattr.ia_mode |= S_IFDIR;
  break;
 case KERNFS_FILE:
  iattr.ia_mode |= S_IFREG;
  break;
 case KERNFS_LINK:
  iattr.ia_mode |= S_IFLNK;
  break;
 }

 ret = kernfs_setattr(kn, &iattr);
 kernfs_put(kn);
 return ret;
}

static int rdtgroup_mkdir_info_resdir(void *priv, char *name,
          unsigned long fflags)
{
 struct kernfs_node *kn_subdir;
 int ret;

 kn_subdir = kernfs_create_dir(kn_info, name,
          kn_info->mode, priv);
 if (IS_ERR(kn_subdir))
  return PTR_ERR(kn_subdir);

 ret = rdtgroup_kn_set_ugid(kn_subdir);
 if (ret)
  return ret;

 ret = rdtgroup_add_files(kn_subdir, fflags);
 if (!ret)
  kernfs_activate(kn_subdir);

 return ret;
}

static unsigned long fflags_from_resource(struct rdt_resource *r)
{
 switch (r->rid) {
 case RDT_RESOURCE_L3:
 case RDT_RESOURCE_L2:
  return RFTYPE_RES_CACHE;
 case RDT_RESOURCE_MBA:
 case RDT_RESOURCE_SMBA:
  return RFTYPE_RES_MB;
 }

 return WARN_ON_ONCE(1);
}

static int rdtgroup_create_info_dir(struct kernfs_node *parent_kn)
{
 struct resctrl_schema *s;
 struct rdt_resource *r;
 unsigned long fflags;
 char name[32];
 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);

 ret = rdtgroup_add_files(kn_info, RFTYPE_TOP_INFO);
 if (ret)
  goto out_destroy;

 /* loop over enabled controls, these are all alloc_capable */
 list_for_each_entry(s, &resctrl_schema_all, list) {
  r = s->res;
  fflags = fflags_from_resource(r) | RFTYPE_CTRL_INFO;
  ret = rdtgroup_mkdir_info_resdir(s, s->name, fflags);
  if (ret)
   goto out_destroy;
 }

 for_each_mon_capable_rdt_resource(r) {
  fflags = fflags_from_resource(r) | RFTYPE_MON_INFO;
  sprintf(name, "%s_MON", r->name);
  ret = rdtgroup_mkdir_info_resdir(r, name, fflags);
  if (ret)
   goto out_destroy;
 }

 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 int
mongroup_create_dir(struct kernfs_node *parent_kn, struct rdtgroup *prgrp,
      char *name, struct kernfs_node **dest_kn)
{
 struct kernfs_node *kn;
 int ret;

 /* create the directory */
 kn = kernfs_create_dir(parent_kn, name, parent_kn->mode, prgrp);
 if (IS_ERR(kn))
  return PTR_ERR(kn);

 if (dest_kn)
  *dest_kn = kn;

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

 kernfs_activate(kn);

 return 0;

out_destroy:
 kernfs_remove(kn);
 return ret;
}

static inline bool is_mba_linear(void)
{
 return resctrl_arch_get_resource(RDT_RESOURCE_MBA)->membw.delay_linear;
}

static int mba_sc_domain_allocate(struct rdt_resource *r, struct rdt_ctrl_domain *d)
{
 u32 num_closid = resctrl_arch_get_num_closid(r);
 int cpu = cpumask_any(&d->hdr.cpu_mask);
 int i;

 d->mbps_val = kcalloc_node(num_closid, sizeof(*d->mbps_val),
       GFP_KERNEL, cpu_to_node(cpu));
 if (!d->mbps_val)
  return -ENOMEM;

 for (i = 0; i < num_closid; i++)
  d->mbps_val[i] = MBA_MAX_MBPS;

 return 0;
}

static void mba_sc_domain_destroy(struct rdt_resource *r,
      struct rdt_ctrl_domain *d)
{
 kfree(d->mbps_val);
 d->mbps_val = NULL;
}

/*
 * MBA software controller is supported only if
 * MBM is supported and MBA is in linear scale,
 * and the MBM monitor scope is the same as MBA
 * control scope.
 */

static bool supports_mba_mbps(void)
{
 struct rdt_resource *rmbm = resctrl_arch_get_resource(RDT_RESOURCE_L3);
 struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_MBA);

 return (resctrl_is_mbm_enabled() &&
  r->alloc_capable && is_mba_linear() &&
  r->ctrl_scope == rmbm->mon_scope);
}

/*
 * Enable or disable the MBA software controller
 * which helps user specify bandwidth in MBps.
 */

static int set_mba_sc(bool mba_sc)
{
 struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
 u32 num_closid = resctrl_arch_get_num_closid(r);
 struct rdt_ctrl_domain *d;
 unsigned long fflags;
 int i;

 if (!supports_mba_mbps() || mba_sc == is_mba_sc(r))
  return -EINVAL;

 r->membw.mba_sc = mba_sc;

 rdtgroup_default.mba_mbps_event = mba_mbps_default_event;

 list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
  for (i = 0; i < num_closid; i++)
   d->mbps_val[i] = MBA_MAX_MBPS;
 }

 fflags = mba_sc ? RFTYPE_CTRL_BASE | RFTYPE_MON_BASE : 0;
 resctrl_file_fflags_init("mba_MBps_event", fflags);

 return 0;
}

/*
 * We don't allow rdtgroup directories to be created anywhere
 * except the root directory. Thus when looking for the rdtgroup
 * structure for a kernfs node we are either looking at a directory,
 * in which case the rdtgroup structure is pointed at by the "priv"
 * field, otherwise we have a file, and need only look to the parent
 * to find the rdtgroup.
 */

static struct rdtgroup *kernfs_to_rdtgroup(struct kernfs_node *kn)
{
 if (kernfs_type(kn) == KERNFS_DIR) {
  /*
 * All the resource directories use "kn->priv"
 * to point to the "struct rdtgroup" for the
 * resource. "info" and its subdirectories don't
 * have rdtgroup structures, so return NULL here.
 */

  if (kn == kn_info ||
      rcu_access_pointer(kn->__parent) == kn_info)
   return NULL;
  else
   return kn->priv;
 } else {
  return rdt_kn_parent_priv(kn);
 }
}

static void rdtgroup_kn_get(struct rdtgroup *rdtgrp, struct kernfs_node *kn)
{
 atomic_inc(&rdtgrp->waitcount);
 kernfs_break_active_protection(kn);
}

static void rdtgroup_kn_put(struct rdtgroup *rdtgrp, struct kernfs_node *kn)
{
 if (atomic_dec_and_test(&rdtgrp->waitcount) &&
     (rdtgrp->flags & RDT_DELETED)) {
  if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
      rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED)
   rdtgroup_pseudo_lock_remove(rdtgrp);
  kernfs_unbreak_active_protection(kn);
  rdtgroup_remove(rdtgrp);
 } else {
  kernfs_unbreak_active_protection(kn);
 }
}

struct rdtgroup *rdtgroup_kn_lock_live(struct kernfs_node *kn)
{
 struct rdtgroup *rdtgrp = kernfs_to_rdtgroup(kn);

 if (!rdtgrp)
  return NULL;

 rdtgroup_kn_get(rdtgrp, kn);

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);

 /* Was this group deleted while we waited? */
 if (rdtgrp->flags & RDT_DELETED)
  return NULL;

 return rdtgrp;
}

void rdtgroup_kn_unlock(struct kernfs_node *kn)
{
 struct rdtgroup *rdtgrp = kernfs_to_rdtgroup(kn);

 if (!rdtgrp)
  return;

 mutex_unlock(&rdtgroup_mutex);
 cpus_read_unlock();

 rdtgroup_kn_put(rdtgrp, kn);
}

static int mkdir_mondata_all(struct kernfs_node *parent_kn,
        struct rdtgroup *prgrp,
        struct kernfs_node **mon_data_kn);

static void rdt_disable_ctx(void)
{
 resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L3, false);
 resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L2, false);
 set_mba_sc(false);

 resctrl_debug = false;
}

static int rdt_enable_ctx(struct rdt_fs_context *ctx)
{
 int ret = 0;

 if (ctx->enable_cdpl2) {
  ret = resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L2, true);
  if (ret)
   goto out_done;
 }

 if (ctx->enable_cdpl3) {
  ret = resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L3, true);
  if (ret)
   goto out_cdpl2;
 }

 if (ctx->enable_mba_mbps) {
  ret = set_mba_sc(true);
  if (ret)
   goto out_cdpl3;
 }

 if (ctx->enable_debug)
  resctrl_debug = true;

 return 0;

out_cdpl3:
 resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L3, false);
out_cdpl2:
 resctrl_arch_set_cdp_enabled(RDT_RESOURCE_L2, false);
out_done:
 return ret;
}

static int schemata_list_add(struct rdt_resource *r, enum resctrl_conf_type type)
{
 struct resctrl_schema *s;
 const char *suffix = "";
 int ret, cl;

 s = kzalloc(sizeof(*s), GFP_KERNEL);
 if (!s)
  return -ENOMEM;

 s->res = r;
 s->num_closid = resctrl_arch_get_num_closid(r);
 if (resctrl_arch_get_cdp_enabled(r->rid))
  s->num_closid /= 2;

 s->conf_type = type;
 switch (type) {
 case CDP_CODE:
  suffix = "CODE";
  break;
 case CDP_DATA:
  suffix = "DATA";
  break;
 case CDP_NONE:
  suffix = "";
  break;
 }

 ret = snprintf(s->name, sizeof(s->name), "%s%s", r->name, suffix);
 if (ret >= sizeof(s->name)) {
  kfree(s);
  return -EINVAL;
 }

 cl = strlen(s->name);

 /*
 * If CDP is supported by this resource, but not enabled,
 * include the suffix. This ensures the tabular format of the
 * schemata file does not change between mounts of the filesystem.
 */

 if (r->cdp_capable && !resctrl_arch_get_cdp_enabled(r->rid))
  cl += 4;

 if (cl > max_name_width)
  max_name_width = cl;

 switch (r->schema_fmt) {
 case RESCTRL_SCHEMA_BITMAP:
  s->fmt_str = "%d=%x";
  break;
 case RESCTRL_SCHEMA_RANGE:
  s->fmt_str = "%d=%u";
  break;
 }

 if (WARN_ON_ONCE(!s->fmt_str)) {
  kfree(s);
  return -EINVAL;
 }

 INIT_LIST_HEAD(&s->list);
 list_add(&s->list, &resctrl_schema_all);

 return 0;
}

static int schemata_list_create(void)
{
 struct rdt_resource *r;
 int ret = 0;

 for_each_alloc_capable_rdt_resource(r) {
  if (resctrl_arch_get_cdp_enabled(r->rid)) {
   ret = schemata_list_add(r, CDP_CODE);
   if (ret)
    break;

   ret = schemata_list_add(r, CDP_DATA);
  } else {
   ret = schemata_list_add(r, CDP_NONE);
  }

  if (ret)
   break;
 }

 return ret;
}

static void schemata_list_destroy(void)
{
 struct resctrl_schema *s, *tmp;

 list_for_each_entry_safe(s, tmp, &resctrl_schema_all, list) {
  list_del(&s->list);
  kfree(s);
 }
}

static int rdt_get_tree(struct fs_context *fc)
{
 struct rdt_fs_context *ctx = rdt_fc2context(fc);
 unsigned long flags = RFTYPE_CTRL_BASE;
 struct rdt_mon_domain *dom;
 struct rdt_resource *r;
 int ret;

 cpus_read_lock();
 mutex_lock(&rdtgroup_mutex);
 /*
 * resctrl file system can only be mounted once.
 */

 if (resctrl_mounted) {
  ret = -EBUSY;
  goto out;
 }

 ret = rdtgroup_setup_root(ctx);
 if (ret)
  goto out;

 ret = rdt_enable_ctx(ctx);
 if (ret)
  goto out_root;

 ret = schemata_list_create();
 if (ret) {
  schemata_list_destroy();
  goto out_ctx;
 }

 ret = closid_init();
 if (ret)
  goto out_schemata_free;

 if (resctrl_arch_mon_capable())
  flags |= RFTYPE_MON;

 ret = rdtgroup_add_files(rdtgroup_default.kn, flags);
 if (ret)
  goto out_closid_exit;

 kernfs_activate(rdtgroup_default.kn);

 ret = rdtgroup_create_info_dir(rdtgroup_default.kn);
 if (ret < 0)
  goto out_closid_exit;

 if (resctrl_arch_mon_capable()) {
  ret = mongroup_create_dir(rdtgroup_default.kn,
       &rdtgroup_default, "mon_groups",
       &kn_mongrp);
  if (ret < 0)
   goto out_info;

  ret = mkdir_mondata_all(rdtgroup_default.kn,
     &rdtgroup_default, &kn_mondata);
  if (ret < 0)
   goto out_mongrp;
  rdtgroup_default.mon.mon_data_kn = kn_mondata;
 }

 ret = rdt_pseudo_lock_init();
 if (ret)
  goto out_mondata;

 ret = kernfs_get_tree(fc);
 if (ret < 0)
  goto out_psl;

 if (resctrl_arch_alloc_capable())
  resctrl_arch_enable_alloc();
 if (resctrl_arch_mon_capable())
  resctrl_arch_enable_mon();

 if (resctrl_arch_alloc_capable() || resctrl_arch_mon_capable())
  resctrl_mounted = true;

 if (resctrl_is_mbm_enabled()) {
  r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
  list_for_each_entry(dom, &r->mon_domains, hdr.list)
   mbm_setup_overflow_handler(dom, MBM_OVERFLOW_INTERVAL,
         RESCTRL_PICK_ANY_CPU);
 }

 goto out;

out_psl:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.