Skip to content

Commit 92ee383

Browse files
shakeelbtorvalds
authored andcommitted
mm: fix race between kmem_cache destroy, create and deactivate
The memcg kmem cache creation and deactivation (SLUB only) is asynchronous. If a root kmem cache is destroyed whose memcg cache is in the process of creation or deactivation, the kernel may crash. Example of one such crash: general protection fault: 0000 [#1] SMP PTI CPU: 1 PID: 1721 Comm: kworker/14:1 Not tainted 4.17.0-smp ... Workqueue: memcg_kmem_cache kmemcg_deactivate_workfn RIP: 0010:has_cpu_slab ... Call Trace: ? on_each_cpu_cond __kmem_cache_shrink kmemcg_cache_deact_after_rcu kmemcg_deactivate_workfn process_one_work worker_thread kthread ret_from_fork+0x35/0x40 To fix this race, on root kmem cache destruction, mark the cache as dying and flush the workqueue used for memcg kmem cache creation and deactivation. SLUB's memcg kmem cache deactivation also includes RCU callback and thus make sure all previous registered RCU callbacks have completed as well. [[email protected]: handle the RCU callbacks for SLUB deactivation] Link: http://lkml.kernel.org/r/[email protected] [[email protected]: add more documentation, rename fields for readability] Link: http://lkml.kernel.org/r/[email protected] [[email protected]: fix build, per Shakeel] [[email protected]: v3. Instead of refcount, flush the workqueue] Link: http://lkml.kernel.org/r/[email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Shakeel Butt <[email protected]> Acked-by: Vladimir Davydov <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Greg Thelen <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Pekka Enberg <[email protected]> Cc: David Rientjes <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Tejun Heo <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2bdce74 commit 92ee383

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

include/linux/slab.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ struct memcg_cache_params {
600600
struct memcg_cache_array __rcu *memcg_caches;
601601
struct list_head __root_caches_node;
602602
struct list_head children;
603+
bool dying;
603604
};
604605
struct {
605606
struct mem_cgroup *memcg;

mm/slab_common.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ void slab_init_memcg_params(struct kmem_cache *s)
136136
s->memcg_params.root_cache = NULL;
137137
RCU_INIT_POINTER(s->memcg_params.memcg_caches, NULL);
138138
INIT_LIST_HEAD(&s->memcg_params.children);
139+
s->memcg_params.dying = false;
139140
}
140141

141142
static int init_memcg_params(struct kmem_cache *s,
@@ -608,7 +609,7 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg,
608609
* The memory cgroup could have been offlined while the cache
609610
* creation work was pending.
610611
*/
611-
if (memcg->kmem_state != KMEM_ONLINE)
612+
if (memcg->kmem_state != KMEM_ONLINE || root_cache->memcg_params.dying)
612613
goto out_unlock;
613614

614615
idx = memcg_cache_id(memcg);
@@ -712,6 +713,9 @@ void slab_deactivate_memcg_cache_rcu_sched(struct kmem_cache *s,
712713
WARN_ON_ONCE(s->memcg_params.deact_fn))
713714
return;
714715

716+
if (s->memcg_params.root_cache->memcg_params.dying)
717+
return;
718+
715719
/* pin memcg so that @s doesn't get destroyed in the middle */
716720
css_get(&s->memcg_params.memcg->css);
717721

@@ -823,11 +827,36 @@ static int shutdown_memcg_caches(struct kmem_cache *s)
823827
return -EBUSY;
824828
return 0;
825829
}
830+
831+
static void flush_memcg_workqueue(struct kmem_cache *s)
832+
{
833+
mutex_lock(&slab_mutex);
834+
s->memcg_params.dying = true;
835+
mutex_unlock(&slab_mutex);
836+
837+
/*
838+
* SLUB deactivates the kmem_caches through call_rcu_sched. Make
839+
* sure all registered rcu callbacks have been invoked.
840+
*/
841+
if (IS_ENABLED(CONFIG_SLUB))
842+
rcu_barrier_sched();
843+
844+
/*
845+
* SLAB and SLUB create memcg kmem_caches through workqueue and SLUB
846+
* deactivates the memcg kmem_caches through workqueue. Make sure all
847+
* previous workitems on workqueue are processed.
848+
*/
849+
flush_workqueue(memcg_kmem_cache_wq);
850+
}
826851
#else
827852
static inline int shutdown_memcg_caches(struct kmem_cache *s)
828853
{
829854
return 0;
830855
}
856+
857+
static inline void flush_memcg_workqueue(struct kmem_cache *s)
858+
{
859+
}
831860
#endif /* CONFIG_MEMCG && !CONFIG_SLOB */
832861

833862
void slab_kmem_cache_release(struct kmem_cache *s)
@@ -845,6 +874,8 @@ void kmem_cache_destroy(struct kmem_cache *s)
845874
if (unlikely(!s))
846875
return;
847876

877+
flush_memcg_workqueue(s);
878+
848879
get_online_cpus();
849880
get_online_mems();
850881

0 commit comments

Comments
 (0)