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 <[email protected]>
---
 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 <[email protected]>
  */
 
+#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

Reply via email to