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
