Skip to contentSkip to conversation

seq-buf/x86/printk: Print all stacks from NMI safely

RFC PATCH Superseded Steven Rostedt 8 participants 77 messages Nov 4, 2014 – Nov 10, 2014

Subsystems (7)
x86 Library code Tracing KVM x86 +3 more subsystems

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Steven Rostedt

author maintainer
#0 lore raw

v3 cover seq-buf/x86/printk: Print all stacks from NMI safely

This is version 3:

This is my proposal to print the NMI stack traces from an RCU stall safely, as well as from a sysrq-l.

Here's the gist of it.

o Add a new layer to trace_seq called seq_buf, that the trace seq uses. The seq_buf is more generic, and does not supply its own buffer. The buffer must be supplied when initializing the seq_buf. Note, these patches may be going into 3.19. There's some places in the tracing code that I wanted to use a different size buffer but still needed trace_seq to have a pre-allocated buffer. Having this new layer will help me out.

I also based this on seq_file, and ideally, I want to merge the code between seq_file.c and trace_seq.c, as they are very similar. I already have a working set of patches that do this, but will get seq_buf merged first.

o Update the seq_buf code to be more like seq_file.

o Move the seq_buf out of the tracing code. It's useful for other purposes too. Like writing from an NMI context, and for seq_file.

o Add a per_cpu "printk_func" that printk calls. By default it calls vprintk_def() which does what it has always done. This allows us to override what printk() calls normally on a per cpu basis.

o Have the NMI handler that dumps the stack trace just change the printk_func to call a NMI safe printk function that writes to a per cpu seq_buf. When all NMI handlers chimed in, the original caller prints out the seq_buf buffers for each CPU from a printk safe context.

Changes since V2:

- Mostly it was modifying seq_buf to be based on seq_file as there were some questions to the way seq_buf should work. Note, there's also a set of patches that are getting ready to remove the return value of the seq_printf() and friends to be void, so the seq_buf_printf() and friends return values can be ignored, because they will be void as well. but for now, they just match what seq_file does.

Changes since V1:

- Note, I based this off of my 3.17 queue that already updated trace_seq with a lot of comments from Andrew Morton. His comments have already been incorporated into the trace_seq.c file. This patch set is on top of those.

- Added a change that is in my 3.17 queue that cleans up open coded calculations of the trace_seq buffer to get the current location of the buffer.

- Biggest change is the added seq_buf. I'm keeping trace_seq doing the stop everything once it fills up. But seq_buf will act more like other utilities, as it will return what I would have written, and fills up the buffer as much as it can. It sets an overflow flag to test to see if there wasn't enough buffer space.

- I updated the last patch to use the seq_buf instead of the trace_seq by using its own struct nmi_seq_buf that allocates the buffer used. I also made some updates from previous reviews like not stripping the log level from the NMI printks and just using it in the final output as well as adding more comments and fixing the "return true" on a void function.

Thoughts?

-- Steve

Steven Rostedt (Red Hat) (12):
x86/kvm/tracing: Use helper function trace_seq_buffer_ptr() RAS/tracing: Use trace_seq_buffer_ptr() helper instead of open coded
tracing: Create seq_buf layer in trace_seq
tracing: Convert seq_buf_path() to be like seq_path()
tracing: Convert seq_buf fields to be like seq_file fields
tracing: Add a seq_buf_clear() helper and clear len and readpos in init
tracing: Have seq_buf use full buffer
tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions
seq_buf: Move the seq_buf code to lib/
seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF
printk: Add per_cpu printk func to allow printk to be diverted x86/nmi: Perform a safe NMI stack trace on all CPUs

----

 arch/x86/kernel/apic/hw_nmi.c        |  90 ++++++++-
 arch/x86/kvm/mmutrace.h              |   4 +-
 drivers/firmware/efi/cper.c          |   2 +-
 include/linux/percpu.h               |   3 +
 include/linux/printk.h               |   2 +
 include/linux/seq_buf.h              | 120 ++++++++++++
 include/linux/trace_seq.h            |  10 +-
 kernel/printk/printk.c               |  38 +++-
 kernel/trace/trace.c                 |  39 ++--
 kernel/trace/trace_events.c          |   6 +-
 kernel/trace/trace_functions_graph.c |   6 +-
 kernel/trace/trace_seq.c             | 184 +++++++++----------
 lib/Makefile                         |   2 +-
 lib/seq_buf.c                        | 344 +++++++++++++++++++++++++++++++++++
 14 files changed, 707 insertions(+), 143 deletions(-)
author maintainer
#1 lore raw

RFC PATCH v3 1/12 x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>
---
 arch/x86/kvm/mmutrace.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +2 -2
index 5aaf35641768..ce463a9cc8fb 100644
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
maintainer Acked-by on v3 1/12
#2 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

Quoted 29 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>
---
 arch/x86/kvm/mmutrace.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +1 -1
index 5aaf35641768..ce463a9cc8fb 100644
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \

arch/x86/kvm/mmutrace.h

Quoted 9 lines
arch/x86/kvm/mmutrace.h +1 -2
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
-- 2.1.1
#3 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

In reply to Steven Rostedt
Quoted 19 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>

---
arch/x86/kvm/mmutrace.h | 4 ++--

1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +0 -0
index 5aaf35641768..ce463a9cc8fb 100644

arch/x86/kvm/mmutrace.h line 25

Quoted 5 lines
arch/x86/kvm/mmutrace.h +1 -1
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\

I think the above should not be a const char *, because the location pointed to is surely being changed. It should either be a char * or a char * const.

Quoted 14 lines
arch/x86/kvm/mmutrace.h +1 -1
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
Signature · 1 line
Mark Rustad, Networking Division, Intel Corporation
author maintainer
#4 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

Quoted 23 lines · Mark D
Quoted 21 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>

---
arch/x86/kvm/mmutrace.h | 4 ++--

1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +0 -0
index 5aaf35641768..ce463a9cc8fb 100644
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
Quoted 5 lines

arch/x86/kvm/mmutrace.h line 25

Quoted 3 lines
arch/x86/kvm/mmutrace.h +1 -1
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\

I think the above should not be a const char *, because the location pointed to is surely being changed. It should either be a char * or a char * const.

Ah right. It should be 'char * const'.

Thanks, I'll update it.

-- Steve

Quoted 16 lines
Quoted 15 lines
arch/x86/kvm/mmutrace.h +1 -1
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
author maintainer
#5 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

Quoted 26 lines · On Tue, 4 Nov 2014 14:09:54 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 23 lines · Mark D
Quoted 21 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>

---
arch/x86/kvm/mmutrace.h | 4 ++--

1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +0 -0
index 5aaf35641768..ce463a9cc8fb 100644
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
Quoted 8 lines
Quoted 5 lines

arch/x86/kvm/mmutrace.h line 25

Quoted 3 lines
arch/x86/kvm/mmutrace.h +1 -1
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\

I think the above should not be a const char *, because the location pointed to is surely being changed. It should either be a char * or a char * const.

Ah right. It should be 'char * const'.

Actually, I take that back. The contents of saved_ptr should not be modified.

It may seem strange, but the update is done via the trace_seq_printf(). Then that content is return back to the user. The user should definitely *not* modify the contents of saved_ptr.

This patch is good as is. It should not be a char *, or char * const.

-- Steve

Quoted 18 lines
Quoted 17 lines
Quoted 16 lines
arch/x86/kvm/mmutrace.h +1 -1
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
#6 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

Quoted 35 lines · Steven Rostedt
Quoted 32 lines · On Tue, 4 Nov 2014 14:09:54 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 28 lines · Mark D
Quoted 19 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>

---
arch/x86/kvm/mmutrace.h | 4 ++--

1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +0 -0
index 5aaf35641768..ce463a9cc8fb 100644

arch/x86/kvm/mmutrace.h line 25

Quoted 5 lines
arch/x86/kvm/mmutrace.h +1 -1
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\

I think the above should not be a const char *, because the location pointed to is surely being changed. It should either be a char * or a char * const.

Quoted 5 lines
Quoted 2 lines

Ah right. It should be 'char * const'.

Actually, I take that back. The contents of saved_ptr should not be modified.

At least not by the caller of the macro. The subsequent call to trace_seq_printf will be changing the contents at that address, but not through use of that pointer.

Quoted 3 lines

It may seem strange, but the update is done via the trace_seq_printf(). Then that content is return back to the user. The user should definitely *not* modify the contents of saved_ptr.

Agreed.

Quoted 1 line

This patch is good as is. It should not be a char *, or char * const.

Yes. I did further checking and agree. Although that memory will be written, it isn't written through that pointer and it is the best type as a return value.

Quoted 22 lines

-- Steve

Quoted 18 lines
Quoted 17 lines
Quoted 16 lines
arch/x86/kvm/mmutrace.h +1 -1
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
Signature · 1 line
Mark Rustad, Networking Division, Intel Corporation
Reviewed-by on v3 1/12
#7 lore raw

Re: [RFC][PATCH 01/12 v3] x86/kvm/tracing: Use helper function trace_seq_buffer_ptr()

In reply to Steven Rostedt
Quoted 10 lines · Steven Rostedt

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

To allow for the restructiong of the trace_seq code, we need users of it to use the helper functions instead of accessing the internals of the trace_seq structure itself.

Cc Mark Rustad <[email protected]>
Cc Jeff Kirsher <[email protected]>
Cc Paolo Bonzini <[email protected]>

Best Regards,
Petr

Quoted 30 lines
---
 arch/x86/kvm/mmutrace.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/kvm/mmutrace.h +2 -2
index 5aaf35641768..ce463a9cc8fb 100644
@@ -22,7 +22,7 @@
 	__entry->unsync = sp->unsync;
 
 #define KVM_MMU_PAGE_PRINTK() ({				        \
-	const u32 saved_len = p->len;					\
+	const char *saved_ptr = trace_seq_buffer_ptr(p);		\
 	static const char *access_str[] = {			        \
 		"---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux"  \
 	};							        \
@@ -41,7 +41,7 @@
 			 role.nxe ? "" : "!",				\
 			 __entry->root_count,				\
 			 __entry->unsync ? "unsync" : "sync", 0);	\
-	p->buffer + saved_len;						\
+	saved_ptr;							\
 		})
 
 #define kvm_mmu_trace_pferr_flags       \
Signature · 1 line
2.1.1
author maintainer
#8 lore raw

RFC PATCH v3 2/12 RAS/tracing: Use trace_seq_buffer_ptr() helper instead of open coded

In reply to Steven Rostedt

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

Use the helper function trace_seq_buffer_ptr() to get the current location of the next buffer write of a trace_seq object, instead of open coding it.

This facilitates the conversion of trace_seq to use seq_buf.

Cc Chen Gong <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tony Luck <[email protected]>
---
 drivers/firmware/efi/cper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/firmware/efi/cper.c +1 -1
index 5b53d6183b6b..4fd9961d552e 100644
@@ -294,7 +294,7 @@ void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
 const char *cper_mem_err_unpack(struct trace_seq *p,
 				struct cper_mem_err_compact *cmem)
 {
-	const char *ret = p->buffer + p->len;
+	const char *ret = trace_seq_buffer_ptr(p);
 
 	if (cper_mem_err_location(cmem, rcd_decode_str))
 		trace_seq_printf(p, "%s", rcd_decode_str);
Signature · 1 line
2.1.1
maintainer Acked-by on v3 2/12
#9 lore raw

Re: [RFC][PATCH 02/12 v3] RAS/tracing: Use trace_seq_buffer_ptr() helper instead of open coded

Quoted 6 lines · Steven Rostedt

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

Use the helper function trace_seq_buffer_ptr() to get the current location of the next buffer write of a trace_seq object, instead of open coding it.

Quoted 6 lines

This facilitates the conversion of trace_seq to use seq_buf.

Cc Chen Gong <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tony Luck <[email protected]>
Signature · 5 lines
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--
Reviewed-by on v3 2/12
#10 lore raw

Re: [RFC][PATCH 02/12 v3] RAS/tracing: Use trace_seq_buffer_ptr() helper instead of open coded

In reply to Steven Rostedt
Quoted 6 lines · Steven Rostedt

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

Use the helper function trace_seq_buffer_ptr() to get the current location of the next buffer write of a trace_seq object, instead of open coding it.

Quoted 6 lines

This facilitates the conversion of trace_seq to use seq_buf.

Cc Chen Gong <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tony Luck <[email protected]>

Best Regards,
Petr

author maintainer
#11 lore raw

RFC PATCH v3 3/12 tracing: Create seq_buf layer in trace_seq

In reply to Steven Rostedt

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.

---
 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
include/linux/seq_buf.h new file +72 -0
new file mode 100644
index 000000000000..97872154d51c
@@ -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 */
include/linux/trace_seq.h +4 -6
index ea6c9dea79e3..27c98fd76503 100644
@@ -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;
 }
 
 /*
kernel/trace/Makefile +1 -0
index 67d6369ddf83..edc98c72a634 100644
@@ -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
kernel/trace/seq_buf.c new file +341 -0

341 lines not on this page · raw message

kernel/trace/trace.c +21 -18
index 8a528392b1f4..0c46168e938e 100644
@@ -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);
 
kernel/trace/trace_events.c +3 -3
index 0cc51edde3a8..33525bf6cbf5 100644
@@ -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);
 
kernel/trace/trace_functions_graph.c +3 -3
index f0a0c982cde3..488273458bfd 100644
@@ -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");
kernel/trace/trace_seq.c +89 -95

330 lines not on this page · raw message

Signature · 1 line
2.1.1
#12 lore raw

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

Quoted 35 lines · Steven Rostedt

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.

---
 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
include/linux/seq_buf.h new file +9 -0
new file mode 100644
index 000000000000..97872154d51c
@@ -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

include/linux/seq_buf.h line 19

Quoted 10 lines
include/linux/seq_buf.h new file +10 -0
+ * 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.
*/

Quoted 8 lines
include/linux/seq_buf.h new file +8 -0
+ */
+struct seq_buf {
+	unsigned char		*buffer;
+	unsigned int		size;
+	unsigned int		len;
+	unsigned int		readpos;
+};
+

[...]

Quoted 10 lines
kernel/trace/seq_buf.c new file +4 -0
new file mode 100644
index 000000000000..2bf582753902
@@ -0,0 +1,341 @@
+/*
+ * seq_buf.c
+ *
+ * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <[email protected]>

kernel/trace/seq_buf.c line 14

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+ *
+ * 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 :-)

kernel/trace/seq_buf.c line 20

Quoted 6 lines
kernel/trace/seq_buf.c new file +6 -0
+ */
+#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 */

kernel/trace/seq_buf.c line 21

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+#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.

kernel/trace/seq_buf.c line 23

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+/* How much buffer is written? */

I would write the following to explain the -1:

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

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)

[...]

kernel/trace/seq_buf.c line 96

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+
+/**
+ * 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.

kernel/trace/seq_buf.c line 107

Quoted 12 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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);

kernel/trace/seq_buf.c

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+		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).

Quoted 23 lines
kernel/trace/seq_buf.c new file +23 -0
+			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.
+ *

kernel/trace/seq_buf.c line 141

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+ * 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().

Quoted 20 lines
kernel/trace/seq_buf.c new file +20 -0
+		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)

kernel/trace/seq_buf.c line 171

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+{
+	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.

Quoted 4 lines
kernel/trace/seq_buf.c new file +4 -0
+	seq_buf_set_overflow(s);
+	return -1;
+}
+

[...]

Quoted 12 lines
kernel/trace/seq_buf.c new file +12 -0
+
+/**
+ * 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

kernel/trace/seq_buf.c line 217

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+ */
+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?

Quoted 38 lines
kernel/trace/seq_buf.c new file +38 -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]);

