Skip to contentSkip to conversation

cgroup/for-4.17: Make cgroup_rstat available to controllers

PATCH In review Tejun Heo 1 participant 13 messages Mar 23, 2018 – Apr 26, 2018 cgroup

lore
Share & tools
lore

Tejun Heo

author maintainer
#0 lore raw

PATCH 1/1 cgroup/for-4.17: Make cgroup_rstat available to controllers

Hello,

cgroup has scalable recursive stat mechanism implemented in kernel/stat.c. It's currently only used to track cpu consumptions and difficult to use outside of cgroup core. This patchset generalizes the mechanism and exposes it as cgroup_rstat so that controllers can use it.

This patchset contains the following eight patches. A follow-up patchset will add usages.

0001-cgroup-Rename-kernel-cgroup-stat.c-to-kernel-cgroup-.patch 0002-cgroup-Rename-stat-to-rstat.patch 0003-cgroup-Distinguish-base-resource-stat-implementation.patch 0004-cgroup-Reorganize-kernel-cgroup-rstat.c.patch 0005-cgroup-Factor-out-and-expose-cgroup_rstat_-interface.patch 0006-cgroup-Replace-cgroup_rstat_mutex-with-a-spinlock.patch 0007-cgroup-Add-cgroup_subsys-css_rstat_flush.patch 0008-cgroup-Add-memory-barriers-to-plug-cgroup_rstat_upda.patch

This patchset is also available in the following git branch.

git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git review-cgroup_rstat

diffstat follows.

 include/linux/cgroup-defs.h     |   50 ++--
 include/linux/cgroup.h          |   12 -
 kernel/cgroup/Makefile          |    2 
 kernel/cgroup/cgroup-internal.h |   11 -
 kernel/cgroup/cgroup.c          |   29 ++
 kernel/cgroup/rstat.c           |  410 ++++++++++++++++++++++++++++++++++++++++
 kernel/cgroup/stat.c            |  338 --------------------------------
 7 files changed, 477 insertions(+), 375 deletions(-)

Thanks.

--
tejun

author maintainer
#1 lore raw

PATCH 1/8 cgroup: Rename kernel/cgroup/stat.c to kernel/cgroup/rstat.c

stat is too generic a name and ends up causing subtle confusions. It'll be made generic so that controllers can plug into it, which will make the problem worse. Let's rename it to something more specific - cgroup_rstat for cgroup recursive stat.

First, rename kernel/cgroup/stat.c to kernel/cgroup/rstat.c. No content changes.

---
 kernel/cgroup/Makefile            | 2 +-
 kernel/cgroup/{stat.c => rstat.c} | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename kernel/cgroup/{stat.c => rstat.c} (100%)
kernel/cgroup/Makefile +1 -1
index 2be89a00..bfcdae8 100644
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-y := cgroup.o stat.o namespace.o cgroup-v1.o
+obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o
 
 obj-$(CONFIG_CGROUP_FREEZER) += freezer.o
 obj-$(CONFIG_CGROUP_PIDS) += pids.o
kernel/cgroup/rstat.c renamed from kernel/cgroup/stat.c +0 -0
similarity index 100%
rename from kernel/cgroup/stat.c
rename to kernel/cgroup/rstat.c
Signature · 1 line
2.9.5
author maintainer
#2 lore raw

PATCH 2/8 cgroup: Rename stat to rstat

In reply to Tejun Heo

stat is too generic a name and ends up causing subtle confusions. It'll be made generic so that controllers can plug into it, which will make the problem worse. Let's rename it to something more specific - cgroup_rstat for cgroup recursive stat.

This patch does the following renames. No other changes.

* cpu_stat -> rstat_cpu
* stat -> rstat
* ?cstat -> ?rstatc

Note that the renames are selective. The unrenamed are the ones which implement basic resource statistics on top of rstat. This will be further cleaned up in the following patches.

---
 include/linux/cgroup-defs.h     |  16 ++--
 kernel/cgroup/cgroup-internal.h |  10 +--
 kernel/cgroup/cgroup.c          |  14 ++--
 kernel/cgroup/rstat.c           | 180 ++++++++++++++++++++--------------------
 4 files changed, 112 insertions(+), 108 deletions(-)
