Files
2018-03-13 20:29:02 +01:00

309 lines
7.3 KiB
C

/*
* kernel/power/tuxonice_alloc.c
*
* Copyright (C) 2008-2010 Nigel Cunningham (nigel at tuxonice net)
*
* This file is released under the GPLv2.
*
*/
#ifdef CONFIG_PM_DEBUG
#include <linux/export.h>
#include <linux/slab.h>
#include "tuxonice_modules.h"
#include "tuxonice_alloc.h"
#include "tuxonice_sysfs.h"
#include "tuxonice.h"
#define TOI_ALLOC_PATHS 40
static DEFINE_MUTEX(toi_alloc_mutex);
static struct toi_module_ops toi_alloc_ops;
static int toi_fail_num;
static atomic_t toi_alloc_count[TOI_ALLOC_PATHS],
toi_free_count[TOI_ALLOC_PATHS],
toi_test_count[TOI_ALLOC_PATHS], toi_fail_count[TOI_ALLOC_PATHS];
static int toi_cur_allocd[TOI_ALLOC_PATHS], toi_max_allocd[TOI_ALLOC_PATHS];
static int cur_allocd, max_allocd;
static char *toi_alloc_desc[TOI_ALLOC_PATHS] = {
"", /* 0 */
"get_io_info_struct",
"extent",
"extent (loading chain)",
"userui channel",
"userui arg", /* 5 */
"attention list metadata",
"extra pagedir memory metadata",
"bdev metadata",
"extra pagedir memory",
"header_locations_read", /* 10 */
"bio queue",
"prepare_readahead",
"i/o buffer",
"writer buffer in bio_init",
"checksum buffer", /* 15 */
"compression buffer",
"filewriter signature op",
"set resume param alloc1",
"set resume param alloc2",
"debugging info buffer", /* 20 */
"check can resume buffer",
"write module config buffer",
"read module config buffer",
"write image header buffer",
"read pageset1 buffer", /* 25 */
"get_have_image_data buffer",
"checksum page",
"worker rw loop",
"get nonconflicting page",
"ps1 load addresses", /* 30 */
"remove swap image",
"swap image exists",
"swap parse sig location",
"sysfs kobj",
"swap mark resume attempted buffer", /* 35 */
"cluster member",
"boot kernel data buffer",
"setting swap signature",
"block i/o bdev struct"
};
#define MIGHT_FAIL(FAIL_NUM, FAIL_VAL) \
do { \
BUG_ON(FAIL_NUM >= TOI_ALLOC_PATHS); \
\
if (FAIL_NUM == toi_fail_num) { \
atomic_inc(&toi_test_count[FAIL_NUM]); \
toi_fail_num = 0; \
return FAIL_VAL; \
} \
} while (0)
static void alloc_update_stats(int fail_num, void *result, int size)
{
if (!result) {
atomic_inc(&toi_fail_count[fail_num]);
return;
}
atomic_inc(&toi_alloc_count[fail_num]);
if (unlikely(test_action_state(TOI_GET_MAX_MEM_ALLOCD))) {
mutex_lock(&toi_alloc_mutex);
toi_cur_allocd[fail_num]++;
cur_allocd += size;
if (unlikely(cur_allocd > max_allocd)) {
int i;
for (i = 0; i < TOI_ALLOC_PATHS; i++)
toi_max_allocd[i] = toi_cur_allocd[i];
max_allocd = cur_allocd;
}
mutex_unlock(&toi_alloc_mutex);
}
}
static void free_update_stats(int fail_num, int size)
{
BUG_ON(fail_num >= TOI_ALLOC_PATHS);
atomic_inc(&toi_free_count[fail_num]);
if (unlikely(atomic_read(&toi_free_count[fail_num]) >
atomic_read(&toi_alloc_count[fail_num])))
dump_stack();
if (unlikely(test_action_state(TOI_GET_MAX_MEM_ALLOCD))) {
mutex_lock(&toi_alloc_mutex);
cur_allocd -= size;
toi_cur_allocd[fail_num]--;
mutex_unlock(&toi_alloc_mutex);
}
}
void *toi_kzalloc(int fail_num, size_t size, gfp_t flags)
{
void *result;
if (toi_alloc_ops.enabled)
MIGHT_FAIL(fail_num, NULL);
result = kzalloc(size, flags);
if (toi_alloc_ops.enabled)
alloc_update_stats(fail_num, result, size);
if (fail_num == toi_trace_allocs)
dump_stack();
return result;
}
EXPORT_SYMBOL_GPL(toi_kzalloc);
unsigned long toi_get_free_pages(int fail_num, gfp_t mask, unsigned int order)
{
unsigned long result;
if (toi_alloc_ops.enabled)
MIGHT_FAIL(fail_num, 0);
result = __get_free_pages(mask, order);
if (toi_alloc_ops.enabled)
alloc_update_stats(fail_num, (void *)result, PAGE_SIZE << order);
if (fail_num == toi_trace_allocs)
dump_stack();
return result;
}
EXPORT_SYMBOL_GPL(toi_get_free_pages);
struct page *toi_alloc_page(int fail_num, gfp_t mask)
{
struct page *result;
if (toi_alloc_ops.enabled)
MIGHT_FAIL(fail_num, NULL);
result = alloc_page(mask);
if (toi_alloc_ops.enabled)
alloc_update_stats(fail_num, (void *)result, PAGE_SIZE);
if (fail_num == toi_trace_allocs)
dump_stack();
return result;
}
EXPORT_SYMBOL_GPL(toi_alloc_page);
unsigned long toi_get_zeroed_page(int fail_num, gfp_t mask)
{
unsigned long result;
if (toi_alloc_ops.enabled)
MIGHT_FAIL(fail_num, 0);
result = get_zeroed_page(mask);
if (toi_alloc_ops.enabled)
alloc_update_stats(fail_num, (void *)result, PAGE_SIZE);
if (fail_num == toi_trace_allocs)
dump_stack();
return result;
}
EXPORT_SYMBOL_GPL(toi_get_zeroed_page);
void toi_kfree(int fail_num, const void *arg, int size)
{
if (arg && toi_alloc_ops.enabled)
free_update_stats(fail_num, size);
if (fail_num == toi_trace_allocs)
dump_stack();
kfree(arg);
}
EXPORT_SYMBOL_GPL(toi_kfree);
void toi_free_page(int fail_num, unsigned long virt)
{
if (virt && toi_alloc_ops.enabled)
free_update_stats(fail_num, PAGE_SIZE);
if (fail_num == toi_trace_allocs)
dump_stack();
free_page(virt);
}
EXPORT_SYMBOL_GPL(toi_free_page);
void toi__free_page(int fail_num, struct page *page)
{
if (page && toi_alloc_ops.enabled)
free_update_stats(fail_num, PAGE_SIZE);
if (fail_num == toi_trace_allocs)
dump_stack();
__free_page(page);
}
EXPORT_SYMBOL_GPL(toi__free_page);
void toi_free_pages(int fail_num, struct page *page, int order)
{
if (page && toi_alloc_ops.enabled)
free_update_stats(fail_num, PAGE_SIZE << order);
if (fail_num == toi_trace_allocs)
dump_stack();
__free_pages(page, order);
}
void toi_alloc_print_debug_stats(void)
{
int i, header_done = 0;
if (!toi_alloc_ops.enabled)
return;
for (i = 0; i < TOI_ALLOC_PATHS; i++)
if (atomic_read(&toi_alloc_count[i]) != atomic_read(&toi_free_count[i])) {
if (!header_done) {
printk(KERN_INFO "Idx Allocs Frees Tests "
" Fails Max Description\n");
header_done = 1;
}
printk(KERN_INFO "%3d %7d %7d %7d %7d %7d %s\n", i,
atomic_read(&toi_alloc_count[i]),
atomic_read(&toi_free_count[i]),
atomic_read(&toi_test_count[i]),
atomic_read(&toi_fail_count[i]),
toi_max_allocd[i], toi_alloc_desc[i]);
}
}
EXPORT_SYMBOL_GPL(toi_alloc_print_debug_stats);
static int toi_alloc_initialise(int starting_cycle)
{
int i;
if (!starting_cycle)
return 0;
if (toi_trace_allocs)
dump_stack();
for (i = 0; i < TOI_ALLOC_PATHS; i++) {
atomic_set(&toi_alloc_count[i], 0);
atomic_set(&toi_free_count[i], 0);
atomic_set(&toi_test_count[i], 0);
atomic_set(&toi_fail_count[i], 0);
toi_cur_allocd[i] = 0;
toi_max_allocd[i] = 0;
};
max_allocd = 0;
cur_allocd = 0;
return 0;
}
static struct toi_sysfs_data sysfs_params[] = {
SYSFS_INT("failure_test", SYSFS_RW, &toi_fail_num, 0, 99, 0, NULL),
SYSFS_INT("trace", SYSFS_RW, &toi_trace_allocs, 0, TOI_ALLOC_PATHS, 0,
NULL),
SYSFS_BIT("find_max_mem_allocated", SYSFS_RW, &toi_bkd.toi_action,
TOI_GET_MAX_MEM_ALLOCD, 0),
SYSFS_INT("enabled", SYSFS_RW, &toi_alloc_ops.enabled, 0, 1, 0,
NULL)
};
static struct toi_module_ops toi_alloc_ops = {
.type = MISC_HIDDEN_MODULE,
.name = "allocation debugging",
.directory = "alloc",
.module = THIS_MODULE,
.early = 1,
.initialise = toi_alloc_initialise,
.sysfs_data = sysfs_params,
.num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
};
int toi_alloc_init(void)
{
int result = toi_register_module(&toi_alloc_ops);
return result;
}
void toi_alloc_exit(void)
{
toi_unregister_module(&toi_alloc_ops);
}
#endif