kernel/trace/seq_buf.c line 265

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+		}
+		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.

Quoted 5 lines
kernel/trace/seq_buf.c new file +5 -0
+			return -1;
+	}
+	return 0;
+}
+

[...]

Quoted 8 lines
kernel/trace/seq_buf.c new file +8 -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

kernel/trace/seq_buf.c line 315

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+ * 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

Quoted 26 lines

+ *
+ * Returns -EFAULT if the copy to userspace fails.
+ */

kernel/trace/seq_buf.c new file +23 -0
+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;
+}

[...]

kernel/trace/trace_seq.c line 30

Quoted 10 lines
kernel/trace/trace_seq.c +1 -1
index 1f24ed99dca2..960ccfb2f50c 100644
@@ -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.

Quoted 42 lines
kernel/trace/trace_seq.c +15 -6
 /* 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;
 

kernel/trace/trace_seq.c int trace_print_seq(struct seq_file *m, struct trace_seq *s) · line 103

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
+	__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

author maintainer
#13 lore raw

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

Quoted 40 lines · On Wed, 5 Nov 2014 15:22:22 +0100 Petr Mladek <[email protected]> wrote:
Quoted 39 lines · Steven Rostedt

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.

---
 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
include/linux/seq_buf.h new file +13 -0
new file mode 100644
index 000000000000..97872154d51c
@@ -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.
+ */
+
+/**
Quoted 9 lines

include/linux/seq_buf.h line 19

Quoted 6 lines
include/linux/seq_buf.h new file +6 -0
+ * 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.

Quoted 7 lines

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.

Quoted 27 lines

*/

Quoted 8 lines
include/linux/seq_buf.h new file +8 -0
+ */
+struct seq_buf {
+	unsigned char		*buffer;
+	unsigned int		size;
+	unsigned int		len;
+	unsigned int		readpos;
+};
+

[...]

Quoted 14 lines
kernel/trace/seq_buf.c new file +8 -0
new file mode 100644
index 000000000000..2bf582753902
@@ -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.
Quoted 8 lines

kernel/trace/seq_buf.c line 14

Quoted 6 lines
kernel/trace/seq_buf.c new file +6 -0
+ *
+ * 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.

Quoted 9 lines

kernel/trace/seq_buf.c line 20

Quoted 7 lines
kernel/trace/seq_buf.c new file +6 -0
+ */
+#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.

Quoted 8 lines

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

kernel/trace/seq_buf.c line 21

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+#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.

Quoted 11 lines

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

kernel/trace/seq_buf.c line 23

Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+/* 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.

Quoted 17 lines
Quoted 1 line
kernel/trace/seq_buf.c new file +1 -0
+#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)

[...]

kernel/trace/seq_buf.c line 96

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+
+/**
+ * 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:

Quoted 1 line

* Returns zero on success, -1 on overflow.

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

Will fix.

Quoted 16 lines

kernel/trace/seq_buf.c line 107

Quoted 13 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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

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

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

Will fix.

Quoted 5 lines

kernel/trace/seq_buf.c

Quoted 2 lines
kernel/trace/seq_buf.c new file +1 -0
+		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.

Quoted 2 lines

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.

Quoted 29 lines
Quoted 29 lines
kernel/trace/seq_buf.c new file +27 -0
+			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)
+{
Quoted 8 lines

kernel/trace/seq_buf.c line 106

Quoted 6 lines
kernel/trace/seq_buf.c new file +6 -0
+	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.

Quoted 3 lines

kernel/trace/seq_buf.c line 143

Quoted 3 lines
kernel/trace/seq_buf.c new file +2 -0
+		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.

Quoted 23 lines
Quoted 23 lines
kernel/trace/seq_buf.c new file +23 -0
+			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);
+
Quoted 8 lines

kernel/trace/seq_buf.c line 171

Quoted 5 lines
kernel/trace/seq_buf.c new file +5 -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.

Quoted 25 lines
Quoted 5 lines
kernel/trace/seq_buf.c new file +4 -0
+	seq_buf_set_overflow(s);
+	return -1;
+}
+

[...]

Quoted 17 lines
kernel/trace/seq_buf.c new file +17 -0
+
+/**
+ * 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);
+
Quoted 8 lines

kernel/trace/seq_buf.c line 217

Quoted 5 lines
kernel/trace/seq_buf.c new file +5 -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.

Quoted 43 lines
Quoted 43 lines
kernel/trace/seq_buf.c new file +42 -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;
+
Quoted 8 lines

kernel/trace/seq_buf.c line 265

Quoted 6 lines
kernel/trace/seq_buf.c new file +6 -0
+		/* 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.

Quoted 21 lines
Quoted 6 lines
kernel/trace/seq_buf.c new file +5 -0
+			return -1;
+	}
+	return 0;
+}
+

[...]

Quoted 12 lines
kernel/trace/seq_buf.c new file +12 -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.
+ *
Quoted 8 lines
Quoted 6 lines

+ * 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.

Quoted 37 lines
Quoted 27 lines

+ *
+ * Returns -EFAULT if the copy to userspace fails.
+ */

kernel/trace/seq_buf.c new file +23 -0
+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;
+}

[...]

Quoted 7 lines
kernel/trace/trace_seq.c +0 -0
index 1f24ed99dca2..960ccfb2f50c 100644
@@ -27,10 +27,19 @@
 #include <linux/trace_seq.h>
 
Quoted 7 lines

kernel/trace/trace_seq.c line 30

Quoted 3 lines
kernel/trace/trace_seq.c +1 -1
 /* 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.

Quoted 50 lines
Quoted 50 lines
kernel/trace/trace_seq.c +18 -7
 /* 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);
 
Quoted 5 lines

kernel/trace/trace_seq.c int trace_print_seq(struct seq_file *m, struct trace_seq *s) · line 103

Quoted 3 lines
kernel/trace/trace_seq.c +1 -1
 	/* 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

Quoted 6 lines

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

Best Regards,
Petr

author maintainer
#14 lore raw

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

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

kernel/trace/seq_buf.c line 107

Quoted 13 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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

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

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

Will fix.

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+		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.

Quoted 1 line
Quoted 6 lines
Quoted 1 line

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;
}
Quoted 57 lines
Quoted 48 lines
Quoted 48 lines
kernel/trace/seq_buf.c new file +42 -0
+			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;
+
Quoted 8 lines

kernel/trace/seq_buf.c line 265

Quoted 6 lines
kernel/trace/seq_buf.c new file +6 -0
+		/* 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.

Quoted 1 line

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

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
kernel/trace/seq_buf.c new file +5 -0
+			return -1;
+	}
+	return 0;
+}
+
author maintainer
#15 lore raw

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

Quoted 38 lines · On Wed, 5 Nov 2014 15:00:07 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 35 lines · On Wed, 5 Nov 2014 13:41:47 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 16 lines

kernel/trace/seq_buf.c line 107

Quoted 13 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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

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

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

Will fix.

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+		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.

Quoted 1 line
Quoted 9 lines
Quoted 6 lines
Quoted 1 line

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

author maintainer
#16 lore raw

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

Quoted 51 lines · On Wed, 5 Nov 2014 16:17:20 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 38 lines · On Wed, 5 Nov 2014 15:00:07 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 35 lines · On Wed, 5 Nov 2014 13:41:47 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 16 lines

kernel/trace/seq_buf.c line 107

Quoted 13 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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

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

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

Will fix.

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+		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.

Quoted 1 line
Quoted 9 lines
Quoted 6 lines
Quoted 1 line

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:

Quoted 6 lines

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

Quoted 25 lines
	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

#17 lore raw

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

Quoted 56 lines · Steven Rostedt
Quoted 53 lines · On Wed, 5 Nov 2014 16:17:20 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 38 lines · On Wed, 5 Nov 2014 15:00:07 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 35 lines · On Wed, 5 Nov 2014 13:41:47 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 16 lines

kernel/trace/seq_buf.c line 107

Quoted 13 lines

+ */

kernel/trace/seq_buf.c new file +10 -0
+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

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

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

Will fix.

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+		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.

Quoted 1 line
Quoted 9 lines
Quoted 6 lines
Quoted 1 line

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:

Quoted 8 lines
Quoted 4 lines

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

Quoted 5 lines
Quoted 5 lines
	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.

Quoted 10 lines
Quoted 10 lines

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.

Quoted 1 line
Quoted 1 line
		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.

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

Best Regards,
Petr

author maintainer Tested-by Jiri Kosina Acked-by Jiri Kosina
#18 lore raw

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

Quoted 1 line

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.

---
 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
include/linux/seq_buf.h new file +78 -0
new file mode 100644
index 000000000000..64bf5a43411e
@@ -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 */
include/linux/trace_seq.h +4 -6
index ea6c9dea79e3..27c98fd76503 100644
@@ -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;
 }
 
 /*
kernel/trace/Makefile +1 -0
index 67d6369ddf83..edc98c72a634 100644
@@ -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
kernel/trace/seq_buf.c new file +341 -0

341 lines not on this page · raw message

kernel/trace/trace.c +21 -18
index 8a528392b1f4..0c46168e938e 100644
@@ -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);
 
kernel/trace/trace_events.c +3 -3
index 0cc51edde3a8..33525bf6cbf5 100644
@@ -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);
 
kernel/trace/trace_functions_graph.c +3 -3
index f0a0c982cde3..488273458bfd 100644
@@ -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");
kernel/trace/trace_seq.c +89 -95

330 lines not on this page · raw message

Signature · 1 line
2.1.1
#19 lore raw

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

Quoted 3 lines · Steven Rostedt

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:

Quoted 5 lines

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.

[]

include/linux/seq_buf.h

Quoted 1 line
include/linux/seq_buf.h +0 -0

[]

Quoted 8 lines
include/linux/seq_buf.h +8 -0
+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,

include/linux/seq_buf.h line 76

Quoted 10 lines
include/linux/seq_buf.h +10 -0
+			   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

include/linux/trace_seq.h

Quoted 1 line
include/linux/trace_seq.h +0 -0

[]

Quoted 6 lines
include/linux/trace_seq.h +0 -2
@@ -12,16 +12,14 @@
 
 struct trace_seq {
 	unsigned char		buffer[PAGE_SIZE];
-	unsigned int		len;
-	unsigned int		readpos;

include/linux/trace_seq.h line 22

Quoted 10 lines
include/linux/trace_seq.h +2 -2
+	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)

kernel/trace/trace_seq.c

Quoted 1 line
kernel/trace/trace_seq.c +0 -0

[]

kernel/trace/trace_seq.c line 33

Quoted 3 lines

/* How much buffer is written? */

kernel/trace/trace_seq.c +1 -1
-#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

author maintainer
#20 lore raw

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

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

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.

[]

include/linux/seq_buf.h

Quoted 1 line
include/linux/seq_buf.h +0 -0

[]

Quoted 12 lines
include/linux/seq_buf.h +12 -0
+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);
Quoted 8 lines

include/linux/seq_buf.h line 76

Quoted 6 lines
include/linux/seq_buf.h +6 -0
+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?

Quoted 1 line

And maybe the unsigned int len/cnt could be size_t

The struct does get changed later to match seq_file.

Quoted 22 lines

include/linux/trace_seq.h

Quoted 2 lines
include/linux/trace_seq.h +0 -0

[]

Quoted 6 lines
include/linux/trace_seq.h +0 -2
@@ -12,16 +12,14 @@
 
 struct trace_seq {
 	unsigned char		buffer[PAGE_SIZE];
-	unsigned int		len;
-	unsigned int		readpos;

include/linux/trace_seq.h line 22

Quoted 10 lines
include/linux/trace_seq.h +2 -2
+	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)

Quoted 8 lines

kernel/trace/trace_seq.c

Quoted 1 line
kernel/trace/trace_seq.c +0 -0

[]

kernel/trace/trace_seq.c line 33

Quoted 3 lines

/* How much buffer is written? */

kernel/trace/trace_seq.c +1 -1
-#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

#21 lore raw

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

In reply to Steven Rostedt
Quoted 86 lines · Steven Rostedt

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.

---
 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