include/linux/cgroup-defs.h +9 -7
index 90ede40..02625cf 100644
@@ -259,11 +259,11 @@ struct css_set {
 };
 
 /*
- * cgroup basic resource usage statistics.  Accounting is done per-cpu in
- * cgroup_cpu_stat which is then lazily propagated up the hierarchy on
- * reads.
+ * rstat - cgroup scalable recursive statistics.  Accounting is done
+ * per-cpu in cgroup_rstat_cpu which is then lazily propagated up the
+ * hierarchy on reads.
  *
- * When a stat gets updated, the cgroup_cpu_stat and its ancestors are
+ * When a stat gets updated, the cgroup_rstat_cpu and its ancestors are
  * linked into the updated tree.  On the following read, propagation only
  * considers and consumes the updated tree.  This makes reading O(the
  * number of descendants which have been active since last read) instead of
@@ -274,7 +274,7 @@ struct css_set {
  * become very expensive.  By propagating selectively, increasing reading
  * frequency decreases the cost of each read.
  */
-struct cgroup_cpu_stat {
+struct cgroup_rstat_cpu {
 	/*
 	 * ->sync protects all the current counters.  These are the only
 	 * fields which get updated in the hot path.
@@ -297,7 +297,7 @@ struct cgroup_cpu_stat {
 	 * to the cgroup makes it unnecessary for each per-cpu struct to
 	 * point back to the associated cgroup.
 	 *
-	 * Protected by per-cpu cgroup_cpu_stat_lock.
+	 * Protected by per-cpu cgroup_rstat_cpu_lock.
 	 */
 	struct cgroup *updated_children;	/* terminated by self cgroup */
 	struct cgroup *updated_next;		/* NULL iff not on the list */
@@ -408,8 +408,10 @@ struct cgroup {
 	 */
 	struct cgroup *dom_cgrp;
 
+	/* per-cpu recursive resource statistics */
+	struct cgroup_rstat_cpu __percpu *rstat_cpu;
+
 	/* cgroup basic resource statistics */
-	struct cgroup_cpu_stat __percpu *cpu_stat;
 	struct cgroup_stat pending_stat;	/* pending from children */
 	struct cgroup_stat stat;
 
kernel/cgroup/cgroup-internal.h +5 -5
index b928b27..0927111 100644
@@ -201,13 +201,13 @@ int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
 int cgroup_task_count(const struct cgroup *cgrp);
 
 /*
- * stat.c
+ * rstat.c
  */
-void cgroup_stat_flush(struct cgroup *cgrp);
-int cgroup_stat_init(struct cgroup *cgrp);
-void cgroup_stat_exit(struct cgroup *cgrp);
+void cgroup_rstat_flush(struct cgroup *cgrp);
+int cgroup_rstat_init(struct cgroup *cgrp);
+void cgroup_rstat_exit(struct cgroup *cgrp);
 void cgroup_stat_show_cputime(struct seq_file *seq);
-void cgroup_stat_boot(void);
+void cgroup_rstat_boot(void);
 
 /*
  * namespace.c
kernel/cgroup/cgroup.c +7 -7
index ea31ec5..5549a7c 100644
@@ -144,14 +144,14 @@ static struct static_key_true *cgroup_subsys_on_dfl_key[] = {
 };
 #undef SUBSYS
 
-static DEFINE_PER_CPU(struct cgroup_cpu_stat, cgrp_dfl_root_cpu_stat);
+static DEFINE_PER_CPU(struct cgroup_rstat_cpu, cgrp_dfl_root_rstat_cpu);
 
 /*
  * The default hierarchy, reserved for the subsystems that are otherwise
  * unattached - it never has more than a single cgroup, and all tasks are
  * part of that cgroup.
  */
-struct cgroup_root cgrp_dfl_root = { .cgrp.cpu_stat = &cgrp_dfl_root_cpu_stat };
+struct cgroup_root cgrp_dfl_root = { .cgrp.rstat_cpu = &cgrp_dfl_root_rstat_cpu };
 EXPORT_SYMBOL_GPL(cgrp_dfl_root);
 
 /*
@@ -4592,7 +4592,7 @@ static void css_free_work_fn(struct work_struct *work)
 			cgroup_put(cgroup_parent(cgrp));
 			kernfs_put(cgrp->kn);
 			if (cgroup_on_dfl(cgrp))
-				cgroup_stat_exit(cgrp);
+				cgroup_rstat_exit(cgrp);
 			kfree(cgrp);
 		} else {
 			/*
@@ -4638,7 +4638,7 @@ static void css_release_work_fn(struct work_struct *work)
 		trace_cgroup_release(cgrp);
 
 		if (cgroup_on_dfl(cgrp))
-			cgroup_stat_flush(cgrp);
+			cgroup_rstat_flush(cgrp);
 
 		for (tcgrp = cgroup_parent(cgrp); tcgrp;
 		     tcgrp = cgroup_parent(tcgrp))
@@ -4824,7 +4824,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent)
 		goto out_free_cgrp;
 
 	if (cgroup_on_dfl(parent)) {
-		ret = cgroup_stat_init(cgrp);
+		ret = cgroup_rstat_init(cgrp);
 		if (ret)
 			goto out_cancel_ref;
 	}
@@ -4889,7 +4889,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent)
 	cgroup_idr_remove(&root->cgroup_idr, cgrp->id);
 out_stat_exit:
 	if (cgroup_on_dfl(parent))
-		cgroup_stat_exit(cgrp);
+		cgroup_rstat_exit(cgrp);
 out_cancel_ref:
 	percpu_ref_exit(&cgrp->self.refcnt);
 out_free_cgrp:
@@ -5282,7 +5282,7 @@ int __init cgroup_init(void)
 	BUG_ON(cgroup_init_cftypes(NULL, cgroup_base_files));
 	BUG_ON(cgroup_init_cftypes(NULL, cgroup1_base_files));
 
-	cgroup_stat_boot();
+	cgroup_rstat_boot();
 
 	/*
 	 * The latency of the synchronize_sched() is too high for cgroups,
kernel/cgroup/rstat.c +91 -89

374 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#3 lore raw

PATCH 3/8 cgroup: Distinguish base resource stat implementation from rstat

In reply to Tejun Heo

Base resource stat accounts universial (not specific to any controller) resource consumptions on top of rstat. Currently, its implementation is intermixed with rstat implementation making the code confusing to follow.

This patch clarifies the distintion by doing the followings.

* Encapsulate base resource stat counters, currently only cputime, in struct cgroup_base_stat.

* Move prev_cputime into struct cgroup and initialize it with cgroup.

* Rename the related functions so that they start with cgroup_base_stat.

* Prefix the related variables and field names with b.

This patch doesn't make any functional changes.

---
 include/linux/cgroup-defs.h     | 29 ++++++++++--------
 kernel/cgroup/cgroup-internal.h |  2 +-
 kernel/cgroup/cgroup.c          |  4 ++-
 kernel/cgroup/rstat.c           | 67 ++++++++++++++++++++---------------------
 4 files changed, 52 insertions(+), 50 deletions(-)
include/linux/cgroup-defs.h +16 -13
index 02625cf..cf9db7b 100644
@@ -258,6 +258,10 @@ struct css_set {
 	struct rcu_head rcu_head;
 };
 
+struct cgroup_base_stat {
+	struct task_cputime cputime;
+};
+
 /*
  * rstat - cgroup scalable recursive statistics.  Accounting is done
  * per-cpu in cgroup_rstat_cpu which is then lazily propagated up the
@@ -273,20 +277,24 @@ struct css_set {
  * aren't active and stat may be read frequently.  The combination can
  * become very expensive.  By propagating selectively, increasing reading
  * frequency decreases the cost of each read.
+ *
+ * This struct hosts both the fields which implement the above -
+ * updated_children and updated_next - and the fields which track basic
+ * resource statistics on top of it - bsync, bstat and last_bstat.
  */
 struct cgroup_rstat_cpu {
 	/*
-	 * ->sync protects all the current counters.  These are the only
-	 * fields which get updated in the hot path.
+	 * ->bsync protects ->bstat.  These are the only fields which get
+	 * updated in the hot path.
 	 */
-	struct u64_stats_sync sync;
-	struct task_cputime cputime;
+	struct u64_stats_sync bsync;
+	struct cgroup_base_stat bstat;
 
 	/*
 	 * Snapshots at the last reading.  These are used to calculate the
 	 * deltas to propagate to the global counters.
 	 */
-	struct task_cputime last_cputime;
+	struct cgroup_base_stat last_bstat;
 
 	/*
 	 * Child cgroups with stat updates on this cpu since the last read
@@ -303,12 +311,6 @@ struct cgroup_rstat_cpu {
 	struct cgroup *updated_next;		/* NULL iff not on the list */
 };
 
-struct cgroup_stat {
-	/* per-cpu statistics are collected into the folowing global counters */
-	struct task_cputime cputime;
-	struct prev_cputime prev_cputime;
-};
-
 struct cgroup {
 	/* self css with NULL ->ss, points back to this cgroup */
 	struct cgroup_subsys_state self;
@@ -412,8 +414,9 @@ struct cgroup {
 	struct cgroup_rstat_cpu __percpu *rstat_cpu;
 
 	/* cgroup basic resource statistics */
-	struct cgroup_stat pending_stat;	/* pending from children */
-	struct cgroup_stat stat;
+	struct cgroup_base_stat pending_bstat;	/* pending from children */
+	struct cgroup_base_stat bstat;
+	struct prev_cputime prev_cputime;	/* for printing out cputime */
 
 	/*
 	 * list of pidlists, up to two for each namespace (one for procs, one
kernel/cgroup/cgroup-internal.h +1 -1
index 0927111..aab4d0a 100644
@@ -206,7 +206,7 @@ int cgroup_task_count(const struct cgroup *cgrp);
 void cgroup_rstat_flush(struct cgroup *cgrp);
 int cgroup_rstat_init(struct cgroup *cgrp);
 void cgroup_rstat_exit(struct cgroup *cgrp);
-void cgroup_stat_show_cputime(struct seq_file *seq);
+void cgroup_base_stat_cputime_show(struct seq_file *seq);
 void cgroup_rstat_boot(void);
 
 /*
kernel/cgroup/cgroup.c +3 -1
index 5549a7c..0d3d093 100644
@@ -54,6 +54,7 @@
 #include <linux/proc_ns.h>
 #include <linux/nsproxy.h>
 #include <linux/file.h>
+#include <linux/sched/cputime.h>
 #include <net/sock.h>
 
 #define CREATE_TRACE_POINTS
@@ -1859,6 +1860,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
 	cgrp->dom_cgrp = cgrp;
 	cgrp->max_descendants = INT_MAX;
 	cgrp->max_depth = INT_MAX;
+	prev_cputime_init(&cgrp->prev_cputime);
 
 	for_each_subsys(ss, ssid)
 		INIT_LIST_HEAD(&cgrp->e_csets[ssid]);
@@ -3396,7 +3398,7 @@ static int cpu_stat_show(struct seq_file *seq, void *v)
 	struct cgroup __maybe_unused *cgrp = seq_css(seq)->cgroup;
 	int ret = 0;
 
-	cgroup_stat_show_cputime(seq);
+	cgroup_base_stat_cputime_show(seq);
 #ifdef CONFIG_CGROUP_SCHED
 	ret = cgroup_extra_stat_show(seq, cgrp, cpu_cgrp_id);
 #endif
kernel/cgroup/rstat.c +32 -35

156 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#4 lore raw

PATCH 4/8 cgroup: Reorganize kernel/cgroup/rstat.c

In reply to Tejun Heo

Currently, rstat.c has rstat and base stat implementations intermixed. Collect base stat implementation at the end of the file. Also, reorder the prototypes.

This patch doesn't make any functional changes.

---
 kernel/cgroup/cgroup-internal.h |   2 +-
 kernel/cgroup/rstat.c           | 182 +++++++++++++++++++++-------------------
 2 files changed, 95 insertions(+), 89 deletions(-)
kernel/cgroup/cgroup-internal.h +1 -1
index aab4d0a..2bf6fb4 100644
@@ -206,8 +206,8 @@ int cgroup_task_count(const struct cgroup *cgrp);
 void cgroup_rstat_flush(struct cgroup *cgrp);
 int cgroup_rstat_init(struct cgroup *cgrp);
 void cgroup_rstat_exit(struct cgroup *cgrp);
-void cgroup_base_stat_cputime_show(struct seq_file *seq);
 void cgroup_rstat_boot(void);
+void cgroup_base_stat_cputime_show(struct seq_file *seq);
 
 /*
  * namespace.c
kernel/cgroup/rstat.c +94 -88

203 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#5 lore raw

PATCH 5/8 cgroup: Factor out and expose cgroup_rstat_*() interface functions

In reply to Tejun Heo

cgroup_rstat is being generalized so that controllers can use it too. This patch factors out and exposes the following interface functions.

* cgroup_rstat_updated(): Renamed from cgroup_rstat_cpu_updated() for consistency.

* cgroup_rstat_flush_hold/release(): Factored out from base stat implementation.

* cgroup_rstat_flush(): Verbatim expose.

While at it, drop assert on cgroup_rstat_mutex in cgroup_base_stat_flush() as it crosses layers and make a minor comment update.

---
 include/linux/cgroup.h          | 11 +++++++++--
 kernel/cgroup/cgroup-internal.h |  1 -
 kernel/cgroup/rstat.c           | 41 +++++++++++++++++++++++++++++------------
 3 files changed, 38 insertions(+), 15 deletions(-)
include/linux/cgroup.h +9 -2
index 473e0c0..5c6018f 100644
@@ -690,11 +690,18 @@ static inline void cgroup_path_from_kernfs_id(const union kernfs_node_id *id,
 	char *buf, size_t buflen) {}
 #endif /* !CONFIG_CGROUPS */
 
+#ifdef CONFIG_CGROUPS
 /*
- * Basic resource stats.
+ * cgroup scalable recursive statistics.
  */
-#ifdef CONFIG_CGROUPS
+void cgroup_rstat_updated(struct cgroup *cgrp, int cpu);
+void cgroup_rstat_flush(struct cgroup *cgrp);
+void cgroup_rstat_flush_hold(struct cgroup *cgrp);
+void cgroup_rstat_flush_release(void);
 
+/*
+ * Basic resource stats.
+ */
 #ifdef CONFIG_CGROUP_CPUACCT
 void cpuacct_charge(struct task_struct *tsk, u64 cputime);
 void cpuacct_account_field(struct task_struct *tsk, int index, u64 val);
kernel/cgroup/cgroup-internal.h +0 -1
index 2bf6fb4..b68e1a7 100644
@@ -203,7 +203,6 @@ int cgroup_task_count(const struct cgroup *cgrp);
 /*
  * rstat.c
  */
-void cgroup_rstat_flush(struct cgroup *cgrp);
 int cgroup_rstat_init(struct cgroup *cgrp);
 void cgroup_rstat_exit(struct cgroup *cgrp);
 void cgroup_rstat_boot(void);
kernel/cgroup/rstat.c +29 -12

85 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#6 lore raw

PATCH v2 5/8 cgroup: Factor out and expose cgroup_rstat_*() interface functions

From 6139f87ac4a5650fed5c8be5611becbce96d7f1c Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Sat, 24 Mar 2018 13:39:36 -0700

cgroup_rstat is being generalized so that controllers can use it too. This patch factors out and exposes the following interface functions.

* cgroup_rstat_updated(): Renamed from cgroup_rstat_cpu_updated() for consistency.

* cgroup_rstat_flush_hold/release(): Factored out from base stat implementation.

* cgroup_rstat_flush(): Verbatim expose.

While at it, drop assert on cgroup_rstat_mutex in cgroup_base_stat_flush() as it crosses layers and make a minor comment update.

v2: Added EXPORT_SYMBOL_GPL(cgroup_rstat_updated) to fix a build bug.

---
 include/linux/cgroup.h          | 11 +++++++++--
 kernel/cgroup/cgroup-internal.h |  1 -
 kernel/cgroup/rstat.c           | 42 +++++++++++++++++++++++++++++------------
 3 files changed, 39 insertions(+), 15 deletions(-)
include/linux/cgroup.h +9 -2
index 473e0c0..5c6018f 100644
@@ -690,11 +690,18 @@ static inline void cgroup_path_from_kernfs_id(const union kernfs_node_id *id,
 	char *buf, size_t buflen) {}
 #endif /* !CONFIG_CGROUPS */
 
+#ifdef CONFIG_CGROUPS
 /*
- * Basic resource stats.
+ * cgroup scalable recursive statistics.
  */
-#ifdef CONFIG_CGROUPS
+void cgroup_rstat_updated(struct cgroup *cgrp, int cpu);
+void cgroup_rstat_flush(struct cgroup *cgrp);
+void cgroup_rstat_flush_hold(struct cgroup *cgrp);
+void cgroup_rstat_flush_release(void);
 
+/*
+ * Basic resource stats.
+ */
 #ifdef CONFIG_CGROUP_CPUACCT
 void cpuacct_charge(struct task_struct *tsk, u64 cputime);
 void cpuacct_account_field(struct task_struct *tsk, int index, u64 val);
kernel/cgroup/cgroup-internal.h +0 -1
index 2bf6fb4..b68e1a7 100644
@@ -203,7 +203,6 @@ int cgroup_task_count(const struct cgroup *cgrp);
 /*
  * rstat.c
  */
-void cgroup_rstat_flush(struct cgroup *cgrp);
 int cgroup_rstat_init(struct cgroup *cgrp);
 void cgroup_rstat_exit(struct cgroup *cgrp);
 void cgroup_rstat_boot(void);
kernel/cgroup/rstat.c +30 -12

92 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#7 lore raw

PATCH 6/8 cgroup: Replace cgroup_rstat_mutex with a spinlock

In reply to Tejun Heo

Currently, rstat flush path is protected with a mutex which is fine as all the existing users are from interface file show path. However, rstat is being generalized for use by controllers and flushing from atomic contexts will be necessary.

This patch replaces cgroup_rstat_mutex with a spinlock and adds a irq-safe flush function - cgroup_rstat_flush_irqsafe(). Explicit yield handling is added to the flush path so that other flush functions can yield to other threads and flushers.

---
 include/linux/cgroup.h |  1 +
 kernel/cgroup/rstat.c  | 57 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 45 insertions(+), 13 deletions(-)
include/linux/cgroup.h +1 -0
index 5c6018f..c9fdf6f 100644
@@ -696,6 +696,7 @@ static inline void cgroup_path_from_kernfs_id(const union kernfs_node_id *id,
  */
 void cgroup_rstat_updated(struct cgroup *cgrp, int cpu);
 void cgroup_rstat_flush(struct cgroup *cgrp);
+void cgroup_rstat_flush_irqsafe(struct cgroup *cgrp);
 void cgroup_rstat_flush_hold(struct cgroup *cgrp);
 void cgroup_rstat_flush_release(void);
 
kernel/cgroup/rstat.c +44 -13

105 lines not on this page · raw message

Signature · 1 line
2.9.5
author maintainer
#8 lore raw

PATCH 7/8 cgroup: Add cgroup_subsys->css_rstat_flush()

In reply to Tejun Heo

This patch adds cgroup_subsys->css_rstat_flush(). If a subsystem has this callback, its csses are linked on cgrp->css_rstat_list and rstat will call the function whenever the associated cgroup is flushed. Flush is also performed when such csses are released so that residual counts aren't lost.

Combined with the rstat API previous patches factored out, this allows controllers to plug into rstat to manage their statistics in a scalable way.

---
 include/linux/cgroup-defs.h |  5 +++++
 kernel/cgroup/cgroup.c      | 11 +++++++++++
 kernel/cgroup/rstat.c       | 11 ++++++++++-
 3 files changed, 26 insertions(+), 1 deletion(-)
include/linux/cgroup-defs.h +5 -0
index cf9db7b..8b8dd17 100644
@@ -130,6 +130,9 @@ struct cgroup_subsys_state {
 	struct list_head sibling;
 	struct list_head children;
 
+	/* flush target list anchored at cgrp->rstat_css_list */
+	struct list_head rstat_css_node;
+
 	/*
 	 * PI: Subsys-unique ID.  0 is unused and root is always 1.  The
 	 * matching css can be looked up using css_from_id().
@@ -412,6 +415,7 @@ struct cgroup {
 
 	/* per-cpu recursive resource statistics */
 	struct cgroup_rstat_cpu __percpu *rstat_cpu;
+	struct list_head rstat_css_list;
 
 	/* cgroup basic resource statistics */
 	struct cgroup_base_stat pending_bstat;	/* pending from children */
@@ -577,6 +581,7 @@ struct cgroup_subsys {
 	void (*css_released)(struct cgroup_subsys_state *css);
 	void (*css_free)(struct cgroup_subsys_state *css);
 	void (*css_reset)(struct cgroup_subsys_state *css);
+	void (*css_rstat_flush)(struct cgroup_subsys_state *css, int cpu);
 	int (*css_extra_stat_show)(struct seq_file *seq,
 				   struct cgroup_subsys_state *css);
 
kernel/cgroup/cgroup.c +11 -0
index 0d3d093..f29f84e 100644
@@ -1860,6 +1860,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
 	cgrp->dom_cgrp = cgrp;
 	cgrp->max_descendants = INT_MAX;
 	cgrp->max_depth = INT_MAX;
+	INIT_LIST_HEAD(&cgrp->rstat_css_list);
 	prev_cputime_init(&cgrp->prev_cputime);
 
 	for_each_subsys(ss, ssid)
@@ -4630,6 +4631,11 @@ static void css_release_work_fn(struct work_struct *work)
 
 	if (ss) {
 		/* css release path */
+		if (!list_empty(&css->rstat_css_node)) {
+			cgroup_rstat_flush(cgrp);
+			list_del_rcu(&css->rstat_css_node);
+		}
+
 		cgroup_idr_replace(&ss->css_idr, NULL, css->id);
 		if (ss->css_released)
 			ss->css_released(css);
@@ -4690,6 +4696,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
 	css->id = -1;
 	INIT_LIST_HEAD(&css->sibling);
 	INIT_LIST_HEAD(&css->children);
+	INIT_LIST_HEAD(&css->rstat_css_node);
 	css->serial_nr = css_serial_nr_next++;
 	atomic_set(&css->online_cnt, 0);
 
@@ -4698,6 +4705,9 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
 		css_get(css->parent);
 	}
 
+	if (cgroup_on_dfl(cgrp) && ss->css_rstat_flush)
+		list_add_rcu(&css->rstat_css_node, &cgrp->rstat_css_list);
+
 	BUG_ON(cgroup_css(cgrp, ss));
 }
 
@@ -4799,6 +4809,7 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
 err_list_del:
 	list_del_rcu(&css->sibling);
 err_free_css:
+	list_del_rcu(&css->rstat_css_node);
 	call_rcu(&css->rcu_head, css_free_rcu_fn);
 	return ERR_PTR(err);
 }
kernel/cgroup/rstat.c +10 -1
index a5f9338..18b464a 100644
@@ -143,8 +143,17 @@ static void cgroup_rstat_flush_locked(struct cgroup *cgrp, bool may_sleep)
 		struct cgroup *pos = NULL;
 
 		raw_spin_lock(cpu_lock);
-		while ((pos = cgroup_rstat_cpu_pop_updated(pos, cgrp, cpu)))
+		while ((pos = cgroup_rstat_cpu_pop_updated(pos, cgrp, cpu))) {
+			struct cgroup_subsys_state *css;
+
 			cgroup_base_stat_flush(pos, cpu);
+
+			rcu_read_lock();
+			list_for_each_entry_rcu(css, &pos->rstat_css_list,
+						rstat_css_node)
+				css->ss->css_rstat_flush(css, cpu);
+			rcu_read_unlock();
+		}
 		raw_spin_unlock(cpu_lock);
 
 		/* if @may_sleep, play nice and yield if necessary */
Signature · 1 line
2.9.5
author maintainer
#9 lore raw

PATCH 8/8 cgroup: Add memory barriers to plug cgroup_rstat_updated() race window

In reply to Tejun Heo

cgroup_rstat_updated() has a small race window where an updated signaling can race with flush and could be lost till the next update. This wasn't a problem for the existing usages, but we plan to use rstat to track counters which need to be accurate.

This patch plugs the race window by synchronizing cgroup_rstat_updated() and flush path with memory barriers around cgroup_rstat_cpu->updated_next pointer.

---
 kernel/cgroup/rstat.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
kernel/cgroup/rstat.c +13 -3
index 18b464a..662d7ae 100644
@@ -28,9 +28,12 @@ void cgroup_rstat_updated(struct cgroup *cgrp, int cpu)
 	unsigned long flags;
 
 	/*
-	 * Speculative already-on-list test.  This may race leading to
-	 * temporary inaccuracies, which is fine.
-	 *
+	 * Paired with the one in cgroup_rstat_cpu_pop_upated().  Either we
+	 * see NULL updated_next or they see our updated stat.
+	 */
+	smp_mb();
+
+	/*
 	 * Because @parent's updated_children is terminated with @parent
 	 * instead of NULL, we can tell whether @cgrp is on the list by
 	 * testing the next pointer for NULL.
@@ -125,6 +128,13 @@ static struct cgroup *cgroup_rstat_cpu_pop_updated(struct cgroup *pos,
 
 		*nextp = rstatc->updated_next;
 		rstatc->updated_next = NULL;
+
+		/*
+		 * Paired with the one in cgroup_rstat_cpu_updated().
+		 * Either they see NULL updated_next or we see their
+		 * updated stat.
+		 */
+		smp_mb();
 	}
 
 	return pos;
Signature · 1 line
2.9.5
author maintainer
#10 lore raw

PATCH 1/1 cgroup: Make cgroup_rstat_updated() ready for root cgroup usage

In reply to Tejun Heo

From b7be022bf811d02605098fa61b7545bc146b78e7 Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Mon, 2 Apr 2018 14:45:20 -0700

cgroup_rstat_updated() ensures that the cgroup's rstat is linked to the parent. If there's no parent, it never gets linked and the function ends up grabbing and releasing the cgroup_rstat_lock each time for no reason which can be expensive.

This hasn't been a problem till now because nobody was calling the function for the root cgroup but rstat is gonna be exposed to controllers and use cases, so let's get ready. Make cgroup_rstat_updated() an no-op for the root cgroup.

---
Hello,

This should address the scalability issue reported by kenrel test bot. All git branches updated accordingly.

Thanks.

 kernel/cgroup/rstat.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/cgroup/rstat.c +4 -0
index 0d1d5fc..9a30b51 100644
@@ -27,6 +27,10 @@ void cgroup_rstat_updated(struct cgroup *cgrp, int cpu)
 	struct cgroup *parent;
 	unsigned long flags;
 
+	/* nothing to do for root */
+	if (!cgroup_parent(cgrp))
+		return;
+
 	/*
 	 * Paired with the one in cgroup_rstat_cpu_pop_upated().  Either we
 	 * see NULL updated_next or they see our updated stat.
Signature · 1 line
2.9.5
author maintainer Applied
#11 lore raw

Re: [PATCH] cgroup: Make cgroup_rstat_updated() ready for root cgroup usage

Quoted 9 lines · Tejun Heo

From b7be022bf811d02605098fa61b7545bc146b78e7 Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Mon, 2 Apr 2018 14:45:20 -0700

cgroup_rstat_updated() ensures that the cgroup's rstat is linked to the parent. If there's no parent, it never gets linked and the function ends up grabbing and releasing the cgroup_rstat_lock each time for no reason which can be expensive.

Quoted 6 lines

This hasn't been a problem till now because nobody was calling the function for the root cgroup but rstat is gonna be exposed to controllers and use cases, so let's get ready. Make cgroup_rstat_updated() an no-op for the root cgroup.

Applied to cgroup/for-4.18.

Thanks.

Signature · 1 line
tejun
author maintainer
#12 lore raw
In reply to Tejun Heo
Quoted 11 lines · Tejun Heo

Hello,

cgroup has scalable recursive stat mechanism implemented in kernel/stat.c. It's currently only used to track cpu consumptions and difficult to use outside of cgroup core. This patchset generalizes the mechanism and exposes it as cgroup_rstat so that controllers can use it.

This patchset contains the following eight patches. A follow-up patchset will add usages.

Quoted 8 lines

0001-cgroup-Rename-kernel-cgroup-stat.c-to-kernel-cgroup-.patch 0002-cgroup-Rename-stat-to-rstat.patch 0003-cgroup-Distinguish-base-resource-stat-implementation.patch 0004-cgroup-Reorganize-kernel-cgroup-rstat.c.patch 0005-cgroup-Factor-out-and-expose-cgroup_rstat_-interface.patch 0006-cgroup-Replace-cgroup_rstat_mutex-with-a-spinlock.patch 0007-cgroup-Add-cgroup_subsys-css_rstat_flush.patch 0008-cgroup-Add-memory-barriers-to-plug-cgroup_rstat_upda.patch

Applied 1-8 to cgroup/for-4.18.

Thanks.

Signature · 1 line
tejun