mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
To: David Hildenbrand <david@kernel.org>
Cc: linux-kernel@vger.kernel.org,
	Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
	"Paul E. McKenney" <paulmck@kernel.org>,
	Steven Rostedt <rostedt@goodmis.org>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Dennis Zhou <dennis@kernel.org>, Tejun Heo <tj@kernel.org>,
	Christoph Lameter <cl@gentwo.org>,
	Martin Liu <liumartin@google.com>,
	David Rientjes <rientjes@google.com>,
	christian.koenig@amd.com, Shakeel Butt <shakeel.butt@linux.dev>,
	SeongJae Park <sj@kernel.org>, Michal Hocko <mhocko@suse.com>,
	Johannes Weiner <hannes@cmpxchg.org>,
	Sweet Tea Dorminy <sweettea-kernel@dorminy.me>,
	Lorenzo Stoakes <ljs@kernel.org>,
	"Liam R. Howlett" <liam@infradead.org>,
	Mike Rapoport <rppt@kernel.org>,
	Suren Baghdasaryan <surenb@google.com>,
	Vlastimil Babka <vbabka@kernel.org>,
	Christian Brauner <brauner@kernel.org>,
	Wei Yang <richard.weiyang@gmail.com>,
	Miaohe Lin <linmiaohe@huawei.com>,
	Al Viro <viro@zeniv.linux.org.uk>, Yu Zhao <yuzhao@google.com>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	Mateusz Guzik <mjguzik@gmail.com>,
	Matthew Wilcox <willy@infradead.org>,
	Baolin Wang <baolin.wang@linux.alibaba.com>,
	Aboorva Devarajan <aboorvad@linux.ibm.com>,
	David Carlier <devnexen@gmail.com>,
	Josh Law <objecting@objecting.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	linux-mm@kvack.org, Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>,
	linux-doc@vger.kernel.org, Kees Cook <kees@kernel.org>,
	Ingo Molnar <mingo@redhat.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Juri Lelli <juri.lelli@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>,
	Dietmar Eggemann <dietmar.eggemann@arm.com>,
	Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
	Valentin Schneider <vschneid@redhat.com>,
	K Prateek Nayak <kprateek.nayak@amd.com>,
	linux-trace-kernel@vger.kernel.org, Jan Kara <jack@suse.cz>,
	linux-fsdevel@vger.kernel.org, Oleg Nesterov <oleg@redhat.com>
Subject: [RFC PATCH v23 01/10] lib: introduce hierarchical per-cpu counters
Date: Mon,  2 Nov 2026 14:06:59 -0500	[thread overview]
Message-ID: <20261102190708.1287100-2-mathieu.desnoyers@efficios.com> (raw)
In-Reply-To: <20261102190708.1287100-1-mathieu.desnoyers@efficios.com>

Introduce hierarchical per-cpu counters, struct percpu_counter_tree: a
split counter whose approximate sum stays within a fixed bound of its
precise sum, without taking a global lock on updates.  They are meant
for counters updated from many CPUs whose approximation is read often,
such as the RSS counters of an mm.  A following patch uses them for the
RSS counters, to bound their inaccuracy on large many-core systems [1].

* Motivation

The purpose of this hierarchical split-counter scheme is to:

- Minimize contention when incrementing and decrementing counters,
- Provide fast access to a sum approximation,
- Provide a sum approximation with an acceptable accuracy level when
  scaling to many-core systems.
- Provide approximate and precise comparison of two counters, and
  between a counter and a value.
- Provide possible precise sum ranges for a given sum approximation.

* Design

The hierarchical per-CPU counters propagate a sum approximation through an
N-way tree.  When reaching the batch size, the carry is propagated up the
tree, whose fan-out at each level is selected at boot from nr_cpu_ids.  The
batch size of each level is the batch size of the prior level multiplied by
the prior level's fan-out.

The following diagram illustrates the propagation with 8 CPUs through a
binary tree.  This is not an actual configuration: at 8 CPUs, the
default tree is flat (see the default shapes below).  CPU c propagates
into item (c & (L - 1)) of a level holding L items, so the CPUs are
listed in bit-reversed order:

Level 3:  0  (root)
          |   \
          |         \
          |               \
Level 2:  0                   1
          | \                 | \
          |    \              |    \
          |       \           |       \
Level 1:  0         2         1         3
          | \       | \       | \       | \
          |  \      |  \      |  \      |  \
          |   \     |   \     |   \     |   \
Level 0:  0    4    2    6    1    5    3    7

Each counter below the root keeps a residual: once it reaches its batch
size in either direction, it propagates its multiples of the batch size
and subtracts them, which leaves a residual within (-batch, +batch).
This hysteresis absorbs updates going back and forth around a batch size
boundary, instead of propagating them up to the root on each update.
The residual moves in steps of the counter's input: 1 for the per-cpu
counters, which receive the updates, and BATCH(N - 1), the batch size of
the level below, for the counters of level N, which only receive its
carries.  The largest residual is therefore batch - 1 at level 0, and
BATCH(N) - BATCH(N - 1) at level N.  The approximate sum is the precise
sum minus the residuals, so it is within the same distance of the
precise sum in both directions:

  accuracy = nr_cpu_ids * (batch - 1) +
             sum(N >= 1) (BATCH(N) - BATCH(N - 1)) * nr_items(N)

The level 0 per-cpu counters are signed bytes, updated with a
this_cpu_try_cmpxchg() loop, which limits the batch size to 128.

Up to 32 CPUs, the tree is flat: the per-cpu counters propagate directly
to the root, like struct percpu_counter.  From 33 to 64 CPUs, the
per-cpu counters propagate to 4 intermediate counters, each shared by up
to 16 CPUs, so the carries of all CPUs do not contend on the root.
Beyond, each level has a fan-out of 8: a remaining order of 1 gives the
root a fan-out of 16, and a remaining order of 2 adds a level with a
fan-out of 4 below the root.  With a fixed batch size, the maximum
inaccuracy grows with O(n*log(n)) as the number of CPUs increases,
compared to O(n^2) for struct percpu_counter, whose batch size grows
with the number of CPUs.  For example, at 384 logical CPUs with a batch
size of 32, the bound is 40576, compared to 294528 for a struct
percpu_counter with its default batch size of 768.

* CPU hotplug

Like the per-cpu data of struct percpu_counter, the tree is sized at
boot, and is not reshaped on CPU hotplug.  The level 0 residual of an
offline CPU stays in place: the precise sum still reads it, and the
bound, computed from nr_cpu_ids, still holds.  Unlike the default batch
size of struct percpu_counter, which follows the number of online CPUs,
the bound does not shrink when fewer CPUs are online.  When the possible
CPUs are sparse, the tree is still shaped for nr_cpu_ids, the highest
possible CPU number plus one, and the bound also counts the level 0
residuals of the missing CPUs: it holds, but cannot be reached.

* Memory Use

Each counter uses a 32-byte struct percpu_counter_tree, one byte of
per-cpu data per possible CPU, and its tree items.
percpu_counter_tree_init_many() allocates the per-cpu data and the tree
items, whose sizes depend on the number of possible CPUs and on
nr_cpu_ids, so struct percpu_counter_tree has a fixed size.  The items
at the same tree position of all the counters initialized together are
packed in a cache line aligned block, so a set of counters updated
together, such as the 4 RSS counters of an mm, shares cache lines
without false sharing between CPUs updating different branches of the
tree.  When the items of a tree position fit in a cache line, the tree
items come from a slab cache created by
percpu_counter_tree_subsystem_init(), which runs once the slab allocator
is up.

The memory use of nr_counters initialized together, up to 8, on a
64-bit arch with 64-byte cache lines is:

  nr_counters * 32 + ALIGN(nr_counters, 4) * nr_possible_cpus
  + nr_items * 64

as the per-cpu allocator rounds sizes up to 4 bytes.  The tree is shaped
for nr_cpu_ids rounded up to a power of 2: nr_items is 1 (the root) up
to 32 CPUs, 5 up to 64, 17 up to 128, 41 up to 256, 73 up to 512, 145 up
to 1024 and 585 up to 4096.  In comparison, a struct percpu_counter uses
40 bytes and 4 bytes of per-cpu data per possible CPU.

For a set of 4 counters, the hierarchical counters use less memory than
as many struct percpu_counter from 3 to 64 CPUs, and beyond when the
number of CPUs is a power of 2 (at most 0.89 times as much).  Between
powers of 2, fewer CPUs share the cost of the tree items: 0.94 to 1.09
times at 3 * 2^n CPUs (96, 192, 384, ...), and up to 1.47 times just
above a power of 2 (129 CPUs).  A lone counter uses one item of each
cache line: 3.0 to 3.5 times the memory of a struct percpu_counter when
the number of CPUs is a power of 2 beyond 64, and up to 5.7 times just
above.

Link: https://lkml.kernel.org/r/20260227153730.1556542-1-mathieu.desnoyers@efficios.com
Link: https://lore.kernel.org/lkml/20250331223516.7810-2-sweettea-kernel@dorminy.me/ # [1]
Link: https://lkml.kernel.org/r/20260227153730.1556542-2-mathieu.desnoyers@efficios.com
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Dennis Zhou <dennis@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Lameter <cl@gentwo.org>
Cc: Martin Liu <liumartin@google.com>
Cc: David Rientjes <rientjes@google.com>
Cc: christian.koenig@amd.com
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: SeongJae Park <sj@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Wei Yang <richard.weiyang@gmail.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Miaohe Lin <linmiaohe@huawei.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Yu Zhao <yuzhao@google.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Mateusz Guzik <mjguzik@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Aboorva Devarajan <aboorvad@linux.ibm.com>
Cc: David Carlier <devnexen@gmail.com>
Cc: Josh Law <objecting@objecting.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: linux-mm@kvack.org
---
Changes since v22:
- Refactor configuration table, remove duplicated definitions,
  document heuristic, add table content validation, run by the KUnit
  and userspace tests.
- Allocate the per-cpu counters and the tree items in
  percpu_counter_tree_init_many(), instead of taking tree items from
  the caller: remove percpu_counter_tree_items_size(),
  PERCPU_COUNTER_TREE_ITEMS_STATIC_SIZE and the items argument of the
  init functions.  struct percpu_counter_tree has a fixed size of 32
  bytes, down from 64.