include/linux/seq_buf.h new file +46 -0
new file mode 100644
index 000000000000..64bf5a43411e
@@ -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)
+{

include/linux/seq_buf.h line 56

Quoted 10 lines
include/linux/seq_buf.h new file +10 -0
+	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.

[...]

Quoted 5 lines
kernel/trace/seq_buf.c new file +0 -0
new file mode 100644
index 000000000000..88738b200bf3

[...]

Quoted 15 lines
kernel/trace/seq_buf.c new file +15 -0
+
+/**
+ * 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);

kernel/trace/seq_buf.c line 107

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+	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.

Quoted 9 lines
kernel/trace/seq_buf.c new file +9 -0
+		if (ret < len) {
+			s->len += ret;
+			return 0;
+		}
+	}
+	seq_buf_set_overflow(s);
+	return -1;
+}
+

[...]

Quoted 4 lines
kernel/trace/trace_seq.c +0 -0
index 1f24ed99dca2..3ad8738aea19 100644

[...]

Quoted 6 lines
kernel/trace/trace_seq.c +1 -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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 171

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
 	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

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 180

Quoted 12 lines
kernel/trace/trace_seq.c +3 -4
 	/* 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;
Quoted 9 lines
kernel/trace/trace_seq.c +1 -1
 }
 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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_vprintf); · line 211

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
 	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.

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 180

Quoted 12 lines
kernel/trace/trace_seq.c +3 -4
 	/* 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;
Quoted 3 lines
kernel/trace/trace_seq.c +0 -0
 }
 EXPORT_SYMBOL_GPL(trace_seq_bprintf);
 

[...]

Quoted 36 lines

/**
* trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
* @s: trace sequence descriptor

kernel/trace/trace_seq.c +2 -23
@@ -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);

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem); · line 346

Quoted 10 lines
kernel/trace/trace_seq.c +10 -0
+	__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

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem); · line 354

Quoted 9 lines
kernel/trace/trace_seq.c +7 -1
+
+	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;
Quoted 22 lines
kernel/trace/trace_seq.c +4 -2
 }
 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);

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 385

Quoted 10 lines
kernel/trace/trace_seq.c +1 -9
-	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.

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 393

Quoted 9 lines
kernel/trace/trace_seq.c +5 -2
+	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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 395

Quoted 2 lines
kernel/trace/trace_seq.c +0 -0
 }
 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.

author maintainer
#22 lore raw

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

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

include/linux/seq_buf.h line 56

Quoted 7 lines
include/linux/seq_buf.h new file +7 -0
+/*
+ * 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

Quoted 6 lines
	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.

Quoted 39 lines

[...]

Quoted 5 lines
kernel/trace/seq_buf.c new file +0 -0
new file mode 100644
index 000000000000..88738b200bf3

[...]

Quoted 15 lines
kernel/trace/seq_buf.c new file +15 -0
+
+/**
+ * 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);

kernel/trace/seq_buf.c line 107

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+	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:

Quoted 4 lines
		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.

Quoted 47 lines
Quoted 10 lines
kernel/trace/seq_buf.c new file +9 -0
+		if (ret < len) {
+			s->len += ret;
+			return 0;
+		}
+	}
+	seq_buf_set_overflow(s);
+	return -1;
+}
+

[...]

Quoted 4 lines
kernel/trace/trace_seq.c +0 -0
index 1f24ed99dca2..3ad8738aea19 100644

[...]

Quoted 6 lines
kernel/trace/trace_seq.c +1 -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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 171

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
 	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

Quoted 8 lines
kernel/trace/trace_seq.c +2 -1
 	/* 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;
 	}
 
Quoted 8 lines

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 180

Quoted 4 lines
kernel/trace/trace_seq.c +1 -3
-	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.

Quoted 31 lines
Quoted 10 lines
kernel/trace/trace_seq.c +1 -1
 }
 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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_vprintf); · line 211

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
 	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.

Quoted 8 lines
kernel/trace/trace_seq.c +2 -1
 	/* 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;
 	}
 
Quoted 6 lines

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 180

Quoted 4 lines
kernel/trace/trace_seq.c +1 -3
-	s->len += ret;
-
-	return len;
+	return ret;

and

same thing.

Quoted 70 lines
	return s->seq.len - save_len;
Quoted 3 lines
kernel/trace/trace_seq.c +0 -0
 }
 EXPORT_SYMBOL_GPL(trace_seq_bprintf);
 

[...]

Quoted 36 lines

/**
* trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
* @s: trace sequence descriptor

kernel/trace/trace_seq.c +2 -23
@@ -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);

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem); · line 346

Quoted 10 lines
kernel/trace/trace_seq.c +10 -0
+	__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

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem); · line 354

Quoted 9 lines
kernel/trace/trace_seq.c +7 -1
+
+	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:

Quoted 2 lines
	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.

Quoted 52 lines
Quoted 22 lines
kernel/trace/trace_seq.c +4 -2
 }
 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);

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 385

Quoted 10 lines
kernel/trace/trace_seq.c +1 -9
-	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.

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 393

Quoted 9 lines
kernel/trace/trace_seq.c +5 -2
+	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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); · line 395

Quoted 2 lines
kernel/trace/trace_seq.c +0 -0
 }
 EXPORT_SYMBOL_GPL(trace_seq_path);
Quoted 6 lines

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

#23 lore raw

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

Quoted 17 lines · Steven Rostedt
Quoted 14 lines · On Mon, 10 Nov 2014 14:53:30 +0100 Petr Mladek <[email protected]> wrote:

include/linux/seq_buf.h line 56

Quoted 7 lines
include/linux/seq_buf.h new file +7 -0
+/*
+ * 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;
Quoted 8 lines
Quoted 2 lines

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

Quoted 38 lines
Quoted 38 lines

[...]

Quoted 5 lines
kernel/trace/seq_buf.c new file +0 -0
new file mode 100644
index 000000000000..88738b200bf3

[...]

Quoted 15 lines
kernel/trace/seq_buf.c new file +15 -0
+
+/**
+ * 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);

kernel/trace/seq_buf.c line 107

Quoted 10 lines
kernel/trace/seq_buf.c new file +10 -0
+	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:

Quoted 7 lines
Quoted 4 lines
		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.

Quoted 59 lines
Quoted 47 lines
Quoted 10 lines
kernel/trace/seq_buf.c new file +9 -0
+		if (ret < len) {
+			s->len += ret;
+			return 0;
+		}
+	}
+	seq_buf_set_overflow(s);
+	return -1;
+}
+

[...]

Quoted 4 lines
kernel/trace/trace_seq.c +0 -0
index 1f24ed99dca2..3ad8738aea19 100644

[...]

Quoted 6 lines
kernel/trace/trace_seq.c +1 -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;

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 171

Quoted 10 lines
kernel/trace/trace_seq.c +4 -2
 	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

Quoted 8 lines
kernel/trace/trace_seq.c +2 -1
 	/* 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;
 	}
 
Quoted 8 lines

kernel/trace/trace_seq.c EXPORT_SYMBOL_GPL(trace_seq_bitmask); · line 180

Quoted 4 lines
kernel/trace/trace_seq.c +1 -3
-	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.

Quoted 7 lines

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

#24 lore raw

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

In reply to Steven Rostedt
Quoted 8 lines · Steven Rostedt
Quoted 5 lines · On Wed, 5 Nov 2014 15:22:22 +0100 Petr Mladek <[email protected]> wrote:
Quoted 1 line · Steven Rostedt

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:

Quoted 8 lines
Quoted 4 lines

/*
* 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.

Quoted 11 lines
Quoted 8 lines

kernel/trace/seq_buf.c line 20

Quoted 6 lines

+ */

kernel/trace/seq_buf.c new file +5 -0
+#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.

Quoted 11 lines
Quoted 8 lines

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

Quoted 1 line

+#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.

Quoted 25 lines
Quoted 16 lines
Quoted 16 lines
kernel/trace/seq_buf.c new file +16 -0
+
+/**
+ * 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);
+
Quoted 8 lines

kernel/trace/seq_buf.c line 171

Quoted 5 lines
kernel/trace/seq_buf.c new file +5 -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.

Quoted 1 line

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

Fair enough.

Quoted 46 lines
Quoted 38 lines
Quoted 28 lines

+ *
+ * Returns -EFAULT if the copy to userspace fails.
+ */

kernel/trace/seq_buf.c new file +23 -0
+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;
+}

[...]

Quoted 7 lines
kernel/trace/trace_seq.c +0 -0
index 1f24ed99dca2..960ccfb2f50c 100644
@@ -27,10 +27,19 @@
 #include <linux/trace_seq.h>
 
Quoted 7 lines

kernel/trace/trace_seq.c line 30

Quoted 3 lines
kernel/trace/trace_seq.c +1 -1
 /* 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.

Quoted 2 lines

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.

Quoted 56 lines
Quoted 50 lines
Quoted 50 lines
kernel/trace/trace_seq.c +18 -7
 /* 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);
 
Quoted 5 lines

kernel/trace/trace_seq.c int trace_print_seq(struct seq_file *m, struct trace_seq *s) · line 103

Quoted 3 lines
kernel/trace/trace_seq.c +1 -1
 	/* 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.

Quoted 4 lines

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.

Quoted 1 line

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

Good to know.

Best Regards,
Petr

#25 lore raw

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

In reply to Steven Rostedt
Quoted 26 lines · Steven Rostedt

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.

---
 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
include/linux/seq_buf.h new file +0 -0
new file mode 100644
index 000000000000..97872154d51c

include/linux/seq_buf.h line 6

Quoted 6 lines
include/linux/seq_buf.h new file +6 -0
@@ -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

author maintainer
#26 lore raw

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

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

index 000000000000..97872154d51c

include/linux/seq_buf.h new file +1 -0
@@ -0,0 +1,72 @@
+#ifndef _LINUX_SEQ_BUF_H
Quoted 8 lines

include/linux/seq_buf.h line 6

Quoted 5 lines
include/linux/seq_buf.h new file +5 -0
+#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

author maintainer
#27 lore raw

RFC PATCH v3 4/12 tracing: Convert seq_buf_path() to be like seq_path()

In reply to Steven Rostedt

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

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 30 ++++++++++++++++--------------
 kernel/trace/trace_seq.c |  2 +-
 3 files changed, 18 insertions(+), 16 deletions(-)
include/linux/seq_buf.h +1 -1
index 97872154d51c..6d1c57d6073f 100644
@@ -64,7 +64,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +16 -14
index 2bf582753902..243123b12d16 100644
@@ -272,28 +272,30 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *
  * Returns zero on success, -1 on overflow
  */
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = SEQ_BUF_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;
+	char *buf = s->buffer + s->len;
+	size_t size = SEQ_BUF_LEFT(s);
+	int res = -1;
+
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;
 }
 
 /**
kernel/trace/trace_seq.c +1 -1
index 960ccfb2f50c..6599577cea15 100644
@@ -382,7 +382,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path)
 		return 0;
 	}
 
-	ret = seq_buf_path(&s->seq, path);
+	ret = seq_buf_path(&s->seq, path, "\n");
 
 	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
 		s->seq.len = save_len;
Signature · 1 line
2.1.1
#28 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 40 lines · Steven Rostedt

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

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 30 ++++++++++++++++--------------
 kernel/trace/trace_seq.c |  2 +-
 3 files changed, 18 insertions(+), 16 deletions(-)
include/linux/seq_buf.h +1 -1
index 97872154d51c..6d1c57d6073f 100644
@@ -64,7 +64,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +1 -0
index 2bf582753902..243123b12d16 100644
@@ -272,28 +272,30 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 286

Quoted 10 lines
kernel/trace/seq_buf.c +1 -5
  * Returns zero on success, -1 on overflow
 
  */
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = SEQ_BUF_LEFT(s);
-	unsigned char *p;
-
-	WARN_ON(s->size == 0);

I would keep this check.

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 284

Quoted 8 lines
kernel/trace/seq_buf.c +2 -6
-	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;
+	char *buf = s->buffer + s->len;
+	size_t size = SEQ_BUF_LEFT(s);

I would use the variable name "len" to make it consistent with the other fucntions in seq_buf.c.

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 295

Quoted 11 lines
kernel/trace/seq_buf.c +8 -1
+	int res = -1;
+
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);

We still should set overflow on failure.

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 298

Quoted 5 lines
kernel/trace/seq_buf.c +4 -1
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;

It returns -1 on failure and the number of written characters on success. This is incompatible with the other seq_buf functions and with the comment above this function. Also it changes the return value from trace_seq_path().

I do not mind about the used scheme but I think that we should make it consistent.

Best Regards,
Petr

author maintainer
#29 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 10 lines · On Wed, 5 Nov 2014 15:45:53 +0100 Petr Mladek <[email protected]> wrote:

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 285

Quoted 8 lines

*/

kernel/trace/seq_buf.c +1 -5
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = SEQ_BUF_LEFT(s);
-	unsigned char *p;
-
-	WARN_ON(s->size == 0);

I would keep this check.

Yeah, I could.

Quoted 12 lines

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 284

Quoted 9 lines
kernel/trace/seq_buf.c +2 -6
-	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;
+	char *buf = s->buffer + s->len;
+	size_t size = SEQ_BUF_LEFT(s);

I would use the variable name "len" to make it consistent with the other fucntions in seq_buf.c.

Note, seq_path() is a different beast than the other seq_*() functions (this will be keeping a return code). And the inconsistency is in seq_file.c as well. I'm not saying we shouldn't keep it consistent. But as this patch is to make seq_buf like seq_file, I'll keep the inconsistencies the same too.

We can always do a clean up later.

Quoted 14 lines

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 295

Quoted 12 lines
kernel/trace/seq_buf.c +8 -1
+	int res = -1;
+
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);

We still should set overflow on failure.

Again, this is different in seq_file too. I'm leaving it as is.

Quoted 12 lines

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 298

Quoted 6 lines
kernel/trace/seq_buf.c +4 -1
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;

It returns -1 on failure and the number of written characters on success. This is incompatible with the other seq_buf functions and with the comment above this function. Also it changes the return value from trace_seq_path().

Quoted 3 lines

I do not mind about the used scheme but I think that we should make it consistent.

As seq_file has had this inconsistency for a long time, and this code is to try to merge the code between trace_seq and seq_file, I'm going to follow seq_file as that has been around much longer than trace_seq.

-- Steve

#30 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 15 lines · Steven Rostedt
Quoted 10 lines · On Wed, 5 Nov 2014 15:45:53 +0100 Petr Mladek <[email protected]> wrote:

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 285

Quoted 8 lines

*/

kernel/trace/seq_buf.c +1 -5
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = SEQ_BUF_LEFT(s);
-	unsigned char *p;
-
-	WARN_ON(s->size == 0);

I would keep this check.

Quoted 1 line

Yeah, I could.

great :-)

Quoted 13 lines
Quoted 12 lines

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 284

Quoted 9 lines
kernel/trace/seq_buf.c +2 -6
-	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;
+	char *buf = s->buffer + s->len;
+	size_t size = SEQ_BUF_LEFT(s);

I would use the variable name "len" to make it consistent with the other fucntions in seq_buf.c.

Quoted 5 lines

Note, seq_path() is a different beast than the other seq_*() functions (this will be keeping a return code). And the inconsistency is in seq_file.c as well. I'm not saying we shouldn't keep it consistent. But as this patch is to make seq_buf like seq_file, I'll keep the inconsistencies the same too.

Ah, I have missed this.

Quoted 1 line

We can always do a clean up later.

Fair enough. The result of this patch set (safe printing of backtraces from all CPUs) is more important than this clean up.

[...]

Quoted 12 lines
Quoted 12 lines

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 298

Quoted 6 lines
kernel/trace/seq_buf.c +4 -1
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;

It returns -1 on failure and the number of written characters on success. This is incompatible with the other seq_buf functions and with the comment above this function. Also it changes the return value from trace_seq_path().

Quoted 7 lines
Quoted 3 lines

I do not mind about the used scheme but I think that we should make it consistent.

As seq_file has had this inconsistency for a long time, and this code is to try to merge the code between trace_seq and seq_file, I'm going to follow seq_file as that has been around much longer than trace_seq.

I see. Well, we still need to fix trace_seq_path() or its callers. For example, seq_print_user_ip() checks trace_seq_path() return value and expects 0 on failure.

Best Regards,
Petr

author maintainer
#31 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 3 lines · On Thu, 6 Nov 2014 15:18:22 +0100 Petr Mladek <[email protected]> wrote:

I see. Well, we still need to fix trace_seq_path() or its callers. For example, seq_print_user_ip() checks trace_seq_path() return value and expects 0 on failure.

Crap, thanks for pointing that out. Luckily, there's only one user, and it's not used much. It's the user backtrace and that only prints if a file is found, and this would only cause the offset not to be printed.

-- Steve

#32 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

In reply to Steven Rostedt
Quoted 33 lines · Steven Rostedt

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

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 30 ++++++++++++++++--------------
 kernel/trace/trace_seq.c |  2 +-
 3 files changed, 18 insertions(+), 16 deletions(-)
include/linux/seq_buf.h +1 -1
index 97872154d51c..6d1c57d6073f 100644
@@ -64,7 +64,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +0 -0
index 2bf582753902..243123b12d16 100644

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 279

Quoted 8 lines
kernel/trace/seq_buf.c +1 -0
@@ -272,28 +272,30 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *
  * Returns zero on success, -1 on overflow

Ah, I have missed this. We should also update this comment to reflect the change. I would also add a note that it is because of compatibility with seq_path(). So, something like:

* Return the number of written bytes on success, -1 on overflow *
* The behavior is consistent with seq_path()

