tracing: Create seq_buf layer in trace_seq
[cascardo/linux.git] / kernel / trace / trace_seq.c
index 1f24ed9..8c0c54f 100644 (file)
 #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
  */
 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
@@ -69,34 +79,26 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s)
  * trace_seq_printf() is used to store strings into a special
  * buffer (@s). Then the output may be either used by
  * the sequencer or pulled into another buffer.
- *
- * Returns 1 if we successfully written all the contents to
- *   the buffer.
-  * Returns 0 if we the length to write is bigger than the
- *   reserved buffer space. In this case, nothing gets written.
  */
-int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
+void trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
 {
-       unsigned int len = TRACE_SEQ_BUF_LEFT(s);
+       unsigned int save_len = s->seq.len;
        va_list ap;
-       int ret;
 
-       if (s->full || !len)
-               return 0;
+       if (s->full)
+               return;
+
+       __trace_seq_init(s);
 
        va_start(ap, fmt);
-       ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+       seq_buf_vprintf(&s->seq, fmt, ap);
        va_end(ap);
 
        /* If we can't write it all, don't bother writing anything */
-       if (ret >= len) {
+       if (unlikely(seq_buf_has_overflowed(&s->seq))) {
+               s->seq.len = save_len;
                s->full = 1;
-               return 0;
        }
-
-       s->len += ret;
-
-       return 1;
 }
 EXPORT_SYMBOL_GPL(trace_seq_printf);
 
@@ -107,25 +109,23 @@ EXPORT_SYMBOL_GPL(trace_seq_printf);
  * @nmaskbits: The number of bits that are valid in @maskp
  *
  * Writes a ASCII representation of a bitmask string into @s.
- *
- * Returns 1 if we successfully written all the contents to
- *   the buffer.
- * Returns 0 if we the length to write is bigger than the
- *   reserved buffer space. In this case, nothing gets written.
  */
-int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
+void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
                      int nmaskbits)
 {
-       unsigned int len = TRACE_SEQ_BUF_LEFT(s);
-       int ret;
+       unsigned int save_len = s->seq.len;
 
-       if (s->full || !len)
-               return 0;
+       if (s->full)
+               return;
 
-       ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
-       s->len += ret;
+       __trace_seq_init(s);
 
-       return 1;
+       seq_buf_bitmask(&s->seq, maskp, nmaskbits);
+
+       if (unlikely(seq_buf_has_overflowed(&s->seq))) {
+               s->seq.len = save_len;
+               s->full = 1;
+       }
 }
 EXPORT_SYMBOL_GPL(trace_seq_bitmask);
 
@@ -139,28 +139,23 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask);
  * trace_seq_printf is used to store strings into a special
  * buffer (@s). Then the output may be either used by
  * the sequencer or pulled into another buffer.
- *
- * Returns how much it wrote to the buffer.
  */
-int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
+void trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
 {
-       unsigned int len = TRACE_SEQ_BUF_LEFT(s);
-       int ret;
+       unsigned int save_len = s->seq.len;
 
-       if (s->full || !len)
-               return 0;
+       if (s->full)
+               return;
+
+       __trace_seq_init(s);
 
-       ret = vsnprintf(s->buffer + s->len, len, fmt, args);
+       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;
 }
 EXPORT_SYMBOL_GPL(trace_seq_vprintf);
 
@@ -178,28 +173,24 @@ EXPORT_SYMBOL_GPL(trace_seq_vprintf);
  *
  * This function will take the format and the binary array and finish
  * the conversion into the ASCII string within the buffer.
- *
- * Returns how much it wrote to the buffer.
  */
