Skip to content

Commit df3e764

Browse files
committed
module: add debug stats to help identify memory pressure
Loading modules with finit_module() can end up using vmalloc(), vmap() and vmalloc() again, for a total of up to 3 separate allocations in the worst case for a single module. We always kernel_read*() the module, that's a vmalloc(). Then vmap() is used for the module decompression, and if so the last read buffer is freed as we use the now decompressed module buffer to stuff data into our copy module. The last allocation is specific to each architectures but pretty much that's generally a series of vmalloc() calls or a variation of vmalloc to handle ELF sections with special permissions. Evaluation with new stress-ng module support [1] with just 100 ops is proving that you can end up using GiBs of data easily even with all care we have in the kernel and userspace today in trying to not load modules which are already loaded. 100 ops seems to resemble the sort of pressure a system with about 400 CPUs can create on module loading. Although issues relating to duplicate module requests due to each CPU inucurring a new module reuest is silly and some of these are being fixed, we currently lack proper tooling to help diagnose easily what happened, when it happened and who likely is to blame -- userspace or kernel module autoloading. Provide an initial set of stats which use debugfs to let us easily scrape post-boot information about failed loads. This sort of information can be used on production worklaods to try to optimize *avoiding* redundant memory pressure using finit_module(). There's a few examples that can be provided: A 255 vCPU system without the next patch in this series applied: Startup finished in 19.143s (kernel) + 7.078s (userspace) = 26.221s graphical.target reached after 6.988s in userspace And 13.58 GiB of virtual memory space lost due to failed module loading: root@big ~ # cat /sys/kernel/debug/modules/stats Mods ever loaded 67 Mods failed on kread 0 Mods failed on decompress 0 Mods failed on becoming 0 Mods failed on load 1411 Total module size 1146470 Total mod text size 4194304 Failed kread bytes 0 Failed decompress bytes 0 Failed becoming bytes 0 Failed kmod bytes 14588526272 Virtual mem wasted bytes 14588526272 Average mod size 171115 Average mod text size 62602 Average fail load bytes 10339140 Duplicate failed modules: module-name How-many-times Reason kvm_intel 249 Load kvm 249 Load irqbypass 8 Load crct10dif_pclmul 128 Load ghash_clmulni_intel 27 Load sha512_ssse3 50 Load sha512_generic 200 Load aesni_intel 249 Load crypto_simd 41 Load cryptd 131 Load evdev 2 Load serio_raw 1 Load virtio_pci 3 Load nvme 3 Load nvme_core 3 Load virtio_pci_legacy_dev 3 Load virtio_pci_modern_dev 3 Load t10_pi 3 Load virtio 3 Load crc32_pclmul 6 Load crc64_rocksoft 3 Load crc32c_intel 40 Load virtio_ring 3 Load crc64 3 Load The following screen shot, of a simple 8vcpu 8 GiB KVM guest with the next patch in this series applied, shows 226.53 MiB are wasted in virtual memory allocations which due to duplicate module requests during boot. It also shows an average module memory size of 167.10 KiB and an an average module .text + .init.text size of 61.13 KiB. The end shows all modules which were detected as duplicate requests and whether or not they failed early after just the first kernel_read*() call or late after we've already allocated the private space for the module in layout_and_allocate(). A system with module decompression would reveal more wasted virtual memory space. We should put effort now into identifying the source of these duplicate module requests and trimming these down as much possible. Larger systems will obviously show much more wasted virtual memory allocations. root@kmod ~ # cat /sys/kernel/debug/modules/stats Mods ever loaded 67 Mods failed on kread 0 Mods failed on decompress 0 Mods failed on becoming 83 Mods failed on load 16 Total module size 1146470 Total mod text size 4194304 Failed kread bytes 0 Failed decompress bytes 0 Failed becoming bytes 228959096 Failed kmod bytes 8578080 Virtual mem wasted bytes 237537176 Average mod size 171115 Average mod text size 62602 Avg fail becoming bytes 2758544 Average fail load bytes 536130 Duplicate failed modules: module-name How-many-times Reason kvm_intel 7 Becoming kvm 7 Becoming irqbypass 6 Becoming & Load crct10dif_pclmul 7 Becoming & Load ghash_clmulni_intel 7 Becoming & Load sha512_ssse3 6 Becoming & Load sha512_generic 7 Becoming & Load aesni_intel 7 Becoming crypto_simd 7 Becoming & Load cryptd 3 Becoming & Load evdev 1 Becoming serio_raw 1 Becoming nvme 3 Becoming nvme_core 3 Becoming t10_pi 3 Becoming virtio_pci 3 Becoming crc32_pclmul 6 Becoming & Load crc64_rocksoft 3 Becoming crc32c_intel 3 Becoming virtio_pci_modern_dev 2 Becoming virtio_pci_legacy_dev 1 Becoming crc64 2 Becoming virtio 2 Becoming virtio_ring 2 Becoming [0] https://github.com/ColinIanKing/stress-ng.git [1] echo 0 > /proc/sys/vm/oom_dump_tasks ./stress-ng --module 100 --module-name xfs Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
1 parent f71afa6 commit df3e764