- Call percpu_counter_tree_subsystem_init() after mm_core_init(), once
  its boot parameters are parsed and slab is up, before the first
  counter is used, and warn about counters initialized before it.
- Use signed bytes for the level 0 per-cpu counters, with the same
  hysteresis as the intermediate levels, updated with a
  this_cpu_try_cmpxchg() loop. Limit the batch size to 128.
- Pack the tree items of the counters initialized together by
  percpu_counter_tree_init_many() in cache lines, allocated from a slab
  cache when the items of a tree position fit in one cache line.
- Use a flat tree up to 32 CPUs, a fan-out of 16 then 4 from 33 to 64
  CPUs to keep the carries of all CPUs from contending on the root, and
  a fan-out of 8 beyond, with a root fan-out of 16 for a remaining order
  of 1, to match the memory footprint and accuracy of struct
  percpu_counter at low CPU counts and bound the inaccuracy with
  O(n*log(n)) at high CPU counts.
- Print the tree shape at boot.
- Document that the accuracy range is a property of settled counters.
  (Reported by Sashiko.)
- Add a diagram of the approximation ranges above compare_delta().
  (Reported by Sashiko.)
- Introduce wrapped_delta(), and document that comparisons are valid
  only for counters differing by less than LONG_MAX. (Reported by
  Sashiko.)
- Fix the direction of the approximation margins in the comparison
  functions. They were swapped, which returned wrong definitive answers
  when 2^arity of a level exceeds the batch size (batch size 2 from 9
  CPUs, batch size 4 from 65 CPUs).
- Add hysteresis to the intermediate tree levels: they keep a residual
  within (-batch, +batch) of their level, and only propagate once it
  reaches their batch size, so updates going back and forth around a
  batch size boundary no longer propagate up to the root on each
  update.
- Replace percpu_counter_tree_approximate_accuracy_range() and its
  "under" and "over" ranges by a single accuracy, the exact bound of the
  residuals, which is the same in both directions:
  percpu_counter_tree_approximate_accuracy().
- Add percpu_counter_tree_check_residuals() for the tests: it checks the
  residuals of the intermediate counters, and that no carry was lost
  or duplicated.
- Extract the carries of the intermediate counters with a cmpxchg on
  the value returned by the addition, so concurrent updaters never
  subtract a carry computed from a stale residual, and explain why
  relaxed ordering is sufficient.
- Read the level 0 counters with READ_ONCE() in the precise sum.
- Document that percpu_counter_tree_set() must not run concurrently
  with updates.
- Add the documentation to the core-api toctree.
- Fix the propagation diagram to match the CPU to item mapping.
- Mark boot-time topology state __ro_after_init, use pr_err().
- Validate the batch size on UP as on SMP.
- Fix the sign of the !CONFIG_SMP value comparisons, and compare on
  the wrapped difference as on SMP.
- Add a MAINTAINERS entry.
- Fix checkpatch warnings.
- Fix documentation typos.
---
 Documentation/core-api/index.rst              |    1 +
 .../core-api/percpu-counter-tree.rst          |  109 ++
 MAINTAINERS                                   |    9 +
 include/linux/percpu_counter_tree.h           |  292 +++++
 init/main.c                                   |    3 +
 lib/Makefile                                  |    1 +
 lib/percpu_counter_tree.c                     | 1119 +++++++++++++++++
 7 files changed, 1534 insertions(+)
 create mode 100644 Documentation/core-api/percpu-counter-tree.rst
 create mode 100644 include/linux/percpu_counter_tree.h
 create mode 100644 lib/percpu_counter_tree.c

diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst
index 92f91c6a0d79..fe03cefc1734 100644
--- a/Documentation/core-api/index.rst
+++ b/Documentation/core-api/index.rst
@@ -48,6 +48,7 @@ Library functionality that is used throughout the kernel.
    generic-radix-tree
    packing
    this_cpu_ops
+   percpu-counter-tree
    timekeeping
    errseq
    wrappers/atomic_t
diff --git a/Documentation/core-api/percpu-counter-tree.rst b/Documentation/core-api/percpu-counter-tree.rst
new file mode 100644
index 000000000000..6b381deacd83
--- /dev/null
+++ b/Documentation/core-api/percpu-counter-tree.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0+ OR MIT
+
+========================================
+The Hierarchical Per-CPU Counters (HPCC)
+========================================
+
+:Author: Mathieu Desnoyers
+
+Introduction
+============
+
+Counters come in many varieties, each with their own trade offs:
+
+ * A global atomic counter provides a fast read access to the current
+   sum, at the expense of cache-line bouncing on updates. This leads to
+   poor performance of frequent updates from various cores on large SMP
+   systems.
+
+ * A per-cpu split counter provides fast updates to per-cpu counters,
+   at the expense of a slower aggregation (sum). The sum operation needs
+   to iterate over all per-cpu counters to calculate the current total.
+
+The hierarchical per-cpu counters attempt to provide the best of both
+worlds (fast updates, and fast sum) by relaxing requirements on the sum
+accuracy. It allows quickly querying an approximated sum value, along
+with the possible min/max ranges of the associated precise sum. The
+exact precise sum can still be calculated with an iteration on all
+per-cpu counters, but the availability of an approximated sum value with
+possible precise sum min/max ranges allows eliminating candidates which
+are certainly outside of a known target range without the overhead of
+precise sums.
+
+Overview
+========
+
+The hierarchical per-cpu counters are organized as a tree with the
+per-cpu counters as its first level and the tree root as its last
+level.
+
+The intermediate tree levels contain carry propagation counters. When
+reaching a threshold (batch size), the carry is propagated up the
+tree.
+
+Each counter below the root, from the per-cpu counters up, keeps the
+residual it has not propagated yet, and only propagates once this
+residual reaches its batch size in either direction. This hysteresis
+prevents updates going back and forth around a batch size boundary from
+propagating further up the tree each time.
+
+This allows reading an approximated value at the root, which has a
+bounded accuracy determined by the tree topology and the batch size:
+the precise sum is within the same distance of the approximation in
+both directions. This bound grows as O(nr_cpus * log(nr_cpus)).
+
+The per-cpu counters are bytes, which limits the batch size to 128. The
+tree positions are cache line aligned to prevent false sharing between
+CPUs updating different branches of the tree. A tree position holds the
+items of all the counter trees initialized together by
+percpu_counter_tree_init_many(), so a set of counters updated together,
+such as the RSS counters of an mm, shares cache lines.
+percpu_counter_tree_init_many() allocates the per-cpu counters, one per
+possible CPU, and the tree items, whose number depends on nr_cpu_ids, so
+struct percpu_counter_tree has a fixed size.
+
+The fan-out of each tree level is selected at boot from nr_cpu_ids, the
+highest possible CPU number plus one.
+
+The bounded accuracy is a property of the settled tree. While updates are
+in flight the transient error can exceed it, bounded by the number of
+updaters and their increment size.
+
+Likewise, the precise sum is exact when no update runs concurrently. It
+takes no lock: an update which carries from the per-cpu counter to the
+upper levels can be counted in part, the residual it left below read and
+its carry missed on the way up.
+
+Use Cases
+=========
+
+Use cases HPCC is meant to handle involve tracking resources which are
+used across many CPUs to quickly sum as feedback for decision making to
+apply throttling, quota limits, sort tasks, and perform memory or task
+migration decisions. When considering approximated sums within the
+accuracy range of the decision threshold, the user can either:
+
+ * Be conservative and fast: Consider that the sum has reached the
+   limit as soon as the given limit is within the approximation range.
+
+ * Be aggressive and fast: Consider that the sum is over the
+   limit only when the approximation range is over the given limit.
+
+ * Be precise and slow: Do a precise comparison with the limit, which
+   requires a precise sum when the limit is within the approximated
+   range.
+
+One use-case for these hierarchical counters is to implement a two-pass
+algorithm to speed up picking a maximum/minimum sum value from
+a set. A first pass compares the approximated values, and then a second
+pass only needs the precise sum for counter trees which are within the
+possible precise sum range of the counter tree chosen by the first pass.
+
+Because the comparison is on the wrapped difference, it is not transitive:
+ordering is undefined over a set of counters spanning more than LONG_MAX.
+
+Functions and structures
+========================
+
+.. kernel-doc:: include/linux/percpu_counter_tree.h
+.. kernel-doc:: lib/percpu_counter_tree.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 93abbb315065..712af0536005 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11947,6 +11947,15 @@ L:	linux-input@vger.kernel.org
 S:	Maintained
 F:	drivers/hid/hid-logitech-hidpp.c
 
+HIERARCHICAL PER-CPU COUNTERS (HPCC)
+M:	Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+L:	linux-mm@kvack.org
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+F:	Documentation/core-api/percpu-counter-tree.rst
+F:	include/linux/percpu_counter_tree.h
+F:	lib/percpu_counter_tree.c
+
 HIGH-RESOLUTION TIMERS, TIMER WHEEL, CLOCKEVENTS
 M:	Anna-Maria Behnsen <anna-maria@linutronix.de>
 M:	Frederic Weisbecker <frederic@kernel.org>
