[PATCH 1/2] cgroup/dmem: add per-region event counters

From: Hongfu Li

Date: Tue Jun 23 2026 - 23:12:07 EST


Add dmem.events to report hierarchical low/max event counts per DMEM
region. Increment counters on dmem.max allocation failures and
dmem.low protection events. The file is available for non-root cgroups
only.

Signed-off-by: Hongfu Li <lihongfu@xxxxxxxxxx>
---
Documentation/admin-guide/cgroup-v2.rst | 16 +++++++
kernel/cgroup/dmem.c | 61 ++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 993446ab66d0..afc924539a41 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -2881,6 +2881,22 @@ DMEM Interface Files
drm/0000:03:00.0/vram0 12550144
drm/0000:03:00.0/stolen 8650752

+ dmem.events
+ A read-only file that reports the number of times each cgroup
+ has hit its configured memory limits. The format lists each
+ region on a single line, followed by the event counters::
+
+ drm/0000:03:00.0/vram0 low 0 max 3
+ drm/0000:03:00.0/stolen low 0 max 0
+
+ ``low`` counts how many times reclaim or eviction considered
+ the cgroup to be below its effective ``dmem.low`` protection.
+ ``max`` counts how many times an allocation failed because the
+ cgroup or one of its ancestors hit ``dmem.max``.
+
+ ``dmem.events`` contains hierarchical counts. This file exists
+ for all cgroups except root.
+
HugeTLB
-------

diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4753a67d0f0f..79d4c5d0a046 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -8,6 +8,7 @@
* Copyright (C) 2016 Parav Pandit <pandit.parav@xxxxxxxxx>
*/

+#include <linux/atomic.h>
#include <linux/cgroup.h>
#include <linux/cgroup_dmem.h>
#include <linux/list.h>
@@ -57,6 +58,14 @@ struct dmemcg_state {
struct cgroup_subsys_state css;

struct list_head pools;
+
+ struct cgroup_file events_file;
+};
+
+enum dmemcg_memory_event {
+ DMEMCG_LOW,
+ DMEMCG_MAX,
+ DMEMCG_NR_EVENTS,
};

struct dmem_cgroup_pool_state {
@@ -74,6 +83,8 @@ struct dmem_cgroup_pool_state {
struct page_counter cnt;
struct dmem_cgroup_pool_state *parent;

+ atomic_long_t events[DMEMCG_NR_EVENTS];
+
refcount_t ref;
bool inited;
};
@@ -182,6 +193,24 @@ static u64 get_resource_current(struct dmem_cgroup_pool_state *pool)
return pool ? page_counter_read(&pool->cnt) : 0;
}

+static void dmemcg_memory_event(struct dmem_cgroup_pool_state *pool,
+ enum dmemcg_memory_event event)
+{
+ for (; pool; pool = pool->parent) {
+ atomic_long_inc(&pool->events[event]);
+ cgroup_file_notify(&pool->cs->events_file);
+ }
+}
+
+static long dmemcg_get_event(struct dmem_cgroup_pool_state *pool,
+ enum dmemcg_memory_event event)
+{
+ if (!pool)
+ return 0;
+
+ return atomic_long_read(&pool->events[event]);
+}
+
static void reset_all_resource_limits(struct dmem_cgroup_pool_state *rpool)
{
set_resource_min(rpool, 0);
@@ -345,6 +374,7 @@ bool dmem_cgroup_state_evict_valuable(struct dmem_cgroup_pool_state *limit_pool,
return true;

*ret_hit_low = true;
+ dmemcg_memory_event(test_pool, DMEMCG_LOW);
return false;
}
return true;
@@ -675,8 +705,12 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,
}

if (!page_counter_try_charge(&pool->cnt, size, &fail)) {
+ struct dmem_cgroup_pool_state *limit_pool;
+
+ limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt);
+ dmemcg_memory_event(limit_pool, DMEMCG_MAX);
if (ret_limit_pool) {
- *ret_limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt);
+ *ret_limit_pool = limit_pool;
css_get(&(*ret_limit_pool)->cs->css);
dmemcg_pool_get(*ret_limit_pool);
}
@@ -840,6 +874,25 @@ static int dmem_cgroup_region_max_show(struct seq_file *sf, void *v)
return dmemcg_limit_show(sf, v, get_resource_max);
}

+static int dmem_cgroup_region_events_show(struct seq_file *sf, void *v)
+{
+ struct dmemcg_state *dmemcs = css_to_dmemcs(seq_css(sf));
+ struct dmem_cgroup_region *region;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(region, &dmem_cgroup_regions, region_node) {
+ struct dmem_cgroup_pool_state *pool = find_cg_pool_locked(dmemcs, region);
+
+ seq_puts(sf, region->name);
+ seq_printf(sf, " low %ld max %ld\n",
+ dmemcg_get_event(pool, DMEMCG_LOW),
+ dmemcg_get_event(pool, DMEMCG_MAX));
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
+
static ssize_t dmem_cgroup_region_max_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
@@ -874,6 +927,12 @@ static struct cftype files[] = {
.seq_show = dmem_cgroup_region_max_show,
.flags = CFTYPE_NOT_ON_ROOT,
},
+ {
+ .name = "events",
+ .seq_show = dmem_cgroup_region_events_show,
+ .file_offset = offsetof(struct dmemcg_state, events_file),
+ .flags = CFTYPE_NOT_ON_ROOT,
+ },
{ } /* Zero entry terminates. */
};

--
2.25.1