2014-11-04 16:02:36

by Steven Rostedt

[permalink] [raw]
Subject: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

From: "Steven Rostedt (Red Hat)" <[email protected]>

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.

Signed-off-by: Steven Rostedt <[email protected]>
---
include/linux/seq_buf.h | 72 ++++++++
include/linux/trace_seq.h | 10 +-
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 | 184 +++++++++----------
8 files changed, 534 insertions(+), 125 deletions(-)
create mode 100644 include/linux/seq_buf.h
create mode 100644 kernel/trace/seq_buf.c

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
new file mode 100644
index 000000000000..97872154d51c
--- /dev/null
+++ b/include/linux/seq_buf.h
@@ -0,0 +1,72 @@
+#ifndef _LINUX_SEQ_BUF_H
+#define _LINUX_SEQ_BUF_H
+
+#include <linux/fs.h>
+
+#include <asm/page.h>
+
+/*
+ * Trace sequences are used to allow a function to call several other functions
+ * to create a string of data to use.
+ */
+
+/**
+ * seq_buf - seq buffer structure
+ * @buffer: pointer to the buffer
+ * @size: size of the buffer
+ * @len: the amount of data inside the buffer
+ * @readpos: The next position to read in the buffer.
+ * @overflow: Set if more bytes should have been written to buffer
+ */
+struct seq_buf {
+ unsigned char *buffer;
+ unsigned int size;
+ unsigned int len;
+ unsigned int readpos;
+};
+
+static inline void
+seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
+{
+ s->buffer = buf;
+ s->size = size;
+ s->len = 0;
+ s->readpos = 0;
+}
+
+/*
+ * seq_buf have a buffer that might overflow. When this happens
+ * the len and size are set to be equal.
+ */
+static inline bool
+seq_buf_has_overflowed(struct seq_buf *s)
+{
+ return s->len == s->size;
+}
+
+static inline void
+seq_buf_set_overflow(struct seq_buf *s)
+{
+ s->len = s->size;
+}
+
+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);
+extern int seq_buf_puts(struct seq_buf *s, const char *str);
+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_bitmask(struct seq_buf *s, const unsigned long *maskp,
+ int nmaskbits);
+
+#endif /* _LINUX_SEQ_BUF_H */
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
index ea6c9dea79e3..27c98fd76503 100644
--- a/include/linux/trace_seq.h
+++ b/include/linux/trace_seq.h
@@ -1,7 +1,7 @@
#ifndef _LINUX_TRACE_SEQ_H
#define _LINUX_TRACE_SEQ_H

-#include <linux/fs.h>
+#include <linux/seq_buf.h>

#include <asm/page.h>

@@ -12,16 +12,14 @@

struct trace_seq {
unsigned char buffer[PAGE_SIZE];
- unsigned int len;
- unsigned int readpos;
+ struct seq_buf seq;
int full;
};

static inline void
trace_seq_init(struct trace_seq *s)
{
- s->len = 0;
- s->readpos = 0;
+ seq_buf_init(&s->seq, s->buffer, PAGE_SIZE);
s->full = 0;
}

@@ -37,7 +35,7 @@ trace_seq_init(struct trace_seq *s)
static inline unsigned char *
trace_seq_buffer_ptr(struct trace_seq *s)
{
- return s->buffer + s->len;
+ return s->buffer + s->seq.len;
}

/*
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..2bf582753902
--- /dev/null
+++ b/kernel/trace/seq_buf.c
@@ -0,0 +1,341 @@
+/*
+ * seq_buf.c
+ *
+ * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>
+ *
+ * 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 <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/seq_buf.h>
+
+/* How much buffer is left on the seq_buf? */
+#define SEQ_BUF_LEFT(s) (((s)->size - 1) - (s)->len)
+
+/* 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 the number of bytes written.
+ */
+int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
+ int nmaskbits)
+{
+ unsigned int len = SEQ_BUF_LEFT(s);
+ int ret;
+
+ WARN_ON(s->size == 0);
+
+ if (s->len < s->size) {
+ ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
+ if (s->len + ret < s->size) {
+ 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_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_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
+ * sequenc (@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 8a528392b1f4..0c46168e938e 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -938,19 +938,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;
}

@@ -4313,6 +4314,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.
@@ -4509,18 +4512,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;

/*
@@ -4536,7 +4539,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);

/*
@@ -4574,16 +4577,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;
}

@@ -4664,13 +4667,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);
}
@@ -5671,7 +5674,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);

@@ -6634,11 +6637,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 0cc51edde3a8..33525bf6cbf5 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 f0a0c982cde3..488273458bfd 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -1291,9 +1291,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--;
}

ret = trace_seq_puts(s, " */\n");
diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
index 1f24ed99dca2..960ccfb2f50c 100644
--- a/kernel/trace/trace_seq.c
+++ b/kernel/trace/trace_seq.c
@@ -27,10 +27,19 @@
#include <linux/trace_seq.h>

/* 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) ((PAGE_SIZE - 1) - (s)->seq.len)

/* 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
@@ -77,25 +87,25 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
*/
int 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 0;

+ __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 0;
}

- s->len += ret;
-
return 1;
}
EXPORT_SYMBOL_GPL(trace_seq_printf);
@@ -116,14 +126,20 @@ EXPORT_SYMBOL_GPL(trace_seq_printf);
int 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 0;

- ret = bitmap_scnprintf(s->buffer, 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;
+ return 0;
+ }

return 1;
}
@@ -144,23 +160,24 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
*/
int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
{
- unsigned int len = TRACE_SEQ_BUF_LEFT(s);
+ unsigned int save_len = s->seq.len;
int ret;

- if (s->full || !len)
+ if (s->full)
return 0;

- ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+ __trace_seq_init(s);
+
+ ret = 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 0;
}

- s->len += ret;
-
- return len;
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_vprintf);

@@ -183,23 +200,24 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
*/
int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
{
- unsigned int len = TRACE_SEQ_BUF_LEFT(s);
+ unsigned int save_len = s->seq.len;
int ret;

- if (s->full || !len)
+ if (s->full)
return 0;

- ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
+ __trace_seq_init(s);
+
+ ret = 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 0;
}

- s->len += ret;
-
- return len;
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_bprintf);

@@ -222,13 +240,14 @@ int trace_seq_puts(struct trace_seq *s, const char *str)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}

- memcpy(s->buffer + s->len, str, len);
- s->len += len;
+ seq_buf_putmem(&s->seq, str, len);

return len;
}
@@ -251,12 +270,14 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}

- s->buffer[s->len++] = c;
+ seq_buf_putc(&s->seq, c);

return 1;
}
@@ -279,21 +300,19 @@ int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}

- memcpy(s->buffer + s->len, mem, len);
- s->len += len;
+ seq_buf_putmem(&s->seq, mem, len);

return 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
@@ -309,35 +328,30 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
int 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;
- int cnt = 0;
+ unsigned int save_len = s->seq.len;
+ int ret;

if (s->full)
return 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++] = ' ';
-
- cnt += 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 0;
+ }
+
+ /* The added spaces can still cause an overflow */
+ ret = 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 0;
}
- return cnt;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);

@@ -355,30 +369,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);

@@ -404,25 +416,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);
--
2.1.1


2014-11-05 14:22:41

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Tue 2014-11-04 10:52:40, Steven Rostedt wrote:
> From: "Steven Rostedt (Red Hat)" <[email protected]>
>
> 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.
>
> Signed-off-by: Steven Rostedt <[email protected]>
> ---
> include/linux/seq_buf.h | 72 ++++++++
> include/linux/trace_seq.h | 10 +-
> 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 | 184 +++++++++----------
> 8 files changed, 534 insertions(+), 125 deletions(-)
> create mode 100644 include/linux/seq_buf.h
> create mode 100644 kernel/trace/seq_buf.c
>
> diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
> new file mode 100644
> index 000000000000..97872154d51c
> --- /dev/null
> +++ b/include/linux/seq_buf.h
> @@ -0,0 +1,72 @@
> +#ifndef _LINUX_SEQ_BUF_H
> +#define _LINUX_SEQ_BUF_H
> +
> +#include <linux/fs.h>
> +
> +#include <asm/page.h>
> +
> +/*
> + * Trace sequences are used to allow a function to call several other functions
> + * to create a string of data to use.
> + */
> +
> +/**
> + * seq_buf - seq buffer structure
> + * @buffer: pointer to the buffer
> + * @size: size of the buffer
> + * @len: the amount of data inside the buffer
> + * @readpos: The next position to read in the buffer.
> + * @overflow: Set if more bytes should have been written to buffer

There is no @overflow flag in the end.

Also I would add an explanation of the overall logic. If I get it
correctly from the code, it is:

/*
* The last byte of the buffer is used to detect an overflow in some
* operations. Therefore, the buffer offers (@size - 1) bytes for valid
* data.
*/

> + */
> +struct seq_buf {
> + unsigned char *buffer;
> + unsigned int size;
> + unsigned int len;
> + unsigned int readpos;
> +};
> +

[...]

> diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c
> new file mode 100644
> index 000000000000..2bf582753902
> --- /dev/null
> +++ b/kernel/trace/seq_buf.c
> @@ -0,0 +1,341 @@
> +/*
> + * seq_buf.c
> + *
> + * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>
> + *
> + * 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.
> + *

^ trailing whitespace :-)

> + */
> +#include <linux/uaccess.h>
> +#include <linux/seq_file.h>
> +#include <linux/seq_buf.h>
> +
> +/* How much buffer is left on the seq_buf? */

I would write the following to explain the -1:

/* How much buffer is left for valid data */

> +#define SEQ_BUF_LEFT(s) (((s)->size - 1) - (s)->len)

Hmm, it might overflow when the buffer has overflown (s->len == s->size)
or when the buffer is not initialized (s->size == 0). Note that the
result should be unsigned int.

I can't find any cool solution as a macro at the moment. It might be
better to define an inline function for this.


> +/* How much buffer is written? */

I would write the following to explain the -1:

/* How much buffer is written with valid data */

> +#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)

[...]

> +
> +/**
> + * 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 the number of bytes written.

The text should be:

* 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_LEFT(s);
>
> + int ret;
> +
> + WARN_ON(s->size == 0);
> +
> + if (s->len < s->size) {
> + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);

It writes to the beginning of the buffer. It should be

ret = bitmap_scnprintf(s->buffer + s->len, len,
maskp, nmaskbits);


> + if (s->len + ret < s->size) {

This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
and it currently returns the remaining size - len - 1.

You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).


> + 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_LEFT(s);
> + int ret;
> +
> + WARN_ON(s->size == 0);
> +
> + if (s->len < s->size) {

Always true. It is the same problem as in seq_buf_bitmask().

> + 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;
> + }

We might want to copy the maximum possible number of bytes.
It will then behave the same as the other functions.

> + 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;
> + }

Same as seq_buf_puts(). Do we want to always copy the possible number of
bytes?

> + 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))

We might want to use the seq_buf_putmem() return value here.

> + return -1;
> + }
> + return 0;
> +}
> +

[...]

> +
> +/**
> + * 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
> + * sequenc (@s->len == @s->readpos).

sequenc -> sequence

> + *
> + * 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_seq.c b/kernel/trace/trace_seq.c
> index 1f24ed99dca2..960ccfb2f50c 100644
> --- a/kernel/trace/trace_seq.c
> +++ b/kernel/trace/trace_seq.c
> @@ -27,10 +27,19 @@
> #include <linux/trace_seq.h>
>
> /* 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) ((PAGE_SIZE - 1) - (s)->seq.len)

This might overflow when s->len == PAGE_SIZE. I think that it
newer happenes because we always check s->full before. The question
is if we really want to depend on this.

> /* 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
> @@ -77,25 +87,25 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
> */
> int 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 0;
>
> + __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))) {

We might check the return value from seq_buf_vprintf() here.

We could do similar thing also in the other functions. We even
already store the ret value in some of them.

Best Regards,
Petr

2014-11-05 14:26:35

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Tue 2014-11-04 10:52:40, Steven Rostedt wrote:
> From: "Steven Rostedt (Red Hat)" <[email protected]>
>
> 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.
>
> Signed-off-by: Steven Rostedt <[email protected]>
> ---
> include/linux/seq_buf.h | 72 ++++++++
> include/linux/trace_seq.h | 10 +-
> 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 | 184 +++++++++----------
> 8 files changed, 534 insertions(+), 125 deletions(-)
> create mode 100644 include/linux/seq_buf.h
> create mode 100644 kernel/trace/seq_buf.c
>
> diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
> new file mode 100644
> index 000000000000..97872154d51c
> --- /dev/null
> +++ b/include/linux/seq_buf.h
> @@ -0,0 +1,72 @@
> +#ifndef _LINUX_SEQ_BUF_H
> +#define _LINUX_SEQ_BUF_H
> +
> +#include <linux/fs.h>
> +
> +#include <asm/page.h>

One more small thing. We do not need this include because
seq_buf does not use the PAGE_SIZE.

Best Regards,
Petr

2014-11-05 18:42:43

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed, 5 Nov 2014 15:22:22 +0100
Petr Mladek <[email protected]> wrote:

> On Tue 2014-11-04 10:52:40, Steven Rostedt wrote:
> > From: "Steven Rostedt (Red Hat)" <[email protected]>
> >
> > 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.
> >
> > Signed-off-by: Steven Rostedt <[email protected]>
> > ---
> > include/linux/seq_buf.h | 72 ++++++++
> > include/linux/trace_seq.h | 10 +-
> > 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 | 184 +++++++++----------
> > 8 files changed, 534 insertions(+), 125 deletions(-)
> > create mode 100644 include/linux/seq_buf.h
> > create mode 100644 kernel/trace/seq_buf.c
> >
> > diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
> > new file mode 100644
> > index 000000000000..97872154d51c
> > --- /dev/null
> > +++ b/include/linux/seq_buf.h
> > @@ -0,0 +1,72 @@
> > +#ifndef _LINUX_SEQ_BUF_H
> > +#define _LINUX_SEQ_BUF_H
> > +
> > +#include <linux/fs.h>
> > +
> > +#include <asm/page.h>
> > +
> > +/*
> > + * Trace sequences are used to allow a function to call several other functions
> > + * to create a string of data to use.
> > + */
> > +
> > +/**
> > + * seq_buf - seq buffer structure
> > + * @buffer: pointer to the buffer
> > + * @size: size of the buffer
> > + * @len: the amount of data inside the buffer
> > + * @readpos: The next position to read in the buffer.
> > + * @overflow: Set if more bytes should have been written to buffer
>
> There is no @overflow flag in the end.
>

Crap, that was left over from a previos version.

> Also I would add an explanation of the overall logic. If I get it
> correctly from the code, it is:
>
> /*
> * The last byte of the buffer is used to detect an overflow in some
> * operations. Therefore, the buffer offers (@size - 1) bytes for valid
> * data.

Well, this will change in the future. And it is commented with the
seq_buf_has_overflowed() function. I don't want to comment about it
with the structure.


> */
>
> > + */
> > +struct seq_buf {
> > + unsigned char *buffer;
> > + unsigned int size;
> > + unsigned int len;
> > + unsigned int readpos;
> > +};
> > +
>
> [...]
>
> > diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c
> > new file mode 100644
> > index 000000000000..2bf582753902
> > --- /dev/null
> > +++ b/kernel/trace/seq_buf.c
> > @@ -0,0 +1,341 @@
> > +/*
> > + * seq_buf.c
> > + *
> > + * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>
> > + *
> > + * 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.
> > + *
>
> ^ trailing whitespace :-)

I had that fixed in my latest version. But I'm nuking the extra line
anyway.

>
> > + */
> > +#include <linux/uaccess.h>
> > +#include <linux/seq_file.h>
> > +#include <linux/seq_buf.h>
> > +
> > +/* How much buffer is left on the seq_buf? */
>
> I would write the following to explain the -1:

Later patches gets rid of the -1. It's -1 because seq_file is -1 as
well.

>
> /* How much buffer is left for valid data */
>
> > +#define SEQ_BUF_LEFT(s) (((s)->size - 1) - (s)->len)
>
> Hmm, it might overflow when the buffer has overflown (s->len == s->size)
> or when the buffer is not initialized (s->size == 0). Note that the
> result should be unsigned int.

The two places that use it is "unsigned int" so that should not be a
problem.

>
> I can't find any cool solution as a macro at the moment. It might be
> better to define an inline function for this.
>
>
> > +/* How much buffer is written? */
>
> I would write the following to explain the -1:
>
> /* How much buffer is written with valid data */
>

Again, this gets changed in later patches. I don't want to expand too
much time commenting what I want to remove ;-)

I did it this way to match seq_file, and I have patches to change
seq_file to have overflow be len > size too.


> > +#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)
>
> [...]
>
> > +
> > +/**
> > + * 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 the number of bytes written.
>
> The text should be:
>
> * Returns zero on success, -1 on overflow.

Darn, I thought I caught all the updates of the return value.

Will fix.

>
> > + */
> > +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > + int nmaskbits)
> > +{
> > + unsigned int len = SEQ_BUF_LEFT(s);
> >
> > + int ret;
> > +
> > + WARN_ON(s->size == 0);
> > +
> > + if (s->len < s->size) {
> > + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
>
> It writes to the beginning of the buffer. It should be
>
> ret = bitmap_scnprintf(s->buffer + s->len, len,
> maskp, nmaskbits);
>

Yep thanks. Luckily its only user didn't care.

Will fix.

>
> > + if (s->len + ret < s->size) {
>
> This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
> and it currently returns the remaining size - len - 1.

Hmm, that's correct, as bitmap_scnprintf() returns the amount written
instead of the amount that it would write like snprintf() would.


>
> You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).

That wont help when we make overflow len > size.

Probably should see if ret == the amount of bits required for the
bitmask.

>
>
> > + 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_LEFT(s);
> > + int ret;
> > +
> > + WARN_ON(s->size == 0);
> > +
> > + if (s->len < s->size) {
>
> Always true. It is the same problem as in seq_buf_bitmask().

No this is not the same. This will not be true if we have overflowed.

>
> > + ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
> > + if (s->len + ret < s->size) {

This is the "if" you are thinking of with seq_buf_bitmask(). But unlike
bitmap_scnprintf(), bstr_printf() returns the number of characters that
would be written. Not the number that were. This condition does make
sense.


> > + 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;
> > + }
>
> We might want to copy the maximum possible number of bytes.
> It will then behave the same as the other functions.

I'm converting all this to be like seq_file in the other patches.


>
> > + 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;
> > + }
>
> Same as seq_buf_puts(). Do we want to always copy the possible number of
> bytes?

Again, this will be made to be like seq_file.

>
> > + 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))
>
> We might want to use the seq_buf_putmem() return value here.

We could do that.

>
> > + return -1;
> > + }
> > + return 0;
> > +}
> > +
>
> [...]
>
> > +
> > +/**
> > + * 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
> > + * sequenc (@s->len == @s->readpos).
>
> sequenc -> sequence

Thanks.

>
> > + *
> > + * 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_seq.c b/kernel/trace/trace_seq.c
> > index 1f24ed99dca2..960ccfb2f50c 100644
> > --- a/kernel/trace/trace_seq.c
> > +++ b/kernel/trace/trace_seq.c
> > @@ -27,10 +27,19 @@
> > #include <linux/trace_seq.h>
> >
> > /* 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) ((PAGE_SIZE - 1) - (s)->seq.len)
>
> This might overflow when s->len == PAGE_SIZE. I think that it
> newer happenes because we always check s->full before. The question
> is if we really want to depend on this.

Yeah, we should make this check seq_buf itself. Maybe make a static
inline function that is in seq_buf.h.


>
> > /* 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
> > @@ -77,25 +87,25 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
> > */
> > int 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 0;
> >
> > + __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))) {
>
> We might check the return value from seq_buf_vprintf() here.

No, we are working to get rid of the return values for the seq_*()
functions (with a few exceptions). One now must check if the buffer has
overflowed, and not the return value of the seq_*() functions
themselves.

There's already patches out to convert the seq_file calls as well.

-- Steve


>
> We could do similar thing also in the other functions. We even
> already store the ret value in some of them.
>
> Best Regards,
> Petr

2014-11-05 18:43:04

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed, 5 Nov 2014 15:26:20 +0100
Petr Mladek <[email protected]> wrote:


> > index 000000000000..97872154d51c
> > --- /dev/null
> > +++ b/include/linux/seq_buf.h
> > @@ -0,0 +1,72 @@
> > +#ifndef _LINUX_SEQ_BUF_H
> > +#define _LINUX_SEQ_BUF_H
> > +
> > +#include <linux/fs.h>
> > +
> > +#include <asm/page.h>
>
> One more small thing. We do not need this include because
> seq_buf does not use the PAGE_SIZE.

Good catch.

I'll remove it.

-- Steve

2014-11-05 20:00:12

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed, 5 Nov 2014 13:41:47 -0500
Steven Rostedt <[email protected]> wrote:

> >
> > > + */
> > > +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > > + int nmaskbits)
> > > +{
> > > + unsigned int len = SEQ_BUF_LEFT(s);
> > >
> > > + int ret;
> > > +
> > > + WARN_ON(s->size == 0);
> > > +
> > > + if (s->len < s->size) {
> > > + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> >
> > It writes to the beginning of the buffer. It should be
> >
> > ret = bitmap_scnprintf(s->buffer + s->len, len,
> > maskp, nmaskbits);
> >
>
> Yep thanks. Luckily its only user didn't care.
>
> Will fix.
>
> >
> > > + if (s->len + ret < s->size) {
> >
> > This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
> > and it currently returns the remaining size - len - 1.
>
> Hmm, that's correct, as bitmap_scnprintf() returns the amount written
> instead of the amount that it would write like snprintf() would.
>
>
> >
> > You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).
>
> That wont help when we make overflow len > size.
>
> Probably should see if ret == the amount of bits required for the
> bitmask.

Here's the new version:

int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
int nmaskbits)
{
unsigned int len = seq_buf_buffer_left(s);
int size;
int ret;

WARN_ON(s->size == 0);

if (s->len < s->size) {
/*
* Calculate to see if we have enough room to fit
* @nmaskbits as a string
*/
size = (nmaskbits + 3) / 4;
/* Add the commas that are used for groups of 8 hex digits */
size += size / 8;
if (len >= size) {
ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
WARN_ON_ONCE(s->len + ret >= s->size);
s->len += ret;
return 0;
}
}
seq_buf_set_overflow(s);
return -1;
}


>
> >
> >
> > > + s->len += ret;
> > > + return 0;
> > > + }
> > > + }
> > > + seq_buf_set_overflow(s);
> > > + return -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))
> >
> > We might want to use the seq_buf_putmem() return value here.
>
> We could do that.