diff --git a/include/linux/percpu_counter_tree.h b/include/linux/percpu_counter_tree.h
new file mode 100644
index 000000000000..741a6877aa99
--- /dev/null
+++ b/include/linux/percpu_counter_tree.h
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR MIT */
+/* SPDX-FileCopyrightText: 2025-2026 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> */
+
+#ifndef _PERCPU_COUNTER_TREE_H
+#define _PERCPU_COUNTER_TREE_H
+
+#include <linux/bug.h>
+#include <linux/atomic.h>
+#include <linux/limits.h>
+#include <linux/percpu.h>
+
+/* Largest batch size: level 0 residuals are within (-batch, +batch) of a signed byte. */
+#define PERCPU_COUNTER_TREE_MAX_BATCH	128
+static_assert(PERCPU_COUNTER_TREE_MAX_BATCH - 1 <= S8_MAX);
+
+#ifdef CONFIG_SMP
+
+struct percpu_counter_tree {
+	/*
+	 * Per-CPU split counters (tree level 0): residuals not propagated
+	 * yet, within (-batch, +batch).
+	 */
+	s8 __percpu *level0;
+	/*
+	 * The batch size is the increment step at level 0 which triggers a
+	 * carry propagation: a power of 2 between 2 and
+	 * PERCPU_COUNTER_TREE_MAX_BATCH. It is stored next to level0, the
+	 * other field read by the update fast path, so both usually share a
+	 * cache line: this leaves a hole before items_stride.
+	 */
+	unsigned char batch_order;
+	unsigned int items_stride;	/* Distance between this counter's consecutive items. */
+	/*
+	 * Approximate sum: this counter's item of the root, in the tree
+	 * items allocated with the per-CPU counters.
+	 */
+	atomic_long_t *approx_sum;
+	long bias;	/* Bias to apply to counter precise and approximate values. */
+};
+
+int percpu_counter_tree_init_many(struct percpu_counter_tree *counters,
+				  unsigned int nr_counters, unsigned long batch_size,
+				  gfp_t gfp_flags);
+int percpu_counter_tree_init(struct percpu_counter_tree *counter,
+			     unsigned long batch_size, gfp_t gfp_flags);
+void percpu_counter_tree_destroy_many(struct percpu_counter_tree *counters,
+				      unsigned int nr_counters);
+void percpu_counter_tree_destroy(struct percpu_counter_tree *counter);
+void percpu_counter_tree_add(struct percpu_counter_tree *counter, long inc);
+long percpu_counter_tree_precise_sum(struct percpu_counter_tree *counter);
+int percpu_counter_tree_approximate_compare(struct percpu_counter_tree *a,
+					    struct percpu_counter_tree *b);
+int percpu_counter_tree_approximate_compare_value(struct percpu_counter_tree *counter, long v);
+int percpu_counter_tree_precise_compare(struct percpu_counter_tree *a,
+					struct percpu_counter_tree *b);
+int percpu_counter_tree_precise_compare_value(struct percpu_counter_tree *counter, long v);
+void percpu_counter_tree_set(struct percpu_counter_tree *counter, long v);
+int percpu_counter_tree_subsystem_init(void);
+
+/**
+ * percpu_counter_tree_approximate_sum() - Return approximate counter sum.
+ * @counter: The counter to sum.
+ *
+ * Querying the approximate sum is fast, but it is only accurate within
+ * the bound returned by percpu_counter_tree_approximate_accuracy().
+ * This is meant to be used when speed is preferred over accuracy.
+ *
+ * Return: The current approximate counter sum.
+ */
+static inline
+long percpu_counter_tree_approximate_sum(struct percpu_counter_tree *counter)
+{
+	unsigned long v = atomic_long_read(counter->approx_sum);
+
+	return (long)(v + (unsigned long)READ_ONCE(counter->bias));
+}
+
+unsigned long percpu_counter_tree_approximate_accuracy(struct percpu_counter_tree *counter);
+
+#if IS_ENABLED(CONFIG_KUNIT)
+unsigned int percpu_counter_tree_check_config(void);
+unsigned int percpu_counter_tree_check_residuals(struct percpu_counter_tree *counter);
+void percpu_counter_tree_copy_state(struct percpu_counter_tree *dst,
+				    struct percpu_counter_tree *src);
+#endif
+
+#else	/* !CONFIG_SMP */
+
+struct percpu_counter_tree {
+	atomic_long_t count;
+};
+
+static inline
+int percpu_counter_tree_init_many(struct percpu_counter_tree *counters,
+				  unsigned int nr_counters, unsigned long batch_size,
+				  gfp_t gfp_flags)
+{
+	/*
+	 * There is no tree to batch into on UP, so @batch_size has no
+	 * effect. Validate it anyway so callers get the same result as
+	 * on SMP.
+	 */
+	if (WARN_ON(batch_size <= 1 || (batch_size & (batch_size - 1)) ||
+		    batch_size > PERCPU_COUNTER_TREE_MAX_BATCH))
+		return -EINVAL;
+	for (unsigned int i = 0; i < nr_counters; i++)
+		atomic_long_set(&counters[i].count, 0);
+	return 0;
+}
+
+static inline
+int percpu_counter_tree_init(struct percpu_counter_tree *counter,
+			     unsigned long batch_size, gfp_t gfp_flags)
+{
+	return percpu_counter_tree_init_many(counter, 1, batch_size, gfp_flags);
+}
+
+static inline
+void percpu_counter_tree_destroy_many(struct percpu_counter_tree *counters,
+				      unsigned int nr_counters)
+{
+}
+
+static inline
+void percpu_counter_tree_destroy(struct percpu_counter_tree *counter)
+{
+}
+
+static inline
+long percpu_counter_tree_precise_sum(struct percpu_counter_tree *counter)
+{
+	return atomic_long_read(&counter->count);
+}
+
+static inline
+int percpu_counter_tree_precise_compare(struct percpu_counter_tree *a,
+					struct percpu_counter_tree *b)
+{
+	/* Compare on the wrapped difference, as on SMP. */
+	long delta = (long)((unsigned long)percpu_counter_tree_precise_sum(a) -
+			    (unsigned long)percpu_counter_tree_precise_sum(b));
+
+	if (delta < 0)
+		return -1;
+	if (delta > 0)
+		return 1;
+	return 0;
+}
+
+static inline
+int percpu_counter_tree_precise_compare_value(struct percpu_counter_tree *counter, long v)
+{
+	/* Compare on the wrapped difference, as on SMP. */
+	long delta = (long)((unsigned long)v -
+			    (unsigned long)percpu_counter_tree_precise_sum(counter));
+
+	if (delta < 0)
+		return -1;
+	if (delta > 0)
+		return 1;
+	return 0;
+}
+
+static inline
+int percpu_counter_tree_approximate_compare(struct percpu_counter_tree *a,
+					    struct percpu_counter_tree *b)
+{
+	return percpu_counter_tree_precise_compare(a, b);
+}
+
+static inline
+int percpu_counter_tree_approximate_compare_value(struct percpu_counter_tree *counter, long v)
+{
+	return percpu_counter_tree_precise_compare_value(counter, v);
+}
+
+static inline
+void percpu_counter_tree_set(struct percpu_counter_tree *counter, long v)
+{
+	atomic_long_set(&counter->count, v);
+}
+
+static inline
+unsigned long percpu_counter_tree_approximate_accuracy(struct percpu_counter_tree *counter)
+{
+	return 0;
+}
+
+static inline
+void percpu_counter_tree_add(struct percpu_counter_tree *counter, long inc)
+{
+	atomic_long_add(inc, &counter->count);
+}
+
+static inline
+long percpu_counter_tree_approximate_sum(struct percpu_counter_tree *counter)
+{
+	return percpu_counter_tree_precise_sum(counter);
+}
+
+static inline
+int percpu_counter_tree_subsystem_init(void)
+{
+	return 0;
+}
+
+#endif	/* CONFIG_SMP */
+
+/**
+ * percpu_counter_tree_approximate_sum_positive() - Return a positive approximate counter sum.
+ * @counter: The counter to sum.
+ *
+ * Return an approximate counter sum which is guaranteed to be greater
+ * or equal to 0.
+ *
+ * Return: The current positive approximate counter sum.
+ */
+static inline
+long percpu_counter_tree_approximate_sum_positive(struct percpu_counter_tree *counter)
+{
+	long v = percpu_counter_tree_approximate_sum(counter);
+
+	return v > 0 ? v : 0;
+}
+
+/**
+ * percpu_counter_tree_precise_sum_positive() - Return a positive precise counter sum.
+ * @counter: The counter to sum.
+ *
+ * Return a precise counter sum which is guaranteed to be greater
+ * or equal to 0.
+ *
+ * Return: The current positive precise counter sum.
+ */
+static inline
+long percpu_counter_tree_precise_sum_positive(struct percpu_counter_tree *counter)
+{
+	long v = percpu_counter_tree_precise_sum(counter);
+
+	return v > 0 ? v : 0;
+}
+
+/**
+ * percpu_counter_tree_approximate_min_max_range() - Return the min and max precise values.
+ * @approx_sum: Approximated sum.
+ * @accuracy: Tree accuracy, from percpu_counter_tree_approximate_accuracy().
+ * @precise_min: Minimum possible value for precise sum (output).
+ * @precise_max: Maximum possible value for precise sum (output).
+ *
+ * Calculate the minimum and maximum precise values for a given approximation
+ * and accuracy. The accuracy is a bound on a settled tree: one with no update
+ * in progress.
+ *
+ * The approximation is within the accuracy of the precise sum, in both
+ * directions:
+ *
+ *   precise_sum - accuracy <= approx_sum <= precise_sum + accuracy
+ *
+ * Therefore, the precise sum is within the accuracy of the approximation:
+ *
+ *   approx_sum - accuracy <= precise_sum <= approx_sum + accuracy
+ */
+static inline
+void percpu_counter_tree_approximate_min_max_range(long approx_sum, unsigned long accuracy,
+						   long *precise_min, long *precise_max)
+{
+	*precise_min = approx_sum - accuracy;
+	*precise_max = approx_sum + accuracy;
+}
+
+/**
+ * percpu_counter_tree_approximate_min_max() - Return the approximation, min and max precise values.
+ * @counter: The counter to sum.
+ * @approx_sum: Approximate sum (output).
+ * @precise_min: Minimum possible value for precise sum (output).
+ * @precise_max: Maximum possible value for precise sum (output).
+ *
+ * Return the approximate sum, minimum and maximum precise values for
+ * a counter.
+ */
+static inline
+void percpu_counter_tree_approximate_min_max(struct percpu_counter_tree *counter,
+					     long *approx_sum, long *precise_min, long *precise_max)
+{
+	unsigned long accuracy = percpu_counter_tree_approximate_accuracy(counter);
+	long v = percpu_counter_tree_approximate_sum(counter);
+
+	percpu_counter_tree_approximate_min_max_range(v, accuracy, precise_min, precise_max);
+	*approx_sum = v;
+}
+
+#endif  /* _PERCPU_COUNTER_TREE_H */
diff --git a/init/main.c b/init/main.c
index 7b0c8c34e6a9..d9614cc5bd63 100644
--- a/init/main.c
+++ b/init/main.c
@@ -108,6 +108,7 @@
 #include <linux/time_namespace.h>
 #include <linux/unaligned.h>
 #include <linux/vdso_datastore.h>
