History

mm: memcontrol: fix recursive statistics correctness & scalabilty

Right now, when somebody needs to know the recursive memory statistics and events of a cgroup subtree, they need to walk the entire subtree and sum up the counters manually. There are two issues with this: 1. When a cgroup gets deleted, its stats are lost. The state counters should all be 0 at that point, of course, but the events are not. When this happens, the event counters, which are supposed to be monotonic, can go backwards in the parent cgroups. 2. During regular operation, we always have a certain number of lazily freed cgroups sitting around that have been deleted, have no tasks, but have a few cache pages remaining. These groups' statistics do not change until we eventually hit memory pressure, but somebody watching, say, memory.stat on an ancestor has to iterate those every time. This patch addresses both issues by introducing recursive counters at each level that are propagated from the write side when stats change. Upward propagation happens when the per-cpu caches spill over into the local atomic counter. This is the same thing we do during charge and uncharge, except that the latter uses atomic RMWs, which are more expensive; stat changes happen at around the same rate. In a sparse file test (page faults and reclaim at maximum CPU speed) with 5 cgroup nesting levels, perf shows __mod_memcg_page state at ~1%.

Johannes Weiner committed 7 years ago

Commit 42a30031 parent db9adbc2 files · +150 −109Browse files

Showing the changes to include/linux (1 of 2 files). Show all

Unified · Split

include/linux/memcontrol.h+51−3
@@ -128,6 +128,7 @@ struct mem_cgroup_per_node { · 127 unmodified lines
128 128
129 struct lruvec_stat __percpu *lruvec_stat_cpu;129 struct lruvec_stat __percpu *lruvec_stat_cpu;
130 atomic_long_t lruvec_stat[NR_VM_NODE_STAT_ITEMS];130 atomic_long_t lruvec_stat[NR_VM_NODE_STAT_ITEMS];
131 atomic_long_t lruvec_stat_local[NR_VM_NODE_STAT_ITEMS];
131 132
132 unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];133 unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
133 134
@@ -279,8 +280,12 @@ struct mem_cgroup { · 145 unmodified lines
279 MEMCG_PADDING(_pad2_);280 MEMCG_PADDING(_pad2_);
280 281
281 atomic_long_t vmstats[MEMCG_NR_STAT];282 atomic_long_t vmstats[MEMCG_NR_STAT];
283 atomic_long_t vmstats_local[MEMCG_NR_STAT];
284
282 atomic_long_t vmevents[NR_VM_EVENT_ITEMS];285 atomic_long_t vmevents[NR_VM_EVENT_ITEMS];
283 atomic_long_t memory_events[MEMCG_NR_MEMORY_EVENTS];286 atomic_long_t vmevents_local[NR_VM_EVENT_ITEMS];
287
288 atomic_long_t memory_events[MEMCG_NR_MEMORY_EVENTS];
284 289
285 unsigned long socket_pressure;290 unsigned long socket_pressure;
286 291
@@ -550,6 +555,20 @@ struct mem_cgroup *lock_page_memcg(struct page *page); · 263 unmodified lines
550void __unlock_page_memcg(struct mem_cgroup *memcg);555void __unlock_page_memcg(struct mem_cgroup *memcg);
551void unlock_page_memcg(struct page *page);556void unlock_page_memcg(struct page *page);
552 557
558/*
559 * idx can be of type enum memcg_stat_item or node_stat_item.
560 * Keep in sync with memcg_exact_page_state().
561 */
562static inline unsigned long memcg_page_state(struct mem_cgroup *memcg, int idx)
563{
564 long x = atomic_long_read(&memcg->vmstats[idx]);
565#ifdef CONFIG_SMP
566 if (x < 0)
567 x = 0;
568#endif
569 return x;
570}
571
553/*572/*
554 * idx can be of type enum memcg_stat_item or node_stat_item.573 * idx can be of type enum memcg_stat_item or node_stat_item.
555 * Keep in sync with memcg_exact_page_state().574 * Keep in sync with memcg_exact_page_state().
@@ -557,7 +576,7 @@ void unlock_page_memcg(struct page *page); · 1 unmodified line
557static inline unsigned long memcg_page_state_local(struct mem_cgroup *memcg,576static inline unsigned long memcg_page_state_local(struct mem_cgroup *memcg,
558 int idx)577 int idx)
559{578{
560 long x = atomic_long_read(&memcg->vmstats[idx]);579 long x = atomic_long_read(&memcg->vmstats_local[idx]);
561#ifdef CONFIG_SMP580#ifdef CONFIG_SMP
562 if (x < 0)581 if (x < 0)
563 x = 0;582 x = 0;
@@ -609,6 +628,24 @@ static inline void mod_memcg_page_state(struct page *page, · 45 unmodified lines
609 mod_memcg_state(page->mem_cgroup, idx, val);628 mod_memcg_state(page->mem_cgroup, idx, val);
610}629}
611 630
631static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
632 enum node_stat_item idx)
633{
634 struct mem_cgroup_per_node *pn;
635 long x;
636
637 if (mem_cgroup_disabled())
638 return node_page_state(lruvec_pgdat(lruvec), idx);
639
640 pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
641 x = atomic_long_read(&pn->lruvec_stat[idx]);
642#ifdef CONFIG_SMP
643 if (x < 0)
644 x = 0;
645#endif
646 return x;
647}
648
612static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,649static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
613 enum node_stat_item idx)650 enum node_stat_item idx)
614{651{
@@ -619,7 +656,7 @@ static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec, · 4 unmodified lines
619 return node_page_state(lruvec_pgdat(lruvec), idx);656 return node_page_state(lruvec_pgdat(lruvec), idx);
620 657
621 pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);658 pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
622 x = atomic_long_read(&pn->lruvec_stat[idx]);659 x = atomic_long_read(&pn->lruvec_stat_local[idx]);
623#ifdef CONFIG_SMP660#ifdef CONFIG_SMP
624 if (x < 0)661 if (x < 0)
625 x = 0;662 x = 0;
@@ -959,6 +996,11 @@ static inline void mem_cgroup_print_oom_group(struct mem_cgroup *memcg) · 333 unmodified lines
959{996{
960}997}
961 998
999static inline unsigned long memcg_page_state(struct mem_cgroup *memcg, int idx)
1000{
1001 return 0;
1002}
1003
962static inline unsigned long memcg_page_state_local(struct mem_cgroup *memcg,1004static inline unsigned long memcg_page_state_local(struct mem_cgroup *memcg,
963 int idx)1005 int idx)
964{1006{
@@ -989,6 +1031,12 @@ static inline void mod_memcg_page_state(struct page *page, · 24 unmodified lines
989{1031{
990}1032}
991 1033
1034static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
1035 enum node_stat_item idx)
1036{
1037 return node_page_state(lruvec_pgdat(lruvec), idx);
1038}
1039
992static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,1040static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
993 enum node_stat_item idx)1041 enum node_stat_item idx)
994{1042{

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t