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
152152 int kmem_cache_shrink(struct kmem_cache *);
153153
154154 void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
155−void memcg_deactivate_kmem_caches(struct mem_cgroup *);
155+void memcg_deactivate_kmem_caches(struct mem_cgroup *, struct mem_cgroup *);
156156
157157 /*
158158 * 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
32843284 */
32853285 memcg->kmem_state = KMEM_ALLOCATED;
32863286
3287− memcg_deactivate_kmem_caches(memcg);
3288−
3289− kmemcg_id = memcg->kmemcg_id;
3290− BUG_ON(kmemcg_id < 0);
3291−
32923287 parent = parent_mem_cgroup(memcg);
32933288 if (!parent)
32943289 parent = root_mem_cgroup;
32953290
3291+ memcg_deactivate_kmem_caches(memcg, parent);
3292+
3293+ kmemcg_id = memcg->kmemcg_id;
3294+ BUG_ON(kmemcg_id < 0);
3295+
32963296 /*
32973297 * Change kmemcg_id of this cgroup and all its descendants to the
32983298 * 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
33253325 if (memcg->kmem_state == KMEM_ALLOCATED) {
33263326 WARN_ON(!list_empty(&memcg->kmem_caches));
33273327 static_branch_dec(&memcg_kmem_enabled_key);
3328− WARN_ON(page_counter_read(&memcg->kmem));
33293328 }
33303329 }
33313330 #else
@@ -4773,6 +4772,9 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) · 1441 unmodified lines
47734772
47744773 /* The following stuff does not apply to the root */
47754774 if (!parent) {
4775+#ifdef CONFIG_MEMCG_KMEM
4776+ INIT_LIST_HEAD(&memcg->kmem_caches);
4777+#endif
47764778 root_mem_cgroup = memcg;
47774779 return &memcg->css;
47784780 }
mm/slab.h+32−9
@@ -261,6 +261,9 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) · 260 unmodified lines
261261 * which do not have slab_cache pointer set.
262262 * So this function assumes that the page can pass PageHead() and PageSlab()
263263 * 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.
264267 */
265268 static inline struct mem_cgroup *memcg_from_slab_page(struct page *page)
266269 {
@@ -268,7 +271,7 @@ static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) · 1 unmodified line
268271
269272 s = READ_ONCE(page->slab_cache);
270273 if (s && !is_root_cache(s))
271− return s->memcg_params.memcg;
274+ return READ_ONCE(s->memcg_params.memcg);
272275
273276 return NULL;
274277 }
@@ -285,10 +288,22 @@ static __always_inline int memcg_charge_slab(struct page *page, · 10 unmodified lines
285288 struct lruvec *lruvec;
286289 int ret;
287290
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+
289304 ret = memcg_kmem_charge_memcg(page, gfp, order, memcg);
290305 if (ret)
291− return ret;
306+ goto out;
292307
293308 lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);
294309 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
296311 /* transer try_charge() page references to kmem_cache */
297312 percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order);
298313 css_put_many(&memcg->css, 1 << order);
299−
300− return 0;
314+out:
315+ css_put(&memcg->css);
316+ return ret;
301317 }
302318
303319 /*
@@ -310,10 +326,17 @@ static __always_inline void memcg_uncharge_slab(struct page *page, int order, · 6 unmodified lines
310326 struct mem_cgroup *memcg;
311327 struct lruvec *lruvec;
312328
313− memcg = s->memcg_params.memcg;
314− lruvec = mem_cgroup_lruvec(page_pgdat(page), memcg);
315− mod_lruvec_state(lruvec, cache_vmstat_idx(s), -(1 << order));
316− memcg_kmem_uncharge_memcg(page, order, memcg);
329+ rcu_read_lock();
330+ memcg = READ_ONCE(s->memcg_params.memcg);
331+ if (likely(!mem_cgroup_is_root(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();
317340
318341 percpu_ref_put_many(&s->memcg_params.refcnt, 1 << order);
319342 }
mm/slab_common.c+17−2
@@ -252,7 +252,8 @@ static void memcg_unlink_cache(struct kmem_cache *s) · 251 unmodified lines
252252 } else {
253253 list_del(&s->memcg_params.children_node);
254254 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);
256257 }
257258 }
258259 #else
@@ -785,11 +786,13 @@ unlock: · 526 unmodified lines
785786 spin_unlock_irq(&memcg_kmem_wq_lock);
786787 }
787788
788−void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg)
789+void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg,
790+ struct mem_cgroup *parent)
789791 {
790792 int idx;
791793 struct memcg_cache_array *arr;
792794 struct kmem_cache *s, *c;
795+ unsigned int nr_reparented;
793796
794797 idx = memcg_cache_id(memcg);
795798
@@ -807,6 +810,18 @@ void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg) · 11 unmodified lines
807810 kmemcg_cache_deactivate(c);
808811 arr->entries[idx] = NULL;
809812 }
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+ }
810825 mutex_unlock(&slab_mutex);
811826
812827 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