+#include <linux/percpu_counter_tree.h>
 #include <net/net_namespace.h>
 
 #include <asm/io.h>
@@ -1104,6 +1105,8 @@ void start_kernel(void)
 	sort_main_extable();
 	trap_init();
 	mm_core_init();
+	/* After parsing its boot parameters and setting up slab, before the first counter. */
+	percpu_counter_tree_subsystem_init();
 	maple_tree_init();
 	poking_init();
 	ftrace_init();
diff --git a/lib/Makefile b/lib/Makefile
index a2afe954805c..07410b1d20a3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -182,6 +182,7 @@ obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o
 obj-$(CONFIG_TEXTSEARCH_BM) += ts_bm.o
 obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o
 obj-$(CONFIG_SMP) += percpu_counter.o
+obj-$(CONFIG_SMP) += percpu_counter_tree.o
 obj-$(CONFIG_AUDIT_GENERIC) += audit.o
 obj-$(CONFIG_AUDIT_COMPAT_GENERIC) += compat_audit.o
 
diff --git a/lib/percpu_counter_tree.c b/lib/percpu_counter_tree.c
new file mode 100644
index 000000000000..67cdb22c3786
--- /dev/null
+++ b/lib/percpu_counter_tree.c
@@ -0,0 +1,1119 @@
+// SPDX-License-Identifier: GPL-2.0+ OR MIT
+// SPDX-FileCopyrightText: 2025-2026 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+
+/*
+ * Split Counters With Tree Approximation Propagation
+ *
+ * * Overview
+ *
+ * Each counter is a tree of counters. A CPU updates its own level 0
+ * counter, a signed byte. Each counter keeps the residual it has not
+ * propagated yet, and propagates a carry to its parent on the next level
+ * once that residual reaches the batch size of its level, up to the root.
+ * Most updates therefore stay on the updating CPU. The root holds the
+ * approximate sum, read in O(1), and the precise sum adds the residuals of
+ * all levels to it.
+ *
+ * * Tree shape
+ *
+ * Illustration with 8 CPUs and a fan-out of 2 at each level, which makes
+ * 3 levels of counters below the root. This is not an actual
+ * configuration: at 8 CPUs, the default tree is flat.
+ *
+ * Level 3:  0  (root)
+ *           |   \
+ *           |         \
+ *           |               \
+ * Level 2:  0                   1
+ *           | \                 | \
+ *           |    \              |    \
+ *           |       \           |       \
+ * Level 1:  0         2         1         3
+ *           | \       | \       | \       | \
+ *           |  \      |  \      |  \      |  \
+ *           |   \     |   \     |   \     |   \
+ * Level 0:  0    4    2    6    1    5    3    7
+ *
+ * CPU c propagates into item (c & (L - 1)) of a level holding L items,
+ * so the CPUs sharing an item are not adjacent: CPUs 0 and 4 share
+ * level 1 item 0, and CPUs 0, 2, 4, 6 share level 2 item 0. This mapping
+ * uses every item of each level and gives each item the same number of
+ * CPUs, give or take one, even when nr_cpu_ids is not a power of 2. The
+ * diagram lists the CPUs in bit-reversed order to avoid crossing lines.
+ *
+ * The actual fan-out of each level depends on the topology (see
+ * per_nr_cpu_order_config): up to 32 CPUs, the tree is flat, and the
+ * level 0 counters propagate directly to the root.
+ *
+ * * Batch sizes and residuals
+ *
+ * The batch size of a level, BATCH(level N), is the batch size of the
+ * level below multiplied by the fan-out of that level. Example for
+ * BATCH(level 0) = 32, with the tree of the illustration above:
+ *
+ * BATCH(level 0) =  32
+ * BATCH(level 1) =  64
+ * BATCH(level 2) = 128
+ *
+ * Once the residual of a counter reaches +/- BATCH(level N), it propagates
+ * its multiples of BATCH(level N), truncated toward zero, and subtracts
+ * them. This hysteresis prevents updates going back and forth around a
+ * BATCH(level N) boundary from propagating up the tree each time.
+ *
+ * The residual of a settled counter therefore stays within
+ * (-BATCH(level N), +BATCH(level N)), and moves in steps of the counter's
+ * input: level 0 receives the updates, in steps of 1, while a counter
+ * above level 0 only receives the carries of the level below, in steps of
+ * BATCH(level N - 1). The largest residual of a counter is the largest
+ * multiple of its input step below its batch size: BATCH(level 0) - 1 at
+ * level 0, and BATCH(level N) - BATCH(level N - 1) above:
+ *
+ *            input   residual        worst case        worst case
+ *            step    per counter     below precise     above precise
+ * Level 0:   1       [-31 .. 31]     248  (8 * 31)     248  (8 * 31)
+ * Level 1:   32      {-32, 0, 32}    128  (4 * 32)     128  (4 * 32)
+ * Level 2:   64      {-64, 0, 64}    128  (2 * 64)     128  (2 * 64)
+ * Total:                             504               504
+ *
+ * * Approximation accuracy
+ *
+ * The approximate sum is the precise sum minus the residuals, so a
+ * settled counter (no update in progress) is within:
+ *
+ *   precise - 504 <= approx <= precise + 504
+ *
+ * The accuracy returned by percpu_counter_tree_approximate_accuracy() is
+ * exact. It adds the largest residuals of all the counters:
+ * BATCH(level 0) - 1 for each of the nr_cpu_ids level 0 counters, and
+ * BATCH(level N) - BATCH(level N - 1) for each counter of level N above.
+ * Counting each level 0 counter as one BATCH(level 0), minus its input
+ * step of 1:
+ *
+ *   accuracy_multiplier = nr_cpu_ids + sum over levels N >= 1 of
+ *       (number of counters at level N) *
+ *       (BATCH(level N) - BATCH(level N - 1)) / BATCH(level 0)
+ *
+ *   accuracy = BATCH(level 0) * accuracy_multiplier - nr_cpu_ids
+ *
+ * In the example above, accuracy_multiplier = 8 + 4 * 1 + 2 * 2 = 16, and:
+ *
+ *   accuracy = 32 * 16 - 8 = 504
+ *
+ * Every counter above level 0 is reachable from a CPU below nr_cpu_ids,
+ * even when nr_cpu_ids is not a power of 2, so both bounds can be reached
+ * when the possible CPUs are contiguous. When they are sparse, the bound
+ * also counts the level 0 residuals of the missing CPUs: it holds, but
+ * cannot be reached.
+ *
+ * * Carry propagation
+ *
+ * The items at each tree position of the counters initialized together
+ * share cache lines, which are not shared with other tree positions to
+ * prevent false sharing between CPUs.
+ *
+ * Example with expanded values, for the tree and batch sizes above:
+ *
+ * counter_add(counter, inc):
+ *
+ *         if (!inc)
+ *                 return;
+ *
+ *         // Level 0: per-CPU residual within (-32, +32).
+ *         old = percpu_read(counter @ Level 0);
+ *         do {
+ *                 v = old + inc;
+ *                 out = v < 0 ? -(-v & ~0b00011111) : (v & ~0b00011111);
+ *         } while (!percpu_try_cmpxchg(counter @ Level 0, &old, v - out));
+ *         inc = out;
+ *         if (!inc)
+ *                 return;
+ *
+ *         // Levels 1 and 2: add the carry, then extract the multiples
+ *         // of the level batch held by the counter, truncated toward
+ *         // zero. A failed cmpxchg updates res to the current residual.
+ *         res = atomic_long_add_return(counter @ Level 1, inc);
+ *         do {
+ *                 inc = res < 0 ? -(-res & ~0b00111111) : (res & ~0b00111111);
+ *                 if (!inc)
+ *                         return;
+ *         } while (!atomic_long_try_cmpxchg(counter @ Level 1, &res, res - inc));
+ *
+ *         res = atomic_long_add_return(counter @ Level 2, inc);
+ *         do {
+ *                 inc = res < 0 ? -(-res & ~0b01111111) : (res & ~0b01111111);
+ *                 if (!inc)
+ *                         return;
+ *         } while (!atomic_long_try_cmpxchg(counter @ Level 2, &res, res - inc));
+ *
+ *         atomic_long_add(counter @ Level 3, inc);
+ */
+
+#define pr_fmt(fmt) "percpu_counter_tree: " fmt
+
+#include <linux/percpu_counter_tree.h>
+#include <linux/align.h>
+#include <linux/cache.h>
+#include <linux/cpumask.h>
+#include <linux/atomic.h>
+#include <linux/export.h>
+#include <linux/percpu.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/math.h>
+#include <linux/log2.h>
+#include <linux/args.h>
+#include <kunit/visibility.h>
+
+#define MAX_NR_LEVELS 7
+
+/*
+ * The counter configuration is selected at boot time based on the
+ * hardware topology.
+ */
+struct counter_config {
+	/*
+	 * nr_levels is the number of hierarchical counter tree levels. It
+	 * excludes the final level (approximate sum).
+	 */
+	unsigned char nr_levels;
+	/* n-arity of each level from 0 to (nr_levels - 1). */
+	unsigned char n_arity_order[MAX_NR_LEVELS];
+};
+
+/*
+ * The fan-out of each level of the tree is chosen with a heuristic trying to
+ * minimize both the tree depth and the fan-out.
+ */
+#define PCT_COUNTER_CONFIG(order, ...)						\
+	[order] = {								\
+		.nr_levels = COUNT_ARGS(__VA_ARGS__),				\
+		.n_arity_order = { __VA_ARGS__ },				\
+	}
+
+/*
+ * Up to 32 CPUs, the tree is flat: the level 0 counters propagate directly
+ * to the root, like struct percpu_counter. From 33 to 64 CPUs, the level 0
+ * counters propagate to 4 intermediate counters, each shared by up to 16
+ * CPUs, so the carries of all CPUs do not contend on the root. Beyond, each
+ * level has a fan-out of 8. When the order is not a multiple of 3, a
+ * remaining order of 1 gives the root a fan-out of 16, and a remaining
+ * order of 2 adds a level with a fan-out of 4 below the root: a root
+ * fan-out of 16 updates about as fast as a fan-out of 2 below the root,
+ * with one level less, which shrinks the accuracy bound and the tree
+ * items, while a root fan-out of 32 is slower than a fan-out of 4 below
+ * it. This keeps the carry rate reaching each tree item low with the RSS
+ * batch size of 32, and the approximation error grows as
+ * O(nr_cpus * log(nr_cpus)), compared to O(nr_cpus^2) for struct
+ * percpu_counter whose batch size grows with the number of CPUs.
+ *
+ * The tree is shaped for nr_cpu_ids rounded up to a power of 2, and each
+ * tree position takes a cache line. A set of 4 counters initialized together,
+ * such as the RSS counters of an mm, uses less memory than 4 struct
+ * percpu_counter from a few CPUs up to 64, and beyond when nr_cpu_ids is a
+ * power of 2 (at most 0.89 times as much). Between powers of 2, fewer CPUs
+ * share the cost of the tree items: up to 1.1 times at 3 * 2^n CPUs, and
+ * up to 1.48 times just above a power of 2. A lone counter uses one item
+ * of each cache line: 3.0 to 3.5 times the memory of a struct
+ * percpu_counter when nr_cpu_ids is a power of 2 beyond 64, and up to 5.7
+ * times just above.
+ */
+static const struct counter_config per_nr_cpu_order_config[] = {
+	PCT_COUNTER_CONFIG(0),
+	PCT_COUNTER_CONFIG(1, 1),
+	PCT_COUNTER_CONFIG(2, 2),
+	PCT_COUNTER_CONFIG(3, 3),
+	PCT_COUNTER_CONFIG(4, 4),
+	PCT_COUNTER_CONFIG(5, 5),
+	PCT_COUNTER_CONFIG(6, 4, 2),
+	PCT_COUNTER_CONFIG(7, 3, 4),
+	PCT_COUNTER_CONFIG(8, 3, 2, 3),
+	PCT_COUNTER_CONFIG(9, 3, 3, 3),
+	PCT_COUNTER_CONFIG(10, 3, 3, 4),
+	PCT_COUNTER_CONFIG(11, 3, 3, 2, 3),
+	PCT_COUNTER_CONFIG(12, 3, 3, 3, 3),
+	PCT_COUNTER_CONFIG(13, 3, 3, 3, 4),
+	PCT_COUNTER_CONFIG(14, 3, 3, 3, 2, 3),
+	PCT_COUNTER_CONFIG(15, 3, 3, 3, 3, 3),
+	PCT_COUNTER_CONFIG(16, 3, 3, 3, 3, 4),
+	PCT_COUNTER_CONFIG(17, 3, 3, 3, 3, 2, 3),
+	PCT_COUNTER_CONFIG(18, 3, 3, 3, 3, 3, 3),
+	PCT_COUNTER_CONFIG(19, 3, 3, 3, 3, 3, 4),
+	PCT_COUNTER_CONFIG(20, 3, 3, 3, 3, 3, 2, 3),
+};
+
+#undef PCT_COUNTER_CONFIG
+
+/* The configuration table covers every possible number of CPUs. */
+#define MAX_NR_CPUS_ORDER	20
+#if NR_CPUS > (1UL << MAX_NR_CPUS_ORDER)
+#error "NR_CPUS exceeds the hierarchical counter configuration table"
+#endif
+static_assert(ARRAY_SIZE(per_nr_cpu_order_config) == MAX_NR_CPUS_ORDER + 1);
+
+/* Tree topology, selected at boot and shared by all the counters. */
+static struct {
+	/* Hierarchical counter configuration for the hardware topology. */
+	const struct counter_config *config;
+	/* Order of nr_cpu_ids. */
+	unsigned int nr_cpus_order;
+	/*
+	 * Tree items per counter, including the root. UINT_MAX catches early
+	 * boot misuses.
+	 */
+	unsigned int nr_items;
+	/*
+	 * Largest residuals of all the counters, in units of the level 0
+	 * batch size: see percpu_counter_tree_accuracy().
+	 */
+	unsigned long accuracy_multiplier;
+	/* Tree items when those of a tree position fit in one cache line. */
+	struct kmem_cache *items_cachep;
+} tree_topology __ro_after_init = {
+	.nr_items = UINT_MAX,
+};
+
+/*
+ * Size of the items of one tree position for the counters initialized
+ * together by percpu_counter_tree_init_many(), packed in cache lines which
+ * are not shared with other tree positions. Each item is the residual of
+ * the carries received and not propagated yet, within (-batch, +batch) of
+ * its tree level when settled. The items of a tree position are updated
+ * by the same CPUs, so packing them does not add contention between
+ * unrelated CPUs.
+ */
+static
+size_t percpu_counter_tree_position_size(unsigned int nr_counters)
+{
+	return ALIGN(nr_counters * sizeof(atomic_long_t), SMP_CACHE_BYTES);
+}
+
+/* Distance between the consecutive items of each of @nr_counters counters. */
+static inline
+unsigned int percpu_counter_tree_items_stride(unsigned int nr_counters)
+{
+	return percpu_counter_tree_position_size(nr_counters) / sizeof(atomic_long_t);
+}
+
+/* Allocate the zeroed tree items of @nr_counters counters, the root last. */
+static
+atomic_long_t *percpu_counter_tree_alloc_items(unsigned int nr_counters, gfp_t gfp_flags)
+{
+	size_t position_size = percpu_counter_tree_position_size(nr_counters);
+
+	if (position_size == SMP_CACHE_BYTES)
+		return kmem_cache_zalloc(tree_topology.items_cachep, gfp_flags);
+	/* kmalloc() aligns power of 2 sizes on their size. */
+	return kzalloc(roundup_pow_of_two(tree_topology.nr_items * position_size),
+		       gfp_flags);
+}
+
+static
+void percpu_counter_tree_free_items(atomic_long_t *items, unsigned int nr_counters)
+{
+	if (percpu_counter_tree_position_size(nr_counters) == SMP_CACHE_BYTES)
+		kmem_cache_free(tree_topology.items_cachep, items);
+	else
+		kfree(items);
+}
+
+static
+int __percpu_counter_tree_init(struct percpu_counter_tree *counter,
+			       unsigned long batch_size,
+			       s8 __percpu *level0,
+			       atomic_long_t *items, unsigned int items_stride)
+{
+	/* Batch size must be greater than 1, a power of 2, and fit in a byte. */
+	if (WARN_ON(batch_size <= 1 || (batch_size & (batch_size - 1)) ||
+		    batch_size > PERCPU_COUNTER_TREE_MAX_BATCH))
+		return -EINVAL;
+	counter->batch_order = ilog2(batch_size);
+	counter->bias = 0;
+	counter->level0 = level0;
+	counter->items_stride = items_stride;
+	/* The root is the last item. */
+	counter->approx_sum = &items[(tree_topology.nr_items - 1) * items_stride];
+	return 0;
+}
+
+static inline
+unsigned long percpu_counter_tree_batch_size(struct percpu_counter_tree *counter)
+{
+	return 1UL << counter->batch_order;
+}
+
+/* This counter's first item. */
+static inline
+atomic_long_t *percpu_counter_tree_items(struct percpu_counter_tree *counter)
+{
+	return counter->approx_sum - (tree_topology.nr_items - 1) * counter->items_stride;
+}
+
+/*
+ * The residuals of all levels are within (-batch, +batch) of their level,
+ * so the approximation is within the same distance of the precise sum in
+ * both directions: the largest residuals of all the counters, which
+ * accuracy_multiplier counts in units of the level 0 batch size, minus the
+ * input step of 1 of each level 0 counter. See "Approximation accuracy" at
+ * the top of this file.
+ */
+static inline
+unsigned long percpu_counter_tree_accuracy(struct percpu_counter_tree *counter)
+{
+	unsigned long batch_size = percpu_counter_tree_batch_size(counter);
+
+	return batch_size * tree_topology.accuracy_multiplier - nr_cpu_ids;
+}
+
+/**
+ * percpu_counter_tree_init_many() - Initialize many per-CPU counter trees.
+ * @counters: An array of @nr_counters counters to initialize.
+ *	      Their memory is provided by the caller.
+ * @nr_counters: The number of counter trees to initialize
+ * @batch_size: The batch size is the increment step at level 0 which triggers a
+ *		carry propagation.
+ *		The batch size is required to be greater than 1, a power of 2, and at
+ *		most PERCPU_COUNTER_TREE_MAX_BATCH.
+ * @gfp_flags: gfp flags to pass to the per-CPU and tree item allocators.
+ *
+ * Initialize many per-CPU counter trees, with a single allocation of
+ * per-CPU counters and a single allocation of tree items for the
+ * @nr_counters counters. The items at each tree position of the
+ * @nr_counters counters share cache lines. When they fit in one cache
+ * line (up to 8 counters on 64-bit with 64-byte cache lines), the tree
+ * items come from a slab cache; beyond, their allocation is
+ * rounded up to a power of 2, which can double it.
+ *
+ * Return:
+ * * %0: Success
+ * * %-EINVAL:		- Invalid @batch_size argument
+ * * %-ENOMEM:		- Out of memory
+ */
+int percpu_counter_tree_init_many(struct percpu_counter_tree *counters,
+				  unsigned int nr_counters, unsigned long batch_size,
+				  gfp_t gfp_flags)
+{
+	unsigned int items_stride = percpu_counter_tree_items_stride(nr_counters);
+	s8 __percpu *level0;
+	atomic_long_t *items;
+	unsigned int i;
+	int ret;
+
+	/* The tree shape is selected by percpu_counter_tree_subsystem_init() at boot. */
+	if (WARN_ON_ONCE(tree_topology.nr_items == UINT_MAX))
+		return -EINVAL;
+	level0 = __alloc_percpu_gfp(nr_counters * sizeof(*level0),
+				    __alignof__(*counters->level0), gfp_flags);
+	if (!level0)
+		return -ENOMEM;
+	items = percpu_counter_tree_alloc_items(nr_counters, gfp_flags);
+	if (!items) {
+		ret = -ENOMEM;
+		goto free_level0;
+	}
+	for (i = 0; i < nr_counters; i++) {
+		ret = __percpu_counter_tree_init(&counters[i], batch_size, level0 + i,
+						 items + i, items_stride);
+		if (ret)
+			goto free_items;
+	}
+	return 0;
+
+free_items:
+	percpu_counter_tree_free_items(items, nr_counters);
+free_level0:
+	free_percpu(level0);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_init_many);
+
+/**
+ * percpu_counter_tree_init() - Initialize one per-CPU counter tree.
+ * @counter: Counter to initialize.
+ *	     Its memory is provided by the caller.
+ * @batch_size: The batch size is the increment step at level 0 which triggers a
+ *		carry propagation.
+ *		The batch size is required to be greater than 1, a power of 2, and at
+ *		most PERCPU_COUNTER_TREE_MAX_BATCH.
+ * @gfp_flags: gfp flags to pass to the per-CPU and tree item allocators.
+ *
+ * Initialize one per-CPU counter tree.
+ *
+ * Return:
+ * * %0: Success
+ * * %-EINVAL:		- Invalid @batch_size argument
+ * * %-ENOMEM:		- Out of memory
+ */
+int percpu_counter_tree_init(struct percpu_counter_tree *counter,
+			     unsigned long batch_size, gfp_t gfp_flags)
+{
+	return percpu_counter_tree_init_many(counter, 1, batch_size, gfp_flags);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_init);
+
+/**
+ * percpu_counter_tree_destroy_many() - Destroy many per-CPU counter trees.
+ * @counters: Array of counters trees to destroy.
+ * @nr_counters: The number of counter trees to destroy.
+ *
+ * Release internal resources allocated for @nr_counters per-CPU counter trees.
+ * @counters and @nr_counters must be those passed to
+ * percpu_counter_tree_init_many().
+ */
+void percpu_counter_tree_destroy_many(struct percpu_counter_tree *counters,
+				      unsigned int nr_counters)
+{
+	free_percpu(counters->level0);
+	/*
+	 * @nr_counters selects the allocator of the tree items: on a mismatch
+	 * with the initialization, leak them rather than corrupt the allocator.
+	 */
+	if (WARN_ON_ONCE(counters->items_stride !=
+			 percpu_counter_tree_items_stride(nr_counters)))
+		return;
+	/* The first item of the first counter is the base of the tree items. */
+	percpu_counter_tree_free_items(percpu_counter_tree_items(counters), nr_counters);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_destroy_many);
+
+/**
+ * percpu_counter_tree_destroy() - Destroy one per-CPU counter tree.
+ * @counter: Counter to destroy.
+ *
+ * Release internal resources allocated for one per-CPU counter tree.
+ */
+void percpu_counter_tree_destroy(struct percpu_counter_tree *counter)
+{
+	return percpu_counter_tree_destroy_many(counter, 1);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_destroy);
+
+/*
+ * Amount propagated by a counter holding @v: the multiples of @bit_mask,
+ * truncated toward zero.
+ */
+static inline
+long percpu_counter_tree_item_carry(long v, unsigned long bit_mask)
+{
+	if (v < 0)
+		return -(long)(-(unsigned long)v & ~(bit_mask - 1));
+	return (long)((unsigned long)v & ~(bit_mask - 1));
+}
+
+/*
+ * It does not matter through which path the carry propagates up the
+ * tree, therefore there is no need to disable preemption because the
+ * cpu number is only used to favor cache locality.
+ */
+static
+void percpu_counter_tree_add_slowpath(struct percpu_counter_tree *counter, long inc)
+{
+	unsigned int level_items, nr_levels = tree_topology.config->nr_levels,
+		     level, n_arity_order;
+	unsigned long bit_mask;
+	unsigned int stride = counter->items_stride;
+	atomic_long_t *item = percpu_counter_tree_items(counter);
+	unsigned int cpu = raw_smp_processor_id();
+
+	n_arity_order = tree_topology.config->n_arity_order[0];
+	bit_mask = percpu_counter_tree_batch_size(counter) << n_arity_order;
+	level_items = 1U << (tree_topology.nr_cpus_order - n_arity_order);
+
+	for (level = 1; level < nr_levels; level++) {
+		/*
+		 * A multiple of bit_mask would leave the residual of this
+		 * level unchanged: propagate it to the next level directly.
+		 * This trades the hysteresis of this level for not touching
+		 * its cache line.
+		 */
+		if (abs(inc) & (bit_mask - 1)) {
+			atomic_long_t *count = &item[(cpu & (level_items - 1)) * stride];
+			long res;
+
+			/*
+			 * Add the carry to the residual of this level, then
+			 * extract the multiples of bit_mask of the residual,
+			 * truncated toward zero, and propagate them to the
+			 * next level, which leaves the residual within
+			 * (-bit_mask, +bit_mask).
+			 *
+			 * The extraction is a cmpxchg on the value returned by
+			 * the addition, whose cache line is already exclusive,
+			 * so it usually succeeds at once. When a concurrent
+			 * updater changed the residual in between, the cmpxchg
+			 * fails and the carry is recomputed from the current
+			 * value, which is 0 if the other updater extracted it.
+			 * Each carry is thus extracted once, from an exact
+			 * value: the residual only exceeds its range by the
+			 * additions in flight, and is within range once all
+			 * the updaters are done. An addition which does not
+			 * carry stays a single atomic operation, which cannot
+			 * fail.
+			 *
+			 * Relaxed ordering is sufficient for the residual
+			 * itself: the reasoning above only involves this
+			 * atomic, whose modifications all CPUs see in the same
+			 * order, each read-modify-write reading the value
+			 * written by the one just before it, whatever its
+			 * ordering. Ordering only matters relative to the other
+			 * levels, for the precise sum: the release ordering of
+			 * the addition orders the departure of the carry from
+			 * the level below before its arrival here.
+			 */
+			res = atomic_long_add_return_release(inc, count);
+			do {
+				inc = percpu_counter_tree_item_carry(res, bit_mask);
+				if (likely(!inc))
+					return;
+			} while (!atomic_long_try_cmpxchg_relaxed(count, &res, res - inc));
+		}
+		item += level_items * stride;
+		n_arity_order = tree_topology.config->n_arity_order[level];
+		level_items >>= n_arity_order;
+		bit_mask <<= n_arity_order;
+	}
+	atomic_long_add_return_release(inc, counter->approx_sum);
+}
+
+/**
+ * percpu_counter_tree_add() - Add to a per-CPU counter tree.
+ * @counter: Counter added to.
+ * @inc: Increment value (either positive or negative).
+ *
+ * Add @inc to a per-CPU counter tree. This is a fast-path which will
+ * typically increment per-CPU counters as long as there is no carry
+ * greater or equal to the counter tree batch size.
+ */
+void percpu_counter_tree_add(struct percpu_counter_tree *counter, long inc)
+{
+	unsigned long bit_mask = percpu_counter_tree_batch_size(counter);
+	s8 old, new;
+	long v, carry;
+
+	/*
+	 * Keep the residual within (-batch, +batch) and propagate its
+	 * multiples of the batch size, truncated toward zero. This
+	 * hysteresis prevents updates going back and forth around a batch
+	 * boundary from propagating each time.
+	 *
+	 * Preemption stays enabled: the update may go to the level 0
+	 * counter of any CPU, and the cmpxchg validates the value read.
+	 * After a migration or an interrupt between the read and the
+	 * cmpxchg, the cmpxchg either fails and retries with the current
+	 * residual of the current CPU, or succeeds on a residual equal to
+	 * the value read, which the new value correctly replaces. Each
+	 * this_cpu operation is itself safe against preemption and
+	 * interrupts, and the carry may propagate from any CPU, see
+	 * percpu_counter_tree_add_slowpath().
+	 */
+	old = this_cpu_read(*counter->level0);
+	do {
+		v = old + inc;
+		carry = percpu_counter_tree_item_carry(v, bit_mask);
+		new = (s8)(v - carry);
+	} while (!this_cpu_try_cmpxchg(*counter->level0, &old, new));
+	if (likely(!carry))
+		return;
+	percpu_counter_tree_add_slowpath(counter, carry);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_add);
+
+/*
+ * The precise sum is the sum of the residuals of all tree levels. A carry
+ * leaves a level before reaching a higher level, and the release ordering
+ * of its arrival orders its departure before it. Reading the levels from
+ * the root down, separated by read barriers, therefore never counts a carry
+ * twice. A carry in flight during the sum may be missed: the result is
+ * precise for a counter without concurrent updates.
+ */
+static
+long percpu_counter_tree_precise_sum_unbiased(struct percpu_counter_tree *counter)
+{
+	unsigned int nr_levels = tree_topology.config->nr_levels, i,
+		     level_items[MAX_NR_LEVELS], stride = counter->items_stride;
+	atomic_long_t *item;
+	unsigned long sum;
+	int level, cpu;
+
+	/* Number of items of each level above level 0. */
+	level_items[0] = 1U << tree_topology.nr_cpus_order;
+	for (level = 1; level < (int)nr_levels; level++)
+		level_items[level] = level_items[level - 1] >>
+				     tree_topology.config->n_arity_order[level - 1];
+	sum = atomic_long_read(counter->approx_sum);
+	smp_rmb();	/* Read each level before the level below it. */
+	/* The items of each level precede those of the level above it. */
+	item = counter->approx_sum;
+	for (level = (int)nr_levels - 1; level >= 1; level--) {
+		item -= level_items[level] * stride;
+		for (i = 0; i < level_items[level]; i++)
+			sum += atomic_long_read(&item[i * stride]);
+		smp_rmb();	/* Read this level before the level below it. */
+	}
+	for_each_possible_cpu(cpu)
+		sum += (long)READ_ONCE(*per_cpu_ptr(counter->level0, cpu));
+	return (long)sum;
+}
+
+/**
+ * percpu_counter_tree_precise_sum() - Return precise counter sum.
+ * @counter: The counter to sum.
+ *
+ * Querying the precise sum is relatively expensive because it needs to
+ * iterate over all CPUs.
+ * This is meant to be used when accuracy is preferred over speed.
+ *
+ * The sum is exact when no update runs concurrently. It takes no lock, so
+ * a concurrent update which carries from level 0 to the levels above can
+ * be counted in part: the sum may read the residual it left below and miss
+ * its carry on the way up, an error in either direction of at most the
+ * carries in flight. A carry is never counted twice.
+ *
+ * Return: The current precise counter sum.
+ */
+long percpu_counter_tree_precise_sum(struct percpu_counter_tree *counter)
+{
+	return percpu_counter_tree_precise_sum_unbiased(counter) + READ_ONCE(counter->bias);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_precise_sum);
+
+/*
+ * The comparison between counters is on the wrapped difference. It is valid
+ * only when the two counters differ by less than LONG_MAX, and results are
+ * undefined beyond that.
+ */
+static inline
+long wrapped_delta(long a, long b)
+{
+	return (long)((unsigned long)a - (unsigned long)b);
+}
+
+/*
+ * Each counter's approximation lies within its accuracy of its precise sum,
+ * in both directions. The comparison only observes the approximations, so
+ * it must reason about the possible precise values around each observed
+ * approximation, which lie within [approx - accuracy, approx + accuracy]:
+ *
+ *                 precise_a range
+ *            <-----------+----------->
+ *         accuracy_a     |     accuracy_a
+ *                    approx_a
+ *                        |                         precise_b range
+ *                        |                    <-----------+----------->
+ *                        |                 accuracy_b     |     accuracy_b
+ *                        |                             approx_b
+ *                        |                                |
+ *                        |<------------ gap ------------->|
+ *  ----------------------+--------------------------------+-------------> values
+ *
+ * The two precise ranges are disjoint, and the comparison can return a
+ * definitive answer, only once the gap between approximations exceeds the
+ * sum of the two accuracies:
+ *
+ *   gap > accuracy_a + accuracy_b
+ *
+ * When comparing against a plain value, the value has zero width, so the
+ * margin reduces to the accuracy of the counter: the value is above the
+ * counter once it exceeds approx + accuracy, and below it once it is less
+ * than approx - accuracy.
+ */
+static
+int compare_delta(long delta, unsigned long accuracy)
+{
+	if (delta >= 0) {
+		if (delta <= accuracy)
+			return 0;
+		else
+			return 1;
+	} else {
+		if (-delta <= accuracy)
+			return 0;
+		else
+			return -1;
+	}
+}
+
+/**
+ * percpu_counter_tree_approximate_compare - Approximated comparison of two counter trees.
+ * @a: First counter to compare.
+ * @b: Second counter to compare.
+ *
+ * Evaluate an approximate comparison of two counter trees. This approximation
+ * comparison is fast, and provides an accurate answer if the counters are
+ * found to be either less than or greater than the other. However, if the
+ * approximated comparison returns 0, the counters respective sums are found to
+ * be within the two counters accuracy range. The two counters are read
+ * independently; the result is not an atomic snapshot of both.
+ * The comparison between counters is on the wrapped difference. It is valid
+ * only when the two counters differ by less than LONG_MAX, and results are
+ * undefined beyond that.
+ *
+ * Return:
+ * * %0		- Counters @a and @b do not differ by more than the sum of their respective
+ *                accuracy ranges.
+ * * %-1	- Counter @a less than counter @b.
+ * * %1		- Counter @a is greater than counter @b.
+ */
+int percpu_counter_tree_approximate_compare(struct percpu_counter_tree *a,
+					    struct percpu_counter_tree *b)
+{
+	unsigned long accuracy = percpu_counter_tree_accuracy(a) + percpu_counter_tree_accuracy(b);
+
+	/* See the range geometry above compare_delta(). */
+	return compare_delta(wrapped_delta(percpu_counter_tree_approximate_sum(a),
+					   percpu_counter_tree_approximate_sum(b)),
+			     accuracy);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_approximate_compare);
+
+/**
+ * percpu_counter_tree_approximate_compare_value - Approximated comparison of a counter tree
+ *						   against a given value.
+ * @counter: Counter to compare.
+ * @v: Value to compare.
+ *
+ * Evaluate an approximate comparison of a counter tree against a given value.
+ * This approximation comparison is fast, and provides an accurate
+ * answer if the counter is found to be either less than or greater
+ * than the value. However, if the approximated comparison returns
+ * 0, the value is within the counter accuracy range.
+ * The comparison between counters is on the wrapped difference. It is valid
+ * only when @v and the counter differ by less than LONG_MAX, and results are
+ * undefined beyond that.
+ *
+ * Return:
+ * * %0		- The value @v is within the accuracy range of the counter.
+ * * %-1	- The value @v is less than the counter.
+ * * %1		- The value @v is greater than the counter.
+ */
+int percpu_counter_tree_approximate_compare_value(struct percpu_counter_tree *counter, long v)
+{
+	unsigned long accuracy = percpu_counter_tree_accuracy(counter);
+
+	/* See the range geometry above compare_delta(). */
+	return compare_delta(wrapped_delta(v, percpu_counter_tree_approximate_sum(counter)),
+			     accuracy);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_approximate_compare_value);
+
+/**
+ * percpu_counter_tree_precise_compare - Precise comparison of two counter trees.
+ * @a: First counter to compare.
+ * @b: Second counter to compare.
+ *
+ * Evaluate a precise comparison of two counter trees. As an optimization, it
+ * uses the approximate counter comparison to quickly compare counters which
+ * are far apart. Only cases where counter sums are within the accuracy range
+ * require precise counter sums. The two counters are read independently; the
+ * result is not an atomic snapshot of both. With concurrent updates, the
+ * precise sums can count updates in part, see
+ * percpu_counter_tree_precise_sum().
+ * The comparison between counters is on the wrapped difference. It is valid
+ * only when the two counters differ by less than LONG_MAX, and results are
+ * undefined beyond that.
+ *
+ * Return:
+ * * %0		- Counters are equal.
+ * * %-1	- Counter @a less than counter @b.
+ * * %1		- Counter @a is greater than counter @b.
+ */
+int percpu_counter_tree_precise_compare(struct percpu_counter_tree *a,
+					struct percpu_counter_tree *b)
+{
+	long count_a = percpu_counter_tree_approximate_sum(a),
+	     count_b = percpu_counter_tree_approximate_sum(b);
+	unsigned long accuracy_a = percpu_counter_tree_accuracy(a),
+		      accuracy_b = percpu_counter_tree_accuracy(b);
+	long delta = wrapped_delta(count_a, count_b);
+	int res;
+
+	/* See the range geometry above compare_delta(). */
+	res = compare_delta(delta, accuracy_a + accuracy_b);
+	/* The values are distanced enough for an accurate approximated comparison. */
+	if (res)
+		return res;
+
+	/*
+	 * The approximated comparison is within the accuracy range, therefore at least one
+	 * precise sum is needed. Sum the counter which has the largest accuracy first.
+	 */
+	if (accuracy_b < accuracy_a) {
+		count_a = percpu_counter_tree_precise_sum(a);
+		/* precise_a - approx_b: only b's precise range remains. */
+		res = compare_delta(wrapped_delta(count_a, count_b), accuracy_b);
+		if (res)
+			return res;
+		/* Precise sum of second counter is required. */
+		count_b = percpu_counter_tree_precise_sum(b);
+	} else {
+		count_b = percpu_counter_tree_precise_sum(b);
+		/* approx_a - precise_b: only a's precise range remains. */
+		res = compare_delta(wrapped_delta(count_a, count_b), accuracy_a);
+		if (res)
+			return res;
+		/* Precise sum of second counter is required. */
+		count_a = percpu_counter_tree_precise_sum(a);
+	}
+	delta = wrapped_delta(count_a, count_b);
+	if (delta < 0)
+		return -1;
+	if (delta > 0)
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_precise_compare);
+
+/**
+ * percpu_counter_tree_precise_compare_value - Precise comparison of a counter tree against
+ *					       a given value.
+ * @counter: Counter to compare.
+ * @v: Value to compare.
+ *
+ * Evaluate a precise comparison of a counter tree against a given value. As
+ * an optimization, it uses the approximate counter comparison to quickly
+ * identify whether the counter and value are far apart. Only cases where the
+ * value is within the counter accuracy range require a precise counter sum.
+ * With concurrent updates, the precise sum can count updates in part, see
+ * percpu_counter_tree_precise_sum().
+ * The comparison between counters is on the wrapped difference. It is valid
+ * only when @v and the counter differ by less than LONG_MAX, and results are
+ * undefined beyond that.
+ *
+ * Return:
+ * * %0		- The value @v is equal to the counter.
+ * * %-1	- The value @v is less than the counter.
+ * * %1		- The value @v is greater than the counter.
+ */
+int percpu_counter_tree_precise_compare_value(struct percpu_counter_tree *counter, long v)
+{
+	long count = percpu_counter_tree_approximate_sum(counter), delta;
+	unsigned long accuracy = percpu_counter_tree_accuracy(counter);
+	int res;
+
+	/* See the range geometry above compare_delta(). */
+	res = compare_delta(wrapped_delta(v, count), accuracy);
+	/* The values are distanced enough for an accurate approximated comparison. */
+	if (res)
+		return res;
+
+	/* Precise sum is required. */
+	count = percpu_counter_tree_precise_sum(counter);
+	delta = wrapped_delta(v, count);
+	if (delta < 0)
+		return -1;
+	if (delta > 0)
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_precise_compare_value);
+
+/**
+ * percpu_counter_tree_set - Set the counter tree sum to a given value.
+ * @counter: Counter to set.
+ * @v: Value to set.
+ *
+ * Set the counter sum to a given value. It can be useful for instance
+ * to reset the counter sum to 0. Note that even after setting the
+ * counter sum to a given value, the counter sum approximation can
+ * return any value within the accuracy range around that value.
+ *
+ * The caller is responsible for preventing concurrent updates and
+ * precise sums of @counter while it is being set.
+ */
+void percpu_counter_tree_set(struct percpu_counter_tree *counter, long v)
+{
+	WRITE_ONCE(counter->bias, v - percpu_counter_tree_precise_sum_unbiased(counter));
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_set);
+
+/**
+ * percpu_counter_tree_approximate_accuracy - Query the accuracy of a counter tree.
+ * @counter: Counter to query.
+ *
+ * The approximation differs from the precise sum by the residuals not yet propagated
+ * up the tree. The residuals of all levels are symmetric, so the approximation is
+ * within the same distance of the precise sum in both directions.
+ * The accuracy is derived from the hardware topology and the counter tree batch size.
+ * It is invariant for a given counter tree.
+ * The accuracy describes a settled counter: one with no update in progress.
+ * An update in flight between its per-CPU update and its carry propagation is not covered.
+ * Using this function should not be typically required, see the following functions instead:
+ * * percpu_counter_tree_approximate_compare(),
+ * * percpu_counter_tree_approximate_compare_value(),
+ * * percpu_counter_tree_precise_compare(),
+ * * percpu_counter_tree_precise_compare_value().
+ *
+ * Return: The accuracy of the counter: the approximate sum of a settled counter
+ * is within [precise sum - accuracy, precise sum + accuracy].
+ */
+unsigned long percpu_counter_tree_approximate_accuracy(struct percpu_counter_tree *counter)
+{
+	return percpu_counter_tree_accuracy(counter);
+}
+EXPORT_SYMBOL_GPL(percpu_counter_tree_approximate_accuracy);
+
+/* Calculate the number of tree items and the accuracy multiplier. */
+static
+void __init calculate_topology(void)
+{
+	unsigned int nr_levels = tree_topology.config->nr_levels, level;
+	unsigned int level_items = 1U << tree_topology.nr_cpus_order;
+	unsigned long batch_size = 1;
+
+	/* The root, and the items of each level above level 0. */
+	tree_topology.nr_items = 1;
+	/* One batch per level 0 counter: see percpu_counter_tree_accuracy(). */
+	tree_topology.accuracy_multiplier = nr_cpu_ids;
+
+	/*
+	 * The multiplier is derived from a batch size of 1 to speed up
+	 * calculating the accuracy at tree initialization. A level N
+	 * counter holds at most BATCH(level N) - BATCH(level N - 1) in
+	 * either direction.
+	 */
+	for (level = 1; level < nr_levels; level++) {
+		unsigned int n_arity_order = tree_topology.config->n_arity_order[level - 1];
+
+		level_items >>= n_arity_order;
+		tree_topology.nr_items += level_items;
+		tree_topology.accuracy_multiplier +=
+			((batch_size << n_arity_order) - batch_size) * level_items;
+		batch_size <<= n_arity_order;
+	}
+}
+
+#if IS_ENABLED(CONFIG_KUNIT)
+/*
+ * Validate the configuration table: the n-arity orders of each entry must
+ * sum to its order.
+ *
+ * Return: The number of invalid entries.
+ */
+unsigned int percpu_counter_tree_check_config(void)
+{
+	unsigned int order, nr_errors = 0;
+
+	for (order = 0; order < ARRAY_SIZE(per_nr_cpu_order_config); order++) {
+		const struct counter_config *c = &per_nr_cpu_order_config[order];
+		unsigned int level, shift = 0;
+
+		for (level = 0; level < c->nr_levels; level++)
+			shift += c->n_arity_order[level];
+		if (shift != order) {
+			pr_err("order %u: arity sum %u\n", order, shift);
+			nr_errors++;
+		}
+	}
+	return nr_errors;
+}
+EXPORT_SYMBOL_IF_KUNIT(percpu_counter_tree_check_config);
+
+/*
+ * Check the invariants of a settled counter (no update in progress):
+ * - a level 0 counter is within (-BATCH(level 0), +BATCH(level 0)),
+ * - a counter at level N, above level 0, holds a multiple of
+ *   BATCH(level N - 1) within (-BATCH(level N), +BATCH(level N)),
+ * - the root holds a multiple of the batch of the last level.
+ *
+ * Return: The number of violated invariants.
+ */
+unsigned int percpu_counter_tree_check_residuals(struct percpu_counter_tree *counter)
+{
+	unsigned int nr_levels = tree_topology.config->nr_levels, level, level_items,
+		     nr_errors = 0, stride = counter->items_stride;
+	atomic_long_t *item = percpu_counter_tree_items(counter);
+	unsigned long lo = percpu_counter_tree_batch_size(counter);
+	long root;
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		long v = READ_ONCE(*per_cpu_ptr(counter->level0, cpu));
+
+		if (v <= -(long)lo || v >= (long)lo) {
+			pr_err("cpu %d: level 0 residual %ld, batch %lu\n", cpu, v, lo);
+			nr_errors++;
+		}
+	}
+	level_items = 1U << tree_topology.nr_cpus_order;
+	for (level = 1; level < nr_levels; level++) {
+		unsigned int n_arity_order = tree_topology.config->n_arity_order[level - 1], i;
+		unsigned long hi = lo << n_arity_order;
+
+		level_items >>= n_arity_order;
+		for (i = 0; i < level_items; i++) {
+			long v = atomic_long_read(&item[i * stride]);
+
+			if (v <= -(long)hi || v >= (long)hi || ((unsigned long)v & (lo - 1))) {
+				pr_err("level %u item %u: residual %ld, batch %lu\n",
+				       level, i, v, hi);
+				nr_errors++;
+			}
+		}
+		item += level_items * stride;
+		lo = hi;
+	}
+	root = atomic_long_read(counter->approx_sum);
+	if ((unsigned long)root & (lo - 1)) {
+		pr_err("root %ld: not a multiple of %lu\n", root, lo);
+		nr_errors++;
+	}
+	return nr_errors;
+}
+EXPORT_SYMBOL_IF_KUNIT(percpu_counter_tree_check_residuals);
+
+/*
+ * Copy the state of counter @src into counter @dst, initialized with the
+ * same batch size and without concurrent updates: their per-CPU counters,
+ * tree items and bias.
+ */
+void percpu_counter_tree_copy_state(struct percpu_counter_tree *dst,
+				    struct percpu_counter_tree *src)
+{
+	atomic_long_t *dst_items = percpu_counter_tree_items(dst),
+		      *src_items = percpu_counter_tree_items(src);
+	unsigned int pos;
+	int cpu;
+
+	for_each_possible_cpu(cpu)
+		*per_cpu_ptr(dst->level0, cpu) = *per_cpu_ptr(src->level0, cpu);
+	for (pos = 0; pos < tree_topology.nr_items; pos++)
+		atomic_long_set(&dst_items[pos * dst->items_stride],
+				atomic_long_read(&src_items[pos * src->items_stride]));
+	dst->bias = src->bias;
+}
+EXPORT_SYMBOL_IF_KUNIT(percpu_counter_tree_copy_state);
+#endif
+
+/* Format the fan-out of each level, from level 0 to the root, e.g. "8,2,8". */
+static
+int format_shape(char *buf, size_t size, const struct counter_config *config)
+{
+	unsigned int level;
+	int len = 0;
+
+	for (level = 0; level < config->nr_levels; level++)
+		len += scnprintf(buf + len, size - len, "%s%u", level ? "," : "",
+				 1U << config->n_arity_order[level]);
+	return len;
+}
+
+int __init percpu_counter_tree_subsystem_init(void)
+{
+	char shape[8 * MAX_NR_LEVELS + 1] = "";
+
+	tree_topology.nr_cpus_order = get_count_order(nr_cpu_ids);
+	if (tree_topology.nr_cpus_order >= ARRAY_SIZE(per_nr_cpu_order_config)) {
+		pr_err("Unsupported number of CPUs (%u)\n", nr_cpu_ids);
+		return -EINVAL;
+	}
+	tree_topology.config = &per_nr_cpu_order_config[tree_topology.nr_cpus_order];
+	calculate_topology();
+	tree_topology.items_cachep =
+		kmem_cache_create("percpu_counter_tree_items",
+				  tree_topology.nr_items * SMP_CACHE_BYTES,
+				  SMP_CACHE_BYTES, SLAB_PANIC, NULL);
+	format_shape(shape, sizeof(shape), tree_topology.config);
+	pr_info("%u CPUs, fan-out %s, %u tree items\n",
+		nr_cpu_ids, tree_topology.nr_cpus_order ? shape : "none",
+		tree_topology.nr_items);
+	return 0;
+}
-- 
2.47.3


  reply	other threads:[~2026-11-02 19:07 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 19:06 [RFC PATCH v23 00/10] Hierarchical Percpu Counters for RSS Mathieu Desnoyers
2026-11-02 19:06 ` Mathieu Desnoyers [this message]
2026-11-02 19:07 ` [RFC PATCH v23 02/10] lib: test hierarchical per-cpu counters Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 03/10] mm: use hierarchical per-cpu counters for the RSS counters Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 04/10] tools/testing/hpcc: add userspace test of hierarchical per-CPU counters Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 05/10] mm: report the approximate RSS in the rss_stat tracepoint Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 06/10] lib: percpu_counter_tree: add lazy tree activation Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 07/10] mm: keep the RSS counters of single-threaded processes exact Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 08/10] lib: percpu_counter_tree: add the arity boot parameter Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 09/10] mm: add the rss_stat_batch " Mathieu Desnoyers
2026-11-02 19:07 ` [RFC PATCH v23 10/10] lib: add a benchmark of hierarchical per-cpu counters Mathieu Desnoyers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261102190708.1287100-2-mathieu.desnoyers@efficios.com \
    --to=mathieu.desnoyers@efficios.com \
    --cc=aboorvad@linux.ibm.com \
    --cc=akpm@linux-foundation.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=brauner@kernel.org \
    --cc=bsegall@google.com \
    --cc=christian.koenig@amd.com \
    --cc=cl@gentwo.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=dennis@kernel.org \
    --cc=devnexen@gmail.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=hannes@cmpxchg.org \
    --cc=jack@suse.cz \
    --cc=juri.lelli@redhat.com \
    --cc=kees@kernel.org \
    --cc=kprateek.nayak@amd.com \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=liumartin@google.com \
    --cc=ljs@kernel.org \
    --cc=mgorman@suse.de \
    --cc=mhiramat@kernel.org \
    --cc=mhocko@suse.com \
    --cc=mingo@redhat.com \
    --cc=mjguzik@gmail.com \
    --cc=objecting@objecting.org \
    --cc=oleg@redhat.com \
    --cc=paulmck@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rdunlap@infradead.org \
    --cc=richard.weiyang@gmail.com \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=rostedt@goodmis.org \
    --cc=rppt@kernel.org \
    --cc=shakeel.butt@linux.dev \
    --cc=sj@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=surenb@google.com \
    --cc=sweettea-kernel@dorminy.me \
    --cc=tj@kernel.org \
    --cc=vbabka@kernel.org \
    --cc=vincent.guittot@linaro.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=vschneid@redhat.com \
    --cc=willy@infradead.org \
    --cc=yuzhao@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®