Skip to content

Commit 7e384db

Browse files
urezkitehcaster
authored andcommitted
kunit, slub: Add test_kfree_rcu_wq_destroy use case
Add a test_kfree_rcu_wq_destroy test to verify a kmem_cache_destroy() from a workqueue context. The problem is that, before destroying any cache the kvfree_rcu_barrier() is invoked to guarantee that in-flight freed objects are flushed. The _barrier() function queues and flushes its own internal workers which might conflict with a workqueue type a kmem-cache gets destroyed from. One example is when a WQ_MEM_RECLAIM workqueue is flushing !WQ_MEM_RECLAIM events which leads to a kernel splat. See the check_flush_dependency() in the workqueue.c file. If this test does not emits any kernel warning, it is passed. Reviewed-by: Keith Busch <[email protected]> Co-developed-by: Vlastimil Babka <[email protected]> Signed-off-by: Uladzislau Rezki (Sony) <[email protected]> Signed-off-by: Vlastimil Babka <[email protected]>
1 parent 4b183dd commit 7e384db

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

lib/slub_kunit.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/module.h>
77
#include <linux/kernel.h>
88
#include <linux/rcupdate.h>
9+
#include <linux/delay.h>
910
#include "../mm/slab.h"
1011

1112
static struct kunit_resource resource;
@@ -181,6 +182,63 @@ static void test_kfree_rcu(struct kunit *test)
181182
KUNIT_EXPECT_EQ(test, 0, slab_errors);
182183
}
183184

185+
struct cache_destroy_work {
186+
struct work_struct work;
187+
struct kmem_cache *s;
188+
};
189+
190+
static void cache_destroy_workfn(struct work_struct *w)
191+
{
192+
struct cache_destroy_work *cdw;
193+
194+
cdw = container_of(w, struct cache_destroy_work, work);
195+
kmem_cache_destroy(cdw->s);
196+
}
197+
198+
#define KMEM_CACHE_DESTROY_NR 10
199+
200+
static void test_kfree_rcu_wq_destroy(struct kunit *test)
201+
{
202+
struct test_kfree_rcu_struct *p;
203+
struct cache_destroy_work cdw;
204+
struct workqueue_struct *wq;
205+
struct kmem_cache *s;
206+
unsigned int delay;
207+
int i;
208+
209+
if (IS_BUILTIN(CONFIG_SLUB_KUNIT_TEST))
210+
kunit_skip(test, "can't do kfree_rcu() when test is built-in");
211+
212+
INIT_WORK_ONSTACK(&cdw.work, cache_destroy_workfn);
213+
wq = alloc_workqueue("test_kfree_rcu_destroy_wq",
214+
WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
215+
216+
if (!wq)
217+
kunit_skip(test, "failed to alloc wq");
218+
219+
for (i = 0; i < KMEM_CACHE_DESTROY_NR; i++) {
220+
s = test_kmem_cache_create("TestSlub_kfree_rcu_wq_destroy",
221+
sizeof(struct test_kfree_rcu_struct),
222+
SLAB_NO_MERGE);
223+
224+
if (!s)
225+
kunit_skip(test, "failed to create cache");
226+
227+
delay = get_random_u8();
228+
p = kmem_cache_alloc(s, GFP_KERNEL);
229+
kfree_rcu(p, rcu);
230+
231+
cdw.s = s;
232+
233+
msleep(delay);
234+
queue_work(wq, &cdw.work);
235+
flush_work(&cdw.work);
236+
}
237+
238+
destroy_workqueue(wq);
239+
KUNIT_EXPECT_EQ(test, 0, slab_errors);
240+
}
241+
184242
static void test_leak_destroy(struct kunit *test)
185243
{
186244
struct kmem_cache *s = test_kmem_cache_create("TestSlub_leak_destroy",
@@ -254,6 +312,7 @@ static struct kunit_case test_cases[] = {
254312
KUNIT_CASE(test_clobber_redzone_free),
255313
KUNIT_CASE(test_kmalloc_redzone_access),
256314
KUNIT_CASE(test_kfree_rcu),
315+
KUNIT_CASE(test_kfree_rcu_wq_destroy),
257316
KUNIT_CASE(test_leak_destroy),
258317
KUNIT_CASE(test_krealloc_redzone_zeroing),
259318
{}

0 commit comments

Comments
 (0)