Quoted 4 lines
kernel/trace/seq_buf.c +1 -1
  */
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {

[...]

Quoted 4 lines
kernel/trace/seq_buf.c +4 -0
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 298

Quoted 10 lines
kernel/trace/seq_buf.c +6 -2
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;

Best Regards,
Petr

author maintainer Tested-by Jiri Kosina Acked-by Jiri Kosina
#33 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

I made a few touch ups on this patch.

-- Steve

Quoted 1 line

From 309e8ce4cc1605d3038b16b8e43219a2867f47b1 Mon Sep 17 00:00:00 2001

From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 13:48:37 -0400
Subject: [PATCH] tracing: Convert seq_buf_path() to be like seq_path()

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 26 +++++++++++++++-----------
 kernel/trace/trace_seq.c |  4 ++--
 3 files changed, 18 insertions(+), 14 deletions(-)
include/linux/seq_buf.h +1 -1
index 64bf5a43411e..aec09b126082 100644
@@ -70,7 +70,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +15 -11
index 88738b200bf3..ac6eb864c946 100644
@@ -272,28 +272,32 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *
  * Returns zero on success, -1 on overflow
  */
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = seq_buf_buffer_left(s);
-	unsigned char *p;
+	char *buf = s->buffer + s->len;
+	size_t size = seq_buf_buffer_left(s);
+	int res = -1;
 
 	WARN_ON(s->size == 0);
 
-	p = d_path(path, s->buffer + s->len, len);
-	if (!IS_ERR(p)) {
-		p = mangle_path(s->buffer + s->len, p, "\n");
-		if (p) {
-			s->len = p - s->buffer;
-			return 0;
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;
 }
 
 /**
kernel/trace/trace_seq.c +2 -2
index 3ad8738aea19..64550ed28e76 100644
@@ -382,7 +382,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path)
 		return 0;
 	}
 
-	ret = seq_buf_path(&s->seq, path);
+	ret = seq_buf_path(&s->seq, path, "\n");
 
 	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
 		s->seq.len = save_len;
@@ -390,7 +390,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path)
 		return 0;
 	}
 
-	return ret;
+	return 1;
 }
 EXPORT_SYMBOL_GPL(trace_seq_path);
 
Signature · 1 line
2.1.1
Reviewed-by
#34 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 45 lines · Steven Rostedt

I made a few touch ups on this patch.

-- Steve

From 309e8ce4cc1605d3038b16b8e43219a2867f47b1 Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 13:48:37 -0400
Subject: [PATCH] tracing: Convert seq_buf_path() to be like seq_path()

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 26 +++++++++++++++-----------
 kernel/trace/trace_seq.c |  4 ++--
 3 files changed, 18 insertions(+), 14 deletions(-)
include/linux/seq_buf.h +1 -1
index 64bf5a43411e..aec09b126082 100644
@@ -70,7 +70,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +0 -0
index 88738b200bf3..ac6eb864c946 100644

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 279

Quoted 8 lines
kernel/trace/seq_buf.c +1 -0
@@ -272,28 +272,32 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *
  * Returns zero on success, -1 on overflow

This should be

* Returns the number of written bytes on success, -1 on overflow

This is just a minor issue. I believe the you would fix it. Feel free to add:

Best Regards,
Petr

Quoted 59 lines
kernel/trace/seq_buf.c +14 -11
-int seq_buf_path(struct seq_buf *s, const struct path *path)
+int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	unsigned int len = seq_buf_buffer_left(s);
-	unsigned char *p;
+	char *buf = s->buffer + s->len;
+	size_t size = seq_buf_buffer_left(s);
+	int res = -1;
 
 	WARN_ON(s->size == 0);
 
-	p = d_path(path, s->buffer + s->len, len);
-	if (!IS_ERR(p)) {
-		p = mangle_path(s->buffer + s->len, p, "\n");
-		if (p) {
-			s->len = p - s->buffer;
-			return 0;
+	if (size) {
+		char *p = d_path(path, buf, size);
+		if (!IS_ERR(p)) {
+			char *end = mangle_path(buf, p, esc);
+			if (end)
+				res = end - buf;
 		}
 	}
-	seq_buf_set_overflow(s);
-	return -1;
+	if (res > 0)
+		s->len += res;
+
+	return res;
 }
 
 /**
kernel/trace/trace_seq.c +2 -2
index 3ad8738aea19..64550ed28e76 100644
@@ -382,7 +382,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path)
 		return 0;
 	}
 
-	ret = seq_buf_path(&s->seq, path);
+	ret = seq_buf_path(&s->seq, path, "\n");
 
 	if (unlikely(seq_buf_has_overflowed(&s->seq))) {
 		s->seq.len = save_len;
@@ -390,7 +390,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path)
 		return 0;
 	}
 
-	return ret;
+	return 1;
 }
 EXPORT_SYMBOL_GPL(trace_seq_path);
 
Signature · 1 line
2.1.1
author maintainer
#35 lore raw

Re: [RFC][PATCH 04/12 v3] tracing: Convert seq_buf_path() to be like seq_path()

Quoted 57 lines · On Mon, 10 Nov 2014 15:03:25 +0100 Petr Mladek <[email protected]> wrote:
Quoted 45 lines · Steven Rostedt

I made a few touch ups on this patch.

-- Steve

From 309e8ce4cc1605d3038b16b8e43219a2867f47b1 Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 13:48:37 -0400
Subject: [PATCH] tracing: Convert seq_buf_path() to be like seq_path()

Rewrite seq_buf_path() like it is done in seq_path() and allow it to accept any escape character instead of just "\n".

Making seq_buf_path() like seq_path() will help prevent problems when converting seq_file to use the seq_buf logic.

---
 include/linux/seq_buf.h  |  2 +-
 kernel/trace/seq_buf.c   | 26 +++++++++++++++-----------
 kernel/trace/trace_seq.c |  4 ++--
 3 files changed, 18 insertions(+), 14 deletions(-)
include/linux/seq_buf.h +1 -1
index 64bf5a43411e..aec09b126082 100644
@@ -70,7 +70,7 @@ extern int seq_buf_putc(struct seq_buf *s, unsigned char c);
 extern int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len);
 extern int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
 			      unsigned int len);
-extern int seq_buf_path(struct seq_buf *s, const struct path *path);
+extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc);
 
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
kernel/trace/seq_buf.c +0 -0
index 88738b200bf3..ac6eb864c946 100644

kernel/trace/seq_buf.c int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, · line 279

Quoted 8 lines
kernel/trace/seq_buf.c +1 -0
@@ -272,28 +272,32 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  * seq_buf_path - copy a path into the sequence buffer
  * @s: seq_buf descriptor
  * @path: path to write into the sequence buffer.
+ * @esc: set of characters to escape in the output
  *
  * Write a path name into the sequence buffer.
  *
  * Returns zero on success, -1 on overflow

This should be

Quoted 1 line

* Returns the number of written bytes on success, -1 on overflow

Yep.

Quoted 5 lines

This is just a minor issue. I believe the you would fix it. Feel free to add:

Thanks,

-- Steve

author maintainer
#36 lore raw

RFC PATCH v3 5/12 tracing: Convert seq_buf fields to be like seq_file fields

In reply to Steven Rostedt

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

In facilitating the conversion of seq_file to use seq_buf, have the seq_buf fields match the types used by seq_file.

---
 include/linux/seq_buf.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +4 -4
index 6d1c57d6073f..a4d114e6f740 100644
@@ -19,10 +19,10 @@
  * @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;
+	char			*buffer;
+	size_t			size;
+	size_t			len;
+	loff_t			readpos;
 };
 
 static inline void
Signature · 1 line
2.1.1
#37 lore raw

Re: [RFC][PATCH 05/12 v3] tracing: Convert seq_buf fields to be like seq_file fields

Quoted 15 lines · Steven Rostedt

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

In facilitating the conversion of seq_file to use seq_buf, have the seq_buf fields match the types used by seq_file.

---
 include/linux/seq_buf.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +0 -0
index 6d1c57d6073f..a4d114e6f740 100644

include/linux/seq_buf.h line 22

Quoted 8 lines
include/linux/seq_buf.h +1 -4
@@ -19,10 +19,10 @@
  * @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;
+	char			*buffer;

It would make sense to use "char" from the beginning. In fact, it is already used on many locations in seq_buf.c. Or we might want to get rid of "unsigned char" in seq_buf.c here as well.

include/linux/seq_buf.h line 25

Quoted 3 lines
include/linux/seq_buf.h +3 -0
+	size_t			size;
+	size_t			len;
+	loff_t			readpos;

I have just noticed that the variable is called "read_pos" in seq_file. Are you going to sync the name later?

Also I am a bit curious that "readpos" use another type than "len" and "size". Well, this is not in the scope of this patchset. I am fine with keeping "loff_t" at this point.

Best Regards,
Petr

author maintainer
#38 lore raw

Re: [RFC][PATCH 05/12 v3] tracing: Convert seq_buf fields to be like seq_file fields

Quoted 20 lines · On Wed, 5 Nov 2014 16:57:11 +0100 Petr Mladek <[email protected]> wrote:
Quoted 19 lines · Steven Rostedt

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

In facilitating the conversion of seq_file to use seq_buf, have the seq_buf fields match the types used by seq_file.

---
 include/linux/seq_buf.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +0 -1
index 6d1c57d6073f..a4d114e6f740 100644
@@ -19,10 +19,10 @@
  * @overflow:	Set if more bytes should have been written to buffer
  */
 struct seq_buf {
-	unsigned char		*buffer;
Quoted 8 lines

include/linux/seq_buf.h line 22

Quoted 4 lines
include/linux/seq_buf.h +1 -3
-	unsigned int		size;
-	unsigned int		len;
-	unsigned int		readpos;
+	char			*buffer;

It would make sense to use "char" from the beginning. In fact, it is already used on many locations in seq_buf.c. Or we might want to get rid of "unsigned char" in seq_buf.c here as well.

I could, but I'm being lazy ;-) No reason to change the patch series for something as small as this. It doesn't break bisect.

Quoted 7 lines

include/linux/seq_buf.h line 25

Quoted 4 lines
include/linux/seq_buf.h +3 -0
+	size_t			size;
+	size_t			len;
+	loff_t			readpos;

I have just noticed that the variable is called "read_pos" in seq_file. Are you going to sync the name later?

Yeah, I purposely kept them different to find the two when needed.

Quoted 4 lines

Also I am a bit curious that "readpos" use another type than "len" and "size". Well, this is not in the scope of this patchset. I am fine with keeping "loff_t" at this point.

Again, seq_file has been around for a long time with these types. But as you said, it's out of scope for this patch series. I'm just trying to keep with what's been the norm here.

-- Steve

Reviewed-by
#39 lore raw

Re: [RFC][PATCH 05/12 v3] tracing: Convert seq_buf fields to be like seq_file fields

Quoted 36 lines · Steven Rostedt
Quoted 20 lines · On Wed, 5 Nov 2014 16:57:11 +0100 Petr Mladek <[email protected]> wrote:
Quoted 19 lines · Steven Rostedt

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

In facilitating the conversion of seq_file to use seq_buf, have the seq_buf fields match the types used by seq_file.

---
 include/linux/seq_buf.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +0 -1
index 6d1c57d6073f..a4d114e6f740 100644
@@ -19,10 +19,10 @@
  * @overflow:	Set if more bytes should have been written to buffer
  */
 struct seq_buf {
-	unsigned char		*buffer;
Quoted 8 lines

include/linux/seq_buf.h line 22

Quoted 4 lines
include/linux/seq_buf.h +1 -3
-	unsigned int		size;
-	unsigned int		len;
-	unsigned int		readpos;
+	char			*buffer;

It would make sense to use "char" from the beginning. In fact, it is already used on many locations in seq_buf.c. Or we might want to get rid of "unsigned char" in seq_buf.c here as well.

I could, but I'm being lazy ;-) No reason to change the patch series for something as small as this. It doesn't break bisect.

Quoted 1 line
Quoted 8 lines
Quoted 6 lines

include/linux/seq_buf.h line 25

Quoted 3 lines
include/linux/seq_buf.h +3 -0
+	size_t			size;
+	size_t			len;
+	loff_t			readpos;

I have just noticed that the variable is called "read_pos" in seq_file. Are you going to sync the name later?

Yeah, I purposely kept them different to find the two when needed.

I see. And if I get it right, you want to use "seq_buf" in "file_seq", so there will be only one variable and no confusion in the end.

Quoted 8 lines
Quoted 4 lines

Also I am a bit curious that "readpos" use another type than "len" and "size". Well, this is not in the scope of this patchset. I am fine with keeping "loff_t" at this point.

Again, seq_file has been around for a long time with these types. But as you said, it's out of scope for this patch series. I'm just trying to keep with what's been the norm here.

Fair enough. I am fine with the explanation.

Best Regards,
Petr

author maintainer
#40 lore raw

RFC PATCH v3 6/12 tracing: Add a seq_buf_clear() helper and clear len and readpos in init

In reply to Steven Rostedt

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

Add a helper function seq_buf_clear() that resets the len and readpos fields of a seq_buf. Currently it is only used in the seq_buf_init() but will be used later when updating the seq_file code.

---
 include/linux/seq_buf.h | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
include/linux/seq_buf.h +7 -2
index a4d114e6f740..064a8604ad33 100644
@@ -25,13 +25,18 @@ struct seq_buf {
 	loff_t			readpos;
 };
 
+static inline void seq_buf_clear(struct seq_buf *s)
+{
+	s->len = 0;
+	s->readpos = 0;
+}
+
 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_clear(s);
 }
 
 /*
Signature · 1 line
2.1.1
Reviewed-by on v3 6/12
#41 lore raw

Re: [RFC][PATCH 06/12 v3] tracing: Add a seq_buf_clear() helper and clear len and readpos in init

Quoted 7 lines · Steven Rostedt

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

Add a helper function seq_buf_clear() that resets the len and readpos fields of a seq_buf. Currently it is only used in the seq_buf_init() but will be used later when updating the seq_file code.

Best Regards,
Petr

author maintainer
#42 lore raw

RFC PATCH v3 7/12 tracing: Have seq_buf use full buffer

In reply to Steven Rostedt

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

Currently seq_buf is full when all but one byte of the buffer is filled. Change it so that the seq_buf is full when all of the buffer is filled.

Some of the functions would fill the buffer completely and report everything was fine. This was inconsistent with the max of size - 1. Changing this to be max of size makes all functions consistent.

---
 include/linux/seq_buf.h |  4 ++--
 kernel/trace/seq_buf.c  | 18 +++++++++---------
 2 files changed, 11 insertions(+), 11 deletions(-)
include/linux/seq_buf.h +2 -2
index 064a8604ad33..3cd25038cb5e 100644
@@ -46,13 +46,13 @@ seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
 static inline bool
 seq_buf_has_overflowed(struct seq_buf *s)
 {
-	return s->len == s->size;
+	return s->len > s->size;
 }
 
 static inline void
 seq_buf_set_overflow(struct seq_buf *s)
 {
-	s->len = s->size;
+	s->len = s->size + 1;
 }
 
 extern __printf(2, 3)
kernel/trace/seq_buf.c +9 -9
index 243123b12d16..06fd1833e692 100644
@@ -11,17 +11,17 @@
  * 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)
+#define SEQ_BUF_LEFT(s) ((s)->size - (s)->len)
 
 /* How much buffer is written? */
-#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)
+#define SEQ_BUF_USED(s) min((s)->len, (s)->size)
 
 /**
  * seq_buf_print_seq - move the contents of seq_buf into a seq_file
@@ -55,7 +55,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 
 	if (s->len < s->size) {
 		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
+		if (s->len + len <= s->size) {
 			s->len += len;
 			return 0;
 		}
@@ -105,7 +105,7 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 
 	if (s->len < s->size) {
 		ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
-		if (s->len + ret < s->size) {
+		if (s->len + ret <= s->size) {
 			s->len += ret;
 			return 0;
 		}
@@ -140,7 +140,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
 
 	if (s->len < s->size) {
 		ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
-		if (s->len + ret < s->size) {
+		if (s->len + ret <= s->size) {
 			s->len += ret;
 			return 0;
 		}
@@ -164,7 +164,7 @@ int seq_buf_puts(struct seq_buf *s, const char *str)
 
 	WARN_ON(s->size == 0);
 
-	if (s->len + len < s->size) {
+	if (s->len + len <= s->size) {
 		memcpy(s->buffer + s->len, str, len);
 		s->len += len;
 		return 0;
@@ -186,7 +186,7 @@ int seq_buf_putc(struct seq_buf *s, unsigned char c)
 {
 	WARN_ON(s->size == 0);
 
-	if (s->len + 1 < s->size) {
+	if (s->len + 1 <= s->size) {
 		s->buffer[s->len++] = c;
 		return 0;
 	}
@@ -210,7 +210,7 @@ int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
 {
 	WARN_ON(s->size == 0);
 
-	if (s->len + len < s->size) {
+	if (s->len + len <= s->size) {
 		memcpy(s->buffer + s->len, mem, len);
 		s->len += len;
 		return 0;
Signature · 1 line
2.1.1
#43 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 62 lines · Steven Rostedt

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

Currently seq_buf is full when all but one byte of the buffer is filled. Change it so that the seq_buf is full when all of the buffer is filled.

Some of the functions would fill the buffer completely and report everything was fine. This was inconsistent with the max of size - 1. Changing this to be max of size makes all functions consistent.

---
 include/linux/seq_buf.h |  4 ++--
 kernel/trace/seq_buf.c  | 18 +++++++++---------
 2 files changed, 11 insertions(+), 11 deletions(-)
include/linux/seq_buf.h +2 -2
index 064a8604ad33..3cd25038cb5e 100644
@@ -46,13 +46,13 @@ seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
 static inline bool
 seq_buf_has_overflowed(struct seq_buf *s)
 {
-	return s->len == s->size;
+	return s->len > s->size;
 }
 
 static inline void
 seq_buf_set_overflow(struct seq_buf *s)
 {
-	s->len = s->size;
+	s->len = s->size + 1;
 }
 
 extern __printf(2, 3)
kernel/trace/seq_buf.c +3 -3
index 243123b12d16..06fd1833e692 100644
@@ -11,17 +11,17 @@
  * 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)
+#define SEQ_BUF_LEFT(s) ((s)->size - (s)->len)
 
 /* How much buffer is written? */
-#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)
+#define SEQ_BUF_USED(s) min((s)->len, (s)->size)
 
 /**
  * seq_buf_print_seq - move the contents of seq_buf into a seq_file

kernel/trace/seq_buf.c int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) · line 58

Quoted 5 lines
kernel/trace/seq_buf.c +1 -1
@@ -55,7 +55,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 
 	if (s->len < s->size) {
 		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
+		if (s->len + len <= s->size) {

This is always true because we limit vsnprintf() to write (s->size - s->len) bytes. Similar problem is also in the other parts of this patch.

I wonder if we want this change at all. It means that we are not able to detect overflow in some functions. It is pity because the users might want to increase the buffer size and try again if the print was incomplete.

I think that we need to leave the one byte for the overflow detection if we want to detect it properly.

Best Regards,
Petr

Quoted 12 lines
kernel/trace/seq_buf.c +1 -1
 			s->len += len;
 			return 0;
 		}
@@ -105,7 +105,7 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 
 	if (s->len < s->size) {
 		ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
-		if (s->len + ret < s->size) {
+		if (s->len + ret <= s->size) {
 			s->len += ret;
 			return 0;
 		}
author maintainer
#44 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 4 lines · On Wed, 5 Nov 2014 17:31:50 +0100 Petr Mladek <[email protected]> wrote:
Quoted 4 lines

/**
* seq_buf_print_seq - move the contents of seq_buf into a seq_file

(quoted diff) +0 -0
@@ -55,7 +55,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 
Quoted 8 lines

kernel/trace/seq_buf.c int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) · line 58

Quoted 4 lines
kernel/trace/seq_buf.c +1 -1
 	if (s->len < s->size) {
 		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
+		if (s->len + len <= s->size) {

This is always true because we limit vsnprintf() to write (s->size - s->len) bytes. Similar problem is also in the other parts of this patch.

No, len is the length of bytes that should have been written, not the amount that has been written.

Quoted 5 lines

I wonder if we want this change at all. It means that we are not able to detect overflow in some functions. It is pity because the users might want to increase the buffer size and try again if the print was incomplete.

What do you mean we can't detect overflow? That's what seq_buf_has_overflowed() does.

Quoted 3 lines

I think that we need to leave the one byte for the overflow detection if we want to detect it properly.

I don't.

-- Steve

author maintainer
#45 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 9 lines · On Wed, 5 Nov 2014 15:21:30 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 5 lines

I wonder if we want this change at all. It means that we are not able to detect overflow in some functions. It is pity because the users might want to increase the buffer size and try again if the print was incomplete.

What do you mean we can't detect overflow? That's what seq_buf_has_overflowed() does.

Although I'm looking at the seq_file versions of the bitmap code, which does only return the len of what was written and not what would have been written, and it does have this issue.

I hate to go back to the -1 of the size of buffer as that causes inconsistencies within the functions themselves, as proved with the seq_file code.

What I might do as just have the bitmap calls not be allowed to fill the buffer and keep the logic the same. That is, if the bitmap calls fill the rest of the length, assume we overflowed, otherwise we are fine.

I'm going to change seq_buf to do that instead of my new update with the bitmask code.

-- Steve

#46 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 13 lines · Steven Rostedt
Quoted 9 lines · On Wed, 5 Nov 2014 15:21:30 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 5 lines

I wonder if we want this change at all. It means that we are not able to detect overflow in some functions. It is pity because the users might want to increase the buffer size and try again if the print was incomplete.

What do you mean we can't detect overflow? That's what seq_buf_has_overflowed() does.

Quoted 7 lines

Although I'm looking at the seq_file versions of the bitmap code, which does only return the len of what was written and not what would have been written, and it does have this issue.

I hate to go back to the -1 of the size of buffer as that causes inconsistencies within the functions themselves, as proved with the seq_file code.

Yeah, the -1 and the unused byte is strange and it would be great to avoid it.

On the other hand, I am slightly afraid of the "len = size + 1" that signalizes the buffer overflow. It might be prone for creating security bugs. If people forget to check seq_buf_has_overflowed() before reading or if there is a race, they might read outside of the buffer.

Quoted 7 lines

What I might do as just have the bitmap calls not be allowed to fill the buffer and keep the logic the same. That is, if the bitmap calls fill the rest of the length, assume we overflowed, otherwise we are fine.

I'm going to change seq_buf to do that instead of my new update with the bitmask code.

I like the idea of having the exception only in the bitmap code and filling the whole buffer in other cases.

I am now in doubts about the overflow state. A solution would be to add an "overflow" flag to struct seq_bug. I agree that it is ugly but it looks more secure then "len = size + 1".

Well, I do not have that strong opinion about it. What do you think?

Best Regards,
Petr

author maintainer
#47 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 8 lines · On Thu, 6 Nov 2014 16:31:07 +0100 Petr Mladek <[email protected]> wrote:

I like the idea of having the exception only in the bitmap code and filling the whole buffer in other cases.

I am now in doubts about the overflow state. A solution would be to add an "overflow" flag to struct seq_bug. I agree that it is ugly but it looks more secure then "len = size + 1".

Well, I do not have that strong opinion about it. What do you think?

Ideally, I want struct seq_buf defined only within seq_buf.c, and all users must access the buffer via function methods.

We would need to remove all the inline calls, which will have a small affect on performance. But most seq_buf code is for output of text, where this overhead would be a nit to the total cost of operations.

That would get rid of all users that think it's safe to access s->buffer + s->len.

-- Steve

#48 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 16 lines · Steven Rostedt
Quoted 8 lines · On Thu, 6 Nov 2014 16:31:07 +0100 Petr Mladek <[email protected]> wrote:

I like the idea of having the exception only in the bitmap code and filling the whole buffer in other cases.

I am now in doubts about the overflow state. A solution would be to add an "overflow" flag to struct seq_bug. I agree that it is ugly but it looks more secure then "len = size + 1".

Well, I do not have that strong opinion about it. What do you think?

Ideally, I want struct seq_buf defined only within seq_buf.c, and all users must access the buffer via function methods.

Quoted 6 lines

We would need to remove all the inline calls, which will have a small affect on performance. But most seq_buf code is for output of text, where this overhead would be a nit to the total cost of operations.

That would get rid of all users that think it's safe to access s->buffer + s->len.

Sounds like a good plan.

Best Regards,
Petr

author maintainer Tested-by Jiri Kosina Acked-by Jiri Kosina
#49 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Some more touch ups.

-- Steve

Quoted 1 line

From 7e724556c21178a9890b31ff57f69761b41f435a Mon Sep 17 00:00:00 2001

From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 15:26:09 -0400
Subject: [PATCH] tracing: Have seq_buf use full buffer

Currently seq_buf is full when all but one byte of the buffer is filled. Change it so that the seq_buf is full when all of the buffer is filled.

Some of the functions would fill the buffer completely and report everything was fine. This was inconsistent with the max of size - 1. Changing this to be max of size makes all functions consistent.

---
 include/linux/seq_buf.h |  6 +++---
 kernel/trace/seq_buf.c  | 19 +++++++++++--------
 2 files changed, 14 insertions(+), 11 deletions(-)
include/linux/seq_buf.h +3 -3
index f68fde850ef7..4aab47d10760 100644
@@ -43,13 +43,13 @@ seq_buf_init(struct seq_buf *s, unsigned char *buf, unsigned int size)
 static inline bool
 seq_buf_has_overflowed(struct seq_buf *s)
 {
-	return s->len == s->size;
+	return s->len > s->size;
 }
 
 static inline void
 seq_buf_set_overflow(struct seq_buf *s)
 {
-	s->len = s->size;
+	s->len = s->size + 1;
 }
 
 /*
@@ -58,7 +58,7 @@ seq_buf_set_overflow(struct seq_buf *s)
 static inline unsigned int
 seq_buf_buffer_left(struct seq_buf *s)
 {
-	return (s->size - 1) - s->len;
+	return s->size - s->len;
 }
 
 extern __printf(2, 3)
kernel/trace/seq_buf.c +11 -8
index ac6eb864c946..8e60b5c1b9f3 100644
@@ -17,7 +17,7 @@
 #include <linux/seq_buf.h>
 
 /* How much buffer is written? */
-#define SEQ_BUF_USED(s) min((s)->len, (s)->size - 1)
+#define SEQ_BUF_USED(s) min((s)->len, (s)->size)
 
 /**
  * seq_buf_print_seq - move the contents of seq_buf into a seq_file
@@ -51,7 +51,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 
 	if (s->len < s->size) {
 		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
+		if (s->len + len <= s->size) {
 			s->len += len;
 			return 0;
 		}
@@ -100,8 +100,11 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 	WARN_ON(s->size == 0);
 
 	/*
-	 * The last byte of the buffer is used to determine if we
-	 * overflowed or not.
+	 * Note, because bitmap_scnprintf() only returns the number of bytes
+	 * written and not the number that would be written, we use the last
+	 * byte of the buffer to let us know if we overflowed. There's a small
+	 * chance that the bitmap could have fit exactly inside the buffer, but
+	 * it's not that critical if that does happen.
 	 */
 	if (len > 1) {
 		ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
@@ -140,7 +143,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
 
 	if (s->len < s->size) {
 		ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
-		if (s->len + ret < s->size) {
+		if (s->len + ret <= s->size) {
 			s->len += ret;
 			return 0;
 		}
@@ -164,7 +167,7 @@ int seq_buf_puts(struct seq_buf *s, const char *str)
 
 	WARN_ON(s->size == 0);
 
-	if (s->len + len < s->size) {
+	if (s->len + len <= s->size) {
 		memcpy(s->buffer + s->len, str, len);
 		s->len += len;
 		return 0;
@@ -186,7 +189,7 @@ int seq_buf_putc(struct seq_buf *s, unsigned char c)
 {
 	WARN_ON(s->size == 0);
 
-	if (s->len + 1 < s->size) {
+	if (s->len + 1 <= s->size) {
 		s->buffer[s->len++] = c;
 		return 0;
 	}
@@ -210,7 +213,7 @@ int seq_buf_putmem(struct seq_buf *s, const void *mem, unsigned int len)
 {
 	WARN_ON(s->size == 0);
 
-	if (s->len + len < s->size) {
+	if (s->len + len <= s->size) {
 		memcpy(s->buffer + s->len, mem, len);
 		s->len += len;
 		return 0;
Signature · 1 line
2.1.1
Reviewed-by
#50 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

Quoted 18 lines · Steven Rostedt

Some more touch ups.

-- Steve

From 7e724556c21178a9890b31ff57f69761b41f435a Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 15:26:09 -0400
Subject: [PATCH] tracing: Have seq_buf use full buffer

Currently seq_buf is full when all but one byte of the buffer is filled. Change it so that the seq_buf is full when all of the buffer is filled.

Some of the functions would fill the buffer completely and report everything was fine. This was inconsistent with the max of size - 1. Changing this to be max of size makes all functions consistent.

Quoted 5 lines

This patch might need a refresh if you add the extra buffer overflow check into seq_buf_set_overflow() in the 3rd patch. Anyway, changes in this patch looks good.

I am happy with all the changes in the patchset. Thanks a lot for driving it forward.

Best Regards,
Petr

#51 lore raw

Re: [RFC][PATCH 07/12 v3] tracing: Have seq_buf use full buffer

In reply to Steven Rostedt
Quoted 17 lines · Steven Rostedt
Quoted 4 lines · On Wed, 5 Nov 2014 17:31:50 +0100 Petr Mladek <[email protected]> wrote:
Quoted 4 lines

/**
* seq_buf_print_seq - move the contents of seq_buf into a seq_file

(quoted diff) +0 -0
@@ -55,7 +55,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args)
 
Quoted 8 lines

kernel/trace/seq_buf.c int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) · line 58

Quoted 4 lines
kernel/trace/seq_buf.c +1 -1
 	if (s->len < s->size) {
 		len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
-		if (s->len + len < s->size) {
+		if (s->len + len <= s->size) {

This is always true because we limit vsnprintf() to write (s->size - s->len) bytes. Similar problem is also in the other parts of this patch.

Quoted 2 lines

No, len is the length of bytes that should have been written, not the amount that has been written.

That is cool! I did not know that there was also this variant of the printf() functions. I am still learning.

Best Regards,
Petr

author maintainer
#52 lore raw

RFC PATCH v3 8/12 tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

In reply to Steven Rostedt

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

Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the
seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq.

---
 include/linux/seq_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/trace/seq_buf.c  |  7 +++----
 2 files changed, 43 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +40 -0
index 3cd25038cb5e..10c17c61c7a4 100644
@@ -55,6 +55,46 @@ seq_buf_set_overflow(struct seq_buf *s)
 	s->len = s->size + 1;
 }
 
+/**
+ * seq_buf_get_buf - get buffer to write arbitrary data to
+ * @s: the seq_buf handle
+ * @bufp: the beginning of the buffer is stored here
+ *
+ * Return the number of bytes available in the buffer, or zero if
+ * there's no space.
+ */
+static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
+{
+	BUG_ON(s->len > s->size + 1);
+
+	if (s->len < s->size) {
+		*bufp = s->buffer + s->len;
+		return s->size - s->len;
+	}
+
+	*bufp = NULL;
+	return 0;
+}
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle
+ * @num: the number of bytes to commit
+ *
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
+	if (num < 0) {
+		s->len = s->size;
+	} else {
+		BUG_ON(s->len + num > s->size + 1);
+		s->len += num;
+	}
+}
+
 extern __printf(2, 3)
 int seq_buf_printf(struct seq_buf *s, const char *fmt, ...);
 extern __printf(2, 0)
kernel/trace/seq_buf.c +3 -4
index 06fd1833e692..d118d0d6c956 100644
@@ -280,8 +280,8 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  */
 int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	char *buf = s->buffer + s->len;
-	size_t size = SEQ_BUF_LEFT(s);
+	char *buf;
+	size_t size = seq_buf_get_buf(s, &buf);
 	int res = -1;
 
 	if (size) {
@@ -292,8 +292,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 				res = end - buf;
 		}
 	}
-	if (res > 0)
-		s->len += res;
+	seq_buf_commit(s, res);
 
 	return res;
 }
Signature · 1 line
2.1.1
#53 lore raw

Re: [RFC][PATCH 08/12 v3] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

Quoted 43 lines · Steven Rostedt

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

Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the
seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq.

---
 include/linux/seq_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/trace/seq_buf.c  |  7 +++----
 2 files changed, 43 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +22 -0
index 3cd25038cb5e..10c17c61c7a4 100644
@@ -55,6 +55,46 @@ seq_buf_set_overflow(struct seq_buf *s)
 	s->len = s->size + 1;
 }
 
+/**
+ * seq_buf_get_buf - get buffer to write arbitrary data to
+ * @s: the seq_buf handle
+ * @bufp: the beginning of the buffer is stored here
+ *
+ * Return the number of bytes available in the buffer, or zero if
+ * there's no space.
+ */
+static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
+{
+	BUG_ON(s->len > s->size + 1);
+	if (s->len < s->size) {
+		*bufp = s->buffer + s->len;
+		return s->size - s->len;
+	}
+	*bufp = NULL;
+	return 0;
+}
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle

include/linux/seq_buf.h seq_buf_set_overflow(struct seq_buf *s) · line 89

Quoted 10 lines
include/linux/seq_buf.h +10 -0
+ * @num: the number of bytes to commit
+ *
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
+	if (num < 0) {
+		s->len = s->size;

I guess that you want to get the buffer into an overflow state. If yes, it should be:

		s->len = s->size + 1;

or even better

		seq_buf_set_overflow(s);

Note that this whole patch depends on the decision about the 7th patch ("tracing: Have seq_buf use full buffer").

It might make sense to move the SEQ_BUF_LEFT, SEQ_BUF_USED macros to this header and use them in these two function. Or move the functions to seq_file.c.

Best Regards,
Petr

author maintainer
#54 lore raw

Re: [RFC][PATCH 08/12 v3] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

Quoted 18 lines · On Wed, 5 Nov 2014 17:51:25 +0100 Petr Mladek <[email protected]> wrote:
Quoted 4 lines
include/linux/seq_buf.h +4 -0
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle

include/linux/seq_buf.h seq_buf_set_overflow(struct seq_buf *s) · line 91

Quoted 10 lines
include/linux/seq_buf.h +10 -0
+ * @num: the number of bytes to commit
+ *
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
+	if (num < 0) {
+		s->len = s->size;

I guess that you want to get the buffer into an overflow state. If yes, it should be:

Quoted 5 lines
		s->len = s->size + 1;

or even better

		seq_buf_set_overflow(s);

Yeah, I agree with using the seq_buf_set_overflow().

Quoted 4 lines

Note that this whole patch depends on the decision about the 7th patch ("tracing: Have seq_buf use full buffer").

Which I have decided to do.

Quoted 5 lines

It might make sense to move the SEQ_BUF_LEFT, SEQ_BUF_USED macros to this header and use them in these two function. Or move the functions to seq_file.c.

I moved SEQ_BUF_LEFT as a static inline seq_buf_buffer_left() and have used it in trace_seq.c in the TRACE_SEQ_BUF_LEFT macro.

-- Steve

author maintainer Tested-by Jiri Kosina Acked-by Jiri Kosina
#55 lore raw

Re: [RFC][PATCH 08/12 v3] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

In reply to Petr Mladek

More updates. Hmm, maybe I should have posted the full series ;-)

-- Steve

Quoted 1 line

From 41a3f3f5e772ca26ef4441a0312d3f108693d7dc Mon Sep 17 00:00:00 2001

From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 17:30:50 -0400
Subject: [PATCH] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper
functions

Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the
seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq.

---
 include/linux/seq_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/trace/seq_buf.c  |  7 +++----
 2 files changed, 43 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +40 -0
index 4aab47d10760..7dacdc791225 100644
@@ -61,6 +61,46 @@ seq_buf_buffer_left(struct seq_buf *s)
 	return s->size - s->len;
 }
 
+/**
+ * seq_buf_get_buf - get buffer to write arbitrary data to
+ * @s: the seq_buf handle
+ * @bufp: the beginning of the buffer is stored here
+ *
+ * Return the number of bytes available in the buffer, or zero if
+ * there's no space.
+ */
+static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
+{
+	BUG_ON(s->len > s->size + 1);
+
+	if (s->len < s->size) {
+		*bufp = s->buffer + s->len;
+		return s->size - s->len;
+	}
+
+	*bufp = NULL;
+	return 0;
+}
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle
+ * @num: the number of bytes to commit
+ *
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
+	if (num < 0) {
+		seq_buf_set_overflow(s);
+	} else {
+		BUG_ON(s->len + num > s->size + 1);
+		s->len += num;
+	}
+}
+
 extern __printf(2, 3)
 int seq_buf_printf(struct seq_buf *s, const char *fmt, ...);
 extern __printf(2, 0)
kernel/trace/seq_buf.c +3 -4
index 8e60b5c1b9f3..25090f81ea8a 100644
@@ -283,8 +283,8 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem,
  */
 int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 {
-	char *buf = s->buffer + s->len;
-	size_t size = seq_buf_buffer_left(s);
+	char *buf;
+	size_t size = seq_buf_get_buf(s, &buf);
 	int res = -1;
 
 	WARN_ON(s->size == 0);
@@ -297,8 +297,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc)
 				res = end - buf;
 		}
 	}
-	if (res > 0)
-		s->len += res;
+	seq_buf_commit(s, res);
 
 	return res;
 }
Signature · 1 line
2.1.1
Reviewed-by
#56 lore raw

Re: [RFC][PATCH 08/12 v3] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

Quoted 15 lines · Steven Rostedt

More updates. Hmm, maybe I should have posted the full series ;-)

-- Steve

From 41a3f3f5e772ca26ef4441a0312d3f108693d7dc Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 17:30:50 -0400
Subject: [PATCH] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper
functions

Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the
seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq.

Quoted 5 lines

Well, I am curious about the BUG_ONs, see below.

Quoted 14 lines
---
 include/linux/seq_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/trace/seq_buf.c  |  7 +++----
 2 files changed, 43 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +1 -0
index 4aab47d10760..7dacdc791225 100644
@@ -61,6 +61,46 @@ seq_buf_buffer_left(struct seq_buf *s)
 	return s->size - s->len;
 }
 
+/**

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 74

Quoted 10 lines
include/linux/seq_buf.h +10 -0
+ * seq_buf_get_buf - get buffer to write arbitrary data to
+ * @s: the seq_buf handle
+ * @bufp: the beginning of the buffer is stored here
+ *
+ * Return the number of bytes available in the buffer, or zero if
+ * there's no space.
+ */
+static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
+{
+	BUG_ON(s->len > s->size + 1);

I just wonder if the BUG_ON() is appropriate here. There is used WARN_ON() for the other similar checks.

On one hand. This function will be used by a code that manipulates the buffer its own way. Therefore the BUG() would help to debug potential problems.

On the other hand, this function is used just to get the buffer. Therefore the BUG() might come too late. The buffer was broken somewhere else.

Quoted 15 lines
include/linux/seq_buf.h +15 -0
+
+	if (s->len < s->size) {
+		*bufp = s->buffer + s->len;
+		return s->size - s->len;
+	}
+
+	*bufp = NULL;
+	return 0;
+}
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle
+ * @num: the number of bytes to commit
+ *

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 99

Quoted 10 lines
include/linux/seq_buf.h +10 -0
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
+	if (num < 0) {
+		seq_buf_set_overflow(s);
+	} else {
+		BUG_ON(s->len + num > s->size + 1);

I agree that the BUG_ON makes sense here. If someone passed too big "num", she probably also wrote too many bytes and the memory is corrupted at this point.

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 103

Quoted 4 lines
include/linux/seq_buf.h +4 -0
+		s->len += num;
+	}
+}
+

Best Regards,
Petr

author maintainer
#57 lore raw

Re: [RFC][PATCH 08/12 v3] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper functions

Quoted 39 lines · On Mon, 10 Nov 2014 19:33:29 +0100 Petr Mladek <[email protected]> wrote:
Quoted 15 lines · Steven Rostedt

More updates. Hmm, maybe I should have posted the full series ;-)

-- Steve

From 41a3f3f5e772ca26ef4441a0312d3f108693d7dc Mon Sep 17 00:00:00 2001
From: "Steven Rostedt (Red Hat)" <[email protected]>
Date: Wed, 29 Oct 2014 17:30:50 -0400
Subject: [PATCH] tracing: Add seq_buf_get_buf() and seq_buf_commit() helper
functions

Add two helper functions; seq_buf_get_buf() and seq_buf_commit() that are used by seq_buf_path(). This makes the code similar to the
seq_file: seq_path() function, and will help to be able to consolidate the functions between seq_file and trace_seq.

Quoted 5 lines

Well, I am curious about the BUG_ONs, see below.

Quoted 13 lines
---
 include/linux/seq_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
 kernel/trace/seq_buf.c  |  7 +++----
 2 files changed, 43 insertions(+), 4 deletions(-)
include/linux/seq_buf.h +0 -0
index 4aab47d10760..7dacdc791225 100644
@@ -61,6 +61,46 @@ seq_buf_buffer_left(struct seq_buf *s)
 	return s->size - s->len;
 }
 