File tree

8 files changed

+635
-13
lines changed

8 files changed

+635
-13
lines changed

Documentation/core-api/kernel-api.rst

+20-2
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,30 @@ relay interface
220220
Module Support
221221
==============
222222

223-
Module Loading
224-
--------------
223+
Kernel module auto-loading
224+
--------------------------
225225

226226
.. kernel-doc:: kernel/module/kmod.c
227227
:export:
228228

229+
Module debugging
230+
----------------
231+
232+
.. kernel-doc:: kernel/module/stats.c
233+
:doc: module debugging statistics overview
234+
235+
dup_failed_modules - tracks duplicate failed modules
236+
****************************************************
237+
238+
.. kernel-doc:: kernel/module/stats.c
239+
:doc: dup_failed_modules - tracks duplicate failed modules
240+
241+
module statistics debugfs counters
242+
**********************************
243+
244+
.. kernel-doc:: kernel/module/stats.c
245+
:doc: module statistics debugfs counters
246+
229247
Inter Module support
230248
--------------------
231249

kernel/module/Kconfig

+40-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,45 @@ menuconfig MODULES
2222

2323
if MODULES
2424

25+
config MODULE_DEBUGFS
26+
bool
27+
28+
config MODULE_DEBUG
29+
bool "Module debugging"
30+
depends on DEBUG_FS
31+
help
32+
Allows you to enable / disable features which can help you debug
33+
modules. You don't need these options on production systems.
34+
35+
if MODULE_DEBUG
36+
37+
config MODULE_STATS
38+
bool "Module statistics"
39+
depends on DEBUG_FS
40+
select MODULE_DEBUGFS
41+
help
42+
This option allows you to maintain a record of module statistics.
43+
For example, size of all modules, average size, text size, a list
44+
of failed modules and the size for each of those. For failed
45+
modules we keep track of modules which failed due to either the
46+
existing module taking too long to load or that module was already
47+
loaded.
48+
49+
You should enable this if you are debugging production loads
50+
and want to see if userspace or the kernel is doing stupid things
51+
with loading modules when it shouldn't or if you want to help
52+
optimize userspace / kernel space module autoloading schemes.
53+
You might want to do this because failed modules tend to use
54+
up significant amount of memory, and so you'd be doing everyone a
55+
favor in avoiding these failures proactively.
56+
57+
This functionality is also useful for those experimenting with
58+
module .text ELF section optimization.
59+
60+
If unsure, say N.
61+
62+
endif # MODULE_DEBUG
63+
2564
config MODULE_FORCE_LOAD
2665
bool "Forced module loading"
2766
default n
@@ -51,7 +90,7 @@ config MODULE_FORCE_UNLOAD
5190
config MODULE_UNLOAD_TAINT_TRACKING
5291
bool "Tainted module unload tracking"
5392
depends on MODULE_UNLOAD
54-
default n
93+
select MODULE_DEBUGFS
5594
help
5695
This option allows you to maintain a record of each unloaded
5796
module that tainted the kernel. In addition to displaying a