-int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
+void trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
 {
-       unsigned int len = TRACE_SEQ_BUF_LEFT(s);
-       int ret;
+       unsigned int save_len = s->seq.len;
 
-       if (s->full || !len)
-               return 0;
+       if (s->full)
+               return;
 
-       ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
+       __trace_seq_init(s);
+
+       seq_buf_bprintf(&s->seq, fmt, binary);
 
        /* If we can't write it all, don't bother writing anything */
-       if (ret >= len) {
+       if (unlikely(seq_buf_has_overflowed(&s->seq))) {
+               s->seq.len = save_len;
                s->full = 1;
-               return 0;
+               return;
        }
-
-       s->len += ret;
-
-       return len;
 }
 EXPORT_SYMBOL_GPL(trace_seq_bprintf);
 
@@ -212,25 +203,22 @@ EXPORT_SYMBOL_GPL(trace_seq_bprintf);
  * copy to user routines. This function records a simple string
  * into a special buffer (@s) for later retrieval by a sequencer
  * or other mechanism.
- *
- * Returns how much it wrote to the buffer.
  */
-int trace_seq_puts(struct trace_seq *s, const char *str)
+void trace_seq_puts(struct trace_seq *s, const char *str)
 {
        unsigned int len = strlen(str);
 
        if (s->full)
-               return 0;
+               return;
+
+       __trace_seq_init(s);
 
        if (len > TRACE_SEQ_BUF_LEFT(s)) {
                s->full = 1;
-               return 0;
+               return;
        }
 
-       memcpy(s->buffer + s->len, str, len);
-       s->len += len;
-
-       return len;
+       seq_buf_putmem(&s->seq, str, len);
 }
 EXPORT_SYMBOL_GPL(trace_seq_puts);
 
@@ -243,22 +231,20 @@ EXPORT_SYMBOL_GPL(trace_seq_puts);
  * copy to user routines. This function records a simple charater
  * into a special buffer (@s) for later retrieval by a sequencer
  * or other mechanism.
- *
- * Returns how much it wrote to the buffer.
  */
-int trace_seq_putc(struct trace_seq *s, unsigned char c)
+void trace_seq_putc(struct trace_seq *s, unsigned char c)
 {
        if (s->full)
-               return 0;
+               return;
+
+       __trace_seq_init(s);
 
        if (TRACE_SEQ_BUF_LEFT(s) < 1) {
                s->full = 1;
-               return 0;
+               return;
        }
 
-       s->buffer[s->len++] = c;
-
-       return 1;
+       seq_buf_putc(&s->seq, c);
 }
 EXPORT_SYMBOL_GPL(trace_seq_putc);
 
@@ -271,29 +257,23 @@ EXPORT_SYMBOL_GPL(trace_seq_putc);
  * 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 how much it wrote to the buffer.
  */
-int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
+void trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
 {
        if (s->full)
-               return 0;
+               return;
+
+       __trace_seq_init(s);
 
        if (len > TRACE_SEQ_BUF_LEFT(s)) {
                s->full = 1;
-               return 0;
+               return;
        }
 
-       memcpy(s->buffer + s->len, mem, len);
-       s->len += len;
-
-       return len;
+       seq_buf_putmem(&s->seq, mem, len);
 }
 EXPORT_SYMBOL_GPL(trace_seq_putmem);
 
-#define MAX_MEMHEX_BYTES       8U
-#define HEX_CHARS              (MAX_MEMHEX_BYTES*2 + 1)
-
 /**
  * trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
  * @s: trace sequence descriptor
@@ -303,41 +283,31 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem);
  * This is similar to trace_seq_putmem() except instead of just copying the
  * raw memory into the buffer it writes its ASCII representation of it
  * in hex characters.
- *
- * Returns how much it wrote to the buffer.
  */
-int trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
+void trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
                         unsigned int len)
 {
-       unsigned char hex[HEX_CHARS];
-       const unsigned char *data = mem;
-       unsigned int start_len;
-       int i, j;
-       int cnt = 0;
+       unsigned int save_len = s->seq.len;
 
        if (s->full)
-               return 0;
+               return;
+
+       __trace_seq_init(s);
+
+       /* Each byte is represented by two chars */
+       if (len * 2 > TRACE_SEQ_BUF_LEFT(s)) {
+               s->full = 1;
+               return;
+       }
 
-       while (len) {
-               start_len = min(len, HEX_CHARS - 1);
-#ifdef __BIG_ENDIAN
-               for (i = 0, j = 0; i < start_len; i++) {
-#else
-               for (i = start_len-1, j = 0; i >= 0; i--) {
-#endif
-                       hex[j++] = hex_asc_hi(data[i]);
-                       hex[j++] = hex_asc_lo(data[i]);
-               }
-               if (WARN_ON_ONCE(j == 0 || j/2 > len))
-                       break;
-
-               /* j increments twice per loop */
-               len -= j / 2;
-               hex[j++] = ' ';
-
-               cnt += trace_seq_putmem(s, hex, j);
+       /* The added spaces can still cause an overflow */
+       seq_buf_putmem_hex(&s->seq, mem, len);
+
+       if (unlikely(seq_buf_has_overflowed(&s->seq))) {
+               s->seq.len = save_len;
+               s->full = 1;
+               return;
        }
-       return cnt;
 }
 EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
 
@@ -355,30 +325,28 @@ EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
  */
 int trace_seq_path(struct trace_seq *s, const struct path *path)
 {
-       unsigned char *p;
+       unsigned int save_len = s->seq.len;
+       int ret;
 
        if (s->full)
                return 0;
 
+       __trace_seq_init(s);
+
        if (TRACE_SEQ_BUF_LEFT(s) < 1) {
                s->full = 1;
                return 0;
        }
 
-       p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
-       if (!IS_ERR(p)) {
-               p = mangle_path(s->buffer + s->len, p, "\n");
-               if (p) {
-                       s->len = p - s->buffer;
-                       return 1;
-               }
-       } else {
-               s->buffer[s->len++] = '?';
-               return 1;
+       ret = seq_buf_path(&s->seq, path);
+
+       if (unlikely(seq_buf_has_overflowed(&s->seq))) {
+               s->seq.len = save_len;
+               s->full = 1;
+               return 0;
        }
 
-       s->full = 1;
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(trace_seq_path);
 
@@ -404,25 +372,7 @@ EXPORT_SYMBOL_GPL(trace_seq_path);
  */
 int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt)
 {
-       int len;
-       int ret;
-
-       if (!cnt)
-               return 0;
-
-       if (s->len <= s->readpos)
-               return -EBUSY;
-
-       len = s->len - s->readpos;
-       if (cnt > len)
-               cnt = len;
-       ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
-       if (ret == cnt)
-               return -EFAULT;
-
-       cnt -= ret;
-
-       s->readpos += cnt;
-       return cnt;
+       __trace_seq_init(s);
+       return seq_buf_to_user(&s->seq, ubuf, cnt);
 }
 EXPORT_SYMBOL_GPL(trace_seq_to_user);