Quoted 14 lines

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 74

Quoted 11 lines
include/linux/seq_buf.h +11 -0
+/**
+ * seq_buf_get_buf - get buffer to write arbitrary data to
+ * @s: the seq_buf handle
+ * @bufp: the beginning of the buffer is stored here
+ *
+ * Return the number of bytes available in the buffer, or zero if
+ * there's no space.
+ */
+static inline size_t seq_buf_get_buf(struct seq_buf *s, char **bufp)
+{
+	BUG_ON(s->len > s->size + 1);

I just wonder if the BUG_ON() is appropriate here. There is used WARN_ON() for the other similar checks.

That should be a WARN_ON(). Thanks.

It's probably a BUG_ON as that was code that was used internally for earlier iterations, and the BUG_ON() was for me to see it quickly. Could have also been that previous versions could access memory that it should not.

But as it's intended to be used by others, it should be a warning instead of a bug.

Quoted 30 lines

On one hand. This function will be used by a code that manipulates the buffer its own way. Therefore the BUG() would help to debug potential problems.

On the other hand, this function is used just to get the buffer. Therefore the BUG() might come too late. The buffer was broken somewhere else.

Quoted 21 lines
include/linux/seq_buf.h +21 -0
+
+	if (s->len < s->size) {
+		*bufp = s->buffer + s->len;
+		return s->size - s->len;
+	}
+
+	*bufp = NULL;
+	return 0;
+}
+
+/**
+ * seq_buf_commit - commit data to the buffer
+ * @s: the seq_buf handle
+ * @num: the number of bytes to commit
+ *
+ * Commit @num bytes of data written to a buffer previously acquired
+ * by seq_buf_get.  To signal an error condition, or that the data
+ * didn't fit in the available space, pass a negative @num value.
+ */
+static inline void seq_buf_commit(struct seq_buf *s, int num)
+{
Quoted 8 lines

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 99

Quoted 4 lines
include/linux/seq_buf.h +4 -0
+	if (num < 0) {
+		seq_buf_set_overflow(s);
+	} else {
+		BUG_ON(s->len + num > s->size + 1);

I agree that the BUG_ON makes sense here. If someone passed too big "num", she probably also wrote too many bytes and the memory is corrupted at this point.

Yeah, this one is worse than the other one and should bug to prevent memory corruption.

Thanks for reviewing this,

-- Steve

Quoted 8 lines

include/linux/seq_buf.h seq_buf_buffer_left(struct seq_buf *s) · line 103

Quoted 5 lines
include/linux/seq_buf.h +4 -0
+		s->len += num;
+	}
+}
+

