Commit bd9a3dba authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sched-psi-2022-10-14' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull PSI updates from Ingo Molnar:

 - Various performance optimizations, resulting in a 4%-9% speedup in
   the mmtests/config-scheduler-perfpipe micro-benchmark.

 - New interface to turn PSI on/off on a per cgroup level.

* tag 'sched-psi-2022-10-14' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  sched/psi: Per-cgroup PSI accounting disable/re-enable interface
  sched/psi: Cache parent psi_group to speed up group iteration
  sched/psi: Consolidate cgroup_psi()
  sched/psi: Add PSI_IRQ to track IRQ/SOFTIRQ pressure
  sched/psi: Remove NR_ONCPU task accounting
  sched/psi: Optimize task switch inside shared cgroups again
  sched/psi: Move private helpers to sched/stats.h
  sched/psi: Save percpu memory when !psi_cgroups_enabled
  sched/psi: Don't create cgroup PSI files when psi_disabled
  sched/psi: Fix periodic aggregation shut off
parents 1df046ab 34f26a15
...@@ -976,6 +976,29 @@ All cgroup core files are prefixed with "cgroup." ...@@ -976,6 +976,29 @@ All cgroup core files are prefixed with "cgroup."
killing cgroups is a process directed operation, i.e. it affects killing cgroups is a process directed operation, i.e. it affects
the whole thread-group. the whole thread-group.
cgroup.pressure
A read-write single value file that allowed values are "0" and "1".
The default is "1".
Writing "0" to the file will disable the cgroup PSI accounting.
Writing "1" to the file will re-enable the cgroup PSI accounting.
This control attribute is not hierarchical, so disable or enable PSI
accounting in a cgroup does not affect PSI accounting in descendants
and doesn't need pass enablement via ancestors from root.
The reason this control attribute exists is that PSI accounts stalls for
each cgroup separately and aggregates it at each level of the hierarchy.
This may cause non-negligible overhead for some workloads when under
deep level of the hierarchy, in which case this control attribute can
be used to disable PSI accounting in the non-leaf cgroups.
irq.pressure
A read-write nested-keyed file.
Shows pressure stall information for IRQ/SOFTIRQ. See
:ref:`Documentation/accounting/psi.rst <psi>` for details.
Controllers Controllers
=========== ===========
......
...@@ -428,6 +428,9 @@ struct cgroup { ...@@ -428,6 +428,9 @@ struct cgroup {
struct cgroup_file procs_file; /* handle for "cgroup.procs" */ struct cgroup_file procs_file; /* handle for "cgroup.procs" */
struct cgroup_file events_file; /* handle for "cgroup.events" */ struct cgroup_file events_file; /* handle for "cgroup.events" */
/* handles for "{cpu,memory,io,irq}.pressure" */
struct cgroup_file psi_files[NR_PSI_RESOURCES];
/* /*
* The bitmask of subsystems enabled on the child cgroups. * The bitmask of subsystems enabled on the child cgroups.
* ->subtree_control is the one configured through * ->subtree_control is the one configured through
......
...@@ -682,11 +682,6 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp) ...@@ -682,11 +682,6 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
pr_cont_kernfs_path(cgrp->kn); pr_cont_kernfs_path(cgrp->kn);
} }
static inline struct psi_group *cgroup_psi(struct cgroup *cgrp)
{
return cgrp->psi;
}
bool cgroup_psi_enabled(void); bool cgroup_psi_enabled(void);
static inline void cgroup_init_kthreadd(void) static inline void cgroup_init_kthreadd(void)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/cgroup-defs.h> #include <linux/cgroup-defs.h>
#include <linux/cgroup.h>
struct seq_file; struct seq_file;
struct css_set; struct css_set;
...@@ -18,10 +19,6 @@ extern struct psi_group psi_system; ...@@ -18,10 +19,6 @@ extern struct psi_group psi_system;
void psi_init(void); void psi_init(void);
void psi_task_change(struct task_struct *task, int clear, int set);
void psi_task_switch(struct task_struct *prev, struct task_struct *next,
bool sleep);
void psi_memstall_enter(unsigned long *flags); void psi_memstall_enter(unsigned long *flags);
void psi_memstall_leave(unsigned long *flags); void psi_memstall_leave(unsigned long *flags);
...@@ -34,9 +31,15 @@ __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file, ...@@ -34,9 +31,15 @@ __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file,
poll_table *wait); poll_table *wait);
#ifdef CONFIG_CGROUPS #ifdef CONFIG_CGROUPS
static inline struct psi_group *cgroup_psi(struct cgroup *cgrp)
{
return cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi;
}
int psi_cgroup_alloc(struct cgroup *cgrp); int psi_cgroup_alloc(struct cgroup *cgrp);
void psi_cgroup_free(struct cgroup *cgrp); void psi_cgroup_free(struct cgroup *cgrp);
void cgroup_move_task(struct task_struct *p, struct css_set *to); void cgroup_move_task(struct task_struct *p, struct css_set *to);
void psi_cgroup_restart(struct psi_group *group);
#endif #endif
#else /* CONFIG_PSI */ #else /* CONFIG_PSI */
...@@ -58,6 +61,7 @@ static inline void cgroup_move_task(struct task_struct *p, struct css_set *to) ...@@ -58,6 +61,7 @@ static inline void cgroup_move_task(struct task_struct *p, struct css_set *to)
{ {
rcu_assign_pointer(p->cgroups, to); rcu_assign_pointer(p->cgroups, to);
} }
static inline void psi_cgroup_restart(struct psi_group *group) {}
#endif #endif
#endif /* CONFIG_PSI */ #endif /* CONFIG_PSI */
......
...@@ -15,13 +15,6 @@ enum psi_task_count { ...@@ -15,13 +15,6 @@ enum psi_task_count {
NR_IOWAIT, NR_IOWAIT,
NR_MEMSTALL, NR_MEMSTALL,
NR_RUNNING, NR_RUNNING,
/*
* This can't have values other than 0 or 1 and could be
* implemented as a bit flag. But for now we still have room
* in the first cacheline of psi_group_cpu, and this way we
* don't have to special case any state tracking for it.
*/
NR_ONCPU,
/* /*
* For IO and CPU stalls the presence of running/oncpu tasks * For IO and CPU stalls the presence of running/oncpu tasks
* in the domain means a partial rather than a full stall. * in the domain means a partial rather than a full stall.
...@@ -32,22 +25,27 @@ enum psi_task_count { ...@@ -32,22 +25,27 @@ enum psi_task_count {
* threads and memstall ones. * threads and memstall ones.
*/ */
NR_MEMSTALL_RUNNING, NR_MEMSTALL_RUNNING,
NR_PSI_TASK_COUNTS = 5, NR_PSI_TASK_COUNTS = 4,
}; };
/* Task state bitmasks */ /* Task state bitmasks */
#define TSK_IOWAIT (1 << NR_IOWAIT) #define TSK_IOWAIT (1 << NR_IOWAIT)
#define TSK_MEMSTALL (1 << NR_MEMSTALL) #define TSK_MEMSTALL (1 << NR_MEMSTALL)
#define TSK_RUNNING (1 << NR_RUNNING) #define TSK_RUNNING (1 << NR_RUNNING)
#define TSK_ONCPU (1 << NR_ONCPU)
#define TSK_MEMSTALL_RUNNING (1 << NR_MEMSTALL_RUNNING) #define TSK_MEMSTALL_RUNNING (1 << NR_MEMSTALL_RUNNING)
/* Only one task can be scheduled, no corresponding task count */
#define TSK_ONCPU (1 << NR_PSI_TASK_COUNTS)
/* Resources that workloads could be stalled on */ /* Resources that workloads could be stalled on */
enum psi_res { enum psi_res {
PSI_IO, PSI_IO,
PSI_MEM, PSI_MEM,
PSI_CPU, PSI_CPU,
NR_PSI_RESOURCES = 3, #ifdef CONFIG_IRQ_TIME_ACCOUNTING
PSI_IRQ,
#endif
NR_PSI_RESOURCES,
}; };
/* /*
...@@ -63,11 +61,17 @@ enum psi_states { ...@@ -63,11 +61,17 @@ enum psi_states {
PSI_MEM_FULL, PSI_MEM_FULL,
PSI_CPU_SOME, PSI_CPU_SOME,
PSI_CPU_FULL, PSI_CPU_FULL,
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
PSI_IRQ_FULL,
#endif
/* Only per-CPU, to weigh the CPU in the global average: */ /* Only per-CPU, to weigh the CPU in the global average: */
PSI_NONIDLE, PSI_NONIDLE,
NR_PSI_STATES = 7, NR_PSI_STATES,
}; };
/* Use one bit in the state mask to track TSK_ONCPU */
#define PSI_ONCPU (1 << NR_PSI_STATES)
enum psi_aggregators { enum psi_aggregators {
PSI_AVGS = 0, PSI_AVGS = 0,
PSI_POLL, PSI_POLL,
...@@ -147,6 +151,9 @@ struct psi_trigger { ...@@ -147,6 +151,9 @@ struct psi_trigger {
}; };
struct psi_group { struct psi_group {
struct psi_group *parent;
bool enabled;
/* Protects data used by the aggregator */ /* Protects data used by the aggregator */
struct mutex avgs_lock; struct mutex avgs_lock;
...@@ -188,6 +195,8 @@ struct psi_group { ...@@ -188,6 +195,8 @@ struct psi_group {
#else /* CONFIG_PSI */ #else /* CONFIG_PSI */
#define NR_PSI_RESOURCES 0
struct psi_group { }; struct psi_group { };
#endif /* CONFIG_PSI */ #endif /* CONFIG_PSI */
......
...@@ -3698,26 +3698,26 @@ static int cpu_stat_show(struct seq_file *seq, void *v) ...@@ -3698,26 +3698,26 @@ static int cpu_stat_show(struct seq_file *seq, void *v)
static int cgroup_io_pressure_show(struct seq_file *seq, void *v) static int cgroup_io_pressure_show(struct seq_file *seq, void *v)
{ {
struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup *cgrp = seq_css(seq)->cgroup;
struct psi_group *psi = cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi; struct psi_group *psi = cgroup_psi(cgrp);
return psi_show(seq, psi, PSI_IO); return psi_show(seq, psi, PSI_IO);
} }
static int cgroup_memory_pressure_show(struct seq_file *seq, void *v) static int cgroup_memory_pressure_show(struct seq_file *seq, void *v)
{ {
struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup *cgrp = seq_css(seq)->cgroup;
struct psi_group *psi = cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi; struct psi_group *psi = cgroup_psi(cgrp);
return psi_show(seq, psi, PSI_MEM); return psi_show(seq, psi, PSI_MEM);
} }
static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v) static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
{ {
struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup *cgrp = seq_css(seq)->cgroup;
struct psi_group *psi = cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi; struct psi_group *psi = cgroup_psi(cgrp);
return psi_show(seq, psi, PSI_CPU); return psi_show(seq, psi, PSI_CPU);
} }
static ssize_t cgroup_pressure_write(struct kernfs_open_file *of, char *buf, static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
size_t nbytes, enum psi_res res) size_t nbytes, enum psi_res res)
{ {
struct cgroup_file_ctx *ctx = of->priv; struct cgroup_file_ctx *ctx = of->priv;
...@@ -3738,7 +3738,7 @@ static ssize_t cgroup_pressure_write(struct kernfs_open_file *of, char *buf, ...@@ -3738,7 +3738,7 @@ static ssize_t cgroup_pressure_write(struct kernfs_open_file *of, char *buf,
return -EBUSY; return -EBUSY;
} }
psi = cgroup_ino(cgrp) == 1 ? &psi_system : cgrp->psi; psi = cgroup_psi(cgrp);
new = psi_trigger_create(psi, buf, res); new = psi_trigger_create(psi, buf, res);
if (IS_ERR(new)) { if (IS_ERR(new)) {
cgroup_put(cgrp); cgroup_put(cgrp);
...@@ -3755,21 +3755,86 @@ static ssize_t cgroup_io_pressure_write(struct kernfs_open_file *of, ...@@ -3755,21 +3755,86 @@ static ssize_t cgroup_io_pressure_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, char *buf, size_t nbytes,
loff_t off) loff_t off)
{ {
return cgroup_pressure_write(of, buf, nbytes, PSI_IO); return pressure_write(of, buf, nbytes, PSI_IO);
} }
static ssize_t cgroup_memory_pressure_write(struct kernfs_open_file *of, static ssize_t cgroup_memory_pressure_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, char *buf, size_t nbytes,
loff_t off) loff_t off)
{ {
return cgroup_pressure_write(of, buf, nbytes, PSI_MEM); return pressure_write(of, buf, nbytes, PSI_MEM);
} }
static ssize_t cgroup_cpu_pressure_write(struct kernfs_open_file *of, static ssize_t cgroup_cpu_pressure_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, char *buf, size_t nbytes,
loff_t off) loff_t off)
{ {
return cgroup_pressure_write(of, buf, nbytes, PSI_CPU); return pressure_write(of, buf, nbytes, PSI_CPU);
}
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
static int cgroup_irq_pressure_show(struct seq_file *seq, void *v)
{
struct cgroup *cgrp = seq_css(seq)->cgroup;
struct psi_group *psi = cgroup_psi(cgrp);
return psi_show(seq, psi, PSI_IRQ);
}
static ssize_t cgroup_irq_pressure_write(struct kernfs_open_file *of,
char *buf, size_t nbytes,
loff_t off)
{
return pressure_write(of, buf, nbytes, PSI_IRQ);
}
#endif
static int cgroup_pressure_show(struct seq_file *seq, void *v)
{
struct cgroup *cgrp = seq_css(seq)->cgroup;
struct psi_group *psi = cgroup_psi(cgrp);
seq_printf(seq, "%d\n", psi->enabled);
return 0;
}
static ssize_t cgroup_pressure_write(struct kernfs_open_file *of,
char *buf, size_t nbytes,
loff_t off)
{
ssize_t ret;
int enable;
struct cgroup *cgrp;
struct psi_group *psi;
ret = kstrtoint(strstrip(buf), 0, &enable);
if (ret)
return ret;
if (enable < 0 || enable > 1)
return -ERANGE;
cgrp = cgroup_kn_lock_live(of->kn, false);
if (!cgrp)
return -ENOENT;
psi = cgroup_psi(cgrp);
if (psi->enabled != enable) {
int i;
/* show or hide {cpu,memory,io,irq}.pressure files */
for (i = 0; i < NR_PSI_RESOURCES; i++)
cgroup_file_show(&cgrp->psi_files[i], enable);
psi->enabled = enable;
if (enable)
psi_cgroup_restart(psi);
}
cgroup_kn_unlock(of->kn);
return nbytes;
} }
static __poll_t cgroup_pressure_poll(struct kernfs_open_file *of, static __poll_t cgroup_pressure_poll(struct kernfs_open_file *of,
...@@ -3789,6 +3854,9 @@ static void cgroup_pressure_release(struct kernfs_open_file *of) ...@@ -3789,6 +3854,9 @@ static void cgroup_pressure_release(struct kernfs_open_file *of)
bool cgroup_psi_enabled(void) bool cgroup_psi_enabled(void)
{ {
if (static_branch_likely(&psi_disabled))
return false;
return (cgroup_feature_disable_mask & (1 << OPT_FEATURE_PRESSURE)) == 0; return (cgroup_feature_disable_mask & (1 << OPT_FEATURE_PRESSURE)) == 0;
} }
...@@ -5175,6 +5243,7 @@ static struct cftype cgroup_psi_files[] = { ...@@ -5175,6 +5243,7 @@ static struct cftype cgroup_psi_files[] = {
#ifdef CONFIG_PSI #ifdef CONFIG_PSI
{ {
.name = "io.pressure", .name = "io.pressure",
.file_offset = offsetof(struct cgroup, psi_files[PSI_IO]),
.seq_show = cgroup_io_pressure_show, .seq_show = cgroup_io_pressure_show,
.write = cgroup_io_pressure_write, .write = cgroup_io_pressure_write,
.poll = cgroup_pressure_poll, .poll = cgroup_pressure_poll,
...@@ -5182,6 +5251,7 @@ static struct cftype cgroup_psi_files[] = { ...@@ -5182,6 +5251,7 @@ static struct cftype cgroup_psi_files[] = {
}, },
{ {
.name = "memory.pressure", .name = "memory.pressure",
.file_offset = offsetof(struct cgroup, psi_files[PSI_MEM]),
.seq_show = cgroup_memory_pressure_show, .seq_show = cgroup_memory_pressure_show,
.write = cgroup_memory_pressure_write, .write = cgroup_memory_pressure_write,
.poll = cgroup_pressure_poll, .poll = cgroup_pressure_poll,
...@@ -5189,11 +5259,27 @@ static struct cftype cgroup_psi_files[] = { ...@@ -5189,11 +5259,27 @@ static struct cftype cgroup_psi_files[] = {
}, },
{ {
.name = "cpu.pressure", .name = "cpu.pressure",
.file_offset = offsetof(struct cgroup, psi_files[PSI_CPU]),
.seq_show = cgroup_cpu_pressure_show, .seq_show = cgroup_cpu_pressure_show,
.write = cgroup_cpu_pressure_write, .write = cgroup_cpu_pressure_write,
.poll = cgroup_pressure_poll, .poll = cgroup_pressure_poll,
.release = cgroup_pressure_release, .release = cgroup_pressure_release,
}, },
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
{
.name = "irq.pressure",
.file_offset = offsetof(struct cgroup, psi_files[PSI_IRQ]),
.seq_show = cgroup_irq_pressure_show,
.write = cgroup_irq_pressure_write,
.poll = cgroup_pressure_poll,
.release = cgroup_pressure_release,
},
#endif
{
.name = "cgroup.pressure",
.seq_show = cgroup_pressure_show,
.write = cgroup_pressure_write,
},
#endif /* CONFIG_PSI */ #endif /* CONFIG_PSI */
{ } /* terminate */ { } /* terminate */
}; };
......
...@@ -701,6 +701,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta) ...@@ -701,6 +701,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta)
rq->prev_irq_time += irq_delta; rq->prev_irq_time += irq_delta;
delta -= irq_delta; delta -= irq_delta;
psi_account_irqtime(rq->curr, irq_delta);
#endif #endif
#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING #ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
if (static_key_false((&paravirt_steal_rq_enabled))) { if (static_key_false((&paravirt_steal_rq_enabled))) {
......
This diff is collapsed.
...@@ -107,6 +107,11 @@ __schedstats_from_se(struct sched_entity *se) ...@@ -107,6 +107,11 @@ __schedstats_from_se(struct sched_entity *se)
} }
#ifdef CONFIG_PSI #ifdef CONFIG_PSI
void psi_task_change(struct task_struct *task, int clear, int set);
void psi_task_switch(struct task_struct *prev, struct task_struct *next,
bool sleep);
void psi_account_irqtime(struct task_struct *task, u32 delta);
/* /*
* PSI tracks state that persists across sleeps, such as iowaits and * PSI tracks state that persists across sleeps, such as iowaits and
* memory stalls. As a result, it has to distinguish between sleeps, * memory stalls. As a result, it has to distinguish between sleeps,
...@@ -201,6 +206,7 @@ static inline void psi_ttwu_dequeue(struct task_struct *p) {} ...@@ -201,6 +206,7 @@ static inline void psi_ttwu_dequeue(struct task_struct *p) {}
static inline void psi_sched_switch(struct task_struct *prev, static inline void psi_sched_switch(struct task_struct *prev,
struct task_struct *next, struct task_struct *next,
bool sleep) {} bool sleep) {}
static inline void psi_account_irqtime(struct task_struct *task, u32 delta) {}
#endif /* CONFIG_PSI */ #endif /* CONFIG_PSI */
#ifdef CONFIG_SCHED_INFO #ifdef CONFIG_SCHED_INFO
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment