From 3a161d99c43ce74c76aecff309be4c3ba455e823 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 25 Jun 2014 15:54:42 -0400 Subject: tracing: Create seq_buf layer in trace_seq Create a seq_buf layer that trace_seq sits on. The seq_buf will not be limited to page size. This will allow other usages of seq_buf instead of a hard set PAGE_SIZE one that trace_seq has. Link: http://lkml.kernel.org/r/20141104160221.864997179@goodmis.org Link: http://lkml.kernel.org/r/20141114011412.170377300@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- kernel/trace/Makefile | 1 + kernel/trace/seq_buf.c | 341 +++++++++++++++++++++++++++++++++++ kernel/trace/trace.c | 39 ++-- kernel/trace/trace_events.c | 6 +- kernel/trace/trace_functions_graph.c | 6 +- kernel/trace/trace_seq.c | 178 +++++++++--------- 6 files changed, 452 insertions(+), 119 deletions(-) create mode 100644 kernel/trace/seq_buf.c (limited to 'kernel') diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 67d6369ddf83..edc98c72a634 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_RING_BUFFER_BENCHMARK) += ring_buffer_benchmark.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_TRACING) += trace_output.o obj-$(CONFIG_TRACING) += trace_seq.o +obj-$(CONFIG_TRACING) += seq_buf.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c new file mode 100644 index 000000000000..e9a7861595d2 --- /dev/null +++ b/kernel/trace/seq_buf.c @@ -0,0 +1,341 @@ +/* + * seq_buf.c + * + * Copyright (C) 2014 Red Hat Inc, Steven Rostedt + * + * The seq_buf is a handy tool that allows you to pass a descriptor around + * to a buffer that other functions can write to. It is similar to the + * seq_file functionality but has some differences. + * + * To use it, the seq_buf must be initialized with seq_buf_init(). + * This will set up the counters within the descriptor. You can call + * seq_buf_init() more than once to reset the seq_buf to start + * from scratch. + */ +#include +#include +#include + +/* How much buffer is written? */ +#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1) + +/** + * seq_buf_print_seq - move the contents of seq_buf into a seq_file + * @m: the seq_file descriptor that is the destination + * @s: the seq_buf descriptor that is the source. + * + * Returns zero on success, non zero otherwise + */ +int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s) +{ + unsigned int len = SEQ_BUF_USED(s); + + return seq_write(m, s->buffer, len); +} + +/** + * seq_buf_vprintf - sequence printing of information. + * @s: seq_buf descriptor + * @fmt: printf format string + * @args: va_list of arguments from a printf() type function + * + * Writes a vnprintf() format into the sequencce buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) +{ + int len; + + WARN_ON(s->size == 0); + + if (s->len < s->size) { + len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); + if (s->len + len < s->size) { + s->len += len; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_printf - sequence printing of information + * @s: seq_buf descriptor + * @fmt: printf format string + * + * Writes a printf() format into the sequence buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_printf(struct seq_buf *s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = seq_buf_vprintf(s, fmt, ap); + va_end(ap); + + return ret; +} + +/** + * seq_buf_bitmask - write a bitmask array in its ASCII representation + * @s: seq_buf descriptor + * @maskp: points to an array of unsigned longs that represent a bitmask + * @nmaskbits: The number of bits that are valid in @maskp + * + * Writes a ASCII representation of a bitmask string into @s. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, + int nmaskbits) +{ + unsigned int len = seq_buf_buffer_left(s); + int ret; + + WARN_ON(s->size == 0); + + /* + * The last byte of the buffer is used to determine if we + * overflowed or not. + */ + if (len > 1) { + ret = bitmap_scnprintf(s->buffer + s->len, len, maskp, nmaskbits); + if (ret < len) { + s->len += ret; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_bprintf - Write the printf string from binary arguments + * @s: seq_buf descriptor + * @fmt: The format string for the @binary arguments + * @binary: The binary arguments for @fmt. + * + * When recording in a fast path, a printf may be recorded with just + * saving the format and the arguments as they were passed to the + * function, instead of wasting cycles converting the arguments into + * ASCII characters. Instead, the arguments are saved in a 32 bit + * word array that is defined by the format string constraints. + * + * This function will take the format and the binary array and finish + * the conversion into the ASCII string within the buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) +{ + unsigned int len = seq_buf_buffer_left(s); + int ret; + + WARN_ON(s->size == 0); + + if (s->len < s->size) { + ret = bstr_printf(s->buffer + s->len, len, fmt, binary); + if (s->len + ret < s->size) { + s->len += ret; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_puts - sequence printing of simple string + * @s: seq_buf descriptor + * @str: simple string to record + * + * Copy a simple string into the sequence buffer. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_puts(struct seq_buf *s, const char *str) +{ + unsigned int len = strlen(str); + + WARN_ON(s->size == 0); + + if (s->len + len < s->size) { + memcpy(s->buffer + s->len, str, len); + s->len += len; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_putc - sequence printing of simple character + * @s: seq_buf descriptor + * @c: simple character to record + * + * Copy a single character into the sequence buffer. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putc(struct seq_buf *s, unsigned char c) +{ + WARN_ON(s->size == 0); + + if (s->len + 1 < s->size) { + s->buffer[s->len++] = c; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_putmem - write raw data into the sequenc buffer + * @s: seq_buf descriptor + * @mem: The raw memory to copy into the buffer + * @len: The length of the raw memory to copy (in bytes) + * + * There may be cases where raw memory needs to be written into the + * buffer and a strcpy() would not work. Using this function allows + * for such cases. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len) +{ + WARN_ON(s->size == 0); + + if (s->len + len < s->size) { + memcpy(s->buffer + s->len, mem, len); + s->len += len; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +#define MAX_MEMHEX_BYTES 8U +#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +/** + * seq_buf_putmem_hex - write raw memory into the buffer in ASCII hex + * @s: seq_buf descriptor + * @mem: The raw memory to write its hex ASCII representation of + * @len: The length of the raw memory to copy (in bytes) + * + * This is similar to seq_buf_putmem() except instead of just copying the + * raw memory into the buffer it writes its ASCII representation of it + * in hex characters. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, + unsigned int len) +{ + unsigned char hex[HEX_CHARS]; + const unsigned char *data = mem; + unsigned int start_len; + int i, j; + + WARN_ON(s->size == 0); + + while (len) { + start_len = min(len, HEX_CHARS - 1); +#ifdef __BIG_ENDIAN + for (i = 0, j = 0; i < start_len; i++) { +#else + for (i = start_len-1, j = 0; i >= 0; i--) { +#endif + hex[j++] = hex_asc_hi(data[i]); + hex[j++] = hex_asc_lo(data[i]); + } + if (WARN_ON_ONCE(j == 0 || j/2 > len)) + break; + + /* j increments twice per loop */ + len -= j / 2; + hex[j++] = ' '; + + seq_buf_putmem(s, hex, j); + if (seq_buf_has_overflowed(s)) + return -1; + } + return 0; +} + +/** + * seq_buf_path - copy a path into the sequence buffer + * @s: seq_buf descriptor + * @path: path to write into the sequence buffer. + * + * Write a path name into the sequence buffer. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_path(struct seq_buf *s, const struct path *path) +{ + unsigned int len = seq_buf_buffer_left(s); + unsigned char *p; + + WARN_ON(s->size == 0); + + p = d_path(path, s->buffer + s->len, len); + if (!IS_ERR(p)) { + p = mangle_path(s->buffer + s->len, p, "\n"); + if (p) { + s->len = p - s->buffer; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_to_user - copy the squence buffer to user space + * @s: seq_buf descriptor + * @ubuf: The userspace memory location to copy to + * @cnt: The amount to copy + * + * Copies the sequence buffer into the userspace memory pointed to + * by @ubuf. It starts from the last read position (@s->readpos) + * and writes up to @cnt characters or till it reaches the end of + * the content in the buffer (@s->len), which ever comes first. + * + * On success, it returns a positive number of the number of bytes + * it copied. + * + * On failure it returns -EBUSY if all of the content in the + * sequence has been already read, which includes nothing in the + * sequence (@s->len == @s->readpos). + * + * Returns -EFAULT if the copy to userspace fails. + */ +int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt) +{ + int len; + int ret; + + if (!cnt) + return 0; + + if (s->len <= s->readpos) + return -EBUSY; + + len = s->len - s->readpos; + if (cnt > len) + cnt = len; + ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + + s->readpos += cnt; + return cnt; +} diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3ce3c4ccfc94..7d7a07e9b9e9 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -939,19 +939,20 @@ out: return ret; } +/* TODO add a seq_buf_to_buffer() */ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) { int len; - if (s->len <= s->readpos) + if (s->seq.len <= s->seq.readpos) return -EBUSY; - len = s->len - s->readpos; + len = s->seq.len - s->seq.readpos; if (cnt > len) cnt = len; - memcpy(buf, s->buffer + s->readpos, cnt); + memcpy(buf, s->buffer + s->seq.readpos, cnt); - s->readpos += cnt; + s->seq.readpos += cnt; return cnt; } @@ -4315,6 +4316,8 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) goto out; } + trace_seq_init(&iter->seq); + /* * We make a copy of the current tracer to avoid concurrent * changes on it while we are reading. @@ -4511,18 +4514,18 @@ waitagain: trace_access_lock(iter->cpu_file); while (trace_find_next_entry_inc(iter) != NULL) { enum print_line_t ret; - int len = iter->seq.len; + int len = iter->seq.seq.len; ret = print_trace_line(iter); if (ret == TRACE_TYPE_PARTIAL_LINE) { /* don't print partial lines */ - iter->seq.len = len; + iter->seq.seq.len = len; break; } if (ret != TRACE_TYPE_NO_CONSUME) trace_consume(iter); - if (iter->seq.len >= cnt) + if (iter->seq.seq.len >= cnt) break; /* @@ -4538,7 +4541,7 @@ waitagain: /* Now copy what we have to the user */ sret = trace_seq_to_user(&iter->seq, ubuf, cnt); - if (iter->seq.readpos >= iter->seq.len) + if (iter->seq.seq.readpos >= iter->seq.seq.len) trace_seq_init(&iter->seq); /* @@ -4576,16 +4579,16 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter) /* Seq buffer is page-sized, exactly what we need. */ for (;;) { - count = iter->seq.len; + count = iter->seq.seq.len; ret = print_trace_line(iter); - count = iter->seq.len - count; + count = iter->seq.seq.len - count; if (rem < count) { rem = 0; - iter->seq.len -= count; + iter->seq.seq.len -= count; break; } if (ret == TRACE_TYPE_PARTIAL_LINE) { - iter->seq.len -= count; + iter->seq.seq.len -= count; break; } @@ -4666,13 +4669,13 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, /* Copy the data into the page, so we can start over. */ ret = trace_seq_to_buffer(&iter->seq, page_address(spd.pages[i]), - iter->seq.len); + iter->seq.seq.len); if (ret < 0) { __free_page(spd.pages[i]); break; } spd.partial[i].offset = 0; - spd.partial[i].len = iter->seq.len; + spd.partial[i].len = iter->seq.seq.len; trace_seq_init(&iter->seq); } @@ -5673,7 +5676,7 @@ tracing_stats_read(struct file *filp, char __user *ubuf, cnt = ring_buffer_read_events_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "read events: %ld\n", cnt); - count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->len); + count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->seq.len); kfree(s); @@ -6636,11 +6639,11 @@ void trace_printk_seq(struct trace_seq *s) { /* Probably should print a warning here. */ - if (s->len >= TRACE_MAX_PRINT) - s->len = TRACE_MAX_PRINT; + if (s->seq.len >= TRACE_MAX_PRINT) + s->seq.len = TRACE_MAX_PRINT; /* should be zero ended, but we are paranoid. */ - s->buffer[s->len] = 0; + s->buffer[s->seq.len] = 0; printk(KERN_TRACE "%s", s->buffer); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index f9d0cbe014b7..4d0067dd7f88 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1044,7 +1044,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, mutex_unlock(&event_mutex); if (file) - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); + r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); kfree(s); @@ -1210,7 +1210,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, trace_seq_init(s); print_subsystem_event_filter(system, s); - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); + r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); kfree(s); @@ -1265,7 +1265,7 @@ show_header(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) trace_seq_init(s); func(s); - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); + r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); kfree(s); diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 100288d10e1f..6d1342ae7a44 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -1154,9 +1154,9 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, } /* Strip ending newline */ - if (s->buffer[s->len - 1] == '\n') { - s->buffer[s->len - 1] = '\0'; - s->len--; + if (s->buffer[s->seq.len - 1] == '\n') { + s->buffer[s->seq.len - 1] = '\0'; + s->seq.len--; } trace_seq_puts(s, " */\n"); diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index fabfa0f190a3..8c0c54fe674b 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -27,10 +27,19 @@ #include /* How much buffer is left on the trace_seq? */ -#define TRACE_SEQ_BUF_LEFT(s) ((PAGE_SIZE - 1) - (s)->len) +#define TRACE_SEQ_BUF_LEFT(s) seq_buf_buffer_left(&(s)->seq) /* How much buffer is written? */ -#define TRACE_SEQ_BUF_USED(s) min((s)->len, (unsigned int)(PAGE_SIZE - 1)) +#define TRACE_SEQ_BUF_USED(s) min((s)->seq.len, (unsigned int)(PAGE_SIZE - 1)) + +/* + * trace_seq should work with being initialized with 0s. + */ +static inline void __trace_seq_init(struct trace_seq *s) +{ + if (unlikely(!s->seq.size)) + trace_seq_init(s); +} /** * trace_print_seq - move the contents of trace_seq into a seq_file @@ -43,10 +52,11 @@ */ int trace_print_seq(struct seq_file *m, struct trace_seq *s) { - unsigned int len = TRACE_SEQ_BUF_USED(s); int ret; - ret = seq_write(m, s->buffer, len); + __trace_seq_init(s); + + ret = seq_buf_print_seq(m, &s->seq); /* * Only reset this buffer if we successfully wrote to the @@ -72,24 +82,23 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s) */ void trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { - unsigned int len = TRACE_SEQ_BUF_LEFT(s); + unsigned int save_len = s->seq.len; va_list ap; - int ret; - if (s->full || !len) + if (s->full) return; + __trace_seq_init(s); + va_start(ap, fmt); - ret = vsnprintf(s->buffer + s->len, len, fmt, ap); + seq_buf_vprintf(&s->seq, fmt, ap); va_end(ap); /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; s->full = 1; - return; } - - s->len += ret; } EXPORT_SYMBOL_GPL(trace_seq_printf); @@ -104,14 +113,19 @@ EXPORT_SYMBOL_GPL(trace_seq_printf); void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, int nmaskbits) { - unsigned int len = TRACE_SEQ_BUF_LEFT(s); - int ret; + unsigned int save_len = s->seq.len; - if (s->full || !len) + if (s->full) return; - ret = bitmap_scnprintf(s->buffer + s->len, len, maskp, nmaskbits); - s->len += ret; + __trace_seq_init(s); + + seq_buf_bitmask(&s->seq, maskp, nmaskbits); + + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; + s->full = 1; + } } EXPORT_SYMBOL_GPL(trace_seq_bitmask); @@ -128,21 +142,20 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask); */ void trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) { - unsigned int len = TRACE_SEQ_BUF_LEFT(s); - int ret; + unsigned int save_len = s->seq.len; - if (s->full || !len) + if (s->full) return; - ret = vsnprintf(s->buffer + s->len, len, fmt, args); + __trace_seq_init(s); + + seq_buf_vprintf(&s->seq, fmt, args); /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; s->full = 1; - return; } - - s->len += ret; } EXPORT_SYMBOL_GPL(trace_seq_vprintf); @@ -163,21 +176,21 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf); */ void trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) { - unsigned int len = TRACE_SEQ_BUF_LEFT(s); - int ret; + unsigned int save_len = s->seq.len; - if (s->full || !len) + if (s->full) return; - ret = bstr_printf(s->buffer + s->len, len, fmt, binary); + __trace_seq_init(s); + + seq_buf_bprintf(&s->seq, fmt, binary); /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; s->full = 1; return; } - - s->len += ret; } EXPORT_SYMBOL_GPL(trace_seq_bprintf); @@ -198,13 +211,14 @@ void trace_seq_puts(struct trace_seq *s, const char *str) if (s->full) return; + __trace_seq_init(s); + if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return; } - memcpy(s->buffer + s->len, str, len); - s->len += len; + seq_buf_putmem(&s->seq, str, len); } EXPORT_SYMBOL_GPL(trace_seq_puts); @@ -223,12 +237,14 @@ void trace_seq_putc(struct trace_seq *s, unsigned char c) if (s->full) return; + __trace_seq_init(s); + if (TRACE_SEQ_BUF_LEFT(s) < 1) { s->full = 1; return; } - s->buffer[s->len++] = c; + seq_buf_putc(&s->seq, c); } EXPORT_SYMBOL_GPL(trace_seq_putc); @@ -247,19 +263,17 @@ void trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len) if (s->full) return; + __trace_seq_init(s); + if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return; } - memcpy(s->buffer + s->len, mem, len); - s->len += len; + seq_buf_putmem(&s->seq, mem, len); } EXPORT_SYMBOL_GPL(trace_seq_putmem); -#define MAX_MEMHEX_BYTES 8U -#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) - /** * trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex * @s: trace sequence descriptor @@ -273,32 +287,26 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem); void trace_seq_putmem_hex(struct trace_seq *s, const void *mem, unsigned int len) { - unsigned char hex[HEX_CHARS]; - const unsigned char *data = mem; - unsigned int start_len; - int i, j; + unsigned int save_len = s->seq.len; if (s->full) return; - while (len) { - start_len = min(len, HEX_CHARS - 1); -#ifdef __BIG_ENDIAN - for (i = 0, j = 0; i < start_len; i++) { -#else - for (i = start_len-1, j = 0; i >= 0; i--) { -#endif - hex[j++] = hex_asc_hi(data[i]); - hex[j++] = hex_asc_lo(data[i]); - } - if (WARN_ON_ONCE(j == 0 || j/2 > len)) - break; - - /* j increments twice per loop */ - len -= j / 2; - hex[j++] = ' '; - - trace_seq_putmem(s, hex, j); + __trace_seq_init(s); + + /* Each byte is represented by two chars */ + if (len * 2 > TRACE_SEQ_BUF_LEFT(s)) { + s->full = 1; + return; + } + + /* The added spaces can still cause an overflow */ + seq_buf_putmem_hex(&s->seq, mem, len); + + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; + s->full = 1; + return; } } EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); @@ -317,30 +325,28 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); */ int trace_seq_path(struct trace_seq *s, const struct path *path) { - unsigned char *p; + unsigned int save_len = s->seq.len; + int ret; if (s->full) return 0; + __trace_seq_init(s); + if (TRACE_SEQ_BUF_LEFT(s) < 1) { s->full = 1; return 0; } - p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); - if (!IS_ERR(p)) { - p = mangle_path(s->buffer + s->len, p, "\n"); - if (p) { - s->len = p - s->buffer; - return 1; - } - } else { - s->buffer[s->len++] = '?'; - return 1; + ret = seq_buf_path(&s->seq, path); + + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; + s->full = 1; + return 0; } - s->full = 1; - return 0; + return ret; } EXPORT_SYMBOL_GPL(trace_seq_path); @@ -366,25 +372,7 @@ EXPORT_SYMBOL_GPL(trace_seq_path); */ int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt) { - int len; - int ret; - - if (!cnt) - return 0; - - if (s->len <= s->readpos) - return -EBUSY; - - len = s->len - s->readpos; - if (cnt > len) - cnt = len; - ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); - if (ret == cnt) - return -EFAULT; - - cnt -= ret; - - s->readpos += cnt; - return cnt; + __trace_seq_init(s); + return seq_buf_to_user(&s->seq, ubuf, cnt); } EXPORT_SYMBOL_GPL(trace_seq_to_user); -- cgit v1.2.3 From dd23180aacf4b27d48f40b27249f1e58c8df03be Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 29 Oct 2014 13:48:37 -0400 Subject: tracing: Convert seq_buf_path() to be like seq_path() Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n". Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic. Link: http://lkml.kernel.org/r/20141104160222.048795666@goodmis.org Link: http://lkml.kernel.org/r/20141114011412.338523371@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/seq_buf.h | 2 +- kernel/trace/seq_buf.c | 28 ++++++++++++++++------------ kernel/trace/trace_seq.c | 5 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) (limited to 'kernel') diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h index 4f7a96a9d71a..38770688a627 100644 --- a/include/linux/seq_buf.h +++ b/include/linux/seq_buf.h @@ -73,7 +73,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c); extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len); extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, unsigned int len); -extern int seq_buf_path(struct seq_buf *s, const struct path *path); +extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc); extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, int nmaskbits); diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index e9a7861595d2..7dac34d1235b 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -272,28 +272,32 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, * seq_buf_path - copy a path into the sequence buffer * @s: seq_buf descriptor * @path: path to write into the sequence buffer. + * @esc: set of characters to escape in the output * * Write a path name into the sequence buffer. * - * Returns zero on success, -1 on overflow + * Returns the number of written bytes on success, -1 on overflow */ -int seq_buf_path(struct seq_buf *s, const struct path *path) +int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) { - unsigned int len = seq_buf_buffer_left(s); - unsigned char *p; + char *buf = s->buffer + s->len; + size_t size = seq_buf_buffer_left(s); + int res = -1; WARN_ON(s->size == 0); - p = d_path(path, s->buffer + s->len, len); - if (!IS_ERR(p)) { - p = mangle_path(s->buffer + s->len, p, "\n"); - if (p) { - s->len = p - s->buffer; - return 0; + if (size) { + char *p = d_path(path, buf, size); + if (!IS_ERR(p)) { + char *end = mangle_path(buf, p, esc); + if (end) + res = end - buf; } } - seq_buf_set_overflow(s); - return -1; + if (res > 0) + s->len += res; + + return res; } /** diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 8c0c54fe674b..74cacc930c24 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -326,7 +326,6 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); int trace_seq_path(struct trace_seq *s, const struct path *path) { unsigned int save_len = s->seq.len; - int ret; if (s->full) return 0; @@ -338,7 +337,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path) return 0; } - ret = seq_buf_path(&s->seq, path); + seq_buf_path(&s->seq, path, "\n"); if (unlikely(seq_buf_has_overflowed(&s->seq))) { s->seq.len = save_len; @@ -346,7 +345,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path) return 0; } - return ret; + return 1; } EXPORT_SYMBOL_GPL(trace_seq_path); -- cgit v1.2.3 From eeab98154dc0b49afd398afdd71c464a8af5911f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 6 Nov 2014 16:38:28 -0500 Subject: seq_buf: Create seq_buf_used() to find out how much was written Add a helper function seq_buf_used() that replaces the SEQ_BUF_USED() private macro to let callers have a method to know how much of the seq_buf was written to. Link: http://lkml.kernel.org/r/20141114011412.170377300@goodmis.org Link: http://lkml.kernel.org/r/20141114011413.321654244@goodmis.org Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/seq_buf.h | 6 ++++++ kernel/trace/seq_buf.c | 5 +---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h index 5d91262433e2..93718e570d4c 100644 --- a/include/linux/seq_buf.h +++ b/include/linux/seq_buf.h @@ -64,6 +64,12 @@ seq_buf_buffer_left(struct seq_buf *s) return (s->size - 1) - s->len; } +/* How much buffer was written? */ +static inline unsigned int seq_buf_used(struct seq_buf *s) +{ + return min(s->len, s->size); +} + extern __printf(2, 3) int seq_buf_printf(struct seq_buf *s, const char *fmt, ...); extern __printf(2, 0) diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index 7dac34d1235b..9ec5305d9da7 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -16,9 +16,6 @@ #include #include -/* How much buffer is written? */ -#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1) - /** * seq_buf_print_seq - move the contents of seq_buf into a seq_file * @m: the seq_file descriptor that is the destination @@ -28,7 +25,7 @@ */ int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s) { - unsigned int len = SEQ_BUF_USED(s); + unsigned int len = seq_buf_used(s); return seq_write(m, s->buffer, len); } -- cgit v1.2.3 From 74f06bb72347302a19aac087314388ebd0e4fee9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 17 Nov 2014 13:12:22 -0500 Subject: tracing: Clean up tracing_fill_pipe_page() The function tracing_fill_pipe_page() logic is a little confusing with the use of count saving the seq.len and reusing it. Instead of subtracting a number that is calculated from the saved value of the seq.len from seq.len, just save the seq.len at the start and if we need to reset it, just assign it again. When the seq_buf overflow is len == size + 1, the current logic will break. Changing it to use a saved length for resetting back to the original value is more robust and will work when we change the way seq_buf sets the overflow. Link: http://lkml.kernel.org/r/20141118161546.GJ23958@pathway.suse.cz Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'kernel') diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7d7a07e9b9e9..0aa75be843a0 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4575,20 +4575,33 @@ static size_t tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter) { size_t count; + int save_len; int ret; /* Seq buffer is page-sized, exactly what we need. */ for (;;) { - count = iter->seq.seq.len; + save_len = iter->seq.seq.len; ret = print_trace_line(iter); - count = iter->seq.seq.len - count; - if (rem < count) { - rem = 0; - iter->seq.seq.len -= count; + + if (trace_seq_has_overflowed(&iter->seq)) { + iter->seq.seq.len = save_len; break; } + + /* + * This should not be hit, because it should only + * be set if the iter->seq overflowed. But check it + * anyway to be safe. + */ if (ret == TRACE_TYPE_PARTIAL_LINE) { - iter->seq.seq.len -= count; + iter->seq.seq.len = save_len; + break; + } + + count = iter->seq.seq.len - save_len; + if (rem < count) { + rem = 0; + iter->seq.seq.len = save_len; break; } -- cgit v1.2.3 From 5ac48378414dccca735897c4d7f4e19987c8977c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 14 Nov 2014 15:49:41 -0500 Subject: tracing: Use trace_seq_used() and seq_buf_used() instead of len As the seq_buf->len will soon be +1 size when there's an overflow, we must use trace_seq_used() or seq_buf_used() methods to get the real length. This will prevent buffer overflow issues if just the len of the seq_buf descriptor is used to copy memory. Link: http://lkml.kernel.org/r/20141114121911.09ba3d38@gandalf.local.home Reported-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/trace_seq.h | 20 +++++++++++++++++++- kernel/trace/seq_buf.c | 2 +- kernel/trace/trace.c | 21 +++++++++++---------- kernel/trace/trace_events.c | 9 ++++++--- kernel/trace/trace_functions_graph.c | 5 ++++- kernel/trace/trace_seq.c | 2 +- 6 files changed, 42 insertions(+), 17 deletions(-) (limited to 'kernel') diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 85d37106be3d..cfaf5a1d4bad 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -23,6 +23,24 @@ trace_seq_init(struct trace_seq *s) s->full = 0; } +/** + * trace_seq_used - amount of actual data written to buffer + * @s: trace sequence descriptor + * + * Returns the amount of data written to the buffer. + * + * IMPORTANT! + * + * Use this instead of @s->seq.len if you need to pass the amount + * of data from the buffer to another buffer (userspace, or what not). + * The @s->seq.len on overflow is bigger than the buffer size and + * using it can cause access to undefined memory. + */ +static inline int trace_seq_used(struct trace_seq *s) +{ + return seq_buf_used(&s->seq); +} + /** * trace_seq_buffer_ptr - return pointer to next location in buffer * @s: trace sequence descriptor @@ -35,7 +53,7 @@ trace_seq_init(struct trace_seq *s) static inline unsigned char * trace_seq_buffer_ptr(struct trace_seq *s) { - return s->buffer + s->seq.len; + return s->buffer + seq_buf_used(&s->seq); } /** diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index 9ec5305d9da7..ce17f65268ed 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -328,7 +328,7 @@ int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt) if (s->len <= s->readpos) return -EBUSY; - len = s->len - s->readpos; + len = seq_buf_used(s) - s->readpos; if (cnt > len) cnt = len; ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0aa75be843a0..9023446b2c2b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -944,10 +944,10 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) { int len; - if (s->seq.len <= s->seq.readpos) + if (trace_seq_used(s) <= s->seq.readpos) return -EBUSY; - len = s->seq.len - s->seq.readpos; + len = trace_seq_used(s) - s->seq.readpos; if (cnt > len) cnt = len; memcpy(buf, s->buffer + s->seq.readpos, cnt); @@ -4514,18 +4514,18 @@ waitagain: trace_access_lock(iter->cpu_file); while (trace_find_next_entry_inc(iter) != NULL) { enum print_line_t ret; - int len = iter->seq.seq.len; + int save_len = iter->seq.seq.len; ret = print_trace_line(iter); if (ret == TRACE_TYPE_PARTIAL_LINE) { /* don't print partial lines */ - iter->seq.seq.len = len; + iter->seq.seq.len = save_len; break; } if (ret != TRACE_TYPE_NO_CONSUME) trace_consume(iter); - if (iter->seq.seq.len >= cnt) + if (trace_seq_used(&iter->seq) >= cnt) break; /* @@ -4541,7 +4541,7 @@ waitagain: /* Now copy what we have to the user */ sret = trace_seq_to_user(&iter->seq, ubuf, cnt); - if (iter->seq.seq.readpos >= iter->seq.seq.len) + if (iter->seq.seq.readpos >= trace_seq_used(&iter->seq)) trace_seq_init(&iter->seq); /* @@ -4598,7 +4598,7 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter) break; } - count = iter->seq.seq.len - save_len; + count = trace_seq_used(&iter->seq) - save_len; if (rem < count) { rem = 0; iter->seq.seq.len = save_len; @@ -4682,13 +4682,13 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, /* Copy the data into the page, so we can start over. */ ret = trace_seq_to_buffer(&iter->seq, page_address(spd.pages[i]), - iter->seq.seq.len); + trace_seq_used(&iter->seq)); if (ret < 0) { __free_page(spd.pages[i]); break; } spd.partial[i].offset = 0; - spd.partial[i].len = iter->seq.seq.len; + spd.partial[i].len = trace_seq_used(&iter->seq); trace_seq_init(&iter->seq); } @@ -5689,7 +5689,8 @@ tracing_stats_read(struct file *filp, char __user *ubuf, cnt = ring_buffer_read_events_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "read events: %ld\n", cnt); - count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->seq.len); + count = simple_read_from_buffer(ubuf, count, ppos, + s->buffer, trace_seq_used(s)); kfree(s); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 4d0067dd7f88..935cbea78532 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1044,7 +1044,8 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, mutex_unlock(&event_mutex); if (file) - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); + r = simple_read_from_buffer(ubuf, cnt, ppos, + s->buffer, trace_seq_used(s)); kfree(s); @@ -1210,7 +1211,8 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, trace_seq_init(s); print_subsystem_event_filter(system, s); - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); + r = simple_read_from_buffer(ubuf, cnt, ppos, + s->buffer, trace_seq_used(s)); kfree(s); @@ -1265,7 +1267,8 @@ show_header(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) trace_seq_init(s); func(s); - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->seq.len); + r = simple_read_from_buffer(ubuf, cnt, ppos, + s->buffer, trace_seq_used(s)); kfree(s); diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 6d1342ae7a44..ec35468349a7 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -1153,6 +1153,9 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, return ret; } + if (trace_seq_has_overflowed(s)) + goto out; + /* Strip ending newline */ if (s->buffer[s->seq.len - 1] == '\n') { s->buffer[s->seq.len - 1] = '\0'; @@ -1160,7 +1163,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, } trace_seq_puts(s, " */\n"); - + out: return trace_handle_return(s); } diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 74cacc930c24..f8b45d8792f9 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -30,7 +30,7 @@ #define TRACE_SEQ_BUF_LEFT(s) seq_buf_buffer_left(&(s)->seq) /* How much buffer is written? */ -#define TRACE_SEQ_BUF_USED(s) min((s)->seq.len, (unsigned int)(PAGE_SIZE - 1)) +#define TRACE_SEQ_BUF_USED(s) seq_buf_used(&(s)->seq) /* * trace_seq should work with being initialized with 0s. -- cgit v1.2.3 From 820b75f63d0152dbb9ff4accf274408592d613f2 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 19 Nov 2014 10:56:41 -0500 Subject: tracing: Add paranoid size check in trace_printk_seq() To be really paranoid about writing out of bound data in trace_printk_seq(), add another check of len compared to size. Link: http://lkml.kernel.org/r/20141119144004.GB2332@dhcp128.suse.cz Suggested-by: Petr Mladek Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'kernel') diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 9023446b2c2b..26facec4625e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6656,6 +6656,14 @@ trace_printk_seq(struct trace_seq *s) if (s->seq.len >= TRACE_MAX_PRINT) s->seq.len = TRACE_MAX_PRINT; + /* + * More paranoid code. Although the buffer size is set to + * PAGE_SIZE, and TRACE_MAX_PRINT is 1000, this is just + * an extra layer of protection. + */ + if (WARN_ON_ONCE(s->seq.len >= s->seq.size)) + s->seq.len = s->seq.size - 1; + /* should be zero ended, but we are paranoid. */ s->buffer[s->seq.len] = 0; -- cgit v1.2.3 From 9b77215382b42ef9c5b34293ad3a95332e5b71ef Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 14 Nov 2014 16:18:14 -0500 Subject: seq_buf: Add seq_buf_can_fit() helper function Add a seq_buf_can_fit() helper function that removes the possible mistakes of comparing the seq_buf length plus added data compared to the size of the buffer. Link: http://lkml.kernel.org/r/20141118164025.GL23958@pathway.suse.cz Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- kernel/trace/seq_buf.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'kernel') diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index ce17f65268ed..6fc9d021cbef 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -16,6 +16,19 @@ #include #include +/** + * seq_buf_can_fit - can the new data fit in the current buffer? + * @s: the seq_buf descriptor + * @len: The length to see if it can fit in the current buffer + * + * Returns true if there's enough unused space in the seq_buf buffer + * to fit the amount of new data according to @len. + */ +static bool seq_buf_can_fit(struct seq_buf *s, size_t len) +{ + return s->len + len < s->size; +} + /** * seq_buf_print_seq - move the contents of seq_buf into a seq_file * @m: the seq_file descriptor that is the destination @@ -48,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) if (s->len < s->size) { len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); - if (s->len + len < s->size) { + if (seq_buf_can_fit(s, len)) { s->len += len; return 0; } @@ -137,7 +150,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) if (s->len < s->size) { ret = bstr_printf(s->buffer + s->len, len, fmt, binary); - if (s->len + ret < s->size) { + if (seq_buf_can_fit(s, ret)) { s->len += ret; return 0; } @@ -161,7 +174,7 @@ int seq_buf_puts(struct seq_buf *s, const char *str) WARN_ON(s->size == 0); - if (s->len + len < s->size) { + if (seq_buf_can_fit(s, len)) { memcpy(s->buffer + s->len, str, len); s->len += len; return 0; @@ -183,7 +196,7 @@ int seq_buf_putc(struct seq_buf *s, unsigned char c) { WARN_ON(s->size == 0); - if (s->len + 1 < s->size) { + if (seq_buf_can_fit(s, 1)) { s->buffer[s->len++] = c; return 0; } @@ -207,7 +220,7 @@ int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len) { WARN_ON(s->size == 0); - if (s->len + len < s->size) { + if (seq_buf_can_fit(s, len)) { memcpy(s->buffer + s->len, mem, len); s->len += len; return 0; -- cgit v1.2.3 From 8cd709ae7658a7fd7f6630699e3229188c2591e4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 29 Oct 2014 15:26:09 -0400 Subject: tracing: Have seq_buf use full buffer Currently seq_buf is full when all but one byte of the buffer is filled. Change it so that the seq_buf is full when all of the buffer is filled. Some of the functions would fill the buffer completely and report everything was fine. This was inconsistent with the max of size - 1. Changing this to be max of size makes all functions consistent. Link: http://lkml.kernel.org/r/20141104160222.502133196@goodmis.org Link: http://lkml.kernel.org/r/20141114011412.811957882@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/seq_buf.h | 6 +++--- kernel/trace/seq_buf.c | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'kernel') diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h index 93718e570d4c..0800a24b4348 100644 --- a/include/linux/seq_buf.h +++ b/include/linux/seq_buf.h @@ -43,13 +43,13 @@ seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size) static inline bool seq_buf_has_overflowed(struct seq_buf *s) { - return s->len == s->size; + return s->len > s->size; } static inline void seq_buf_set_overflow(struct seq_buf *s) { - s->len = s->size; + s->len = s->size + 1; } /* @@ -61,7 +61,7 @@ seq_buf_buffer_left(struct seq_buf *s) if (seq_buf_has_overflowed(s)) return 0; - return (s->size - 1) - s->len; + return s->size - s->len; } /* How much buffer was written? */ diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index 6fc9d021cbef..c53f1d5088e8 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -26,7 +26,7 @@ */ static bool seq_buf_can_fit(struct seq_buf *s, size_t len) { - return s->len + len < s->size; + return s->len + len <= s->size; } /** @@ -110,8 +110,11 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, WARN_ON(s->size == 0); /* - * The last byte of the buffer is used to determine if we - * overflowed or not. + * Note, because bitmap_scnprintf() only returns the number of bytes + * written and not the number that would be written, we use the last + * byte of the buffer to let us know if we overflowed. There's a small + * chance that the bitmap could have fit exactly inside the buffer, but + * it's not that critical if that does happen. */ if (len > 1) { ret = bitmap_scnprintf(s->buffer + s->len, len, maskp, nmaskbits); -- cgit v1.2.3 From 01cb06a4c229908d239149017049fdd1fca1dd51 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 29 Oct 2014 17:30:50 -0400 Subject: tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq. Link: http://lkml.kernel.org/r/20141104160222.644881406@goodmis.org Link: http://lkml.kernel.org/r/20141114011412.977571447@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/seq_buf.h | 41 +++++++++++++++++++++++++++++++++++++++++ kernel/trace/seq_buf.c | 7 +++---- 2 files changed, 44 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h index 0800a24b4348..12c64282aa98 100644 --- a/include/linux/seq_buf.h +++ b/include/linux/seq_buf.h @@ -70,6 +70,47 @@ static inline unsigned int seq_buf_used(struct seq_buf *s) return min(s->len, s->size); } +/** + * seq_buf_get_buf - get buffer to write arbitrary data to + * @s: the seq_buf handle + * @bufp: the beginning of the buffer is stored here + * + * Return the number of bytes available in the buffer, or zero if + * there's no space. + */ +static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp) +{ + WARN_ON(s->len > s->size + 1); + + if (s->len < s->size) { + *bufp = s->buffer + s->len; + return s->size - s->len; + } + + *bufp = NULL; + return 0; +} + +/** + * seq_buf_commit - commit data to the buffer + * @s: the seq_buf handle + * @num: the number of bytes to commit + * + * Commit @num bytes of data written to a buffer previously acquired + * by seq_buf_get. To signal an error condition, or that the data + * didn't fit in the available space, pass a negative @num value. + */ +static inline void seq_buf_commit(struct seq_buf *s, int num) +{ + if (num < 0) { + seq_buf_set_overflow(s); + } else { + /* num must be negative on overflow */ + BUG_ON(s->len + num > s->size); + s->len += num; + } +} + extern __printf(2, 3) int seq_buf_printf(struct seq_buf *s, const char *fmt, ...); extern __printf(2, 0) diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index c53f1d5088e8..086f594ac890 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -293,8 +293,8 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, */ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) { - char *buf = s->buffer + s->len; - size_t size = seq_buf_buffer_left(s); + char *buf; + size_t size = seq_buf_get_buf(s, &buf); int res = -1; WARN_ON(s->size == 0); @@ -307,8 +307,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) res = end - buf; } } - if (res > 0) - s->len += res; + seq_buf_commit(s, res); return res; } -- cgit v1.2.3 From 2448913ed2aa7a7424d9b9ca79861d13c746a3f1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 3 Nov 2014 18:53:50 -0500 Subject: seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case. Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF. Link: http://lkml.kernel.org/r/20141104160222.969013383@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/seq_buf.h | 7 +++++-- kernel/trace/seq_buf.c | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h index 12c64282aa98..9aafe0e24c68 100644 --- a/include/linux/seq_buf.h +++ b/include/linux/seq_buf.h @@ -115,8 +115,6 @@ extern __printf(2, 3) int seq_buf_printf(struct seq_buf *s, const char *fmt, ...); extern __printf(2, 0) int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args); -extern int -seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary); extern int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s); extern int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt); @@ -130,4 +128,9 @@ extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char * extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, int nmaskbits); +#ifdef CONFIG_BINARY_PRINTF +extern int +seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary); +#endif + #endif /* _LINUX_SEQ_BUF_H */ diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c index 086f594ac890..4eedfedb9e31 100644 --- a/kernel/trace/seq_buf.c +++ b/kernel/trace/seq_buf.c @@ -127,6 +127,7 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, return -1; } +#ifdef CONFIG_BINARY_PRINTF /** * seq_buf_bprintf - Write the printf string from binary arguments * @s: seq_buf descriptor @@ -161,6 +162,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) seq_buf_set_overflow(s); return -1; } +#endif /* CONFIG_BINARY_PRINTF */ /** * seq_buf_puts - sequence printing of simple string -- cgit v1.2.3 From 8d58e99af5980d444948720977b0976455885391 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 19 Jun 2014 17:33:30 -0400 Subject: seq_buf: Move the seq_buf code to lib/ The seq_buf functions are rather useful outside of tracing. Instead of having it be dependent on CONFIG_TRACING, move the code into lib/ and allow other users to have access to it even when tracing is not configured. The seq_buf utility is similar to the seq_file utility, but instead of writing sending data back up to userland, it writes it into a buffer defined at seq_buf_init(). This allows us to send a descriptor around that writes printf() formatted strings into it that can be retrieved later. It is currently used by the tracing facility for such things like trace events to convert its binary saved data in the ring buffer into an ASCII human readable context to be displayed in /sys/kernel/debug/trace. It can also be used for doing NMI prints safely from NMI context into the seq_buf and retrieved later and dumped to printk() safely. Doing printk() from an NMI context is dangerous because an NMI can preempt a current printk() and deadlock on it. Link: http://lkml.kernel.org/p/20140619213952.058255809@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- kernel/trace/Makefile | 1 - kernel/trace/seq_buf.c | 359 ------------------------------------------------- lib/Makefile | 2 +- lib/seq_buf.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 360 insertions(+), 361 deletions(-) delete mode 100644 kernel/trace/seq_buf.c create mode 100644 lib/seq_buf.c (limited to 'kernel') diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index edc98c72a634..67d6369ddf83 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_RING_BUFFER_BENCHMARK) += ring_buffer_benchmark.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_TRACING) += trace_output.o obj-$(CONFIG_TRACING) += trace_seq.o -obj-$(CONFIG_TRACING) += seq_buf.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c deleted file mode 100644 index 4eedfedb9e31..000000000000 --- a/kernel/trace/seq_buf.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * seq_buf.c - * - * Copyright (C) 2014 Red Hat Inc, Steven Rostedt - * - * The seq_buf is a handy tool that allows you to pass a descriptor around - * to a buffer that other functions can write to. It is similar to the - * seq_file functionality but has some differences. - * - * To use it, the seq_buf must be initialized with seq_buf_init(). - * This will set up the counters within the descriptor. You can call - * seq_buf_init() more than once to reset the seq_buf to start - * from scratch. - */ -#include -#include -#include - -/** - * seq_buf_can_fit - can the new data fit in the current buffer? - * @s: the seq_buf descriptor - * @len: The length to see if it can fit in the current buffer - * - * Returns true if there's enough unused space in the seq_buf buffer - * to fit the amount of new data according to @len. - */ -static bool seq_buf_can_fit(struct seq_buf *s, size_t len) -{ - return s->len + len <= s->size; -} - -/** - * seq_buf_print_seq - move the contents of seq_buf into a seq_file - * @m: the seq_file descriptor that is the destination - * @s: the seq_buf descriptor that is the source. - * - * Returns zero on success, non zero otherwise - */ -int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s) -{ - unsigned int len = seq_buf_used(s); - - return seq_write(m, s->buffer, len); -} - -/** - * seq_buf_vprintf - sequence printing of information. - * @s: seq_buf descriptor - * @fmt: printf format string - * @args: va_list of arguments from a printf() type function - * - * Writes a vnprintf() format into the sequencce buffer. - * - * Returns zero on success, -1 on overflow. - */ -int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) -{ - int len; - - WARN_ON(s->size == 0); - - if (s->len < s->size) { - len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); - if (seq_buf_can_fit(s, len)) { - s->len += len; - return 0; - } - } - seq_buf_set_overflow(s); - return -1; -} - -/** - * seq_buf_printf - sequence printing of information - * @s: seq_buf descriptor - * @fmt: printf format string - * - * Writes a printf() format into the sequence buffer. - * - * Returns zero on success, -1 on overflow. - */ -int seq_buf_printf(struct seq_buf *s, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = seq_buf_vprintf(s, fmt, ap); - va_end(ap); - - return ret; -} - -/** - * seq_buf_bitmask - write a bitmask array in its ASCII representation - * @s: seq_buf descriptor - * @maskp: points to an array of unsigned longs that represent a bitmask - * @nmaskbits: The number of bits that are valid in @maskp - * - * Writes a ASCII representation of a bitmask string into @s. - * - * Returns zero on success, -1 on overflow. - */ -int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, - int nmaskbits) -{ - unsigned int len = seq_buf_buffer_left(s); - int ret; - - WARN_ON(s->size == 0); - - /* - * Note, because bitmap_scnprintf() only returns the number of bytes - * written and not the number that would be written, we use the last - * byte of the buffer to let us know if we overflowed. There's a small - * chance that the bitmap could have fit exactly inside the buffer, but - * it's not that critical if that does happen. - */ - if (len > 1) { - ret = bitmap_scnprintf(s->buffer + s->len, len, maskp, nmaskbits); - if (ret < len) { - s->len += ret; - return 0; - } - } - seq_buf_set_overflow(s); - return -1; -} - -#ifdef CONFIG_BINARY_PRINTF -/** - * seq_buf_bprintf - Write the printf string from binary arguments - * @s: seq_buf descriptor - * @fmt: The format string for the @binary arguments - * @binary: The binary arguments for @fmt. - * - * When recording in a fast path, a printf may be recorded with just - * saving the format and the arguments as they were passed to the - * function, instead of wasting cycles converting the arguments into - * ASCII characters. Instead, the arguments are saved in a 32 bit - * word array that is defined by the format string constraints. - * - * This function will take the format and the binary array and finish - * the conversion into the ASCII string within the buffer. - * - * Returns zero on success, -1 on overflow. - */ -int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) -{ - unsigned int len = seq_buf_buffer_left(s); - int ret; - - WARN_ON(s->size == 0); - - if (s->len < s->size) { - ret = bstr_printf(s->buffer + s->len, len, fmt, binary); - if (seq_buf_can_fit(s, ret)) { - s->len += ret; - return 0; - } - } - seq_buf_set_overflow(s); - return -1; -} -#endif /* CONFIG_BINARY_PRINTF */ - -/** - * seq_buf_puts - sequence printing of simple string - * @s: seq_buf descriptor - * @str: simple string to record - * - * Copy a simple string into the sequence buffer. - * - * Returns zero on success, -1 on overflow - */ -int seq_buf_puts(struct seq_buf *s, const char *str) -{ - unsigned int len = strlen(str); - - WARN_ON(s->size == 0); - - if (seq_buf_can_fit(s, len)) { - memcpy(s->buffer + s->len, str, len); - s->len += len; - return 0; - } - seq_buf_set_overflow(s); - return -1; -} - -/** - * seq_buf_putc - sequence printing of simple character - * @s: seq_buf descriptor - * @c: simple character to record - * - * Copy a single character into the sequence buffer. - * - * Returns zero on success, -1 on overflow - */ -int seq_buf_putc(struct seq_buf *s, unsigned char c) -{ - WARN_ON(s->size == 0); - - if (seq_buf_can_fit(s, 1)) { - s->buffer[s->len++] = c; - return 0; - } - seq_buf_set_overflow(s); - return -1; -} - -/** - * seq_buf_putmem - write raw data into the sequenc buffer - * @s: seq_buf descriptor - * @mem: The raw memory to copy into the buffer - * @len: The length of the raw memory to copy (in bytes) - * - * There may be cases where raw memory needs to be written into the - * buffer and a strcpy() would not work. Using this function allows - * for such cases. - * - * Returns zero on success, -1 on overflow - */ -int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len) -{ - WARN_ON(s->size == 0); - - if (seq_buf_can_fit(s, len)) { - memcpy(s->buffer + s->len, mem, len); - s->len += len; - return 0; - } - seq_buf_set_overflow(s); - return -1; -} - -#define MAX_MEMHEX_BYTES 8U -#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) - -/** - * seq_buf_putmem_hex - write raw memory into the buffer in ASCII hex - * @s: seq_buf descriptor - * @mem: The raw memory to write its hex ASCII representation of - * @len: The length of the raw memory to copy (in bytes) - * - * This is similar to seq_buf_putmem() except instead of just copying the - * raw memory into the buffer it writes its ASCII representation of it - * in hex characters. - * - * Returns zero on success, -1 on overflow - */ -int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, - unsigned int len) -{ - unsigned char hex[HEX_CHARS]; - const unsigned char *data = mem; - unsigned int start_len; - int i, j; - - WARN_ON(s->size == 0); - - while (len) { - start_len = min(len, HEX_CHARS - 1); -#ifdef __BIG_ENDIAN - for (i = 0, j = 0; i < start_len; i++) { -#else - for (i = start_len-1, j = 0; i >= 0; i--) { -#endif - hex[j++] = hex_asc_hi(data[i]); - hex[j++] = hex_asc_lo(data[i]); - } - if (WARN_ON_ONCE(j == 0 || j/2 > len)) - break; - - /* j increments twice per loop */ - len -= j / 2; - hex[j++] = ' '; - - seq_buf_putmem(s, hex, j); - if (seq_buf_has_overflowed(s)) - return -1; - } - return 0; -} - -/** - * seq_buf_path - copy a path into the sequence buffer - * @s: seq_buf descriptor - * @path: path to write into the sequence buffer. - * @esc: set of characters to escape in the output - * - * Write a path name into the sequence buffer. - * - * Returns the number of written bytes on success, -1 on overflow - */ -int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) -{ - char *buf; - size_t size = seq_buf_get_buf(s, &buf); - int res = -1; - - WARN_ON(s->size == 0); - - if (size) { - char *p = d_path(path, buf, size); - if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); - if (end) - res = end - buf; - } - } - seq_buf_commit(s, res); - - return res; -} - -/** - * seq_buf_to_user - copy the squence buffer to user space - * @s: seq_buf descriptor - * @ubuf: The userspace memory location to copy to - * @cnt: The amount to copy - * - * Copies the sequence buffer into the userspace memory pointed to - * by @ubuf. It starts from the last read position (@s->readpos) - * and writes up to @cnt characters or till it reaches the end of - * the content in the buffer (@s->len), which ever comes first. - * - * On success, it returns a positive number of the number of bytes - * it copied. - * - * On failure it returns -EBUSY if all of the content in the - * sequence has been already read, which includes nothing in the - * sequence (@s->len == @s->readpos). - * - * Returns -EFAULT if the copy to userspace fails. - */ -int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt) -{ - int len; - int ret; - - if (!cnt) - return 0; - - if (s->len <= s->readpos) - return -EBUSY; - - len = seq_buf_used(s) - s->readpos; - if (cnt > len) - cnt = len; - ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); - if (ret == cnt) - return -EFAULT; - - cnt -= ret; - - s->readpos += cnt; - return cnt; -} diff --git a/lib/Makefile b/lib/Makefile index 7512dc978f18..a1aa1e81ed36 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,7 +13,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ proportions.o flex_proportions.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ - earlycpio.o + earlycpio.o seq_buf.o obj-$(CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS) += usercopy.o lib-$(CONFIG_MMU) += ioremap.o diff --git a/lib/seq_buf.c b/lib/seq_buf.c new file mode 100644 index 000000000000..4eedfedb9e31 --- /dev/null +++ b/lib/seq_buf.c @@ -0,0 +1,359 @@ +/* + * seq_buf.c + * + * Copyright (C) 2014 Red Hat Inc, Steven Rostedt + * + * The seq_buf is a handy tool that allows you to pass a descriptor around + * to a buffer that other functions can write to. It is similar to the + * seq_file functionality but has some differences. + * + * To use it, the seq_buf must be initialized with seq_buf_init(). + * This will set up the counters within the descriptor. You can call + * seq_buf_init() more than once to reset the seq_buf to start + * from scratch. + */ +#include +#include +#include + +/** + * seq_buf_can_fit - can the new data fit in the current buffer? + * @s: the seq_buf descriptor + * @len: The length to see if it can fit in the current buffer + * + * Returns true if there's enough unused space in the seq_buf buffer + * to fit the amount of new data according to @len. + */ +static bool seq_buf_can_fit(struct seq_buf *s, size_t len) +{ + return s->len + len <= s->size; +} + +/** + * seq_buf_print_seq - move the contents of seq_buf into a seq_file + * @m: the seq_file descriptor that is the destination + * @s: the seq_buf descriptor that is the source. + * + * Returns zero on success, non zero otherwise + */ +int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s) +{ + unsigned int len = seq_buf_used(s); + + return seq_write(m, s->buffer, len); +} + +/** + * seq_buf_vprintf - sequence printing of information. + * @s: seq_buf descriptor + * @fmt: printf format string + * @args: va_list of arguments from a printf() type function + * + * Writes a vnprintf() format into the sequencce buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) +{ + int len; + + WARN_ON(s->size == 0); + + if (s->len < s->size) { + len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); + if (seq_buf_can_fit(s, len)) { + s->len += len; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_printf - sequence printing of information + * @s: seq_buf descriptor + * @fmt: printf format string + * + * Writes a printf() format into the sequence buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_printf(struct seq_buf *s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = seq_buf_vprintf(s, fmt, ap); + va_end(ap); + + return ret; +} + +/** + * seq_buf_bitmask - write a bitmask array in its ASCII representation + * @s: seq_buf descriptor + * @maskp: points to an array of unsigned longs that represent a bitmask + * @nmaskbits: The number of bits that are valid in @maskp + * + * Writes a ASCII representation of a bitmask string into @s. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp, + int nmaskbits) +{ + unsigned int len = seq_buf_buffer_left(s); + int ret; + + WARN_ON(s->size == 0); + + /* + * Note, because bitmap_scnprintf() only returns the number of bytes + * written and not the number that would be written, we use the last + * byte of the buffer to let us know if we overflowed. There's a small + * chance that the bitmap could have fit exactly inside the buffer, but + * it's not that critical if that does happen. + */ + if (len > 1) { + ret = bitmap_scnprintf(s->buffer + s->len, len, maskp, nmaskbits); + if (ret < len) { + s->len += ret; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} + +#ifdef CONFIG_BINARY_PRINTF +/** + * seq_buf_bprintf - Write the printf string from binary arguments + * @s: seq_buf descriptor + * @fmt: The format string for the @binary arguments + * @binary: The binary arguments for @fmt. + * + * When recording in a fast path, a printf may be recorded with just + * saving the format and the arguments as they were passed to the + * function, instead of wasting cycles converting the arguments into + * ASCII characters. Instead, the arguments are saved in a 32 bit + * word array that is defined by the format string constraints. + * + * This function will take the format and the binary array and finish + * the conversion into the ASCII string within the buffer. + * + * Returns zero on success, -1 on overflow. + */ +int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) +{ + unsigned int len = seq_buf_buffer_left(s); + int ret; + + WARN_ON(s->size == 0); + + if (s->len < s->size) { + ret = bstr_printf(s->buffer + s->len, len, fmt, binary); + if (seq_buf_can_fit(s, ret)) { + s->len += ret; + return 0; + } + } + seq_buf_set_overflow(s); + return -1; +} +#endif /* CONFIG_BINARY_PRINTF */ + +/** + * seq_buf_puts - sequence printing of simple string + * @s: seq_buf descriptor + * @str: simple string to record + * + * Copy a simple string into the sequence buffer. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_puts(struct seq_buf *s, const char *str) +{ + unsigned int len = strlen(str); + + WARN_ON(s->size == 0); + + if (seq_buf_can_fit(s, len)) { + memcpy(s->buffer + s->len, str, len); + s->len += len; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_putc - sequence printing of simple character + * @s: seq_buf descriptor + * @c: simple character to record + * + * Copy a single character into the sequence buffer. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putc(struct seq_buf *s, unsigned char c) +{ + WARN_ON(s->size == 0); + + if (seq_buf_can_fit(s, 1)) { + s->buffer[s->len++] = c; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +/** + * seq_buf_putmem - write raw data into the sequenc buffer + * @s: seq_buf descriptor + * @mem: The raw memory to copy into the buffer + * @len: The length of the raw memory to copy (in bytes) + * + * There may be cases where raw memory needs to be written into the + * buffer and a strcpy() would not work. Using this function allows + * for such cases. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len) +{ + WARN_ON(s->size == 0); + + if (seq_buf_can_fit(s, len)) { + memcpy(s->buffer + s->len, mem, len); + s->len += len; + return 0; + } + seq_buf_set_overflow(s); + return -1; +} + +#define MAX_MEMHEX_BYTES 8U +#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +/** + * seq_buf_putmem_hex - write raw memory into the buffer in ASCII hex + * @s: seq_buf descriptor + * @mem: The raw memory to write its hex ASCII representation of + * @len: The length of the raw memory to copy (in bytes) + * + * This is similar to seq_buf_putmem() except instead of just copying the + * raw memory into the buffer it writes its ASCII representation of it + * in hex characters. + * + * Returns zero on success, -1 on overflow + */ +int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, + unsigned int len) +{ + unsigned char hex[HEX_CHARS]; + const unsigned char *data = mem; + unsigned int start_len; + int i, j; + + WARN_ON(s->size == 0); + + while (len) { + start_len = min(len, HEX_CHARS - 1); +#ifdef __BIG_ENDIAN + for (i = 0, j = 0; i < start_len; i++) { +#else + for (i = start_len-1, j = 0; i >= 0; i--) { +#endif + hex[j++] = hex_asc_hi(data[i]); + hex[j++] = hex_asc_lo(data[i]); + } + if (WARN_ON_ONCE(j == 0 || j/2 > len)) + break; + + /* j increments twice per loop */ + len -= j / 2; + hex[j++] = ' '; + + seq_buf_putmem(s, hex, j); + if (seq_buf_has_overflowed(s)) + return -1; + } + return 0; +} + +/** + * seq_buf_path - copy a path into the sequence buffer + * @s: seq_buf descriptor + * @path: path to write into the sequence buffer. + * @esc: set of characters to escape in the output + * + * Write a path name into the sequence buffer. + * + * Returns the number of written bytes on success, -1 on overflow + */ +int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) +{ + char *buf; + size_t size = seq_buf_get_buf(s, &buf); + int res = -1; + + WARN_ON(s->size == 0); + + if (size) { + char *p = d_path(path, buf, size); + if (!IS_ERR(p)) { + char *end = mangle_path(buf, p, esc); + if (end) + res = end - buf; + } + } + seq_buf_commit(s, res); + + return res; +} + +/** + * seq_buf_to_user - copy the squence buffer to user space + * @s: seq_buf descriptor + * @ubuf: The userspace memory location to copy to + * @cnt: The amount to copy + * + * Copies the sequence buffer into the userspace memory pointed to + * by @ubuf. It starts from the last read position (@s->readpos) + * and writes up to @cnt characters or till it reaches the end of + * the content in the buffer (@s->len), which ever comes first. + * + * On success, it returns a positive number of the number of bytes + * it copied. + * + * On failure it returns -EBUSY if all of the content in the + * sequence has been already read, which includes nothing in the + * sequence (@s->len == @s->readpos). + * + * Returns -EFAULT if the copy to userspace fails. + */ +int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt) +{ + int len; + int ret; + + if (!cnt) + return 0; + + if (s->len <= s->readpos) + return -EBUSY; + + len = seq_buf_used(s) - s->readpos; + if (cnt > len) + cnt = len; + ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + + s->readpos += cnt; + return cnt; +} -- cgit v1.2.3 From afdc34a3d3b823a12a93b822ee1efb566f884032 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 19 Jun 2014 17:33:31 -0400 Subject: printk: Add per_cpu printk func to allow printk to be diverted Being able to divert printk to call another function besides the normal logging is useful for such things like NMI handling. If some functions are to be called from NMI that does printk() it is possible to lock up the box if the nmi handler triggers when another printk is happening. One example of this use is to perform a stack trace on all CPUs via NMI. But if the NMI is to do the printk() it can cause the system to lock up. By allowing the printk to be diverted to another function that can safely record the printk output and then print it when it in a safe context then NMIs will be safe to call these functions like show_regs(). Link: http://lkml.kernel.org/p/20140619213952.209176403@goodmis.org Tested-by: Jiri Kosina Acked-by: Jiri Kosina Acked-by: Paul E. McKenney Reviewed-by: Petr Mladek Signed-off-by: Steven Rostedt --- include/linux/percpu.h | 3 +++ include/linux/printk.h | 2 ++ kernel/printk/printk.c | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 9 deletions(-) (limited to 'kernel') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index a3aa63e47637..ba2e85a0ff5b 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -134,4 +134,7 @@ extern phys_addr_t per_cpu_ptr_to_phys(void *addr); (typeof(type) __percpu *)__alloc_percpu(sizeof(type), \ __alignof__(type)) +/* To avoid include hell, as printk can not declare this, we declare it here */ +DECLARE_PER_CPU(printk_func_t, printk_func); + #endif /* __LINUX_PERCPU_H */ diff --git a/include/linux/printk.h b/include/linux/printk.h index d78125f73ac4..3bbd979d32fb 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -162,6 +162,8 @@ extern int kptr_restrict; extern void wake_up_klogd(void); +typedef int(*printk_func_t)(const char *fmt, va_list args); + void log_buf_kexec_setup(void); void __init setup_log_buf(int early); void dump_stack_set_arch_desc(const char *fmt, ...); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index ced2b84b1cb7..f7b723f98cb9 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1807,6 +1807,30 @@ asmlinkage int printk_emit(int facility, int level, } EXPORT_SYMBOL(printk_emit); +int vprintk_default(const char *fmt, va_list args) +{ + int r; + +#ifdef CONFIG_KGDB_KDB + if (unlikely(kdb_trap_printk)) { + r = vkdb_printf(fmt, args); + return r; + } +#endif + r = vprintk_emit(0, -1, NULL, 0, fmt, args); + + return r; +} +EXPORT_SYMBOL_GPL(vprintk_default); + +/* + * This allows printk to be diverted to another function per cpu. + * This is useful for calling printk functions from within NMI + * without worrying about race conditions that can lock up the + * box. + */ +DEFINE_PER_CPU(printk_func_t, printk_func) = vprintk_default; + /** * printk - print a kernel message * @fmt: format string @@ -1830,19 +1854,15 @@ EXPORT_SYMBOL(printk_emit); */ asmlinkage __visible int printk(const char *fmt, ...) { + printk_func_t vprintk_func; va_list args; int r; -#ifdef CONFIG_KGDB_KDB - if (unlikely(kdb_trap_printk)) { - va_start(args, fmt); - r = vkdb_printf(fmt, args); - va_end(args); - return r; - } -#endif va_start(args, fmt); - r = vprintk_emit(0, -1, NULL, 0, fmt, args); + preempt_disable(); + vprintk_func = this_cpu_read(printk_func); + r = vprintk_func(fmt, args); + preempt_enable(); va_end(args); return r; -- cgit v1.2.3 From 04b74b27c2941e5d62120f6fee3a0a9388a30613 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 21 Nov 2014 09:16:58 -0500 Subject: printk/percpu: Define printk_func when printk is not defined To avoid include hell, the per_cpu variable printk_func was declared in percpu.h. But it is only defined if printk is defined. As users of printk may also use the printk_func variable, it needs to be defined even if CONFIG_PRINTK is not. Also add a printk.h include in percpu.h just to be safe. Link: http://lkml.kernel.org/r/20141121183215.01ba539c@canb.auug.org.au Reported-by: Stephen Rothwell Signed-off-by: Steven Rostedt --- include/linux/percpu.h | 1 + include/linux/printk.h | 4 ++-- kernel/printk/printk.c | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index ba2e85a0ff5b..caebf2a758dc 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/include/linux/printk.h b/include/linux/printk.h index 3bbd979d32fb..c69be9ee8f48 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -124,6 +124,8 @@ static inline __printf(1, 2) __cold void early_printk(const char *s, ...) { } #endif +typedef int(*printk_func_t)(const char *fmt, va_list args); + #ifdef CONFIG_PRINTK asmlinkage __printf(5, 0) int vprintk_emit(int facility, int level, @@ -162,8 +164,6 @@ extern int kptr_restrict; extern void wake_up_klogd(void); -typedef int(*printk_func_t)(const char *fmt, va_list args); - void log_buf_kexec_setup(void); void __init setup_log_buf(int early); void dump_stack_set_arch_desc(const char *fmt, ...); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index f7b723f98cb9..5af2b8bc88f0 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1896,6 +1896,9 @@ static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev, bool syslog, char *buf, size_t size) { return 0; } static size_t cont_print_text(char *text, size_t size) { return 0; } +/* Still needs to be defined for users */ +DEFINE_PER_CPU(printk_func_t, printk_func); + #endif /* CONFIG_PRINTK */ #ifdef CONFIG_EARLY_PRINTK -- cgit v1.2.3