Best Regards,
Petr

author maintainer
#58 lore raw

RFC PATCH v3 9/12 seq_buf: Move the seq_buf code to lib/

In reply to Steven Rostedt

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

The seq_buf functions are rather useful outside of tracing. Instead of having it be dependent on CONFIG_TRACING, move the code into lib/ and allow other users to have access to it even when tracing is not configured.

The seq_buf utility is similar to the seq_file utility, but instead of writing sending data back up to userland, it writes it into a buffer defined at seq_buf_init(). This allows us to send a descriptor around that writes printf() formatted strings into it that can be retrieved later.

It is currently used by the tracing facility for such things like trace events to convert its binary saved data in the ring buffer into an ASCII human readable context to be displayed in /sys/kernel/debug/trace.

It can also be used for doing NMI prints safely from NMI context into the seq_buf and retrieved later and dumped to printk() safely. Doing printk() from an NMI context is dangerous because an NMI can preempt a current printk() and deadlock on it.

---
 kernel/trace/Makefile  |   1 -
 kernel/trace/seq_buf.c | 342 -------------------------------------------------
 lib/Makefile           |   2 +-
 lib/seq_buf.c          | 342 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 343 insertions(+), 344 deletions(-)
 delete mode 100644 kernel/trace/seq_buf.c
 create mode 100644 lib/seq_buf.c
kernel/trace/Makefile +0 -1
index edc98c72a634..67d6369ddf83 100644
@@ -29,7 +29,6 @@ obj-$(CONFIG_RING_BUFFER_BENCHMARK) += ring_buffer_benchmark.o
 obj-$(CONFIG_TRACING) += trace.o
 obj-$(CONFIG_TRACING) += trace_output.o
 obj-$(CONFIG_TRACING) += trace_seq.o
-obj-$(CONFIG_TRACING) += seq_buf.o
 obj-$(CONFIG_TRACING) += trace_stat.o
 obj-$(CONFIG_TRACING) += trace_printk.o
 obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
kernel/trace/seq_buf.c deleted +0 -342

342 lines not on this page · raw message

lib/Makefile +1 -1
index 7512dc978f18..a1aa1e81ed36 100644
@@ -13,7 +13,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o flex_proportions.o ratelimit.o show_mem.o \
 	 is_single_threaded.o plist.o decompress.o kobject_uevent.o \
-	 earlycpio.o
+	 earlycpio.o seq_buf.o
 
 obj-$(CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS) += usercopy.o
 lib-$(CONFIG_MMU) += ioremap.o
lib/seq_buf.c new file +342 -0

342 lines not on this page · raw message

Signature · 1 line
2.1.1
Reviewed-by on v3 9/12
#59 lore raw

Re: [RFC][PATCH 09/12 v3] seq_buf: Move the seq_buf code to lib/

Quoted 17 lines · Steven Rostedt

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

The seq_buf functions are rather useful outside of tracing. Instead of having it be dependent on CONFIG_TRACING, move the code into lib/ and allow other users to have access to it even when tracing is not configured.

The seq_buf utility is similar to the seq_file utility, but instead of writing sending data back up to userland, it writes it into a buffer defined at seq_buf_init(). This allows us to send a descriptor around that writes printf() formatted strings into it that can be retrieved later.

It is currently used by the tracing facility for such things like trace events to convert its binary saved data in the ring buffer into an ASCII human readable context to be displayed in /sys/kernel/debug/trace.

Quoted 8 lines

It can also be used for doing NMI prints safely from NMI context into the seq_buf and retrieved later and dumped to printk() safely. Doing printk() from an NMI context is dangerous because an NMI can preempt a current printk() and deadlock on it.

This patch might needed an update when there are changes in the previous patches. But it is a simple file move that makes perfect sense, so:

Best Regards,
Petr

author maintainer
#60 lore raw

Re: [RFC][PATCH 09/12 v3] seq_buf: Move the seq_buf code to lib/

Quoted 3 lines · On Wed, 5 Nov 2014 17:57:26 +0100 Petr Mladek <[email protected]> wrote:

This patch might needed an update when there are changes in the previous patches. But it is a simple file move that makes perfect sense, so:

I love git rebase. It knows that this change was a simple file move and even if you change the file that is moving, it wont have any conflicts during the rebase.

Quoted 2 lines

Thanks,

-- Steve

author maintainer
#61 lore raw

RFC PATCH v3 10/12 seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

In reply to Steven Rostedt

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

The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case.

Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF.

---
 include/linux/seq_buf.h | 7 +++++--
 lib/seq_buf.c           | 2 ++
 2 files changed, 7 insertions(+), 2 deletions(-)
include/linux/seq_buf.h +5 -2
index 10c17c61c7a4..b6291e2de99d 100644
@@ -99,8 +99,6 @@ extern __printf(2, 3)
 int seq_buf_printf(struct seq_buf *s, const char *fmt, ...);
 extern __printf(2, 0)
 int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args);
-extern int
-seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary);
 extern int seq_buf_print_seq(struct seq_file *m, struct seq_buf *s);
 extern int seq_buf_to_user(struct seq_buf *s, char __user *ubuf,
 			   int cnt);
@@ -114,4 +112,9 @@ extern int seq_buf_path(struct seq_buf *s, const struct path *path, const char *
 extern int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 			   int nmaskbits);
 
+#ifdef CONFIG_BINARY_PRINTF
+extern int
+seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary);
+#endif
+
 #endif /* _LINUX_SEQ_BUF_H */
lib/seq_buf.c +2 -0
index d118d0d6c956..ad09f8e6f2a6 100644
@@ -114,6 +114,7 @@ int seq_buf_bitmask(struct seq_buf *s, const unsigned long *maskp,
 	return -1;
 }
 
+#ifdef CONFIG_BINARY_PRINTF
 /**
  * seq_buf_bprintf - Write the printf string from binary arguments
  * @s: seq_buf descriptor
@@ -148,6 +149,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary)
 	seq_buf_set_overflow(s);
 	return -1;
 }
+#endif /* CONFIG_BINARY_PRINTF */
 
 /**
  * seq_buf_puts - sequence printing of simple string
Signature · 1 line
2.1.1
Reviewed-by on v3 10/12
#62 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 8 lines · Steven Rostedt

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

The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case.

Quoted 6 lines

Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF.

Please switch the order and do this change before moving to lib/. IMHO, the current order would break bisecting when tracing is disabled.

Otherwise, the change looks good in itself, so:

Best Regards,
Petr

author maintainer
#63 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 14 lines · On Wed, 5 Nov 2014 18:06:05 +0100 Petr Mladek <[email protected]> wrote:
Quoted 13 lines · Steven Rostedt

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

The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case.

Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF.

Quoted 5 lines
Quoted 1 line

Please switch the order and do this change before moving to lib/. IMHO, the current order would break bisecting when tracing is disabled.

I agree. I was being lazy and when my test broke, I added the update to test again. But to move this change to before the move, I need to monkey with the patch file.

I'll move it before the move regardless.

Quoted 4 lines

Otherwise, the change looks good in itself, so:

Thanks,

-- Steve

author maintainer
#64 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 23 lines · On Wed, 5 Nov 2014 15:33:55 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 14 lines · On Wed, 5 Nov 2014 18:06:05 +0100 Petr Mladek <[email protected]> wrote:
Quoted 13 lines · Steven Rostedt

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

The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case.

Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF.

Quoted 5 lines
Quoted 1 line

Please switch the order and do this change before moving to lib/. IMHO, the current order would break bisecting when tracing is disabled.

Quoted 6 lines

I agree. I was being lazy and when my test broke, I added the update to test again. But to move this change to before the move, I need to monkey with the patch file.

I'll move it before the move regardless.

OK, 'git rebase' is SUPER AWESOME!!!

I just did a git rebase, moved this change to before the file move, and git somehow knew that the change was for the old file, and updated it without any modification from me. The rebase simply succeeded!

I checked, the change still changed seq_buf.c, but in the old kernel/trace directory.

-- Steve

#65 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 33 lines · Steven Rostedt
Quoted 23 lines · On Wed, 5 Nov 2014 15:33:55 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 14 lines · On Wed, 5 Nov 2014 18:06:05 +0100 Petr Mladek <[email protected]> wrote:
Quoted 13 lines · Steven Rostedt

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

The function bstr_printf() from lib/vsprnintf.c is only available if CONFIG_BINARY_PRINTF is defined. This is due to the only user currently being the tracing infrastructure, which needs to select this config when tracing is configured. Until there is another user of the binary printf formats, this will continue to be the case.

Since seq_buf.c is now lives in lib/ and is compiled even without tracing, it must encompass its use of bstr_printf() which is used by seq_buf_printf(). This too is only used by the tracing infrastructure and is still encapsulated by the CONFIG_BINARY_PRINTF.

Quoted 5 lines
Quoted 1 line

Please switch the order and do this change before moving to lib/. IMHO, the current order would break bisecting when tracing is disabled.

Quoted 6 lines

I agree. I was being lazy and when my test broke, I added the update to test again. But to move this change to before the move, I need to monkey with the patch file.

I'll move it before the move regardless.

Quoted 8 lines

OK, 'git rebase' is SUPER AWESOME!!!

I just did a git rebase, moved this change to before the file move, and git somehow knew that the change was for the old file, and updated it without any modification from me. The rebase simply succeeded!

I checked, the change still changed seq_buf.c, but in the old kernel/trace directory.

That is really awesome! Dreams become reality.

Best Regards,
Petr

#66 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 5 lines · Petr Mladek

On Wed 2014-11-05 15:42:03, Steven Rostedt wrote:
...

Quoted 3 lines

OK, 'git rebase' is SUPER AWESOME!!!

Quoted 8 lines
Quoted 6 lines

I just did a git rebase, moved this change to before the file move, and git somehow knew that the change was for the old file, and updated it without any modification from me. The rebase simply succeeded!

I checked, the change still changed seq_buf.c, but in the old kernel/trace directory.

That is really awesome! Dreams become reality.

It is rare to hear somebody talks about Git without having "sucks" in the same sentence ;-)

author maintainer
#67 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

Quoted 11 lines · On Fri, 07 Nov 2014 12:36:05 -0800 Junio C Hamano <[email protected]> wrote:
Quoted 9 lines · Petr Mladek

On Wed 2014-11-05 15:42:03, Steven Rostedt wrote:
...

Quoted 7 lines

OK, 'git rebase' is SUPER AWESOME!!!

I just did a git rebase, moved this change to before the file move, and git somehow knew that the change was for the old file, and updated it without any modification from me. The rebase simply succeeded!

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I checked, the change still changed seq_buf.c, but in the old kernel/trace directory.

That is really awesome! Dreams become reality.

It is rare to hear somebody talks about Git without having "sucks" in the same sentence ;-)

Yes, that's why I Cc'd you. I figured you would want to hear it.

-- Steve

#68 lore raw

Re: [RFC][PATCH 10/12 v3] seq-buf: Make seq_buf_bprintf() conditional on CONFIG_BINARY_PRINTF

In reply to Junio C Hamano
Quoted 11 lines · Junio C Hamano
Quoted 9 lines · Petr Mladek

On Wed 2014-11-05 15:42:03, Steven Rostedt wrote:
...

Quoted 7 lines

OK, 'git rebase' is SUPER AWESOME!!!

I just did a git rebase, moved this change to before the file move, and git somehow knew that the change was for the old file, and updated it without any modification from me. The rebase simply succeeded!

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I checked, the change still changed seq_buf.c, but in the old kernel/trace directory.

That is really awesome! Dreams become reality.

It is rare to hear somebody talks about Git without having "sucks" in the same sentence ;-)

This might be an effect of bugzilla and other bugreport tools. One gets the feeling that there are only people strugling with the software. In fact, there is usually much bigger group of happy users. They just keep the happines inside until something goes wrong :-)

I am happy git user (most of the time :-)

Best Regards,
Petr

author maintainer
#69 lore raw

RFC PATCH v3 11/12 printk: Add per_cpu printk func to allow printk to be diverted

In reply to Steven Rostedt

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

Being able to divert printk to call another function besides the normal logging is useful for such things like NMI handling. If some functions are to be called from NMI that does printk() it is possible to lock up the box if the nmi handler triggers when another printk is happening.

One example of this use is to perform a stack trace on all CPUs via NMI. But if the NMI is to do the printk() it can cause the system to lock up. By allowing the printk to be diverted to another function that can safely record the printk output and then print it when it in a safe context then NMIs will be safe to call these functions like show_regs().

---
 include/linux/percpu.h |  3 +++
 include/linux/printk.h |  2 ++
 kernel/printk/printk.c | 38 +++++++++++++++++++++++++++++---------
 3 files changed, 34 insertions(+), 9 deletions(-)
include/linux/percpu.h +3 -0
index a3aa63e47637..ba2e85a0ff5b 100644
@@ -134,4 +134,7 @@ extern phys_addr_t per_cpu_ptr_to_phys(void *addr);
 	(typeof(type) __percpu *)__alloc_percpu(sizeof(type),		\
 						__alignof__(type))
 
+/* To avoid include hell, as printk can not declare this, we declare it here */
+DECLARE_PER_CPU(printk_func_t, printk_func);
+
 #endif /* __LINUX_PERCPU_H */
include/linux/printk.h +2 -0
index d78125f73ac4..3bbd979d32fb 100644
@@ -162,6 +162,8 @@ extern int kptr_restrict;
 
 extern void wake_up_klogd(void);
 
+typedef int(*printk_func_t)(const char *fmt, va_list args);
+
 void log_buf_kexec_setup(void);
 void __init setup_log_buf(int early);
 void dump_stack_set_arch_desc(const char *fmt, ...);
kernel/printk/printk.c +29 -9
index ced2b84b1cb7..f7b723f98cb9 100644
@@ -1807,6 +1807,30 @@ asmlinkage int printk_emit(int facility, int level,
 }
 EXPORT_SYMBOL(printk_emit);
 
+int vprintk_default(const char *fmt, va_list args)
+{
+	int r;
+
+#ifdef CONFIG_KGDB_KDB
+	if (unlikely(kdb_trap_printk)) {
+		r = vkdb_printf(fmt, args);
+		return r;
+	}
+#endif
+	r = vprintk_emit(0, -1, NULL, 0, fmt, args);
+
+	return r;
+}
+EXPORT_SYMBOL_GPL(vprintk_default);
+
+/*
+ * This allows printk to be diverted to another function per cpu.
+ * This is useful for calling printk functions from within NMI
+ * without worrying about race conditions that can lock up the
+ * box.
+ */
+DEFINE_PER_CPU(printk_func_t, printk_func) = vprintk_default;
+
 /**
  * printk - print a kernel message
  * @fmt: format string
@@ -1830,19 +1854,15 @@ EXPORT_SYMBOL(printk_emit);
  */
 asmlinkage __visible int printk(const char *fmt, ...)
 {
+	printk_func_t vprintk_func;
 	va_list args;
 	int r;
 
-#ifdef CONFIG_KGDB_KDB
-	if (unlikely(kdb_trap_printk)) {
-		va_start(args, fmt);
-		r = vkdb_printf(fmt, args);
-		va_end(args);
-		return r;
-	}
-#endif
 	va_start(args, fmt);
-	r = vprintk_emit(0, -1, NULL, 0, fmt, args);
+	preempt_disable();
+	vprintk_func = this_cpu_read(printk_func);
+	r = vprintk_func(fmt, args);
+	preempt_enable();
 	va_end(args);
 
 	return r;
Signature · 1 line
2.1.1
Reviewed-by on v3 11/12
#70 lore raw

Re: [RFC][PATCH 11/12 v3] printk: Add per_cpu printk func to allow printk to be diverted

Quoted 13 lines · Steven Rostedt

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

Being able to divert printk to call another function besides the normal logging is useful for such things like NMI handling. If some functions are to be called from NMI that does printk() it is possible to lock up the box if the nmi handler triggers when another printk is happening.

One example of this use is to perform a stack trace on all CPUs via NMI. But if the NMI is to do the printk() it can cause the system to lock up. By allowing the printk to be diverted to another function that can safely record the printk output and then print it when it in a safe context then NMIs will be safe to call these functions like show_regs().

Quoted 4 lines

Looks fine to me.

Best Regards,
Petr

author maintainer
#71 lore raw

RFC PATCH v3 12/12 x86/nmi: Perform a safe NMI stack trace on all CPUs

In reply to Steven Rostedt

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

When trigger_all_cpu_backtrace() is called on x86, it will trigger an NMI on each CPU and call show_regs(). But this can lead to a hard lock up if the NMI comes in on another printk().

In order to avoid this, when the NMI triggers, it switches the printk routine for that CPU to call a NMI safe printk function that records the printk in a per_cpu seq_buf descriptor. After all NMIs have finished recording its data, the trace_seqs are printed in a safe context.

---
 arch/x86/kernel/apic/hw_nmi.c | 90 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 85 insertions(+), 5 deletions(-)
arch/x86/kernel/apic/hw_nmi.c +85 -5
index 6a1e71bde323..6e7bb0bc6fcd 100644
@@ -18,6 +18,7 @@
 #include <linux/nmi.h>
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/seq_buf.h>
 
 #ifdef CONFIG_HARDLOCKUP_DETECTOR
 u64 hw_nmi_get_sample_period(int watchdog_thresh)
@@ -29,14 +30,35 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)
 #ifdef arch_trigger_all_cpu_backtrace
 /* For reliability, we're prepared to waste bits here. */
 static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly;
+static cpumask_var_t printtrace_mask;
+
+#define NMI_BUF_SIZE		4096
+
+struct nmi_seq_buf {
+	unsigned char		buffer[NMI_BUF_SIZE];
+	struct seq_buf		seq;
+};
+
+/* Safe printing in NMI context */
+static DEFINE_PER_CPU(struct nmi_seq_buf, nmi_print_seq);
 
 /* "in progress" flag of arch_trigger_all_cpu_backtrace */
 static unsigned long backtrace_flag;
 
+static void print_seq_line(struct nmi_seq_buf *s, int last, int pos)
+{
+	const char *buf = s->buffer + last;
+
+	printk("%.*s", (pos - last) + 1, buf);
+}
+
 void arch_trigger_all_cpu_backtrace(bool include_self)
 {
+	struct nmi_seq_buf *s;
+	int len;
+	int cpu;
 	int i;
-	int cpu = get_cpu();
+	int this_cpu = get_cpu();
 
 	if (test_and_set_bit(0, &backtrace_flag)) {
 		/*
@@ -49,7 +71,17 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
 
 	cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask);
 	if (!include_self)
-		cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
+		cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask));
+
+	cpumask_copy(printtrace_mask, to_cpumask(backtrace_mask));
+	/*
+	 * Set up per_cpu seq_buf buffers that the NMIs running on the other
+	 * CPUs will write to.
+	 */
+	for_each_cpu(cpu, to_cpumask(backtrace_mask)) {
+		s = &per_cpu(nmi_print_seq, cpu);
+		seq_buf_init(&s->seq, s->buffer, NMI_BUF_SIZE);
+	}
 
 	if (!cpumask_empty(to_cpumask(backtrace_mask))) {
 		pr_info("sending NMI to %s CPUs:\n",
@@ -65,11 +97,57 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
 		touch_softlockup_watchdog();
 	}
 
+	/*
+	 * Now that all the NMIs have triggered, we can dump out their
+	 * back traces safely to the console.
+	 */
+	for_each_cpu(cpu, printtrace_mask) {
+		int last_i = 0;
+
+		s = &per_cpu(nmi_print_seq, cpu);
+		len = s->seq.len;
+		if (!len)
+			continue;
+
+		/* Print line by line. */
+		for (i = 0; i < len; i++) {
+			if (s->buffer[i] == '\n') {
+				print_seq_line(s, last_i, i);
+				last_i = i + 1;
+			}
+		}
+		if (last_i < i - 1) {
+			print_seq_line(s, last_i, i);
+			pr_cont("\n");
+		}
+	}
+
 	clear_bit(0, &backtrace_flag);
 	smp_mb__after_atomic();
 	put_cpu();
 }
 
+/*
+ * It is not safe to call printk() directly from NMI handlers.
+ * It may be fine if the NMI detected a lock up and we have no choice
+ * but to do so, but doing a NMI on all other CPUs to get a back trace
+ * can be done with a sysrq-l. We don't want that to lock up, which
+ * can happen if the NMI interrupts a printk in progress.
+ *
+ * Instead, we redirect the vprintk() to this nmi_vprintk() that writes
+ * the content into a per cpu seq_buf buffer. Then when the NMIs are
+ * all done, we can safely dump the contents of the seq_buf to a printk()
+ * from a non NMI context.
+ */
+static int nmi_vprintk(const char *fmt, va_list args)
+{
+	struct nmi_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
+	unsigned int len = s->seq.len;
+
+	seq_buf_vprintf(&s->seq, fmt, args);
+	return s->seq.len - len;
+}
+
 static int
 arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 {
@@ -78,12 +156,14 @@ arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 	cpu = smp_processor_id();
 
 	if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
-		static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
+		printk_func_t printk_func_save = this_cpu_read(printk_func);
 
-		arch_spin_lock(&lock);
+		/* Replace printk to write into the NMI seq */
+		this_cpu_write(printk_func, nmi_vprintk);
 		printk(KERN_WARNING "NMI backtrace for cpu %d\n", cpu);
 		show_regs(regs);
-		arch_spin_unlock(&lock);
+		this_cpu_write(printk_func, printk_func_save);
+
 		cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
 		return NMI_HANDLED;
 	}
Signature · 1 line
2.1.1
Tested-by Acked-by on v3 12/12
#72 lore raw

Re: [RFC][PATCH 12/12 v3] x86/nmi: Perform a safe NMI stack trace on all CPUs

Quoted 11 lines · Steven Rostedt

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

When trigger_all_cpu_backtrace() is called on x86, it will trigger an NMI on each CPU and call show_regs(). But this can lead to a hard lock up if the NMI comes in on another printk().

In order to avoid this, when the NMI triggers, it switches the printk routine for that CPU to call a NMI safe printk function that records the printk in a per_cpu seq_buf descriptor. After all NMIs have finished recording its data, the trace_seqs are printed in a safe context.

Quoted 4 lines

I've been running the whole machinery that used to trigger very quickly the complete hardlock of the machine (*) for the whole evening/night, and it's still running flawlessly.

Plus, as I said previously, I agree with the whole idea (given the general nastiness of the problem and given the fact this simply has to be fixed without pointless delays).

I.e FWIW

for the whole series.

(*) heavy printk() workload (**) + sysrq-l in parallel (**) iptables logging every incoming packet + flood ping from another
machine

Thanks,

Signature · 2 lines
Jiri Kosina
SUSE Labs
author maintainer
#73 lore raw

Re: [RFC][PATCH 12/12 v3] x86/nmi: Perform a safe NMI stack trace on all CPUs

Quoted 4 lines · On Wed, 5 Nov 2014 00:05:12 +0100 (CET) Jiri Kosina <[email protected]> wrote:
Quoted 2 lines · Steven Rostedt

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

Quoted 8 lines
Quoted 8 lines

When trigger_all_cpu_backtrace() is called on x86, it will trigger an NMI on each CPU and call show_regs(). But this can lead to a hard lock up if the NMI comes in on another printk().

In order to avoid this, when the NMI triggers, it switches the printk routine for that CPU to call a NMI safe printk function that records the printk in a per_cpu seq_buf descriptor. After all NMIs have finished recording its data, the trace_seqs are printed in a safe context.

Hmm, I need to update the change log to say seq_bufs instead of trace_seqs.

Quoted 14 lines
Quoted 5 lines

I've been running the whole machinery that used to trigger very quickly the complete hardlock of the machine (*) for the whole evening/night, and it's still running flawlessly.

Plus, as I said previously, I agree with the whole idea (given the general nastiness of the problem and given the fact this simply has to be fixed without pointless delays).

Quoted 6 lines

I.e FWIW

for the whole series.

Thanks! I'll update the commits.

-- Steve

Quoted 7 lines

(*) heavy printk() workload (**) + sysrq-l in parallel (**) iptables logging every incoming packet + flood ping from another
machine

Thanks,

#74 lore raw

Re: [RFC][PATCH 12/12 v3] x86/nmi: Perform a safe NMI stack trace on all CPUs

In reply to Steven Rostedt
Quoted 41 lines · Steven Rostedt

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

When trigger_all_cpu_backtrace() is called on x86, it will trigger an NMI on each CPU and call show_regs(). But this can lead to a hard lock up if the NMI comes in on another printk().

In order to avoid this, when the NMI triggers, it switches the printk routine for that CPU to call a NMI safe printk function that records the printk in a per_cpu seq_buf descriptor. After all NMIs have finished recording its data, the trace_seqs are printed in a safe context.

---
 arch/x86/kernel/apic/hw_nmi.c | 90 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 85 insertions(+), 5 deletions(-)
arch/x86/kernel/apic/hw_nmi.c +7 -0
index 6a1e71bde323..6e7bb0bc6fcd 100644
@@ -18,6 +18,7 @@
 #include <linux/nmi.h>
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/seq_buf.h>
 
 #ifdef CONFIG_HARDLOCKUP_DETECTOR
 u64 hw_nmi_get_sample_period(int watchdog_thresh)
@@ -29,14 +30,35 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)
 #ifdef arch_trigger_all_cpu_backtrace
 /* For reliability, we're prepared to waste bits here. */
 static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly;
+static cpumask_var_t printtrace_mask;
+
+#define NMI_BUF_SIZE		4096
+
+struct nmi_seq_buf {
+	unsigned char		buffer[NMI_BUF_SIZE];

arch/x86/kernel/apic/hw_nmi.c u64 hw_nmi_get_sample_period(int watchdog_thresh) · line 48

Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +6 -0
+	struct seq_buf		seq;
+};
+
+/* Safe printing in NMI context */
+static DEFINE_PER_CPU(struct nmi_seq_buf, nmi_print_seq);
 
 /* "in progress" flag of arch_trigger_all_cpu_backtrace */
 static unsigned long backtrace_flag;
 
+static void print_seq_line(struct nmi_seq_buf *s, int last, int pos)

I would rename the arguments:

	"last -> first"
	"pos -> last"

or maybe better would be to pass first positon and len.

Quoted 46 lines
arch/x86/kernel/apic/hw_nmi.c +26 -2
+{
+	const char *buf = s->buffer + last;
+
+	printk("%.*s", (pos - last) + 1, buf);
+}
 
 
+{
+	const char *buf = s->buffer + last;
+
+	printk("%.*s", (pos - last) + 1, buf);
+}
+
 void arch_trigger_all_cpu_backtrace(bool include_self)
 {
+	struct nmi_seq_buf *s;
+	int len;
+	int cpu;
 	int i;
-	int cpu = get_cpu();
+	int this_cpu = get_cpu();
 
 	if (test_and_set_bit(0, &backtrace_flag)) {
 		/*
@@ -49,7 +71,17 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
 
 	cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask);
 	if (!include_self)
-		cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
+		cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask));
+
+	cpumask_copy(printtrace_mask, to_cpumask(backtrace_mask));
+	/*
+	 * Set up per_cpu seq_buf buffers that the NMIs running on the other
+	 * CPUs will write to.
+	 */
+	for_each_cpu(cpu, to_cpumask(backtrace_mask)) {
+		s = &per_cpu(nmi_print_seq, cpu);
+		seq_buf_init(&s->seq, s->buffer, NMI_BUF_SIZE);
+	}
 
 	if (!cpumask_empty(to_cpumask(backtrace_mask))) {
 		pr_info("sending NMI to %s CPUs:\n",
@@ -65,11 +97,57 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
 		touch_softlockup_watchdog();
 	}

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self) · line 108

Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +9 -0
 
+	/*
+	 * Now that all the NMIs have triggered, we can dump out their
+	 * back traces safely to the console.
+	 */
+	for_each_cpu(cpu, printtrace_mask) {
+		int last_i = 0;
+
+		s = &per_cpu(nmi_print_seq, cpu);
+		len = s->seq.len;

If there is an seq_buf overflow, the len might be size + 1, so we need to do:

		len = min(s->seq.len, s->size);

Well, we should create a function for this in seq_buf.h. Alternatively, we might reconsider the overflow state, use len == size and extra "overflow" flag in the seq_buf struct.

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self)

Quoted 12 lines
arch/x86/kernel/apic/hw_nmi.c +11 -0
+		if (!len)
+			continue;
+
+		/* Print line by line. */
+		for (i = 0; i < len; i++) {
+			if (s->buffer[i] == '\n') {
+				print_seq_line(s, last_i, i);
+				last_i = i + 1;
+			}
+		}
 
+		if (last_i < i - 1) {

IMHO, this should be:

		if (last_i < i)

because last_i = i + 1. Otherwise, we would ignore state when there is one character after a new line. For example, imagine the following:

	buffer = "a\nb";
	len = 3;

it will end with:

	last_i = 2;
	i = 3;

and we still need to print the "b".

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self) · line 121

Quoted 1 line
arch/x86/kernel/apic/hw_nmi.c +1 -0
+			print_seq_line(s, last_i, i);

If I get it correctly, (i == len) here and "printk_seq_line" print_seq_line() prints the characters including "pos" value. So, we should call:

			print_seq_line(s, last_i, i - 1)

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self) · line 124

Quoted 4 lines
arch/x86/kernel/apic/hw_nmi.c +4 -0
+			pr_cont("\n");
+		}
+	}
+

I hope that I have got it correctly. It is getting late here and I feel tired to see the off-by-one problems clearly ;-)

Best Regards,
Petr

author maintainer
#75 lore raw

Re: [RFC][PATCH 12/12 v3] x86/nmi: Perform a safe NMI stack trace on all CPUs

Quoted 11 lines · On Thu, 6 Nov 2014 19:41:55 +0100 Petr Mladek <[email protected]> wrote:
Quoted 4 lines

/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag;

+static void print_seq_line(struct nmi_seq_buf *s, int last, int pos)

I would rename the arguments:

	"last -> first"
	"pos -> last"

or maybe better would be to pass first positon and len.

I switched it to "start" and "end" to not be confused by the last_i that is being passed in.

Quoted 53 lines
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +5 -0
+{
+	const char *buf = s->buffer + last;
+
+	printk("%.*s", (pos - last) + 1, buf);
+}
 
 
 
 

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self) · line 108

Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +9 -0
 
+	/*
+	 * Now that all the NMIs have triggered, we can dump out their
+	 * back traces safely to the console.
+	 */
+	for_each_cpu(cpu, printtrace_mask) {
+		int last_i = 0;
+
+		s = &per_cpu(nmi_print_seq, cpu);
+		len = s->seq.len;

If there is an seq_buf overflow, the len might be size + 1, so we need to do:

		len = min(s->seq.len, s->size);

Well, we should create a function for this in seq_buf.h. Alternatively, we might reconsider the overflow state, use len == size and extra "overflow" flag in the seq_buf struct.

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self)

Quoted 12 lines
arch/x86/kernel/apic/hw_nmi.c +11 -0
+		if (!len)
+			continue;
+
+		/* Print line by line. */
+		for (i = 0; i < len; i++) {
+			if (s->buffer[i] == '\n') {
+				print_seq_line(s, last_i, i);
+				last_i = i + 1;
+			}
+		}
 
+		if (last_i < i - 1) {

IMHO, this should be:

		if (last_i < i)

because last_i = i + 1. Otherwise, we would ignore state when there is one character after a new line. For example, imagine the following:

	buffer = "a\nb";
	len = 3;
Quoted 6 lines

it will end with:

	last_i = 2;
	i = 3;

and we still need to print the "b".

Well, we really don't *need* to ;-)

But for correctness sake, I agree, it should be last_i < i.

Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+			print_seq_line(s, last_i, i);

If I get it correctly, (i == len) here and "printk_seq_line" print_seq_line() prints the characters including "pos" value. So, we should call:

			print_seq_line(s, last_i, i - 1)

Right that was wrong. Actually, I think the best answer would be:

	print_seq_line(s, last_i, len - 1);

This removes the variable 'i'. Probably should add a comment here too that reminds the reviewer that print_seq_line() prints up to and including the last index.

Note, my current code also has:

	len = seq_buf_used(&s->seq);

where we don't need to worry about the semantics of seq_buf internals.

-- Steve

Quoted 6 lines
Quoted 5 lines
arch/x86/kernel/apic/hw_nmi.c +4 -0
+			pr_cont("\n");
+		}
+	}
+
#76 lore raw

Re: [RFC][PATCH 12/12 v3] x86/nmi: Perform a safe NMI stack trace on all CPUs

Quoted 10 lines · Steven Rostedt
Quoted 7 lines · On Thu, 6 Nov 2014 19:41:55 +0100 Petr Mladek <[email protected]> wrote:
Quoted 4 lines

/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag;

+static void print_seq_line(struct nmi_seq_buf *s, int last, int pos)

I would rename the arguments:

Quoted 7 lines
Quoted 4 lines
	"last -> first"
	"pos -> last"

or maybe better would be to pass first positon and len.

I switched it to "start" and "end" to not be confused by the last_i that is being passed in.

I like it.

Quoted 55 lines
Quoted 55 lines
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +5 -0
+{
+	const char *buf = s->buffer + last;
+
+	printk("%.*s", (pos - last) + 1, buf);
+}
 
 
 
 

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self) · line 108

Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +9 -0
 
+	/*
+	 * Now that all the NMIs have triggered, we can dump out their
+	 * back traces safely to the console.
+	 */
+	for_each_cpu(cpu, printtrace_mask) {
+		int last_i = 0;
+
+		s = &per_cpu(nmi_print_seq, cpu);
+		len = s->seq.len;

If there is an seq_buf overflow, the len might be size + 1, so we need to do:

		len = min(s->seq.len, s->size);

Well, we should create a function for this in seq_buf.h. Alternatively, we might reconsider the overflow state, use len == size and extra "overflow" flag in the seq_buf struct.

arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(bool include_self)

Quoted 12 lines
arch/x86/kernel/apic/hw_nmi.c +11 -0
+		if (!len)
+			continue;
+
+		/* Print line by line. */
+		for (i = 0; i < len; i++) {
+			if (s->buffer[i] == '\n') {
+				print_seq_line(s, last_i, i);
+				last_i = i + 1;
+			}
+		}
 
+		if (last_i < i - 1) {

IMHO, this should be:

		if (last_i < i)

because last_i = i + 1. Otherwise, we would ignore state when there is one character after a new line. For example, imagine the following:

	buffer = "a\nb";
	len = 3;

it will end with:

Quoted 8 lines
Quoted 4 lines
	last_i = 2;
	i = 3;

and we still need to print the "b".

Well, we really don't *need* to ;-)

But for correctness sake, I agree, it should be last_i < i.

I agree that one more character does not make much difference but it might save someones day :-)

Quoted 7 lines
Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +1 -0
+			print_seq_line(s, last_i, i);

If I get it correctly, (i == len) here and "printk_seq_line" print_seq_line() prints the characters including "pos" value. So, we should call:

Quoted 5 lines
Quoted 1 line
			print_seq_line(s, last_i, i - 1)

Right that was wrong. Actually, I think the best answer would be:

	print_seq_line(s, last_i, len - 1);

Yup

Quoted 3 lines

This removes the variable 'i'. Probably should add a comment here too that reminds the reviewer that print_seq_line() prints up to and including the last index.

Yes, the comment is worth having.

Quoted 5 lines

Note, my current code also has:

	len = seq_buf_used(&s->seq);

where we don't need to worry about the semantics of seq_buf internals.

Perfect

Thanks a lot for working on it. Please, resend this patch once you are happy with it.

Best Regards,
Petr