kernel/module/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ obj-$(CONFIG_SYSFS) += sysfs.o
2121
obj-$(CONFIG_KGDB_KDB) += kdb.o
2222
obj-$(CONFIG_MODVERSIONS) += version.o
2323
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
24+
obj-$(CONFIG_MODULE_STATS) += stats.o

kernel/module/decompress.c

+4
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ int module_decompress(struct load_info *info, const void *buf, size_t size)
297297
ssize_t data_size;
298298
int error;
299299

300+
#if defined(CONFIG_MODULE_STATS)
301+
info->compressed_len = size;
302+
#endif
303+
300304
/*
301305
* Start with number of pages twice as big as needed for
302306
* compressed data.

kernel/module/internal.h

+78
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ struct load_info {
5959
unsigned long mod_kallsyms_init_off;
6060
#endif
6161
#ifdef CONFIG_MODULE_DECOMPRESS
62+
#ifdef CONFIG_MODULE_STATS
63+
unsigned long compressed_len;
64+
#endif
6265
struct page **pages;
6366
unsigned int max_pages;
6467
unsigned int used_pages;
@@ -143,6 +146,81 @@ static inline bool set_livepatch_module(struct module *mod)
143146
#endif
144147
}
145148

149+
/**
150+
* enum fail_dup_mod_reason - state at which a duplicate module was detected
151+
*
152+
* @FAIL_DUP_MOD_BECOMING: the module is read properly, passes all checks but
153+
* we've determined that another module with the same name is already loaded
154+
* or being processed on our &modules list. This happens on early_mod_check()
155+
* right before layout_and_allocate(). The kernel would have already
156+
* vmalloc()'d space for the entire module through finit_module(). If
157+
* decompression was used two vmap() spaces were used. These failures can
158+
* happen when userspace has not seen the module present on the kernel and
159+
* tries to load the module multiple times at same time.
160+
* @FAIL_DUP_MOD_LOAD: the module has been read properly, passes all validation
161+
* checks and the kernel determines that the module was unique and because
162+
* of this allocated yet another private kernel copy of the module space in
163+
* layout_and_allocate() but after this determined in add_unformed_module()
164+
* that another module with the same name is already loaded or being processed.
165+
* These failures should be mitigated as much as possible and are indicative
166+
* of really fast races in loading modules. Without module decompression
167+
* they waste twice as much vmap space. With module decompression three
168+
* times the module's size vmap space is wasted.
169+
*/
170+
enum fail_dup_mod_reason {
171+
FAIL_DUP_MOD_BECOMING = 0,
172+
FAIL_DUP_MOD_LOAD,
173+
};
174+
175+
#ifdef CONFIG_MODULE_DEBUGFS
176+
extern struct dentry *mod_debugfs_root;
177+
#endif
178+
179+
#ifdef CONFIG_MODULE_STATS
180+
181+
#define mod_stat_add_long(count, var) atomic_long_add(count, var)
182+
#define mod_stat_inc(name) atomic_inc(name)
183+
184+
extern atomic_long_t total_mod_size;
185+
extern atomic_long_t total_text_size;
186+
extern atomic_long_t invalid_kread_bytes;
187+
extern atomic_long_t invalid_decompress_bytes;
188+
189+
extern atomic_t modcount;
190+
extern atomic_t failed_kreads;
191+
extern atomic_t failed_decompress;
192+
struct mod_fail_load {
193+
struct list_head list;
194+
char name[MODULE_NAME_LEN];
195+
atomic_long_t count;
196+
unsigned long dup_fail_mask;
197+
};
198+
199+
int try_add_failed_module(const char *name, enum fail_dup_mod_reason reason);
200+
void mod_stat_bump_invalid(struct load_info *info, int flags);
201+
void mod_stat_bump_becoming(struct load_info *info, int flags);
202+
203+
#else
204+
205+
#define mod_stat_add_long(name, var)
206+
#define mod_stat_inc(name)
207+
208+
static inline int try_add_failed_module(const char *name,
209+
enum fail_dup_mod_reason reason)
210+
{
211+
return 0;
212+
}
213+
214+
static inline void mod_stat_bump_invalid(struct load_info *info, int flags)
215+
{
216+
}
217+
218+
static inline void mod_stat_bump_becoming(struct load_info *info, int flags)
219+
{
220+
}
221+
222+
#endif /* CONFIG_MODULE_STATS */
223+
146224
#ifdef CONFIG_MODULE_UNLOAD_TAINT_TRACKING
147225
struct mod_unload_taint {
148226
struct list_head list;

kernel/module/main.c

+60-5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <linux/dynamic_debug.h>
5757
#include <linux/audit.h>
5858
#include <linux/cfi.h>
59+
#include <linux/debugfs.h>
5960
#include <uapi/linux/module.h>
6061
#include "internal.h"
6162

@@ -2500,6 +2501,18 @@ static noinline int do_init_module(struct module *mod)
25002501
{
25012502
int ret = 0;
25022503
struct mod_initfree *freeinit;
2504+
#if defined(CONFIG_MODULE_STATS)
2505+
unsigned int text_size = 0, total_size = 0;
2506+
2507+
for_each_mod_mem_type(type) {
2508+
const struct module_memory *mod_mem = &mod->mem[type];
2509+
if (mod_mem->size) {
2510+
total_size += mod_mem->size;
2511+
if (type == MOD_TEXT || type == MOD_INIT_TEXT)
2512+
text_size += mod_mem->size;
2513+
}
2514+
}
2515+
#endif
25032516

25042517
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
25052518
if (!freeinit) {
@@ -2561,6 +2574,7 @@ static noinline int do_init_module(struct module *mod)
25612574
mod->mem[type].base = NULL;
25622575
mod->mem[type].size = 0;
25632576
}
2577+
25642578
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
25652579
/* .BTF is not SHF_ALLOC and will get removed, so sanitize pointer */
25662580
mod->btf_data = NULL;
@@ -2584,6 +2598,11 @@ static noinline int do_init_module(struct module *mod)
25842598
mutex_unlock(&module_mutex);
25852599
wake_up_all(&module_wq);
25862600

2601+
mod_stat_add_long(text_size, &total_text_size);
2602+
mod_stat_add_long(total_size, &total_mod_size);
2603+
2604+
mod_stat_inc(&modcount);
2605+
25872606
return 0;
25882607

25892608
fail_free_freeinit:
@@ -2599,6 +2618,7 @@ static noinline int do_init_module(struct module *mod)
25992618
ftrace_release_mod(mod);
26002619
free_module(mod);
26012620
wake_up_all(&module_wq);
2621+
26022622
return ret;
26032623
}
26042624

@@ -2632,7 +2652,8 @@ static bool finished_loading(const char *name)
26322652
}
26332653

