diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:18:27 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:19:04 +0200 |
commit | 6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch) | |
tree | 021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /fs/seq_file.c | |
parent | 682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff) | |
parent | a385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff) |
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree),
to prepare for tooling changes, and also to pick up v3.4 MM
changes that the uprobes code needs to take care of.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs/seq_file.c')
-rw-r--r-- | fs/seq_file.c | 114 |
1 files changed, 92 insertions, 22 deletions
diff --git a/fs/seq_file.c b/fs/seq_file.c index 4023d6be939..0cbd0494b79 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -6,13 +6,29 @@ */ #include <linux/fs.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <asm/uaccess.h> #include <asm/page.h> + +/* + * seq_files have a buffer which can may overflow. When this happens a larger + * buffer is reallocated and all the data will be printed again. + * The overflow state is true when m->count == m->size. + */ +static bool seq_overflow(struct seq_file *m) +{ + return m->count == m->size; +} + +static void seq_set_overflow(struct seq_file *m) +{ + m->count = m->size; +} + /** * seq_open - initialize sequential file * @file: file we initialize @@ -92,7 +108,7 @@ static int traverse(struct seq_file *m, loff_t offset) error = 0; m->count = 0; } - if (m->count == m->size) + if (seq_overflow(m)) goto Eoverflow; if (pos + m->count > offset) { m->from = offset - pos; @@ -140,9 +156,21 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) mutex_lock(&m->lock); + /* + * seq_file->op->..m_start/m_stop/m_next may do special actions + * or optimisations based on the file->f_version, so we want to + * pass the file->f_version to those methods. + * + * seq_file->version is just copy of f_version, and seq_file + * methods can treat it simply as file version. + * It is copied in first and copied out after all operations. + * It is convenient to have it as part of structure to avoid the + * need of passing another argument to all the seq_file methods. + */ + m->version = file->f_version; + /* Don't assume *ppos is where we left it */ if (unlikely(*ppos != m->read_pos)) { - m->read_pos = *ppos; while ((err = traverse(m, *ppos)) == -EAGAIN) ; if (err) { @@ -152,21 +180,11 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) m->index = 0; m->count = 0; goto Done; + } else { + m->read_pos = *ppos; } } - /* - * seq_file->op->..m_start/m_stop/m_next may do special actions - * or optimisations based on the file->f_version, so we want to - * pass the file->f_version to those methods. - * - * seq_file->version is just copy of f_version, and seq_file - * methods can treat it simply as file version. - * It is copied in first and copied out after all operations. - * It is convenient to have it as part of structure to avoid the - * need of passing another argument to all the seq_file methods. - */ - m->version = file->f_version; /* grab buffer if we didn't have one */ if (!m->buf) { m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); @@ -232,7 +250,7 @@ Fill: break; } err = m->op->show(m, p); - if (m->count == m->size || err) { + if (seq_overflow(m) || err) { m->count = offs; if (likely(err <= 0)) break; @@ -359,7 +377,7 @@ int seq_escape(struct seq_file *m, const char *s, const char *esc) *p++ = '0' + (c & 07); continue; } - m->count = m->size; + seq_set_overflow(m); return -1; } m->count = p - m->buf; @@ -381,7 +399,7 @@ int seq_printf(struct seq_file *m, const char *f, ...) return 0; } } - m->count = m->size; + seq_set_overflow(m); return -1; } EXPORT_SYMBOL(seq_printf); @@ -510,7 +528,7 @@ int seq_bitmap(struct seq_file *m, const unsigned long *bits, return 0; } } - m->count = m->size; + seq_set_overflow(m); return -1; } EXPORT_SYMBOL(seq_bitmap); @@ -526,7 +544,7 @@ int seq_bitmap_list(struct seq_file *m, const unsigned long *bits, return 0; } } - m->count = m->size; + seq_set_overflow(m); return -1; } EXPORT_SYMBOL(seq_bitmap_list); @@ -637,11 +655,63 @@ int seq_puts(struct seq_file *m, const char *s) m->count += len; return 0; } - m->count = m->size; + seq_set_overflow(m); return -1; } EXPORT_SYMBOL(seq_puts); +/* + * A helper routine for putting decimal numbers without rich format of printf(). + * only 'unsigned long long' is supported. + * This routine will put one byte delimiter + number into seq_file. + * This routine is very quick when you show lots of numbers. + * In usual cases, it will be better to use seq_printf(). It's easier to read. + */ +int seq_put_decimal_ull(struct seq_file *m, char delimiter, + unsigned long long num) +{ + int len; + + if (m->count + 2 >= m->size) /* we'll write 2 bytes at least */ + goto overflow; + + if (delimiter) + m->buf[m->count++] = delimiter; + + if (num < 10) { + m->buf[m->count++] = num + '0'; + return 0; + } + + len = num_to_str(m->buf + m->count, m->size - m->count, num); + if (!len) + goto overflow; + m->count += len; + return 0; +overflow: + seq_set_overflow(m); + return -1; +} +EXPORT_SYMBOL(seq_put_decimal_ull); + +int seq_put_decimal_ll(struct seq_file *m, char delimiter, + long long num) +{ + if (num < 0) { + if (m->count + 3 >= m->size) { + seq_set_overflow(m); + return -1; + } + if (delimiter) + m->buf[m->count++] = delimiter; + num = -num; + delimiter = '-'; + } + return seq_put_decimal_ull(m, delimiter, num); + +} +EXPORT_SYMBOL(seq_put_decimal_ll); + /** * seq_write - write arbitrary data to buffer * @seq: seq_file identifying the buffer to which data should be written @@ -657,7 +727,7 @@ int seq_write(struct seq_file *seq, const void *data, size_t len) seq->count += len; return 0; } - seq->count = seq->size; + seq_set_overflow(seq); return -1; } EXPORT_SYMBOL(seq_write); |