Actually, no we can't. As I stated before, the return values of most
of the seq_*() functions (for seq_file and seq_buf) will be turning
into void functions.

I'm avoiding checking return values here.

-- Steve

>
> >
> > > + return -1;
> > > + }
> > > + return 0;
> > > +}
> > > +
> >

2014-11-05 21:17:26

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed, 5 Nov 2014 15:00:07 -0500
Steven Rostedt <[email protected]> wrote:

> On Wed, 5 Nov 2014 13:41:47 -0500
> Steven Rostedt <[email protected]> wrote:
>
> > >
> > > > + */
> > > > +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > > > + int nmaskbits)
> > > > +{
> > > > + unsigned int len = SEQ_BUF_LEFT(s);
> > > >
> > > > + int ret;
> > > > +
> > > > + WARN_ON(s->size == 0);
> > > > +
> > > > + if (s->len < s->size) {
> > > > + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> > >
> > > It writes to the beginning of the buffer. It should be
> > >
> > > ret = bitmap_scnprintf(s->buffer + s->len, len,
> > > maskp, nmaskbits);
> > >
> >
> > Yep thanks. Luckily its only user didn't care.
> >
> > Will fix.
> >
> > >
> > > > + if (s->len + ret < s->size) {
> > >
> > > This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
> > > and it currently returns the remaining size - len - 1.
> >
> > Hmm, that's correct, as bitmap_scnprintf() returns the amount written
> > instead of the amount that it would write like snprintf() would.
> >
> >
> > >
> > > You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).
> >
> > That wont help when we make overflow len > size.
> >
> > Probably should see if ret == the amount of bits required for the
> > bitmask.
>
> Here's the new version:
>

Take 2:

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);

if (s->len < s->size) {
ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
/*
* 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 (s->len + ret < s->size) {
s->len += ret;
return 0;
}
}
seq_buf_set_overflow(s);
return -1;
}

-- Steve

2014-11-05 21:21:52

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed, 5 Nov 2014 16:17:20 -0500
Steven Rostedt <[email protected]> wrote:

> On Wed, 5 Nov 2014 15:00:07 -0500
> Steven Rostedt <[email protected]> wrote:
>
> > On Wed, 5 Nov 2014 13:41:47 -0500
> > Steven Rostedt <[email protected]> wrote:
> >
> > > >
> > > > > + */
> > > > > +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > > > > + int nmaskbits)
> > > > > +{
> > > > > + unsigned int len = SEQ_BUF_LEFT(s);
> > > > >
> > > > > + int ret;
> > > > > +
> > > > > + WARN_ON(s->size == 0);
> > > > > +
> > > > > + if (s->len < s->size) {
> > > > > + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> > > >
> > > > It writes to the beginning of the buffer. It should be
> > > >
> > > > ret = bitmap_scnprintf(s->buffer + s->len, len,
> > > > maskp, nmaskbits);
> > > >
> > >
> > > Yep thanks. Luckily its only user didn't care.
> > >
> > > Will fix.
> > >
> > > >
> > > > > + if (s->len + ret < s->size) {
> > > >
> > > > This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
> > > > and it currently returns the remaining size - len - 1.
> > >
> > > Hmm, that's correct, as bitmap_scnprintf() returns the amount written
> > > instead of the amount that it would write like snprintf() would.
> > >
> > >
> > > >
> > > > You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).
> > >
> > > That wont help when we make overflow len > size.
> > >
> > > Probably should see if ret == the amount of bits required for the
> > > bitmask.
> >
> > Here's the new version:
> >
>
> Take 2:
>
> int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> int nmaskbits)
> {
> unsigned int len = seq_buf_buffer_left(s);

Bah, I need to make this: len = s->size - s->len.

As this would work for both cases of what a overflowed buffer is.

-- Steve

> int ret;
>
> WARN_ON(s->size == 0);
>
> if (s->len < s->size) {
> ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> /*
> * 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 (s->len + ret < s->size) {
> s->len += ret;
> return 0;
> }
> }
> seq_buf_set_overflow(s);
> return -1;
> }
>
> -- Steve

2014-11-06 16:13:20

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed 2014-11-05 13:41:47, Steven Rostedt wrote:
> On Wed, 5 Nov 2014 15:22:22 +0100
> Petr Mladek <[email protected]> wrote:
>
> > On Tue 2014-11-04 10:52:40, Steven Rostedt wrote:
> > > From: "Steven Rostedt (Red Hat)" <[email protected]>
> > Also I would add an explanation of the overall logic. If I get it
> > correctly from the code, it is:
> >
> > /*
> > * The last byte of the buffer is used to detect an overflow in some
> > * operations. Therefore, the buffer offers (@size - 1) bytes for valid
> > * data.
>
> Well, this will change in the future. And it is commented with the
> seq_buf_has_overflowed() function. I don't want to comment about it
> with the structure.

Fair enough.

> > > + */
> > > +#include <linux/uaccess.h>
> > > +#include <linux/seq_file.h>
> > > +#include <linux/seq_buf.h>
> > > +
> > > +/* How much buffer is left on the seq_buf? */
> >
> > I would write the following to explain the -1:
>
> Later patches gets rid of the -1. It's -1 because seq_file is -1 as
> well.

Yup. Let's leave it as is.

> >
> > /* How much buffer is left for valid data */
> >
> > > +#define SEQ_BUF_LEFT(s) (((s)->size - 1) - (s)->len)
> >
> > Hmm, it might overflow when the buffer has overflown (s->len == s->size)
> > or when the buffer is not initialized (s->size == 0). Note that the
> > result should be unsigned int.
>
> The two places that use it is "unsigned int" so that should not be a
> problem.

It might overflow. If ("size == len"), the result will be -1
which is UINT_MAX.

Well, this should not happen if callers check the overflow before
any use. Also we are going to remove the -1 in the followup patches.
I am fine with it after all.

> > > +
> > > +/**
> > > + * 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;
> > > + }
> >
> > We might want to copy the maximum possible number of bytes.
> > It will then behave the same as the other functions.
>
> I'm converting all this to be like seq_file in the other patches.

Fair enough.

>
> >
> > > + *
> > > + * 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_seq.c b/kernel/trace/trace_seq.c
> > > index 1f24ed99dca2..960ccfb2f50c 100644
> > > --- a/kernel/trace/trace_seq.c
> > > +++ b/kernel/trace/trace_seq.c
> > > @@ -27,10 +27,19 @@
> > > #include <linux/trace_seq.h>
> > >
> > > /* 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) ((PAGE_SIZE - 1) - (s)->seq.len)
> >
> > This might overflow when s->len == PAGE_SIZE. I think that it
> > newer happenes because we always check s->full before. The question
> > is if we really want to depend on this.
>
> Yeah, we should make this check seq_buf itself. Maybe make a static
> inline function that is in seq_buf.h.

The inline function in seq_buf.h looks like a good idea.

> >
> > > /* 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
> > > @@ -77,25 +87,25 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
> > > */
> > > int 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 0;
> > >
> > > + __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))) {
> >
> > We might check the return value from seq_buf_vprintf() here.
>
> No, we are working to get rid of the return values for the seq_*()
> functions (with a few exceptions). One now must check if the buffer has
> overflowed, and not the return value of the seq_*() functions
> themselves.

I see.

> There's already patches out to convert the seq_file calls as well.

Good to know.

Best Regards,
Petr

2014-11-06 16:33:58

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Wed 2014-11-05 16:21:46, Steven Rostedt wrote:
> On Wed, 5 Nov 2014 16:17:20 -0500
> Steven Rostedt <[email protected]> wrote:
>
> > On Wed, 5 Nov 2014 15:00:07 -0500
> > Steven Rostedt <[email protected]> wrote:
> >
> > > On Wed, 5 Nov 2014 13:41:47 -0500
> > > Steven Rostedt <[email protected]> wrote:
> > >
> > > > >
> > > > > > + */
> > > > > > +int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > > > > > + int nmaskbits)
> > > > > > +{
> > > > > > + unsigned int len = SEQ_BUF_LEFT(s);
> > > > > >
> > > > > > + int ret;
> > > > > > +
> > > > > > + WARN_ON(s->size == 0);
> > > > > > +
> > > > > > + if (s->len < s->size) {
> > > > > > + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> > > > >
> > > > > It writes to the beginning of the buffer. It should be
> > > > >
> > > > > ret = bitmap_scnprintf(s->buffer + s->len, len,
> > > > > maskp, nmaskbits);
> > > > >
> > > >
> > > > Yep thanks. Luckily its only user didn't care.
> > > >
> > > > Will fix.
> > > >
> > > > >
> > > > > > + if (s->len + ret < s->size) {
> > > > >
> > > > > This will always happen because bitmap_scnprintf() is limited by SEQ_BUF_LEFT(s)
> > > > > and it currently returns the remaining size - len - 1.
> > > >
> > > > Hmm, that's correct, as bitmap_scnprintf() returns the amount written
> > > > instead of the amount that it would write like snprintf() would.
> > > >
> > > >
> > > > >
> > > > > You might want to use "s->size - s->len" instead of SEQ_BUF_LEFT(s).
> > > >
> > > > That wont help when we make overflow len > size.
> > > >
> > > > Probably should see if ret == the amount of bits required for the
> > > > bitmask.
> > >
> > > Here's the new version:
> > >
> >
> > Take 2:
> >
> > int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > int nmaskbits)
> > {
> > unsigned int len = seq_buf_buffer_left(s);
>
> Bah, I need to make this: len = s->size - s->len.
>
> As this would work for both cases of what a overflowed buffer is.

Hmm, this would produce -1 (UNIT_MAX) when the overflow is
(s->len = s->size + 1). I would keep seq_buf_buffer_left(s);


> > int ret;
> >
> > WARN_ON(s->size == 0);
> >
> > if (s->len < s->size) {

It does not make sense to try if there is only one byte left. I would do:

if (len > 1)

together with the change below.

> > ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
> > /*
> > * 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.
> > */

This is great explanation. You might want to move it above the
"if (len > 1)" if you agree with it.

> > if (s->len + ret < s->size) {

This should work with both variants of the overflow if "len" is really
the space left.

if (ret < len)

I mean that we fail if we wrote the buffer until the last possible byte.

> > s->len += ret;
> > return 0;
> > }
> > }
> > seq_buf_set_overflow(s);
> > return -1;
> > }


Best Regards,
Petr

2014-11-07 18:30:22

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq


I'm not going to waist bandwidth reposting the entire series. Here's a
new version of this patch. I'm getting ready to pull it into my next
queue.

-- Steve

>From 11674c8df0580a03a2517e3a8e4705c47c663564 Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 25 Jun 2014 15:54:42 -0400
Subject: [PATCH] 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/[email protected]

Tested-by: Jiri Kosina <[email protected]>
Acked-by: Jiri Kosina <[email protected]>
Signed-off-by: Steven Rostedt <[email protected]>
---
include/linux/seq_buf.h | 78 ++++++++
include/linux/trace_seq.h | 10 +-
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 | 184 +++++++++----------
8 files changed, 540 insertions(+), 125 deletions(-)
create mode 100644 include/linux/seq_buf.h
create mode 100644 kernel/trace/seq_buf.c

diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
new file mode 100644
index 000000000000..64bf5a43411e
--- /dev/null
+++ b/include/linux/seq_buf.h
@@ -0,0 +1,78 @@
+#ifndef _LINUX_SEQ_BUF_H
+#define _LINUX_SEQ_BUF_H
+
+#include <linux/fs.h>
+
+/*
+ * Trace sequences are used to allow a function to call several other functions
+ * to create a string of data to use.
+ */
+
+/**
+ * seq_buf - seq buffer structure
+ * @buffer: pointer to the buffer
+ * @size: size of the buffer
+ * @len: the amount of data inside the buffer
+ * @readpos: The next position to read in the buffer.
+ */
+struct seq_buf {
+ unsigned char *buffer;
+ unsigned int size;
+ unsigned int len;
+ unsigned int readpos;
+};
+
+static inline void
+seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
+{
+ s->buffer = buf;
+ s->size = size;
+ s->len = 0;
+ s->readpos = 0;
+}
+
+/*
+ * seq_buf have a buffer that might overflow. When this happens
+ * the len and size are set to be equal.
+ */
+static inline bool
+seq_buf_has_overflowed(struct seq_buf *s)
+{
+ return s->len == s->size;
+}
+
+static inline void
+seq_buf_set_overflow(struct seq_buf *s)
+{
+ s->len = s->size;
+}
+
+/*
+ * How much buffer is left on the seq_buf?
+ */
+static inline unsigned int
+seq_buf_buffer_left(struct seq_buf *s)
+{
+ return (s->size - 1) - s->len;
+}
+
+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);
+extern int seq_buf_puts(struct seq_buf *s, const char *str);
+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_bitmask(struct seq_buf *s, const unsigned long *maskp,
+ int nmaskbits);
+
+#endif /* _LINUX_SEQ_BUF_H */
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
index ea6c9dea79e3..27c98fd76503 100644
--- a/include/linux/trace_seq.h
+++ b/include/linux/trace_seq.h
@@ -1,7 +1,7 @@
#ifndef _LINUX_TRACE_SEQ_H
#define _LINUX_TRACE_SEQ_H

-#include <linux/fs.h>
+#include <linux/seq_buf.h>

#include <asm/page.h>

@@ -12,16 +12,14 @@

struct trace_seq {
unsigned char buffer[PAGE_SIZE];
- unsigned int len;
- unsigned int readpos;
+ struct seq_buf seq;
int full;
};

static inline void
trace_seq_init(struct trace_seq *s)
{
- s->len = 0;
- s->readpos = 0;
+ seq_buf_init(&s->seq, s->buffer, PAGE_SIZE);
s->full = 0;
}

@@ -37,7 +35,7 @@ trace_seq_init(struct trace_seq *s)
static inline unsigned char *
trace_seq_buffer_ptr(struct trace_seq *s)
{
- return s->buffer + s->len;
+ return s->buffer + s->seq.len;
}

/*
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..88738b200bf3
--- /dev/null
+++ b/kernel/trace/seq_buf.c
@@ -0,0 +1,341 @@
+/*
+ * seq_buf.c
+ *
+ * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>
+ *
+ * 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 <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/seq_buf.h>
+
+/* 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, 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 8a528392b1f4..0c46168e938e 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -938,19 +938,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;
}

@@ -4313,6 +4314,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.
@@ -4509,18 +4512,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;

/*
@@ -4536,7 +4539,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);

/*
@@ -4574,16 +4577,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;
}

@@ -4664,13 +4667,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);
}
@@ -5671,7 +5674,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);

@@ -6634,11 +6637,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 0cc51edde3a8..33525bf6cbf5 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 f0a0c982cde3..488273458bfd 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -1291,9 +1291,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--;
}

ret = trace_seq_puts(s, " */\n");
diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
index 1f24ed99dca2..3ad8738aea19 100644
--- a/kernel/trace/trace_seq.c
+++ b/kernel/trace/trace_seq.c
@@ -27,10 +27,19 @@
#include <linux/trace_seq.h>

/* 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
@@ -77,25 +87,25 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
*/
int 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 0;

+ __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 0;
}

- s->len += ret;
-
return 1;
}
EXPORT_SYMBOL_GPL(trace_seq_printf);
@@ -116,14 +126,20 @@ EXPORT_SYMBOL_GPL(trace_seq_printf);
int 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 0;

- ret = bitmap_scnprintf(s->buffer, 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;
+ return 0;
+ }

return 1;
}
@@ -144,23 +160,24 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
*/
int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
{
- unsigned int len = TRACE_SEQ_BUF_LEFT(s);
+ unsigned int save_len = s->seq.len;
int ret;

- if (s->full || !len)
+ if (s->full)
return 0;

- ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+ __trace_seq_init(s);
+
+ ret = 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 0;
}

- s->len += ret;
-
- return len;
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_vprintf);

@@ -183,23 +200,24 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
*/
int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
{
- unsigned int len = TRACE_SEQ_BUF_LEFT(s);
+ unsigned int save_len = s->seq.len;
int ret;

- if (s->full || !len)
+ if (s->full)
return 0;

- ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
+ __trace_seq_init(s);
+
+ ret = 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 0;
}

- s->len += ret;
-
- return len;
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_bprintf);

@@ -222,13 +240,14 @@ int trace_seq_puts(struct trace_seq *s, const char *str)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}

- memcpy(s->buffer + s->len, str, len);
- s->len += len;
+ seq_buf_putmem(&s->seq, str, len);

return len;
}
@@ -251,12 +270,14 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}

- s->buffer[s->len++] = c;
+ seq_buf_putc(&s->seq, c);

return 1;
}
@@ -279,21 +300,19 @@ int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
if (s->full)
return 0;

+ __trace_seq_init(s);
+
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}

- memcpy(s->buffer + s->len, mem, len);
- s->len += len;
+ seq_buf_putmem(&s->seq, mem, len);

return 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
@@ -309,35 +328,30 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
int 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;
- int cnt = 0;
+ unsigned int save_len = s->seq.len;
+ int ret;

if (s->full)
return 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++] = ' ';
-
- cnt += 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 0;
+ }
+
+ /* The added spaces can still cause an overflow */
+ ret = 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 0;
}
- return cnt;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);

@@ -355,30 +369,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);

@@ -404,25 +416,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);
--
2.1.1

2014-11-07 18:59:32

by Joe Perches

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Fri, 2014-11-07 at 13:30 -0500, Steven Rostedt wrote:
> I'm not going to waist bandwidth reposting the entire series. Here's a
> new version of this patch. I'm getting ready to pull it into my next
> queue.

All this seems sensible enough though I think it'd
be nicer if all the touch-ups were compressed into
a single patch.

and some trivia:

> 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/[email protected]
[]
> diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
[]
> +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);
> +extern int seq_buf_puts(struct seq_buf *s, const char *str);
> +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_bitmask(struct seq_buf *s, const unsigned long *maskp,
> + int nmaskbits);

Maybe remove the extern bits from the function definitions?
And maybe the unsigned int len/cnt could be size_t

> diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
[]
> @@ -12,16 +12,14 @@
>
> struct trace_seq {
> unsigned char buffer[PAGE_SIZE];
> - unsigned int len;
> - unsigned int readpos;
> + struct seq_buf seq;
> int full;
> };
>
> static inline void
> trace_seq_init(struct trace_seq *s)
> {
> - s->len = 0;
> - s->readpos = 0;
> + seq_buf_init(&s->seq, s->buffer, PAGE_SIZE);

Maybe the PAGE_SIZE uses could be ARRAY_SIZE(s->buffer)

> diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
[]
> /* 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))

min_t?
maybe this should be FIELD_SIZEOF(struct trace_seq, buffer) - 1



2014-11-07 19:10:45

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Fri, 07 Nov 2014 10:59:27 -0800
Joe Perches <[email protected]> wrote:


> > 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/[email protected]
> []
> > diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
> []
> > +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);
> > +extern int seq_buf_puts(struct seq_buf *s, const char *str);
> > +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_bitmask(struct seq_buf *s, const unsigned long *maskp,
> > + int nmaskbits);
>
> Maybe remove the extern bits from the function definitions?

Yeah, probably should have done that. Not sure why I kept them. Cut and
paste?

> And maybe the unsigned int len/cnt could be size_t

The struct does get changed later to match seq_file.

>
> > diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h
> []
> > @@ -12,16 +12,14 @@
> >
> > struct trace_seq {
> > unsigned char buffer[PAGE_SIZE];
> > - unsigned int len;
> > - unsigned int readpos;
> > + struct seq_buf seq;
> > int full;
> > };
> >
> > static inline void
> > trace_seq_init(struct trace_seq *s)
> > {
> > - s->len = 0;
> > - s->readpos = 0;
> > + seq_buf_init(&s->seq, s->buffer, PAGE_SIZE);
>
> Maybe the PAGE_SIZE uses could be ARRAY_SIZE(s->buffer)
>
> > diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
> []
> > /* 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))
>
> min_t?
> maybe this should be FIELD_SIZEOF(struct trace_seq, buffer) - 1

These can be done at a later time. This patch set is focusing on
creation of seq_buf, not cleaning up trace_seq.

-- Steve

2014-11-10 13:53:17

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Fri 2014-11-07 13:30:17, Steven Rostedt wrote:
>
> I'm not going to waist bandwidth reposting the entire series. Here's a
> new version of this patch. I'm getting ready to pull it into my next
> queue.
>
> -- Steve
>
> From 11674c8df0580a03a2517e3a8e4705c47c663564 Mon Sep 17 00:00:00 2001
> From: "Steven Rostedt (Red Hat)" <[email protected]>
> Date: Wed, 25 Jun 2014 15:54:42 -0400
> Subject: [PATCH] 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/[email protected]
>
> Tested-by: Jiri Kosina <[email protected]>
> Acked-by: Jiri Kosina <[email protected]>
> Signed-off-by: Steven Rostedt <[email protected]>
> ---
> include/linux/seq_buf.h | 78 ++++++++
> include/linux/trace_seq.h | 10 +-
> 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 | 184 +++++++++----------
> 8 files changed, 540 insertions(+), 125 deletions(-)
> create mode 100644 include/linux/seq_buf.h
> create mode 100644 kernel/trace/seq_buf.c
>
> diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
> new file mode 100644
> index 000000000000..64bf5a43411e
> --- /dev/null
> +++ b/include/linux/seq_buf.h
> @@ -0,0 +1,78 @@
> +#ifndef _LINUX_SEQ_BUF_H
> +#define _LINUX_SEQ_BUF_H
> +
> +#include <linux/fs.h>
> +
> +/*
> + * Trace sequences are used to allow a function to call several other functions
> + * to create a string of data to use.
> + */
> +
> +/**
> + * seq_buf - seq buffer structure
> + * @buffer: pointer to the buffer
> + * @size: size of the buffer
> + * @len: the amount of data inside the buffer
> + * @readpos: The next position to read in the buffer.
> + */
> +struct seq_buf {
> + unsigned char *buffer;
> + unsigned int size;
> + unsigned int len;
> + unsigned int readpos;
> +};
> +
> +static inline void
> +seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
> +{
> + s->buffer = buf;
> + s->size = size;
> + s->len = 0;
> + s->readpos = 0;
> +}
> +
> +/*
> + * seq_buf have a buffer that might overflow. When this happens
> + * the len and size are set to be equal.
> + */
> +static inline bool
> +seq_buf_has_overflowed(struct seq_buf *s)
> +{
> + return s->len == s->size;
> +}
> +
> +static inline void
> +seq_buf_set_overflow(struct seq_buf *s)
> +{
> + s->len = s->size;
> +}
> +
> +/*
> + * How much buffer is left on the seq_buf?
> + */
> +static inline unsigned int
> +seq_buf_buffer_left(struct seq_buf *s)
> +{
> + return (s->size - 1) - s->len;

This should be

if (seq_buf_has_overflowed(s)
return 0;
return (s->size - 1) - s->len;

otherwise, it would return UNIT_MAX for the overflown state. If I am
not mistaken.

[...]

> diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c
> new file mode 100644
> index 000000000000..88738b200bf3
> --- /dev/null
> +++ b/kernel/trace/seq_buf.c

[...]

> +
> +/**
> + * 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, len, maskp, nmaskbits);

It should be:

ret = bitmap_scnprintf(s->buffer + s->len, len,
maskp, nmaskbits);

otherwise, we would write to the beginning to the buffer.

> + if (ret < len) {
> + s->len += ret;
> + return 0;
> + }
> + }
> + seq_buf_set_overflow(s);
> + return -1;
> +}
> +

[...]

> diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
> index 1f24ed99dca2..3ad8738aea19 100644
> --- a/kernel/trace/trace_seq.c
> +++ b/kernel/trace/trace_seq.c

[...]

> @@ -144,23 +160,24 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
> */
> int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
> {
> - unsigned int len = TRACE_SEQ_BUF_LEFT(s);
> + unsigned int save_len = s->seq.len;
> int ret;
>
> - if (s->full || !len)
> + if (s->full)
> return 0;
>
> - ret = vsnprintf(s->buffer + s->len, len, fmt, args);
> + __trace_seq_init(s);
> +
> + ret = seq_buf_vprintf(&s->seq, fmt, args);

Note that this returns 0 on success => we do not need to store it

> /* 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 0;
> }
>
> - s->len += ret;
> -
> - return len;
> + return ret;

Instead, we have to do something like:

return s->seq.len - save_len;

> }
> EXPORT_SYMBOL_GPL(trace_seq_vprintf);
>
> @@ -183,23 +200,24 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
> */
> int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
> {
> - unsigned int len = TRACE_SEQ_BUF_LEFT(s);
> + unsigned int save_len = s->seq.len;
> int ret;
>
> - if (s->full || !len)
> + if (s->full)
> return 0;
>
> - ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
> + __trace_seq_init(s);
> +
> + ret = seq_buf_bprintf(&s->seq, fmt, binary);

Same here, it returns 0 on success => no need to store.

> /* 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 0;
> }
>
> - s->len += ret;
> -
> - return len;
> + return ret;

and

return s->seq.len - save_len;

> }
> EXPORT_SYMBOL_GPL(trace_seq_bprintf);
>

[...]

> /**
> * trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
> * @s: trace sequence descriptor
> @@ -309,35 +328,30 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
> int 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;
> - int cnt = 0;
> + unsigned int save_len = s->seq.len;
> + int ret;
>
> if (s->full)
> return 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++] = ' ';
> -
> - cnt += 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 0;
> + }
> +
> + /* The added spaces can still cause an overflow */
> + ret = seq_buf_putmem_hex(&s->seq, mem, len);

and here, it returns zero on success

> +
> + if (unlikely(seq_buf_has_overflowed(&s->seq))) {
> + s->seq.len = save_len;
> + s->full = 1;
> + return 0;
> }
> - return cnt;
> +
> + return ret;

Therefore we need something like:

return s->seq.len - save_len;

> }
> EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
>
> @@ -355,30 +369,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);

This returns zero on sucess.

> + 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;

and we want to return 1 on success =>

return 1;

> }
> EXPORT_SYMBOL_GPL(trace_seq_path);

Best Regards,
Petr


PS: I have to leave for a bit now. I hope that I will be able to look
at the other patches later today.

2014-11-10 17:37:55

by Steven Rostedt

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Mon, 10 Nov 2014 14:53:30 +0100
Petr Mladek <[email protected]> wrote:

> > +/*
> > + * How much buffer is left on the seq_buf?
> > + */
> > +static inline unsigned int
> > +seq_buf_buffer_left(struct seq_buf *s)
> > +{
> > + return (s->size - 1) - s->len;
>
> This should be
>
> if (seq_buf_has_overflowed(s)
> return 0;
> return (s->size - 1) - s->len;
>
> otherwise, it would return UNIT_MAX for the overflown state. If I am
> not mistaken.

I guess I could add that. Probably the safer bet. Or document it that
this is undefined if buffer has overflowed. I have to check how my use
cases worked.

Probably best to add the overflow check anyway.

>
> [...]
>
> > diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c
> > new file mode 100644
> > index 000000000000..88738b200bf3
> > --- /dev/null
> > +++ b/kernel/trace/seq_buf.c
>
> [...]
>
> > +
> > +/**
> > + * 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, len, maskp, nmaskbits);
>
> It should be:
>
> ret = bitmap_scnprintf(s->buffer + s->len, len,
> maskp, nmaskbits);
>
> otherwise, we would write to the beginning to the buffer.

You are correct. But I'll make that a separate patch. This is just
keeping the bug that was in the original code.

>
> > + if (ret < len) {
> > + s->len += ret;
> > + return 0;
> > + }
> > + }
> > + seq_buf_set_overflow(s);
> > + return -1;
> > +}
> > +
>
> [...]
>
> > diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
> > index 1f24ed99dca2..3ad8738aea19 100644
> > --- a/kernel/trace/trace_seq.c
> > +++ b/kernel/trace/trace_seq.c
>
> [...]
>
> > @@ -144,23 +160,24 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
> > */
> > int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
> > {
> > - unsigned int len = TRACE_SEQ_BUF_LEFT(s);
> > + unsigned int save_len = s->seq.len;
> > int ret;
> >
> > - if (s->full || !len)
> > + if (s->full)
> > return 0;
> >
> > - ret = vsnprintf(s->buffer + s->len, len, fmt, args);
> > + __trace_seq_init(s);
> > +
> > + ret = seq_buf_vprintf(&s->seq, fmt, args);
>
> Note that this returns 0 on success => we do not need to store it
>
> > /* 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 0;
> > }
> >
> > - s->len += ret;
> > -
> > - return len;
> > + return ret;
>
> Instead, we have to do something like:
>
> return s->seq.len - save_len;

Actually, I need to make the trace_seq_*() functions return the same as
the seq_buf_*() functions.

I'll update this for now, but it's gotta change later. Probably why I
wasn't so careful about it.

>
> > }
> > EXPORT_SYMBOL_GPL(trace_seq_vprintf);
> >
> > @@ -183,23 +200,24 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
> > */
> > int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
> > {
> > - unsigned int len = TRACE_SEQ_BUF_LEFT(s);
> > + unsigned int save_len = s->seq.len;
> > int ret;
> >
> > - if (s->full || !len)
> > + if (s->full)
> > return 0;
> >
> > - ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
> > + __trace_seq_init(s);
> > +
> > + ret = seq_buf_bprintf(&s->seq, fmt, binary);
>
> Same here, it returns 0 on success => no need to store.
>
> > /* 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 0;
> > }
> >
> > - s->len += ret;
> > -
> > - return len;
> > + return ret;
>
> and

same thing.

>
> return s->seq.len - save_len;
>
> > }
> > EXPORT_SYMBOL_GPL(trace_seq_bprintf);
> >
>
> [...]
>
> > /**
> > * trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
> > * @s: trace sequence descriptor
> > @@ -309,35 +328,30 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
> > int 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;
> > - int cnt = 0;
> > + unsigned int save_len = s->seq.len;
> > + int ret;
> >
> > if (s->full)
> > return 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++] = ' ';
> > -
> > - cnt += 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 0;
> > + }
> > +
> > + /* The added spaces can still cause an overflow */
> > + ret = seq_buf_putmem_hex(&s->seq, mem, len);
>
> and here, it returns zero on success
>
> > +
> > + if (unlikely(seq_buf_has_overflowed(&s->seq))) {
> > + s->seq.len = save_len;
> > + s->full = 1;
> > + return 0;
> > }
> > - return cnt;
> > +
> > + return ret;
>
> Therefore we need something like:
>
> return s->seq.len - save_len;
>

Hmm, I may make the trace_seq_*() functions not return length written
first, before pulling out the seq_buf_*() code. That is, make the
trace_seq_*() behave more like what the seq_buf_*() code does first,
before pulling out the seq_buf_*() code.



> > }
> > EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
> >
> > @@ -355,30 +369,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);
>
> This returns zero on sucess.
>
> > + 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;
>
> and we want to return 1 on success =>
>
> return 1;
>
> > }
> > EXPORT_SYMBOL_GPL(trace_seq_path);
>
> Best Regards,
> Petr
>
>
> PS: I have to leave for a bit now. I hope that I will be able to look
> at the other patches later today.


Thanks for the review. Most of this will be fixed by doing the change
to the return value of the trace_seq_*() code first. I think I'll do
that.

-- Steve

2014-11-10 19:02:56

by Petr Mladek

[permalink] [raw]
Subject: Re: [RFC][PATCH 03/12 v3] tracing: Create seq_buf layer in trace_seq

On Mon 2014-11-10 12:37:47, Steven Rostedt wrote:
> On Mon, 10 Nov 2014 14:53:30 +0100
> Petr Mladek <[email protected]> wrote:
>
> > > +/*
> > > + * How much buffer is left on the seq_buf?
> > > + */
> > > +static inline unsigned int
> > > +seq_buf_buffer_left(struct seq_buf *s)
> > > +{
> > > + return (s->size - 1) - s->len;
> >
> > This should be
> >
> > if (seq_buf_has_overflowed(s)
> > return 0;
> > return (s->size - 1) - s->len;
> >
> > otherwise, it would return UNIT_MAX for the overflown state. If I am
> > not mistaken.
>
> I guess I could add that. Probably the safer bet. Or document it that
> this is undefined if buffer has overflowed. I have to check how my use
> cases worked.
>
> Probably best to add the overflow check anyway.

I vote for it :-)

> > [...]
> >
> > > diff --git a/kernel/trace/seq_buf.c b/kernel/trace/seq_buf.c
> > > new file mode 100644
> > > index 000000000000..88738b200bf3
> > > --- /dev/null
> > > +++ b/kernel/trace/seq_buf.c
> >
> > [...]
> >
> > > +
> > > +/**
> > > + * 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, len, maskp, nmaskbits);
> >
> > It should be:
> >
> > ret = bitmap_scnprintf(s->buffer + s->len, len,
> > maskp, nmaskbits);
> >
> > otherwise, we would write to the beginning to the buffer.
>
> You are correct. But I'll make that a separate patch. This is just
> keeping the bug that was in the original code.

Fair enough.

> >
> > > + if (ret < len) {
> > > + s->len += ret;
> > > + return 0;
> > > + }
> > > + }
> > > + seq_buf_set_overflow(s);
> > > + return -1;
> > > +}
> > > +
> >
> > [...]
> >
> > > diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c
> > > index 1f24ed99dca2..3ad8738aea19 100644
> > > --- a/kernel/trace/trace_seq.c
> > > +++ b/kernel/trace/trace_seq.c
> >
> > [...]
> >
> > > @@ -144,23 +160,24 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
> > > */
> > > int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
> > > {
> > > - unsigned int len = TRACE_SEQ_BUF_LEFT(s);
> > > + unsigned int save_len = s->seq.len;
> > > int ret;
> > >
> > > - if (s->full || !len)
> > > + if (s->full)
> > > return 0;
> > >
> > > - ret = vsnprintf(s->buffer + s->len, len, fmt, args);
> > > + __trace_seq_init(s);
> > > +
> > > + ret = seq_buf_vprintf(&s->seq, fmt, args);
> >
> > Note that this returns 0 on success => we do not need to store it
> >
> > > /* 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 0;
> > > }
> > >
> > > - s->len += ret;
> > > -
> > > - return len;
> > > + return ret;
> >
> > Instead, we have to do something like:
> >
> > return s->seq.len - save_len;
>
> Actually, I need to make the trace_seq_*() functions return the same as
> the seq_buf_*() functions.
>
> I'll update this for now, but it's gotta change later. Probably why I
> wasn't so careful about it.
>
> Hmm, I may make the trace_seq_*() functions not return length written
> first, before pulling out the seq_buf_*() code. That is, make the
> trace_seq_*() behave more like what the seq_buf_*() code does first,
> before pulling out the seq_buf_*() code.

Sounds like the best solution if it does not cause too many changes in
the trace_seq() callers.

Best Regards,
Petr