aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Luck <tony.luck@intel.com>2023-09-13 16:45:14 -0700
committerTony Luck <tony.luck@intel.com>2023-09-19 14:13:59 -0700
commit5f17ce154cce86d2a5de5599418d9f8226db3172 (patch)
treec8e972d4311a98c3575c03caa949998181d36daf
parentedb3b594f3573000420d57343b9023707f774b03 (diff)
downloadlinux-5f17ce154cce86d2a5de5599418d9f8226db3172.tar.gz
resctrl2: Add "write()" operation to tasks file
Writing a task id to the "tasks" file in a resctrl group assigns that task to that group. Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r--arch/x86/include/asm/resctrl.h7
-rw-r--r--fs/resctrl2/internal.h2
-rw-r--r--fs/resctrl2/kernfs.c31
-rw-r--r--fs/resctrl2/tasks.c74
4 files changed, 114 insertions, 0 deletions
diff --git a/arch/x86/include/asm/resctrl.h b/arch/x86/include/asm/resctrl.h
index 1fc8dea4f3d090..cc18045027b3d1 100644
--- a/arch/x86/include/asm/resctrl.h
+++ b/arch/x86/include/asm/resctrl.h
@@ -116,6 +116,13 @@ static inline bool arch_is_resctrl_id_match(struct task_struct *t, struct resctr
return is_closid_match(t, rg);
}
+static inline bool arch_set_task_ids(struct task_struct *t, struct resctrl_group *rg)
+{
+ WRITE_ONCE(t->resctrl_ids, rg->resctrl_ids);
+
+ return true;
+}
+
#else
static inline void resctrl_sched_in(struct task_struct *tsk) {}
diff --git a/fs/resctrl2/internal.h b/fs/resctrl2/internal.h
index a0a92b0e78643a..55e1c53768203b 100644
--- a/fs/resctrl2/internal.h
+++ b/fs/resctrl2/internal.h
@@ -52,6 +52,8 @@ struct info_file_info {
struct core_file_info {
struct resctrl_group *rg;
int (*show)(struct seq_file *sf, struct resctrl_group *rg);
+ ssize_t (*write)(char *buf, size_t nbytes, struct resctrl_group *rg,
+ struct kernfs_open_file *of);
};
#define RESCTRL_GROUP 5
diff --git a/fs/resctrl2/kernfs.c b/fs/resctrl2/kernfs.c
index 69ba3479dccdaa..aaf4d271f4bf6d 100644
--- a/fs/resctrl2/kernfs.c
+++ b/fs/resctrl2/kernfs.c
@@ -38,9 +38,40 @@ out:
return ret;
}
+static ssize_t resctrl_file_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off)
+{
+ struct resctrl_node_info *rni;
+ struct core_file_info *cfi;
+ int ret = -EOPNOTSUPP;
+
+ rni = resctrl_kn_lock_live(of->kn);
+
+ if (!rni) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ switch (rni->type) {
+ case RESCTRL_COREFILE:
+ cfi = (struct core_file_info *)&rni->priv;
+ if (cfi->write)
+ ret = cfi->write(buf, nbytes, cfi->rg, of);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+out:
+ resctrl_kn_unlock(of->kn);
+
+ return ret ?: nbytes;
+}
+
struct kernfs_ops resctrl_file_ops = {
.atomic_write_len = PAGE_SIZE,
.seq_show = resctrl_file_show,
+ .write = resctrl_file_write,
};
/* Set uid and gid of dirs and files to that of the creator */
diff --git a/fs/resctrl2/tasks.c b/fs/resctrl2/tasks.c
index f7765cbff84836..b3817a9b7e1b75 100644
--- a/fs/resctrl2/tasks.c
+++ b/fs/resctrl2/tasks.c
@@ -26,6 +26,79 @@ static int tasks_seq_show(struct seq_file *m, struct resctrl_group *rg)
return 0;
}
+static int __resctrl_move_task(struct task_struct *tsk,
+ struct resctrl_group *rg)
+{
+ /* If the task is already in group, no need to move the task. */
+ if (tsk->resctrl_ids == rg->resctrl_ids)
+ return 0;
+
+ /* Change ID in task structure first */
+ if (!arch_set_task_ids(tsk, rg))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int resctrl_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)) {
+ ret = -EPERM;
+ }
+
+ put_cred(tcred);
+ return ret;
+}
+
+static int resctrl_move_task(pid_t pid, struct resctrl_group *rg, 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();
+ return -ESRCH;
+ }
+ } else {
+ tsk = current;
+ }
+
+ get_task_struct(tsk);
+ rcu_read_unlock();
+
+ ret = resctrl_task_write_permission(tsk, of);
+ if (!ret)
+ ret = __resctrl_move_task(tsk, rg);
+
+ put_task_struct(tsk);
+ return ret;
+}
+
+static ssize_t tasks_write(char *buf, size_t nbytes, struct resctrl_group *rg,
+ struct kernfs_open_file *of)
+{
+ pid_t pid;
+
+ if (kstrtoint(strstrip(buf), 0, &pid) || pid < 0)
+ return -EINVAL;
+
+ return resctrl_move_task(pid, rg, of);
+}
+
bool resctrl_add_task_file(struct kernfs_node *parent_kn)
{
struct resctrl_node_info *rni, *prni;
@@ -38,6 +111,7 @@ bool resctrl_add_task_file(struct kernfs_node *parent_kn)
cfi = (struct core_file_info *)&rni->priv;
cfi->rg = (struct resctrl_group *)&prni->priv;
cfi->show = tasks_seq_show;
+ cfi->write = tasks_write;
return true;
}