2.6 /proc/interrupts fails on systems with many CPUs

From: Erik Jacobson
Date: Tue Nov 11 2003 - 12:22:00 EST


Howdy.

On systems with lots of processors (512 for example), catting /proc/interrupts
fails with a "not enough memory" error.

This was observed in 2.6.0-test8

I tracked this down to this in proc_misc.c:

static int interrupts_open(struct inode *inode, struct file *file)
{
unsigned size = 4096 * (1 + num_online_cpus() / 8);
char *buf = kmalloc(size, GFP_KERNEL);

The kmalloc fails here.

I'm looking for suggestions on how to fix this. I came up with one fix
that seems to work OK for ia64. I have attached it to this message.
I'm looking for advice on what should be proposed for the real fix.

Thanks!

--
Erik Jacobson - Linux System Software - Silicon Graphics - Eagan, Minnesota
===========================================================================
linux/fs/proc/proc_misc.c
===========================================================================

--- /usr/tmp/TmpDir.18901-0/linux/fs/proc/proc_misc.c_1.52 Tue Nov 11 09:55:19 2003
+++ linux/fs/proc/proc_misc.c Tue Nov 11 09:43:32 2003
@@ -445,7 +445,7 @@
/* don't ask for more than the kmalloc() max size, currently 128 KB */
if (size > 128 * 1024)
size = 128 * 1024;
- buf = kmalloc(size, GFP_KERNEL);
+ buf = __vmalloc(size, GFP_KERNEL, PAGE_KERNEL);
if (!buf)
return -ENOMEM;

@@ -476,20 +476,21 @@
extern int show_interrupts(struct seq_file *p, void *v);
static int interrupts_open(struct inode *inode, struct file *file)
{
- unsigned size = 4096 * (1 + num_online_cpus() / 8);
- char *buf = kmalloc(size, GFP_KERNEL);
+ unsigned size = 4096 * (1 + num_online_cpus() / 8);
+ char *buf = __vmalloc(size, GFP_KERNEL, PAGE_KERNEL);
struct seq_file *m;
int res;

- if (!buf)
+ if (!buf)
return -ENOMEM;
+
res = single_open(file, show_interrupts, NULL);
if (!res) {
m = file->private_data;
m->buf = buf;
m->size = size;
} else
- kfree(buf);
+ vfree(buf);
return res;
}
static struct file_operations proc_interrupts_operations = {

===========================================================================
linux/fs/seq_file.c
===========================================================================

--- /usr/tmp/TmpDir.18901-0/linux/fs/seq_file.c_1.8 Tue Nov 11 09:55:19 2003
+++ linux/fs/seq_file.c Tue Nov 11 09:18:06 2003
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>

#include <asm/uaccess.h>
#include <asm/page.h>
@@ -29,6 +30,7 @@
int seq_open(struct file *file, struct seq_operations *op)
{
struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
+
if (!p)
return -ENOMEM;
memset(p, 0, sizeof(*p));
@@ -61,7 +63,7 @@
down(&m->sem);
/* grab buffer if we didn't have one */
if (!m->buf) {
- m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
+ m->buf = __vmalloc(m->size = PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
if (!m->buf)
goto Enomem;
}
@@ -94,8 +96,8 @@
if (m->count < m->size)
goto Fill;
m->op->stop(m, p);
- kfree(m->buf);
- m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
+ vfree(m->buf);
+ m->buf = __vmalloc(m->size <<= 1, GFP_KERNEL, PAGE_KERNEL);
if (!m->buf)
goto Enomem;
m->count = 0;
@@ -160,7 +162,7 @@
if (!offset)
return 0;
if (!m->buf) {
- m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
+ m->buf = __vmalloc(m->size = PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
if (!m->buf)
return -ENOMEM;
}
@@ -192,8 +194,8 @@

Eoverflow:
m->op->stop(m, p);
- kfree(m->buf);
- m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
+ vfree(m->buf);
+ m->buf = __vmalloc(m->size <<= 1, GFP_KERNEL, PAGE_KERNEL);
return !m->buf ? -ENOMEM : -EAGAIN;
}

@@ -246,7 +248,7 @@
int seq_release(struct inode *inode, struct file *file)
{
struct seq_file *m = (struct seq_file *)file->private_data;
- kfree(m->buf);
+ vfree(m->buf);
kfree(m);
return 0;
}