History

mm: memcg/slab: reparent memcg kmem_caches on cgroup removal

Let's reparent non-root kmem_caches on memcg offlining. This allows us to release the memory cgroup without waiting for the last outstanding kernel object (e.g. dentry used by another application). Since the parent cgroup is already charged, everything we need to do is to splice the list of kmem_caches to the parent's kmem_caches list, swap the memcg pointer, drop the css refcounter for each kmem_cache and adjust the parent's css refcounter. Please, note that kmem_cache->memcg_params.memcg isn't a stable pointer anymore. It's safe to read it under rcu_read_lock(), cgroup_mutex held, or any other way that protects the memory cgroup from being released. We can race with the slab allocation and deallocation paths. It's not a big problem: parent's charge and slab global stats are always correct, and we don't care anymore about the child usage and global stats. The child cgroup is already offline, so we don't use or show it anywhere. Local slab stats (NR_SLAB_RECLAIMABLE and NR_SLAB_UNRECLAIMABLE) aren't used anywhere except count_shadow_nodes(). But even there it won't break anything: after reparenting "nodes" will be 0 on child level (because we're already reparenting shrinker lists), and on parent level page stats always were 0, and this patch won't change anything. [guro@fb.com: properly handle kmem_caches reparented to root_mem_cgroup] Link: http://lkml.kernel.org/r/20190620213427.1691847-1-guro@fb.com Link: http://lkml.kernel.org/r/20190611231813.3148843-11-guro@fb.com Signed-off-by: Roman Gushchin <guro@fb.com> Acked-by: Vladimir Davydov <vdavydov.dev@gmail.com> Reviewed-by: Shakeel Butt <shakeelb@google.com> Acked-by: David Rientjes <rientjes@google.com> Cc: Christoph Lameter <cl@linux.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Waiman Long <longman@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: Andrei Vagin <avagin@gmail.com> Cc: Qian Cai <cai@lca.pw> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Roman Gushchin committed 7 years ago

Commit fb2f2b01 parent 4d96ba34 files · +58 −18Browse files

Unified · Split

4 files
  1. include/linux/slab.h +1 −1
  2. mm/memcontrol.c +8 −6
  3. mm/slab.h +32 −9
  4. mm/slab_common.c +17 −2
include/linux/slab.h+1−1
@@ -152,7 +152,7 @@ void kmem_cache_destroy(struct kmem_cache *); · 151 unmodified lines
152int kmem_cache_shrink(struct kmem_cache *);152int kmem_cache_shrink(struct kmem_cache *);
153 153
154void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);154void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
155void memcg_deactivate_kmem_caches(struct mem_cgroup *);155void memcg_deactivate_kmem_caches(struct mem_cgroup *, struct mem_cgroup *);
156 156
157/*157/*
158 * Please use this macro to create slab caches. Simply specify the158 * Please use this macro to create slab caches. Simply specify the
mm/memcontrol.c+8−6
@@ -3284,15 +3284,15 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) · 3283 unmodified lines
3284 */3284 */
3285 memcg->kmem_state = KMEM_ALLOCATED;3285 memcg->kmem_state = KMEM_ALLOCATED;
3286 3286
3287 memcg_deactivate_kmem_caches(memcg);
3288
3289 kmemcg_id = memcg->kmemcg_id;
3290 BUG_ON(kmemcg_id < 0);
3291
3292 parent = parent_mem_cgroup(memcg);3287 parent = parent_mem_cgroup(memcg);
3293 if (!parent)3288 if (!parent)
3294 parent = root_mem_cgroup;3289 parent = root_mem_cgroup;
3295 3290
3291 memcg_deactivate_kmem_caches(memcg, parent);
3292
3293 kmemcg_id = memcg->kmemcg_id;
3294 BUG_ON(kmemcg_id < 0);
3295
3296 /*3296 /*
3297 * Change kmemcg_id of this cgroup and all its descendants to the3297 * Change kmemcg_id of this cgroup and all its descendants to the
3298 * parent's id, and then move all entries from this cgroup's list_lrus3298 * parent's id, and then move all entries from this cgroup's list_lrus
@@ -3325,7 +3325,6 @@ static void memcg_free_kmem(struct mem_cgroup *memcg) · 26 unmodified lines
3325 if (memcg->kmem_state == KMEM_ALLOCATED) {3325 if (memcg->kmem_state == KMEM_ALLOCATED) {
3326 WARN_ON(!list_empty(&memcg->kmem_caches));3326 WARN_ON(!list_empty(&memcg->kmem_caches));
3327 static_branch_dec(&memcg_kmem_enabled_key);3327 static_branch_dec(&memcg_kmem_enabled_key);
3328 WARN_ON(page_counter_read(&memcg->kmem));
3329 }3328 }
3330}3329}
3331#else3330#else
@@ -4773,6 +4772,9 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) · 1441 unmodified lines
4773 4772
4774 /* The following stuff does not apply to the root */4773 /* The following stuff does not apply to the root */
4775 if (!parent) {4774 if (!parent) {
4775#ifdef CONFIG_MEMCG_KMEM
4776 INIT_LIST_HEAD(&memcg->kmem_caches);
4777#endif
4776 root_mem_cgroup = memcg;4778 root_mem_cgroup = memcg;
4777 return &memcg->css;4779 return &memcg->css;
4778 }4780 }
mm/slab.h+32−9
@@ -261,6 +261,9 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) · 260 unmodified lines
261 * which do not have slab_cache pointer set.261 * which do not have slab_cache pointer set.
262 * So this function assumes that the page can pass PageHead() and PageSlab()262 * So this function assumes that the page can pass PageHead() and PageSlab()
263 * checks.263 * checks.
264 *
265 * The kmem_cache can be reparented asynchronously. The caller must ensure
266 * the memcg lifetime, e.g. by taking rcu_read_lock() or cgroup_mutex.
264 */267 */
265static inline struct mem_cgroup *memcg_from_slab_page(struct page *page)268static inline struct mem_cgroup *memcg_from_slab_page(struct page *page)
266{269{
@@ -268,7 +271,7 @@ static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) · 1 unmodified line
268 271
269 s = READ_ONCE(page->slab_cache);272 s = READ_ONCE(page->slab_cache);
270 if (s && !is_root_cache(s))273 if (s && !is_root_cache(s))
271 return s->memcg_params.memcg;274 return READ_ONCE(s->memcg_params.memcg);
272 275
273 return NULL;276 return NULL;
274}277}
@@ -285,10 +288,22 @@ static __always_inline int memcg_charge_slab(struct page *page, · 10 unmodified lines
285 struct lruvec *lruvec;288 struct lruvec *lruvec;
286 int ret;289 int ret;
287 290
288 memcg = s->memcg_params.memcg;291 rcu_read_lock();
292 memcg = READ_ONCE(s->memcg_params.memcg);
293 while (memcg && !css_tryget_online(&memcg->css))
294 memcg = parent_mem_cgroup(memcg);
295 rcu_read_unlock();
296
297 if (unlikely(!memcg || mem_cgroup_is_root(memcg))) {
298 mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s),
299 (1 << order));
300 percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order);
301 return 0;
302 }
303
289 ret = memcg_kmem_charge_memcg(page, gfp, order, memcg);304 ret = memcg_kmem_charge_memcg(page, gfp, order, memcg);
290 if (ret)305 if (ret)
291 return ret;306 goto out;
292 307
293 lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);308 lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);
294 mod_lruvec_state(lruvec, cache_vmstat_idx(s), 1 << order);309 mod_lruvec_state(lruvec, cache_vmstat_idx(s), 1 << order);
@@ -296,8 +311,9 @@ static __always_inline int memcg_charge_slab(struct page *page, · 1 unmodified line
296 /* transer try_charge() page references to kmem_cache */311 /* transer try_charge() page references to kmem_cache */
297 percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order);312 percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order);
298 css_put_many(&memcg->css, 1 << order);313 css_put_many(&memcg->css, 1 << order);
299 314out:
300 return 0;315 css_put(&memcg->css);
316 return ret;
301}317}
302 318
303/*319/*
@@ -310,10 +326,17 @@ static __always_inline void memcg_uncharge_slab(struct page *page, int order, · 6 unmodified lines
310 struct mem_cgroup *memcg;326 struct mem_cgroup *memcg;
311 struct lruvec *lruvec;327 struct lruvec *lruvec;
312 328
313 memcg = s->memcg_params.memcg;329 rcu_read_lock();
314 lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);330 memcg = READ_ONCE(s->memcg_params.memcg);
315 mod_lruvec_state(lruvec, cache_vmstat_idx(s), -(1 << order));331 if (likely(!mem_cgroup_is_root(memcg))) {
316 memcg_kmem_uncharge_memcg(page, order, memcg);332 lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);
333 mod_lruvec_state(lruvec, cache_vmstat_idx(s), -(1 << order));
334 memcg_kmem_uncharge_memcg(page, order, memcg);
335 } else {
336 mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s),
337 -(1 << order));
338 }
339 rcu_read_unlock();
317 340
318 percpu_ref_put_many(&s->memcg_params.refcnt, 1 << order);341 percpu_ref_put_many(&s->memcg_params.refcnt, 1 << order);
319}342}
mm/slab_common.c+17−2
@@ -252,7 +252,8 @@ static void memcg_unlink_cache(struct kmem_cache *s) · 251 unmodified lines
252 } else {252 } else {
253 list_del(&s->memcg_params.children_node);253 list_del(&s->memcg_params.children_node);
254 list_del(&s->memcg_params.kmem_caches_node);254 list_del(&s->memcg_params.kmem_caches_node);
255 css_put(&s->memcg_params.memcg->css);255 mem_cgroup_put(s->memcg_params.memcg);
256 WRITE_ONCE(s->memcg_params.memcg, NULL);
256 }257 }
257}258}
258#else259#else
@@ -785,11 +786,13 @@ unlock: · 526 unmodified lines
785 spin_unlock_irq(&memcg_kmem_wq_lock);786 spin_unlock_irq(&memcg_kmem_wq_lock);
786}787}
787 788
788void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg)789void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg,
790 struct mem_cgroup *parent)
789{791{
790 int idx;792 int idx;
791 struct memcg_cache_array *arr;793 struct memcg_cache_array *arr;
792 struct kmem_cache *s, *c;794 struct kmem_cache *s, *c;
795 unsigned int nr_reparented;
793 796
794 idx = memcg_cache_id(memcg);797 idx = memcg_cache_id(memcg);
795 798
@@ -807,6 +810,18 @@ void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg) · 11 unmodified lines
807 kmemcg_cache_deactivate(c);810 kmemcg_cache_deactivate(c);
808 arr->entries[idx] = NULL;811 arr->entries[idx] = NULL;
809 }812 }
813 nr_reparented = 0;
814 list_for_each_entry(s, &memcg->kmem_caches,
815 memcg_params.kmem_caches_node) {
816 WRITE_ONCE(s->memcg_params.memcg, parent);
817 css_put(&memcg->css);
818 nr_reparented++;
819 }
820 if (nr_reparented) {
821 list_splice_init(&memcg->kmem_caches,
822 &parent->kmem_caches);
823 css_get_many(&parent->css, nr_reparented);
824 }
810 mutex_unlock(&slab_mutex);825 mutex_unlock(&slab_mutex);
811 826
812 put_online_mems();827 put_online_mems();

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