26342654
/* Must be called with module_mutex held */
2635-
static int module_patient_check_exists(const char *name)
2655+
static int module_patient_check_exists(const char *name,
2656+
enum fail_dup_mod_reason reason)
26362657
{
26372658
struct module *old;
26382659
int err = 0;
@@ -2655,6 +2676,9 @@ static int module_patient_check_exists(const char *name)
26552676
old = find_module_all(name, strlen(name), true);
26562677
}
26572678

2679+
if (try_add_failed_module(name, reason))
2680+
pr_warn("Could not add fail-tracking for module: %s\n", name);
2681+
26582682
/*
26592683
* We are here only when the same module was being loaded. Do
26602684
* not try to load it again right now. It prevents long delays
@@ -2679,7 +2703,7 @@ static int add_unformed_module(struct module *mod)
26792703
mod->state = MODULE_STATE_UNFORMED;
26802704

26812705
mutex_lock(&module_mutex);
2682-
err = module_patient_check_exists(mod->name);
2706+
err = module_patient_check_exists(mod->name, FAIL_DUP_MOD_LOAD);
26832707
if (err)
26842708
goto out;
26852709

@@ -2800,6 +2824,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
28002824
int flags)
28012825
{
28022826
struct module *mod;
2827+
bool module_allocated = false;
28032828
long err = 0;
28042829
char *after_dashes;
28052830

@@ -2839,6 +2864,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
28392864
goto free_copy;
28402865
}
28412866

2867+
module_allocated = true;
2868+
28422869
audit_log_kern_module(mod->name);
28432870

28442871
/* Reserve our place in the list. */
@@ -2983,6 +3010,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
29833010
synchronize_rcu();
29843011
mutex_unlock(&module_mutex);
29853012
free_module:
3013+
mod_stat_bump_invalid(info, flags);
29863014
/* Free lock-classes; relies on the preceding sync_rcu() */
29873015
for_class_mod_mem_type(type, core_data) {
29883016
lockdep_free_key_range(mod->mem[type].base,
@@ -2991,6 +3019,13 @@ static int load_module(struct load_info *info, const char __user *uargs,
29913019

29923020
module_deallocate(mod, info);
29933021
free_copy:
3022+
/*
3023+
* The info->len is always set. We distinguish between
3024+
* failures once the proper module was allocated and
3025+
* before that.
3026+
*/
3027+
if (!module_allocated)
3028+
mod_stat_bump_becoming(info, flags);
29943029
free_copy(info, flags);
29953030
return err;
29963031
}
@@ -3009,8 +3044,11 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
30093044
umod, len, uargs);
30103045

30113046
err = copy_module_from_user(umod, len, &info);
3012-
if (err)
3047+
if (err) {
3048+
mod_stat_inc(&failed_kreads);
3049+
mod_stat_add_long(len, &invalid_kread_bytes);
30133050
return err;
3051+
}
30143052

30153053
return load_module(&info, uargs, 0);
30163054
}
@@ -3035,14 +3073,20 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
30353073

30363074
len = kernel_read_file_from_fd(fd, 0, &buf, INT_MAX, NULL,
30373075
READING_MODULE);
3038-
if (len < 0)
3076+
if (len < 0) {
3077+
mod_stat_inc(&failed_kreads);
3078+
mod_stat_add_long(len, &invalid_kread_bytes);
30393079
return len;
3080+
}
30403081

30413082
if (flags & MODULE_INIT_COMPRESSED_FILE) {
30423083
err = module_decompress(&info, buf, len);
30433084
vfree(buf); /* compressed data is no longer needed */
3044-
if (err)
3085+
if (err) {
3086+
mod_stat_inc(&failed_decompress);
3087+
mod_stat_add_long(len, &invalid_decompress_bytes);
30453088
return err;
3089+
}
30463090
} else {
30473091
info.hdr = buf;
30483092
info.len = len;
@@ -3216,3 +3260,14 @@ void print_modules(void)
32163260
last_unloaded_module.taints);
32173261
pr_cont("\n");
32183262
}
3263+
3264+
#ifdef CONFIG_MODULE_DEBUGFS
3265+
struct dentry *mod_debugfs_root;
3266+
3267+
static int module_debugfs_init(void)
3268+
{
3269+
mod_debugfs_root = debugfs_create_dir("modules", NULL);
3270+
return 0;
3271+
}
3272+
module_init(module_debugfs_init);
3273+
#endif

0 commit comments

Comments
 (0)