Currently THP deferred split shrinker is not memcg aware, this may cause premature OOM with some configuration. For example the below test would run into premature OOM easily: $ cgcreate -g memory:thp $ echo 4G > /sys/fs/cgroup/memory/thp/memory/limit_in_bytes $ cgexec -g memory:thp transhuge-stress 4000 transhuge-stress comes from kernel selftest. It is easy to hit OOM, but there are still a lot THP on the deferred split queue, memcg direct reclaim can't touch them since the deferred split shrinker is not memcg aware. Convert deferred split shrinker memcg aware by introducing per memcg deferred split queue. The THP should be on either per node or per memcg deferred split queue if it belongs to a memcg. When the page is immigrated to the other memcg, it will be immigrated to the target memcg's deferred split queue too. Reuse the second tail page's deferred_list for per memcg list since the same THP can't be on multiple deferred split queues. [yang.shi@linux.alibaba.com: simplify deferred split queue dereference per Kirill Tkhai] Link: http://lkml.kernel.org/r/1566496227-84952-5-git-send-email-yang.shi@linux.alibaba.com Link: http://lkml.kernel.org/r/1565144277-36240-5-git-send-email-yang.shi@linux.alibaba.com Signed-off-by: Yang Shi <yang.shi@linux.alibaba.com> Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reviewed-by: Kirill Tkhai <ktkhai@virtuozzo.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Michal Hocko <mhocko@suse.com> Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com> Cc: Hugh Dickins <hughd@google.com> Cc: Shakeel Butt <shakeelb@google.com> Cc: David Rientjes <rientjes@google.com> Cc: Qian Cai <cai@lca.pw> Cc: Vladimir Davydov <vdavydov.dev@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing the changes to include/linux (3 of 5 files). Show all
Unified · Split
| @@ -267,6 +267,15 @@ static inline bool thp_migration_supported(void) · 266 unmodified lines | |||
| 267 | return IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION); | 267 | return IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION); |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | static inline struct list_head *page_deferred_list(struct page *page) | ||
| 271 | { | ||
| 272 | /* | ||
| 273 | * Global or memcg deferred list in the second tail pages is | ||
| 274 | * occupied by compound_head. | ||
| 275 | */ | ||
| 276 | return &page[2].deferred_list; | ||
| 277 | } | ||
| 278 | |||
| 270 | #else /* CONFIG_TRANSPARENT_HUGEPAGE */ | 279 | #else /* CONFIG_TRANSPARENT_HUGEPAGE */ |
| 271 | #define HPAGE_PMD_SHIFT ({ BUILD_BUG(); 0; }) | 280 | #define HPAGE_PMD_SHIFT ({ BUILD_BUG(); 0; }) |
| 272 | #define HPAGE_PMD_MASK ({ BUILD_BUG(); 0; }) | 281 | #define HPAGE_PMD_MASK ({ BUILD_BUG(); 0; }) |
| @@ -330,6 +330,10 @@ struct mem_cgroup { · 329 unmodified lines | |||
| 330 | struct list_head event_list; | 330 | struct list_head event_list; |
| 331 | spinlock_t event_list_lock; | 331 | spinlock_t event_list_lock; |
| 332 | 332 | ||
| 333 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
| 334 | struct deferred_split deferred_split_queue; | ||
| 335 | #endif | ||
| 336 | |||
| 333 | struct mem_cgroup_per_node *nodeinfo[0]; | 337 | struct mem_cgroup_per_node *nodeinfo[0]; |
| 334 | /* WARNING: nodeinfo must be the last member here */ | 338 | /* WARNING: nodeinfo must be the last member here */ |
| 335 | }; | 339 | }; |
| @@ -138,6 +138,7 @@ struct page { · 137 unmodified lines | |||
| 138 | struct { /* Second tail page of compound page */ | 138 | struct { /* Second tail page of compound page */ |
| 139 | unsigned long _compound_pad_1; /* compound_head */ | 139 | unsigned long _compound_pad_1; /* compound_head */ |
| 140 | unsigned long _compound_pad_2; | 140 | unsigned long _compound_pad_2; |
| 141 | /* For both global and memcg */ | ||
| 141 | struct list_head deferred_list; | 142 | struct list_head deferred_list; |
| 142 | }; | 143 | }; |
| 143 | struct { /* Page table pages */ | 144 | struct { /* Page table pages */ |