Commit 7c5bd68c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] add /proc/buddyinfo

From David Hansen, Bill Irwin, Martin Bligh.

"It's easier to cat /proc/buddyinfo than to beg users to press
 shift-scrolllock on a machine millions of miles away.  Order 1 and 2
 memory allocations are common.  Memory fragmentation is a problem
 under some workloads, and this is a useful tool for helping diagnose
 these problems."

The following patch exports some information about the buddy allocator.

Each column of numbers represents the number of pages of that order
which are available.  In this case, there are 5 chunks of
2^2*PAGE_SIZE available in ZONE_DMA, and 101 chunks of 2^4*PAGE_SIZE
availble in ZONE_NORMAL, etc...  This information can give you a good
idea about how fragmented memory is and give you a clue as to how big
an area you can safely allocate.

Node 0, zone      DMA      0      4      5      4      4      3 ...
Node 0, zone   Normal      1      0      0      1    101      8 ...
Node 0, zone  HighMem      2      0      0      1      1      0 ...
parent 7030eb35
......@@ -215,6 +215,20 @@ static int meminfo_read_proc(char *page, char **start, off_t off,
#undef K
}
extern struct seq_operations fragmentation_op;
static int fragmentation_open(struct inode *inode, struct file *file)
{
(void)inode;
return seq_open(file, &fragmentation_op);
}
static struct file_operations fragmentation_file_operations = {
open: fragmentation_open,
read: seq_read,
llseek: seq_lseek,
release: seq_release,
};
static int version_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
......@@ -631,6 +645,7 @@ void __init proc_misc_init(void)
create_seq_entry("partitions", 0, &proc_partitions_operations);
create_seq_entry("interrupts", 0, &proc_interrupts_operations);
create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
#ifdef CONFIG_MODULES
create_seq_entry("modules", 0, &proc_modules_operations);
create_seq_entry("ksyms", 0, &proc_ksyms_operations);
......
......@@ -975,3 +975,71 @@ static int __init setup_mem_frac(char *str)
}
__setup("memfrac=", setup_mem_frac);
#ifdef CONFIG_PROC_FS
#include <linux/seq_file.h>
static void *frag_start(struct seq_file *m, loff_t *pos)
{
pg_data_t *pgdat;
loff_t node = *pos;
for (pgdat = pgdat_list; pgdat && node; pgdat = pgdat->pgdat_next)
--node;
return pgdat;
}
static void *frag_next(struct seq_file *m, void *arg, loff_t *pos)
{
pg_data_t *pgdat = (pg_data_t *)arg;
(*pos)++;
return pgdat->pgdat_next;
}
static void frag_stop(struct seq_file *m, void *arg)
{
}
/*
* This walks the freelist for each zone. Whilst this is slow, I'd rather
* be slow here than slow down the fast path by keeping stats - mjbligh
*/
static int frag_show(struct seq_file *m, void *arg)
{
pg_data_t *pgdat = (pg_data_t *)arg;
struct zone *zone;
struct zone *node_zones = pgdat->node_zones;
unsigned long flags;
int order;
for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
if (!zone->size)
continue;
spin_lock_irqsave(&zone->lock, flags);
seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name);
for (order = 0; order < MAX_ORDER; ++order) {
unsigned long nr_bufs = 0;
struct list_head *elem;
list_for_each(elem, &(zone->free_area[order].free_list))
++nr_bufs;
seq_printf(m, "%6lu ", nr_bufs);
}
spin_unlock_irqrestore(&zone->lock, flags);
seq_putc(m, '\n');
}
return 0;
}
struct seq_operations fragmentation_op = {
.start = frag_start,
.next = frag_next,
.stop = frag_stop,
.show = frag_show,
};
#endif /* CONFIG_PROC_FS */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment