Loading MAINTAINERS +1 −0 Original line number Diff line number Diff line Loading @@ -24354,6 +24354,7 @@ F: Documentation/admin-guide/mm/slab.rst F: Documentation/mm/slab.rst F: include/linux/mempool.h F: include/linux/slab.h F: lib/tests/slub_kunit.c F: mm/failslab.c F: mm/mempool.c F: mm/slab.h lib/tests/slub_kunit.c +92 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/rcupdate.h> #include <linux/delay.h> #include <linux/perf_event.h> #include "../mm/slab.h" static struct kunit_resource resource; Loading Loading @@ -291,6 +292,94 @@ static void test_krealloc_redzone_zeroing(struct kunit *test) kmem_cache_destroy(s); } #ifdef CONFIG_PERF_EVENTS #define NR_ITERATIONS 1000 #define NR_OBJECTS 1000 static void *objects[NR_OBJECTS]; struct test_nolock_context { struct kunit *test; int callback_count; int alloc_ok; int alloc_fail; struct perf_event *event; }; static struct perf_event_attr hw_attr = { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES, .size = sizeof(struct perf_event_attr), .pinned = 1, .disabled = 1, .freq = 1, .sample_freq = 100000, }; static void overflow_handler_test_kmalloc_kfree_nolock(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { void *objp; gfp_t gfp; struct test_nolock_context *ctx = event->overflow_handler_context; /* __GFP_ACCOUNT to test kmalloc_nolock() in alloc_slab_obj_exts() */ gfp = (ctx->callback_count % 2) ? 0 : __GFP_ACCOUNT; objp = kmalloc_nolock(64, gfp, NUMA_NO_NODE); if (objp) ctx->alloc_ok++; else ctx->alloc_fail++; kfree_nolock(objp); ctx->callback_count++; } static void test_kmalloc_kfree_nolock(struct kunit *test) { int i, j; struct test_nolock_context ctx = { .test = test }; struct perf_event *event; bool alloc_fail = false; event = perf_event_create_kernel_counter(&hw_attr, -1, current, overflow_handler_test_kmalloc_kfree_nolock, &ctx); if (IS_ERR(event)) kunit_skip(test, "Failed to create perf event"); ctx.event = event; perf_event_enable(ctx.event); for (i = 0; i < NR_ITERATIONS; i++) { for (j = 0; j < NR_OBJECTS; j++) { gfp_t gfp = (i % 2) ? GFP_KERNEL : GFP_KERNEL_ACCOUNT; objects[j] = kmalloc(64, gfp); if (!objects[j]) { j--; while (j >= 0) kfree(objects[j--]); alloc_fail = true; goto cleanup; } } for (j = 0; j < NR_OBJECTS; j++) kfree(objects[j]); } cleanup: perf_event_disable(ctx.event); perf_event_release_kernel(ctx.event); kunit_info(test, "callback_count: %d, alloc_ok: %d, alloc_fail: %d\n", ctx.callback_count, ctx.alloc_ok, ctx.alloc_fail); if (alloc_fail) kunit_skip(test, "Allocation failed"); KUNIT_EXPECT_EQ(test, 0, slab_errors); } #endif static int test_init(struct kunit *test) { slab_errors = 0; Loading @@ -315,6 +404,9 @@ static struct kunit_case test_cases[] = { KUNIT_CASE(test_kfree_rcu_wq_destroy), KUNIT_CASE(test_leak_destroy), KUNIT_CASE(test_krealloc_redzone_zeroing), #ifdef CONFIG_PERF_EVENTS KUNIT_CASE_SLOW(test_kmalloc_kfree_nolock), #endif {} }; Loading mm/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,7 @@ config SLUB config KVFREE_RCU_BATCHED def_bool y depends on !SLUB_TINY && !TINY_RCU depends on !RCU_STRICT_GRACE_PERIOD config SLUB_TINY bool "Configure for minimal memory footprint" Loading mm/slub.c +8 −16 Original line number Diff line number Diff line Loading @@ -8975,7 +8975,7 @@ static ssize_t show_slab_objects(struct kmem_cache *s, return len; } #define to_slab_attr(n) container_of(n, struct slab_attribute, attr) #define to_slab_attr(n) container_of_const(n, struct slab_attribute, attr) #define to_slab(n) container_of(n, struct kmem_cache, kobj) struct slab_attribute { Loading @@ -8985,10 +8985,10 @@ struct slab_attribute { }; #define SLAB_ATTR_RO(_name) \ static struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) static const struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) #define SLAB_ATTR(_name) \ static struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) static const struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) static ssize_t slab_size_show(struct kmem_cache *s, char *buf) { Loading Loading @@ -9382,7 +9382,7 @@ static ssize_t skip_kfence_store(struct kmem_cache *s, SLAB_ATTR(skip_kfence); #endif static struct attribute *slab_attrs[] = { static const struct attribute *const slab_attrs[] = { &slab_size_attr.attr, &object_size_attr.attr, &objs_per_slab_attr.attr, Loading Loading @@ -9459,15 +9459,13 @@ static struct attribute *slab_attrs[] = { NULL }; static const struct attribute_group slab_attr_group = { .attrs = slab_attrs, }; ATTRIBUTE_GROUPS(slab); static ssize_t slab_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct slab_attribute *attribute; const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); Loading @@ -9483,7 +9481,7 @@ static ssize_t slab_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct slab_attribute *attribute; const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); Loading @@ -9508,6 +9506,7 @@ static const struct sysfs_ops slab_sysfs_ops = { static const struct kobj_type slab_ktype = { .sysfs_ops = &slab_sysfs_ops, .release = kmem_cache_release, .default_groups = slab_groups, }; static struct kset *slab_kset; Loading Loading @@ -9595,10 +9594,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (err) goto out; err = sysfs_create_group(&s->kobj, &slab_attr_group); if (err) goto out_del_kobj; if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); Loading @@ -9607,9 +9602,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (!unmergeable) kfree(name); return err; out_del_kobj: kobject_del(&s->kobj); goto out; } void sysfs_slab_unlink(struct kmem_cache *s) Loading Loading
MAINTAINERS +1 −0 Original line number Diff line number Diff line Loading @@ -24354,6 +24354,7 @@ F: Documentation/admin-guide/mm/slab.rst F: Documentation/mm/slab.rst F: include/linux/mempool.h F: include/linux/slab.h F: lib/tests/slub_kunit.c F: mm/failslab.c F: mm/mempool.c F: mm/slab.h
lib/tests/slub_kunit.c +92 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/rcupdate.h> #include <linux/delay.h> #include <linux/perf_event.h> #include "../mm/slab.h" static struct kunit_resource resource; Loading Loading @@ -291,6 +292,94 @@ static void test_krealloc_redzone_zeroing(struct kunit *test) kmem_cache_destroy(s); } #ifdef CONFIG_PERF_EVENTS #define NR_ITERATIONS 1000 #define NR_OBJECTS 1000 static void *objects[NR_OBJECTS]; struct test_nolock_context { struct kunit *test; int callback_count; int alloc_ok; int alloc_fail; struct perf_event *event; }; static struct perf_event_attr hw_attr = { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES, .size = sizeof(struct perf_event_attr), .pinned = 1, .disabled = 1, .freq = 1, .sample_freq = 100000, }; static void overflow_handler_test_kmalloc_kfree_nolock(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { void *objp; gfp_t gfp; struct test_nolock_context *ctx = event->overflow_handler_context; /* __GFP_ACCOUNT to test kmalloc_nolock() in alloc_slab_obj_exts() */ gfp = (ctx->callback_count % 2) ? 0 : __GFP_ACCOUNT; objp = kmalloc_nolock(64, gfp, NUMA_NO_NODE); if (objp) ctx->alloc_ok++; else ctx->alloc_fail++; kfree_nolock(objp); ctx->callback_count++; } static void test_kmalloc_kfree_nolock(struct kunit *test) { int i, j; struct test_nolock_context ctx = { .test = test }; struct perf_event *event; bool alloc_fail = false; event = perf_event_create_kernel_counter(&hw_attr, -1, current, overflow_handler_test_kmalloc_kfree_nolock, &ctx); if (IS_ERR(event)) kunit_skip(test, "Failed to create perf event"); ctx.event = event; perf_event_enable(ctx.event); for (i = 0; i < NR_ITERATIONS; i++) { for (j = 0; j < NR_OBJECTS; j++) { gfp_t gfp = (i % 2) ? GFP_KERNEL : GFP_KERNEL_ACCOUNT; objects[j] = kmalloc(64, gfp); if (!objects[j]) { j--; while (j >= 0) kfree(objects[j--]); alloc_fail = true; goto cleanup; } } for (j = 0; j < NR_OBJECTS; j++) kfree(objects[j]); } cleanup: perf_event_disable(ctx.event); perf_event_release_kernel(ctx.event); kunit_info(test, "callback_count: %d, alloc_ok: %d, alloc_fail: %d\n", ctx.callback_count, ctx.alloc_ok, ctx.alloc_fail); if (alloc_fail) kunit_skip(test, "Allocation failed"); KUNIT_EXPECT_EQ(test, 0, slab_errors); } #endif static int test_init(struct kunit *test) { slab_errors = 0; Loading @@ -315,6 +404,9 @@ static struct kunit_case test_cases[] = { KUNIT_CASE(test_kfree_rcu_wq_destroy), KUNIT_CASE(test_leak_destroy), KUNIT_CASE(test_krealloc_redzone_zeroing), #ifdef CONFIG_PERF_EVENTS KUNIT_CASE_SLOW(test_kmalloc_kfree_nolock), #endif {} }; Loading
mm/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,7 @@ config SLUB config KVFREE_RCU_BATCHED def_bool y depends on !SLUB_TINY && !TINY_RCU depends on !RCU_STRICT_GRACE_PERIOD config SLUB_TINY bool "Configure for minimal memory footprint" Loading
mm/slub.c +8 −16 Original line number Diff line number Diff line Loading @@ -8975,7 +8975,7 @@ static ssize_t show_slab_objects(struct kmem_cache *s, return len; } #define to_slab_attr(n) container_of(n, struct slab_attribute, attr) #define to_slab_attr(n) container_of_const(n, struct slab_attribute, attr) #define to_slab(n) container_of(n, struct kmem_cache, kobj) struct slab_attribute { Loading @@ -8985,10 +8985,10 @@ struct slab_attribute { }; #define SLAB_ATTR_RO(_name) \ static struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) static const struct slab_attribute _name##_attr = __ATTR_RO_MODE(_name, 0400) #define SLAB_ATTR(_name) \ static struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) static const struct slab_attribute _name##_attr = __ATTR_RW_MODE(_name, 0600) static ssize_t slab_size_show(struct kmem_cache *s, char *buf) { Loading Loading @@ -9382,7 +9382,7 @@ static ssize_t skip_kfence_store(struct kmem_cache *s, SLAB_ATTR(skip_kfence); #endif static struct attribute *slab_attrs[] = { static const struct attribute *const slab_attrs[] = { &slab_size_attr.attr, &object_size_attr.attr, &objs_per_slab_attr.attr, Loading Loading @@ -9459,15 +9459,13 @@ static struct attribute *slab_attrs[] = { NULL }; static const struct attribute_group slab_attr_group = { .attrs = slab_attrs, }; ATTRIBUTE_GROUPS(slab); static ssize_t slab_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct slab_attribute *attribute; const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); Loading @@ -9483,7 +9481,7 @@ static ssize_t slab_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct slab_attribute *attribute; const struct slab_attribute *attribute; struct kmem_cache *s; attribute = to_slab_attr(attr); Loading @@ -9508,6 +9506,7 @@ static const struct sysfs_ops slab_sysfs_ops = { static const struct kobj_type slab_ktype = { .sysfs_ops = &slab_sysfs_ops, .release = kmem_cache_release, .default_groups = slab_groups, }; static struct kset *slab_kset; Loading Loading @@ -9595,10 +9594,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (err) goto out; err = sysfs_create_group(&s->kobj, &slab_attr_group); if (err) goto out_del_kobj; if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); Loading @@ -9607,9 +9602,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (!unmergeable) kfree(name); return err; out_del_kobj: kobject_del(&s->kobj); goto out; } void sysfs_slab_unlink(struct kmem_cache *s) Loading