Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Tested-by: David Rientjes <rientjes@google.com>
Cc: Ian Kent <raven@themaw.net>
Cc: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Cc: Thorsten Diehl <thorsten.diehl@de.ibm.com>
Cc: Andrea Righi <andrea@betterlinux.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Stefan Bader <stefan.bader@canonical.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Heiko Carstens and committed by
Linus Torvalds
058504ed f74373a5

+21 -9
+21 -9
fs/seq_file.c
··· 8 8 #include <linux/fs.h> 9 9 #include <linux/export.h> 10 10 #include <linux/seq_file.h> 11 + #include <linux/vmalloc.h> 11 12 #include <linux/slab.h> 12 13 #include <linux/cred.h> 14 + #include <linux/mm.h> 13 15 14 16 #include <asm/uaccess.h> 15 17 #include <asm/page.h> ··· 30 28 static void seq_set_overflow(struct seq_file *m) 31 29 { 32 30 m->count = m->size; 31 + } 32 + 33 + static void *seq_buf_alloc(unsigned long size) 34 + { 35 + void *buf; 36 + 37 + buf = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); 38 + if (!buf && size > PAGE_SIZE) 39 + buf = vmalloc(size); 40 + return buf; 33 41 } 34 42 35 43 /** ··· 108 96 return 0; 109 97 } 110 98 if (!m->buf) { 111 - m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); 99 + m->buf = seq_buf_alloc(m->size = PAGE_SIZE); 112 100 if (!m->buf) 113 101 return -ENOMEM; 114 102 } ··· 147 135 148 136 Eoverflow: 149 137 m->op->stop(m, p); 150 - kfree(m->buf); 138 + kvfree(m->buf); 151 139 m->count = 0; 152 - m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); 140 + m->buf = seq_buf_alloc(m->size <<= 1); 153 141 return !m->buf ? -ENOMEM : -EAGAIN; 154 142 } 155 143 ··· 204 192 205 193 /* grab buffer if we didn't have one */ 206 194 if (!m->buf) { 207 - m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); 195 + m->buf = seq_buf_alloc(m->size = PAGE_SIZE); 208 196 if (!m->buf) 209 197 goto Enomem; 210 198 } ··· 244 232 if (m->count < m->size) 245 233 goto Fill; 246 234 m->op->stop(m, p); 247 - kfree(m->buf); 235 + kvfree(m->buf); 248 236 m->count = 0; 249 - m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); 237 + m->buf = seq_buf_alloc(m->size <<= 1); 250 238 if (!m->buf) 251 239 goto Enomem; 252 240 m->version = 0; ··· 362 350 int seq_release(struct inode *inode, struct file *file) 363 351 { 364 352 struct seq_file *m = file->private_data; 365 - kfree(m->buf); 353 + kvfree(m->buf); 366 354 kfree(m); 367 355 return 0; 368 356 } ··· 617 605 int single_open_size(struct file *file, int (*show)(struct seq_file *, void *), 618 606 void *data, size_t size) 619 607 { 620 - char *buf = kmalloc(size, GFP_KERNEL); 608 + char *buf = seq_buf_alloc(size); 621 609 int ret; 622 610 if (!buf) 623 611 return -ENOMEM; 624 612 ret = single_open(file, show, data); 625 613 if (ret) { 626 - kfree(buf); 614 + kvfree(buf); 627 615 return ret; 628 616 } 629 617 ((struct seq_file *)file->private_data)->buf = buf;