Skip to content

Commit 65cd8ac

Browse files
heicarstIliyan Malchev
authored andcommitted
fs/seq_file: fallback to vmalloc allocation
There are a couple of seq_files which use the single_open() interface. This interface requires that the whole output must fit into a single buffer. E.g. for /proc/stat allocation failures have been observed because an order-4 memory allocation failed due to memory fragmentation. In such situations reading /proc/stat is not possible anymore. Therefore change the seq_file code to fallback to vmalloc allocations which will usually result in a couple of order-0 allocations and hence also work if memory is fragmented. For reference a call trace where reading from /proc/stat failed: sadc: page allocation failure: order:4, mode:0x1040d0 CPU: 1 PID: 192063 Comm: sadc Not tainted 3.10.0-123.el7.s390x #1 [...] Call Trace: show_stack+0x6c/0xe8 warn_alloc_failed+0xd6/0x138 __alloc_pages_nodemask+0x9da/0xb68 __get_free_pages+0x2e/0x58 kmalloc_order_trace+0x44/0xc0 stat_open+0x5a/0xd8 proc_reg_open+0x8a/0x140 do_dentry_open+0x1bc/0x2c8 finish_open+0x46/0x60 do_last+0x382/0x10d0 path_openat+0xc8/0x4f8 do_filp_open+0x46/0xa8 do_sys_open+0x114/0x1f0 sysc_tracego+0x14/0x1a Conflicts: fs/seq_file.c Bug: 17871993 Signed-off-by: Heiko Carstens <[email protected]> Tested-by: David Rientjes <[email protected]> Cc: Ian Kent <[email protected]> Cc: Hendrik Brueckner <[email protected]> Cc: Thorsten Diehl <[email protected]> Cc: Andrea Righi <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Al Viro <[email protected]> Cc: Stefan Bader <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> Git-commit: 058504e Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Change-Id: Iad795a92fee1983c300568429a6283c48625bd9a Signed-off-by: Jeremy Gebben <[email protected]> Signed-off-by: Naveen Ramaraj <[email protected]>
1 parent 1b8711e commit 65cd8ac

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

fs/seq_file.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
#include <linux/fs.h>
99
#include <linux/export.h>
1010
#include <linux/seq_file.h>
11+
#include <linux/vmalloc.h>
1112
#include <linux/slab.h>
13+
#include <linux/cred.h>
14+
#include <linux/mm.h>
1215

1316
#include <asm/uaccess.h>
1417
#include <asm/page.h>
@@ -29,6 +32,16 @@ static void seq_set_overflow(struct seq_file *m)
2932
m->count = m->size;
3033
}
3134

35+
static void *seq_buf_alloc(unsigned long size)
36+
{
37+
void *buf;
38+
39+
buf = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
40+
if (!buf && size > PAGE_SIZE)
41+
buf = vmalloc(size);
42+
return buf;
43+
}
44+
3245
/**
3346
* seq_open - initialize sequential file
3447
* @file: file we initialize
@@ -92,7 +105,7 @@ static int traverse(struct seq_file *m, loff_t offset)
92105
return 0;
93106
}
94107
if (!m->buf) {
95-
m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
108+
m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
96109
if (!m->buf)
97110
return -ENOMEM;
98111
}
@@ -131,9 +144,9 @@ static int traverse(struct seq_file *m, loff_t offset)
131144

132145
Eoverflow:
133146
m->op->stop(m, p);
134-
kfree(m->buf);
147+
kvfree(m->buf);
135148
m->count = 0;
136-
m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
149+
m->buf = seq_buf_alloc(m->size <<= 1);
137150
return !m->buf ? -ENOMEM : -EAGAIN;
138151
}
139152

@@ -188,7 +201,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
188201

189202
/* grab buffer if we didn't have one */
190203
if (!m->buf) {
191-
m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
204+
m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
192205
if (!m->buf)
193206
goto Enomem;
194207
}
@@ -228,9 +241,9 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
228241
if (m->count < m->size)
229242
goto Fill;
230243
m->op->stop(m, p);
231-
kfree(m->buf);
244+
kvfree(m->buf);
232245
m->count = 0;
233-
m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
246+
m->buf = seq_buf_alloc(m->size <<= 1);
234247
if (!m->buf)
235248
goto Enomem;
236249
m->version = 0;
@@ -346,7 +359,7 @@ EXPORT_SYMBOL(seq_lseek);
346359
int seq_release(struct inode *inode, struct file *file)
347360
{
348361
struct seq_file *m = file->private_data;
349-
kfree(m->buf);
362+
kvfree(m->buf);
350363
kfree(m);
351364
return 0;
352365
}
@@ -588,6 +601,24 @@ int single_open(struct file *file, int (*show)(struct seq_file *, void *),
588601
}
589602
EXPORT_SYMBOL(single_open);
590603

604+
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),
605+
void *data, size_t size)
606+
{
607+
char *buf = seq_buf_alloc(size);
608+
int ret;
609+
if (!buf)
610+
return -ENOMEM;
611+
ret = single_open(file, show, data);
612+
if (ret) {
613+
kvfree(buf);
614+
return ret;
615+
}
616+
((struct seq_file *)file->private_data)->buf = buf;
617+
((struct seq_file *)file->private_data)->size = size;
618+
return 0;
619+
}
620+
EXPORT_SYMBOL(single_open_size);
621+
591622
int single_release(struct inode *inode, struct file *file)
592623
{
593624
const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;

0 commit comments

Comments
 (0)