include/linux/gfp.h | 43 +++++++++++++++++++++++++++++++++++++++ include/linux/sysctl.h | 3 ++ kernel/sysctl.c | 10 +++++++++ lib/Kconfig.debug | 9 ++++++++ mm/page_alloc.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+) diff -puN mm/page_alloc.c~__alloc_pages-inject-failure mm/page_alloc.c --- bk-linux/mm/page_alloc.c~__alloc_pages-inject-failure 2004-11-08 15:08:20.957856504 +0300 +++ bk-linux-nikita/mm/page_alloc.c 2004-11-08 15:08:20.983852552 +0300 @@ -581,6 +581,56 @@ buffered_rmqueue(struct zone *zone, int return page; } +#ifdef CONFIG_INJECT_PAGEALLOC_FAILURES + +/* exported by sysctl as /proc/sys/debug/inject-pagealloc-failures */ +struct pagealloc_failure_injector pagealloc_failure_injector = { + /* bootup usually doesn't survive failure injection, disable by + * default. */ + .enabled = 0, + .verbose = 0, + .base_shift = 11, + .high_shift = 2, + .rt_shift = 2, + .interrupt_shift = 2, + .order_shift = 0 +}; + +static int +inject_alloc_failure(unsigned int gfp_mask, unsigned int order, + struct zonelist *zonelist) +{ + static unsigned int bolt = 0; + + unsigned int shift; + int fail; + + if (!pagealloc_failure_injector.enabled) + return 0; + if (gfp_mask & __GFP_DONTLIE) + return 0; + + shift = pagealloc_failure_injector.base_shift; + if (gfp_mask & __GFP_HIGH) + shift += pagealloc_failure_injector.high_shift; + if (rt_task(current)) + shift += pagealloc_failure_injector.rt_shift; + if (in_interrupt() || in_irq()) + shift += pagealloc_failure_injector.interrupt_shift; + shift -= pagealloc_failure_injector.order_shift * order; + fail = ((++bolt & ((1 << shift) - 1)) == 0); + if (unlikely(fail && pagealloc_failure_injector.verbose)) { + printk("%s: failing %i-order allocation for %s [%i, %#x]\n", + __FUNCTION__, order, current->comm, order, gfp_mask); + if (pagealloc_failure_injector.verbose > 1) + dump_stack(); + } + return fail; +} +#else +#define inject_alloc_failure(gfp_mask, order, zonelist) (0) +#endif + /* * This is the 'heart' of the zoned buddy allocator. * @@ -621,6 +671,9 @@ __alloc_pages(unsigned int gfp_mask, uns */ can_try_harder = (unlikely(rt_task(p)) && !in_interrupt()) || !wait; + if (inject_alloc_failure(gfp_mask, order, zonelist)) + return NULL; + zones = zonelist->zones; /* the list of zones suitable for gfp_mask */ if (unlikely(zones[0] == NULL)) { diff -puN include/linux/gfp.h~__alloc_pages-inject-failure include/linux/gfp.h --- bk-linux/include/linux/gfp.h~__alloc_pages-inject-failure 2004-11-08 15:08:20.961855896 +0300 +++ bk-linux-nikita/include/linux/gfp.h 2004-11-08 15:08:20.983852552 +0300 @@ -38,6 +38,13 @@ struct vm_area_struct; #define __GFP_NO_GROW 0x2000 /* Slab internal usage */ #define __GFP_COMP 0x4000 /* Add compound page metadata */ +#ifdef CONFIG_INJECT_PAGEALLOC_FAILURES +#define __GFP_DONTLIE 0x4000 /* Don't inject artificial allocation failures + for this request */ +#else +#define __GFP_DONTLIE 0 +#endif + #define __GFP_BITS_SHIFT 16 /* Room for 16 __GFP_FOO bits */ #define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1) @@ -130,4 +137,40 @@ extern void FASTCALL(free_cold_page(stru void page_alloc_init(void); +#ifdef CONFIG_INJECT_PAGEALLOC_FAILURES + +/* + * Data describing page-alloc failure injector. + */ +struct pagealloc_failure_injector { + /* failure injection enabled/disabled */ + int enabled; + /* reporting injected failures: + * + * 0 don't report + * 1 report + * 2 dump_stack() + */ + int verbose; + /* unless affected by the fields below, failures will be + * injected to each (1 << base_shift)-th allocation. */ + int base_shift; + /* __GFP_HIGH allocations have (1 << high_shift) times less + * chance to fail. */ + int high_shift; + /* allocations done by real-time tasks have (1 << rt_shift) + * times less chance to fail. */ + int rt_shift; + /* allocations done from interrupt context have (1 << + * interrupt_shift) times less chance to fail. */ + int interrupt_shift; + /* high-order allocations has order * (1 << order_shift) times + * greater chance to fail. */ + int order_shift; +}; + +extern struct pagealloc_failure_injector pagealloc_failure_injector; + +#endif + #endif /* __LINUX_GFP_H */ diff -puN arch/i386/Kconfig~__alloc_pages-inject-failure arch/i386/Kconfig diff -puN kernel/sysctl.c~__alloc_pages-inject-failure kernel/sysctl.c --- bk-linux/kernel/sysctl.c~__alloc_pages-inject-failure 2004-11-08 15:08:20.970854528 +0300 +++ bk-linux-nikita/kernel/sysctl.c 2004-11-08 15:08:20.985852248 +0300 @@ -930,6 +930,16 @@ static ctl_table fs_table[] = { }; static ctl_table debug_table[] = { +#ifdef CONFIG_INJECT_PAGEALLOC_FAILURES + { + .ctl_name = DEBUG_INJECT_PAGEALLOC_FAILURES, + .procname = "inject-pagealloc-failures", + .data = &pagealloc_failure_injector, + .maxlen = sizeof(pagealloc_failure_injector), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, +#endif { .ctl_name = 0 } }; diff -puN include/linux/sysctl.h~__alloc_pages-inject-failure include/linux/sysctl.h --- bk-linux/include/linux/sysctl.h~__alloc_pages-inject-failure 2004-11-08 15:08:20.974853920 +0300 +++ bk-linux-nikita/include/linux/sysctl.h 2004-11-08 15:08:20.986852096 +0300 @@ -682,6 +682,9 @@ enum { }; /* CTL_DEBUG names: */ +enum { + DEBUG_INJECT_PAGEALLOC_FAILURES = 1 +}; /* CTL_DEV names: */ enum { diff -puN lib/Kconfig.debug~__alloc_pages-inject-failure lib/Kconfig.debug --- bk-linux/lib/Kconfig.debug~__alloc_pages-inject-failure 2004-11-08 15:08:20.978853312 +0300 +++ bk-linux-nikita/lib/Kconfig.debug 2004-11-08 15:08:20.987851944 +0300 @@ -117,3 +117,12 @@ config FRAME_POINTER If you don't debug the kernel, you can say N, but we may not be able to solve problems without frame pointers. endif + +config INJECT_PAGEALLOC_FAILURES + bool "Inject artificial failures into page allocator" + depends on DEBUG_KERNEL + help + If you say Y here, page allocation requests will be forced to fail with + some probability. This is useful to harden the code. + + _