x86/nmi: Print all cpu stacks from NMI safely
Subsystems (5)
Steven Rostedt
maintainercover x86/nmi: Print all cpu stacks from NMI safely
This is my proposal to print the NMI stack traces from an RCU stall safely. Here's the gist of it.
Patch 1: move the trace_seq out of the tracing code. It's useful for other
purposes too. Like writing from an NMI context.
Patch 2: 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.
Patch 3: 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 trace_seq. When all NMI handlers chimed in, the original caller prints out the trace_seqs for each CPU from a printk safe context.
This is much less intrusive than the other versions out there.
Note, The biggest change was the moving of trace_seq code out of trace_output.c and into the new trace_seq.c library.
Thoughts?
Steven Rostedt (Red Hat) (3):
trace_seq: Move the trace_seq code to lib/
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 | 66 ++++++++- include/linux/percpu.h | 3 + include/linux/printk.h | 2 + include/linux/trace_seq.h | 68 ++-------- kernel/printk/printk.c | 38 ++++-- kernel/trace/trace.c | 24 ---- kernel/trace/trace_output.c | 268 ------------------------------------- kernel/trace/trace_output.h | 16 --- lib/Makefile | 2 +- lib/trace_seq.c | 303 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 412 insertions(+), 378 deletions(-)
Steven Rostedt
maintainerRFC PATCH 1/3 trace_seq: Move the trace_seq code to lib/
From: "Steven Rostedt (Red Hat)" <[email protected]>
The trace_seq 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.
--- include/linux/trace_seq.h | 68 ++-------- kernel/trace/trace.c | 24 ---- kernel/trace/trace_output.c | 268 --------------------------------------- kernel/trace/trace_output.h | 16 --- lib/Makefile | 2 +- lib/trace_seq.c | 303 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 316 insertions(+), 365 deletions(-) create mode 100644 lib/trace_seq.c
include/linux/trace_seq.h +12 -56
@@ -25,10 +25,6 @@ trace_seq_init(struct trace_seq *s) s->full =0 ; } -/* -* Currently only defined when tracing is enabled. -*/ -#ifdef CONFIG_TRACING extern __printf(2, 3) int trace_seq_printf(struct trace_seq *s, const char *fmt, ...); extern __printf(2, 0)
@@ -49,59 +45,19 @@ extern int trace_seq_path(struct trace_seq *s, const struct path *path);extern int trace_seq_bitmask (struct trace_seq *s,const unsigned long *maskp,int nmaskbits); -#else /* CONFIG_TRACING */ -static inline int trace_seq_printf (struct trace_seq *s,const char *fmt, ...) -{ -return 0 ; -} -static inline int -trace_seq_bprintf (struct trace_seq *s,const char *fmt,const u32 *binary) -{ -return 0 ; -} +#define MAX_MEMHEX_BYTES 8 -static inline int -trace_seq_bitmask (struct trace_seq *s,const unsigned long *maskp, -int nmaskbits) -{ -return 0 ; -} +#define SEQ_PUT_FIELD_RET (s, x) \ +do { \ + if (!trace_seq_putmem(s, &(x), sizeof(x))) \ + return TRACE_TYPE_PARTIAL_LINE; \ +} while (0) -static inline int trace_print_seq (struct seq_file *m,struct trace_seq *s) -{ -return 0 ; -} -static inline ssize_t trace_seq_to_user (struct trace_seq *s,char __user *ubuf, -size_t cnt) -{ -return 0 ; -} -static inline int trace_seq_puts (struct trace_seq *s,const char *str) -{ -return 0 ; -} -static inline int trace_seq_putc (struct trace_seq *s,unsigned char c) -{ -return 0 ; -} -static inline int -trace_seq_putmem (struct trace_seq *s,const void *mem,size_t len) -{ -return 0 ; -} -static inline int trace_seq_putmem_hex (struct trace_seq *s,const void *mem, -size_t len) -{ -return 0 ; -} -static inline void *trace_seq_reserve (struct trace_seq *s,size_t len) -{ -return NULL ; -} -static inline int trace_seq_path (struct trace_seq *s,const struct path *path) -{ -return 0 ; -} -#endif /* CONFIG_TRACING */ +#define SEQ_PUT_HEX_FIELD_RET (s, x) \ +do { \ + BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ + if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ + return TRACE_TYPE_PARTIAL_LINE; \ +} while (0)#endif /* _LINUX_TRACE_SEQ_H */
kernel/trace/trace.c +0 -24
@@ -923,30 +923,6 @@ out:return ret; } -ssize_t trace_seq_to_user (struct trace_seq *s,char __user *ubuf,size_t 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; -} -static ssize_t trace_seq_to_buffer (struct trace_seq *s,void *buf,size_t cnt) {int len;
kernel/trace/trace_output.c +0 -268
280 lines not on this page · raw message
kernel/trace/trace_output.h +0 -16
@@ -35,21 +35,5 @@ trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry);extern int __unregister_ftrace_event (struct trace_event *event);extern struct rw_semaphore trace_event_sem; -#define MAX_MEMHEX_BYTES 8 -#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) - -#define SEQ_PUT_FIELD_RET (s, x) \ -do { \ - if (!trace_seq_putmem(s, &(x), sizeof(x))) \ - return TRACE_TYPE_PARTIAL_LINE; \ -} while (0) - -#define SEQ_PUT_HEX_FIELD_RET (s, x) \ -do { \ - BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ - if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ - return TRACE_TYPE_PARTIAL_LINE; \ -} while (0) -#endif
lib/Makefile +1 -1
@@ -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 prio_heap.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ - earlycpio.o + earlycpio.o trace_seq.o obj-$(CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS) += usercopy.o lib-$(CONFIG_MMU) += ioremap.o
lib/trace_seq.c new file +303 -0
303 lines not on this page · raw message
Signature · 1 line
2.0.0.rc2
Re: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 3 lines · Steven Rostedt
From: "Steven Rostedt (Red Hat)" <[email protected]>
The
trace_seqfunctions are rather useful outside of tracing.
They are? Why and where? And why are they still called "trace_xyz()" then?
That commit message is useless and contains no actual information.
Linus
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 6 lines · On Thu, 19 Jun 2014 18:45:57 -1000 Linus Torvalds…
Quoted 3 lines · Steven Rostedt
From: "Steven Rostedt (Red Hat)" <[email protected]>
The
trace_seqfunctions are rather useful outside of tracing.They are?
They will be.
Quoted 1 line
Why and where?
Why? for NMI safe output.
Where? Well, in patch 3. :-)
Quoted 1 line
And why are they still called "
trace_xyz()" then?
Good point, I'd like to rename it too. It was designed like seq_file, so what about seq_buffer_xxx()?
Quoted 2 lines
That commit message is useless and contains no actual information.
Yeah, it was rather weak. It was quick and not a stand alone. It was dependent on reading the rest of the patches.
OK, I can update the name and make a better change log on the move.
Of course if you hate this patch series, I can just not waste any time on it.
-- Steve
Andrew Morton
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
In reply to Steven RostedtQuoted 6 lines · On Thu, 19 Jun 2014 17:33:30 -0400 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (Red Hat)" <[email protected]>
The
trace_seqfunctions are rather useful outside of tracing. Instead of having it be dependent onCONFIG_TRACING, move the code intolib/and allow other users to have access to it even when tracing is not configured.
What LT said. It's pileon time!
Quoted 8 lines
Signed-off-by Steven Rostedt <[email protected]>--- include/linux/trace_seq.h | 68 ++-------- kernel/trace/trace.c | 24 ---- kernel/trace/trace_output.c | 268 --------------------------------------- kernel/trace/trace_output.h | 16 --- lib/Makefile | 2 +- lib/trace_seq.c | 303 ++++++++++++++++++++++++++++++++++++++++++++
Putting it in there makes me look at it ;)
include/linux/trace_seq.h extern int trace_seq_path(struct trace_seq *s, const struct path *path); · line 52
Quoted 8 lines
include/linux/trace_seq.h +3
-0+#define SEQ_PUT_FIELD_RET (s, x) \ +do { \ + if (!trace_seq_putmem(s, &(x), sizeof(x))) \
hm, does sizeof(x++) increment x? I guess it does.
include/linux/trace_seq.h extern int trace_seq_path(struct trace_seq *s, const struct path *path); · line 61
Quoted 12 lines
include/linux/trace_seq.h +8
-0+return TRACE_TYPE_PARTIAL_LINE ; \ +} while (0) +#define SEQ_PUT_HEX_FIELD_RET (s, x) \ +do { \ + BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ + if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ + return TRACE_TYPE_PARTIAL_LINE; \ +} while (0)
Also has side-effects.
Quoted 8 lines
#endif /*
_LINUX_TRACE_SEQ_H*/...
lib/trace_seq.c new file +1
-0@@ -0,0 +1,303 @@ +/*
lib/trace_seq.c line 11
Quoted 10 lines
lib/trace_seq.c new file +10
-0+* trace_seq.c +* +* Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt <[email protected]> +* +*/ +#include <linux/uaccess.h> +#include <linux/seq_file.h> +#include <linux/trace_seq.h> + +int trace_print_seq (struct seq_file *m,struct trace_seq *s)
-ENODOC
lib/trace_seq.c line 13
Quoted 2 lines
lib/trace_seq.c new file +2
-0+{ +int len = s->len >=PAGE_SIZE ?PAGE_SIZE -1 : s->len;
int = uint >= ulong ? ulog : uint
that's spastic. Can we choose a type and stick to it?
Also, min().
lib/trace_seq.c line 20
Quoted 7 lines
lib/trace_seq.c new file +7
-0+int ret; + + ret =seq_write (m, s->buffer, len); + +/* +* Only reset this buffer if we successfully wrote to the +* seq_file buffer.
why?
Quoted 4 lines
lib/trace_seq.c new file +4
-0+*/ +if (!ret) +trace_seq_init (s); +
lib/trace_seq.c line 34
Quoted 10 lines
lib/trace_seq.c new file +10
-0+return ret; +} + +/** +* trace_seq_printf - sequence printing of trace information +* @s: trace sequence descriptor +* @fmt: printf format string +* +* It returns 0 if the trace oversizes the buffer's free +* space, 1 otherwise.
s/oversizes/would overrun/?
lib/trace_seq.c line 43
Quoted 8 lines
lib/trace_seq.c new file +8
-0+* The tracer may use either sequence operations or its own +* copy to user routines. To simplify formating of a trace +* trace_seq_printf is used to store strings into a special +* buffer (@s). Then the output may be either used by +* the sequencer or pulled into another buffer. +*/ +int +trace_seq_printf (struct trace_seq *s,const char *fmt, ...)
unneeded newline
lib/trace_seq.c line 44
Quoted 2 lines
lib/trace_seq.c new file +2
-0+{ +int len = (PAGE_SIZE -1 ) - s->len;
int = ulong - uint;
lib/trace_seq.c line 56
Quoted 11 lines
lib/trace_seq.c new file +11
-0+va_list ap; +int ret; + +if (s->full || !len) +return 0 ; + +va_start (ap, fmt); + ret =vsnprintf (s->buffer + s->len, len, fmt, ap); +va_end (ap); + +/* If we can't write it all, don't bother writing anything */
This is somewhat unusual behavior for a write()-style thing. Comment should explain "why", not "what".
lib/trace_seq.c line 69
Quoted 13 lines
lib/trace_seq.c new file +13
-0+if (ret >= len) { + s->full =1 ; +return 0 ; + } + + s->len += ret; + +return 1 ; +} +EXPORT_SYMBOL_GPL (trace_seq_printf); + +/** +* trace_seq_bitmask - put a list of longs as a bitmask print output
Is that grammatical?
lib/trace_seq.c line 75
Quoted 6 lines
lib/trace_seq.c new file +6
-0+* @s: trace sequence descriptor +* @maskp: points to an array of unsigned longs that represent a bitmask +* @nmaskbits: The number of bits that are valid in @maskp +* +* It returns 0 if the trace oversizes the buffer's free +* space, 1 otherwise.
Ditto
Quoted 8 lines
lib/trace_seq.c new file +8
-0+* Writes a ASCII representation of a bitmask string into @s. +*/ +int +trace_seq_bitmask (struct trace_seq *s,const unsigned long *maskp, +int nmaskbits) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret;
lib/trace_seq.c line 94
Quoted 10 lines
lib/trace_seq.c new file +10
-0+ +if (s->full || !len) +return 0 ; + + ret =bitmap_scnprintf (s->buffer, len, maskp, nmaskbits); + s->len += ret; + +return 1 ; +} +EXPORT_SYMBOL_GPL (trace_seq_bitmask);
More dittos.
lib/trace_seq.c line 103
Quoted 8 lines
lib/trace_seq.c new file +8
-0+/** +* trace_seq_vprintf - sequence printing of trace information +* @s: trace sequence descriptor +* @fmt: printf format string +* +* The tracer may use either sequence operations or its own +* copy to user routines. To simplify formating of a trace +* trace_seq_printf is used to store strings into a special
"trace_seq_printf()". Apparently it makes the kerneldoc output come out right.
Quoted 15 lines
lib/trace_seq.c new file +15
-0+* buffer (@s). Then the output may be either used by +* the sequencer or pulled into another buffer. +*/ +int +trace_seq_vprintf (struct trace_seq *s,const char *fmt,va_list args) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; + + ret =vsnprintf (s->buffer + s->len, len, fmt, args); + +/* If we can't write it all, don't bother writing anything */
lib/trace_seq.c line 128
Quoted 10 lines
lib/trace_seq.c new file +10
-0+if (ret >= len) { + s->full =1 ; +return 0 ; + } + + s->len += ret; + +return len; +} +EXPORT_SYMBOL_GPL (trace_seq_vprintf);
Several dittos.
lib/trace_seq.c line 130
Quoted 1 line
lib/trace_seq.c new file +1
-0+int trace_seq_bprintf (struct trace_seq *s,const char *fmt,const u32 *binary)
-ENODOC
Quoted 9 lines
lib/trace_seq.c new file +9
-0+{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; + + ret =bstr_printf (s->buffer + s->len, len, fmt, binary); +
lib/trace_seq.c line 149
Quoted 10 lines
lib/trace_seq.c new file +10
-0+/* If we can't write it all, don't bother writing anything */ +if (ret >= len) { + s->full =1 ; +return 0 ; + } + + s->len += ret; + +return len; +}
Dittos.
Quoted 17 lines
lib/trace_seq.c new file +17
-0+/** +* trace_seq_puts - trace sequence printing of simple string +* @s: trace sequence descriptor +* @str: simple string to record +* +* The tracer may use either the sequence operations or its own +* copy to user routines. This function records a simple string +* into a special buffer (@s) for later retrieval by a sequencer +* or other mechanism. +*/ +int trace_seq_puts (struct trace_seq *s,const char *str) +{ +int len =strlen (str); + +if (s->full) +return 0 ; +
lib/trace_seq.c line 177
Quoted 10 lines
lib/trace_seq.c new file +10
-0+if (len > ((PAGE_SIZE -1 ) - s->len)) { + s->full =1 ; +return 0 ; + } + +memcpy (s->buffer + s->len, str, len); + s->len += len; + +return len; +}
Missing EXPORT_SYMBOL?
Quoted 5 lines
lib/trace_seq.c new file +5
-0+int trace_seq_putc (struct trace_seq *s,unsigned char c) +{ +if (s->full) +return 0 ; +
lib/trace_seq.c line 193
Quoted 10 lines
lib/trace_seq.c new file +10
-0+if (s->len >= (PAGE_SIZE -1 )) { + s->full =1 ; +return 0 ; + } + + s->buffer[s->len++] = c; + +return 1 ; +} +EXPORT_SYMBOL (trace_seq_putc);
Mix of EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL()
Quoted 6 lines
lib/trace_seq.c new file +6
-0+int trace_seq_putmem (struct trace_seq *s,const void *mem,size_t len) +{ +if (s->full) +return 0 ; + +if (len > ((PAGE_SIZE -1 ) - s->len)) {
lib/trace_seq.c line 210
Quoted 10 lines
lib/trace_seq.c new file +10
-0+ s->full =1 ; +return 0 ; + } + +memcpy (s->buffer + s->len, mem, len); + s->len += len; + +return len; +} +
Lotsa dittos
lib/trace_seq.c line 220
Quoted 10 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;
What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Is this test really needed? trace_seq_putmem() will handle this.
lib/trace_seq.c line 233
Quoted 12 lines
lib/trace_seq.c new file +12
-0+#ifdef __BIG_ENDIAN +for (i =0 , j =0 ; i < len; i++) { +#else +for (i = len-1 , j =0 ; i >=0 ; i--) { +#endif + hex[j++] =hex_asc_hi (data[i]); + hex[j++] =hex_asc_lo (data[i]); + } + hex[j++] =' ' ; + +return trace_seq_putmem (s, hex, j); +}
-ENODOC, missing EXPORT_SYMBOL.
lib/trace_seq.c line 235
Quoted 1 line
lib/trace_seq.c new file +1
-0+void *trace_seq_reserve (struct trace_seq *s,size_t len)
`len' is a size_t here, a uint in trace_seq and an int when it's a local.
Quoted 6 lines
lib/trace_seq.c new file +6
-0+{ +void *ret; + +if (s->full) +return NULL ; +
lib/trace_seq.c line 251
Quoted 10 lines
lib/trace_seq.c new file +10
-0+if (len > ((PAGE_SIZE -1 ) - s->len)) { + s->full =1 ; +return NULL ; + } + + ret = s->buffer + s->len; + s->len += len; + +return ret; +}
Dittos
Quoted 17 lines
lib/trace_seq.c new file +17
-0+int trace_seq_path (struct trace_seq *s,const struct path *path) +{ +unsigned char *p; + +if (s->full) +return 0 ; + +if (s->len >= (PAGE_SIZE -1 )) { + s->full =1 ; +return 0 ; + } + + p =d_path (path, s->buffer + s->len,PAGE_SIZE - s->len); +if (!IS_ERR (p)) { + p =mangle_path (s->buffer + s->len, p,"\n" ); +if (p) { + s->len = p - s->buffer;
lib/trace_seq.c line 279
Quoted 10 lines
lib/trace_seq.c new file +10
-0+return 1 ; + } + }else { + s->buffer[s->len++] ='?' ; +return 1 ; + } + + s->full =1 ; +return 0 ; +}
Dittos
Quoted 13 lines
lib/trace_seq.c new file +13
-0+ssize_t trace_seq_to_user (struct trace_seq *s,char __user *ubuf,size_t cnt) +{ +int len; +int ret; + +if (!cnt) +return 0 ; + +if (s->len <= s->readpos) +return -EBUSY ; + + len = s->len - s->readpos; +if (cnt > len)
lib/trace_seq.c line 303
Quoted 10 lines
lib/trace_seq.c new file +10
-0+ cnt = len; + ret =copy_to_user (ubuf, s->buffer + s->readpos, cnt); +if (ret == cnt) +return -EFAULT ; + + cnt -= ret; + + s->readpos += cnt; +return cnt; +}
Dittos
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 20 lines · On Thu, 19 Jun 2014 22:06:07 -0700 Andrew Morton <[email protected]>…
Quoted 6 lines · On Thu, 19 Jun 2014 17:33:30 -0400 Steven Rostedt <[email protected]> wrote:
From: "Steven Rostedt (Red Hat)" <[email protected]>
The
trace_seqfunctions are rather useful outside of tracing. Instead of having it be dependent onCONFIG_TRACING, move the code intolib/and allow other users to have access to it even when tracing is not configured.What LT said. It's pileon time!
Quoted 8 lines
Signed-off-by Steven Rostedt <[email protected]>--- include/linux/trace_seq.h | 68 ++-------- kernel/trace/trace.c | 24 ---- kernel/trace/trace_output.c | 268 --------------------------------------- kernel/trace/trace_output.h | 16 --- lib/Makefile | 2 +- lib/trace_seq.c | 303 ++++++++++++++++++++++++++++++++++++++++++++
Quoted 1 line
Putting it in there makes me look at it ;)
Damn it! If I had known this, I would have moved it there on day one.
You can't say you didn't know about this. I cc you on all my patches :-)
So next time, to get you to review my code, I just need to threaten to add it to lib?
Quoted 11 lines
include/linux/trace_seq.h extern int trace_seq_path(struct trace_seq *s, const struct path *path); · line 52
Quoted 9 lines
include/linux/trace_seq.h +3
-0+#define SEQ_PUT_FIELD_RET (s, x) \ +do { \ + if (!trace_seq_putmem(s, &(x), sizeof(x))) \hm, does sizeof(x++) increment x? I guess it does.
I hate this macro. I moved it just to make the kernel build. I would love to change it. It is more to do with tracing than trace_seq anyway.
Quoted 7 lines
Quoted 7 lines
include/linux/trace_seq.h +2
-0+return TRACE_TYPE_PARTIAL_LINE ; \ +}while (0 )
Quoted 8 lines
include/linux/trace_seq.h extern int trace_seq_path(struct trace_seq *s, const struct path *path); · line 61
Quoted 6 lines
include/linux/trace_seq.h +6
-0+#define SEQ_PUT_HEX_FIELD_RET (s, x) \ +do { \ + BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ + if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ + return TRACE_TYPE_PARTIAL_LINE; \ +} while (0)Also has side-effects.
Yep, and it shouldn't be in the lib anyway. Again, HATE IT!
Quoted 5 lines
Quoted 5 lines
#endif /*
_LINUX_TRACE_SEQ_H*/...
Quoted 16 lines
Quoted 4 lines
lib/trace_seq.c new file +1
-0@@ -0,0 +1,303 @@ +/* lib/trace_seq.c line 11
Quoted 10 lines
lib/trace_seq.c new file +10
-0+* trace_seq.c +* +* Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt <[email protected]> +* +*/ +#include <linux/uaccess.h> +#include <linux/seq_file.h> +#include <linux/trace_seq.h> + +int trace_print_seq (struct seq_file *m,struct trace_seq *s)
-ENODOC
Heh, I noticed this too, and almost sent out a 2/4 patch that added the docs. I should add it first before moving it. But I was lazy and sent this out because I was more interested in the NMI code feedback than this.
Quoted 7 lines
lib/trace_seq.c line 13
Quoted 3 lines
lib/trace_seq.c new file +2
-0+{ +int len = s->len >=PAGE_SIZE ?PAGE_SIZE -1 : s->len;int = uint >= ulong ? ulog : uint
that's spastic. Can we choose a type and stick to it?
Ah, yeah that should be fixed.
Quoted 2 lines
Also,
min().
Right.
Quoted 10 lines
lib/trace_seq.c line 20
Quoted 8 lines
lib/trace_seq.c new file +7
-0+int ret; + + ret =seq_write (m, s->buffer, len); + +/* +* Only reset this buffer if we successfully wrote to the +* seq_file buffer. why?
Because we might want to try again. I'll update the comment.
Quoted 17 lines
Quoted 5 lines
lib/trace_seq.c new file +4
-0+*/ +if (!ret) +trace_seq_init (s); +lib/trace_seq.c line 34
Quoted 10 lines
lib/trace_seq.c new file +10
-0+return ret; +} + +/** +* trace_seq_printf - sequence printing of trace information +* @s: trace sequence descriptor +* @fmt: printf format string +* +* It returns 0 if the trace oversizes the buffer's free +* space, 1 otherwise. s/oversizes/would overrun/?
It made sense when I first read it (Jiri wrote it). How about:
Returns 0 if the recorded print is bigger than the free space of the buffer. Returns 1 otherwise.
Quoted 11 lines
lib/trace_seq.c line 43
Quoted 9 lines
lib/trace_seq.c new file +8
-0+* The tracer may use either sequence operations or its own +* copy to user routines. To simplify formating of a trace +* trace_seq_printf is used to store strings into a special +* buffer (@s). Then the output may be either used by +* the sequencer or pulled into another buffer. +*/ +int +trace_seq_printf (struct trace_seq *s,const char *fmt, ...)unneeded newline
OK.
Quoted 5 lines
lib/trace_seq.c line 44
Quoted 3 lines
lib/trace_seq.c new file +2
-0+{ +int len = (PAGE_SIZE -1 ) - s->len;int = ulong - uint;
Maybe it should just be initialized via a macro too?
Quoted 15 lines
lib/trace_seq.c line 56
Quoted 12 lines
lib/trace_seq.c new file +11
-0+va_list ap; +int ret; + +if (s->full || !len) +return 0 ; + +va_start (ap, fmt); + ret =vsnprintf (s->buffer + s->len, len, fmt, ap); +va_end (ap); + +/* If we can't write it all, don't bother writing anything */ This is somewhat unusual behavior for a write()-style thing. Comment should explain "why", not "what".
Yeah. It's like that because this was driven by the code that calls it, and is pretty engrained in the process. But taking it out of it's natural habitat, we need to explain these strange rituals that it does.
The why is, this is a buffer write, and users of it expect it to be all or nothing, because a partial write is usually solved by flushing and trying again with the same write arguments. If we did partial writes, then the user would need another buffer to store the full write to be able to just write out the rest of the line. That basically destroys the point of trace_seq as it is suppose to be that extra buffer.
Quoted 16 lines
Quoted 4 lines
lib/trace_seq.c new file +3
-0+if (ret >= len) { + s->full =1 ; +return 0 ;lib/trace_seq.c line 69
Quoted 10 lines
lib/trace_seq.c new file +10
-0+ } + + s->len += ret; + +return 1 ; +} +EXPORT_SYMBOL_GPL (trace_seq_printf); + +/** +* trace_seq_bitmask - put a list of longs as a bitmask print output Is that grammatical?
No but it's almost a haiku:
trace_seq_bitmask -
put a list of longs as a
bitmask print output
Quoted 9 lines
lib/trace_seq.c line 75
Quoted 7 lines
lib/trace_seq.c new file +6
-0+* @s: trace sequence descriptor +* @maskp: points to an array of unsigned longs that represent a bitmask +* @nmaskbits: The number of bits that are valid in @maskp +* +* It returns 0 if the trace oversizes the buffer's free +* space, 1 otherwise. Ditto
The beauty of cut-and-paste.
Quoted 13 lines
Quoted 13 lines
lib/trace_seq.c new file +12
-0+* Writes a ASCII representation of a bitmask string into @s. +*/ +int +trace_seq_bitmask (struct trace_seq *s,const unsigned long *maskp, +int nmaskbits) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; +
Quoted 8 lines
lib/trace_seq.c line 94
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ ret =bitmap_scnprintf (s->buffer, len, maskp, nmaskbits); + s->len += ret; + +return 1 ; +} +EXPORT_SYMBOL_GPL (trace_seq_bitmask);More dittos.
Confused. What dittos is this dittoing?
Quoted 10 lines
lib/trace_seq.c line 103
Quoted 9 lines
lib/trace_seq.c new file +8
-0+/** +* trace_seq_vprintf - sequence printing of trace information +* @s: trace sequence descriptor +* @fmt: printf format string +* +* The tracer may use either sequence operations or its own +* copy to user routines. To simplify formating of a trace +* trace_seq_printf is used to store strings into a special
Quoted 2 lines
"
trace_seq_printf()". Apparently it makes the kerneldoc output come out right.
OK.
Quoted 20 lines
Quoted 20 lines
lib/trace_seq.c new file +19
-0+* buffer (@s). Then the output may be either used by +* the sequencer or pulled into another buffer. +*/ +int +trace_seq_vprintf (struct trace_seq *s,const char *fmt,va_list args) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; + + ret =vsnprintf (s->buffer + s->len, len, fmt, args); + +/* If we can't write it all, don't bother writing anything */ +if (ret >= len) { + s->full =1 ; +return 0 ; + }
Quoted 8 lines
lib/trace_seq.c line 128
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ + s->len += ret; + +return len; +} +EXPORT_SYMBOL_GPL (trace_seq_vprintf);Several dittos.
Oh, just on the function. You're not dittoing a comment about the EXPORT_SYMBOL_GPL() that you forgot to add, are you?
Quoted 4 lines
lib/trace_seq.c line 130
Quoted 2 lines
lib/trace_seq.c new file +1
-0+int trace_seq_bprintf (struct trace_seq *s,const char *fmt,const u32 *binary)
-ENODOC
Ditto
Quoted 14 lines
Quoted 14 lines
lib/trace_seq.c new file +13
-0+{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; + + ret =bstr_printf (s->buffer + s->len, len, fmt, binary); + +/* If we can't write it all, don't bother writing anything */ +if (ret >= len) { + s->full =1 ; +return 0 ;
Quoted 8 lines
lib/trace_seq.c line 127
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ } + + s->len += ret; + +return len; +}Dittos.
Dittos
Quoted 22 lines
Quoted 22 lines
lib/trace_seq.c new file +21
-0+/** +* trace_seq_puts - trace sequence printing of simple string +* @s: trace sequence descriptor +* @str: simple string to record +* +* The tracer may use either the sequence operations or its own +* copy to user routines. This function records a simple string +* into a special buffer (@s) for later retrieval by a sequencer +* or other mechanism. +*/ +int trace_seq_puts (struct trace_seq *s,const char *str) +{ +int len =strlen (str); + +if (s->full) +return 0 ; + +if (len > ((PAGE_SIZE -1 ) - s->len)) { + s->full =1 ; +return 0 ; + }
Quoted 8 lines
lib/trace_seq.c line 177
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ +memcpy (s->buffer + s->len, str, len); + s->len += len; + +return len; +}Missing
EXPORT_SYMBOL?
The others were used by the tracepoint infrastructure. But I'm not even sure they are needed anymore because the ftrace.h file now calls another function that does more work which is exported and then calls these.
But if I'm going to add this to the library, they probably all should be exported.
Quoted 10 lines
Quoted 10 lines
lib/trace_seq.c new file +9
-0+int trace_seq_putc (struct trace_seq *s,unsigned char c) +{ +if (s->full) +return 0 ; + +if (s->len >= (PAGE_SIZE -1 )) { + s->full =1 ; +return 0 ; + }
Quoted 8 lines
lib/trace_seq.c line 193
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ + s->buffer[s->len++] = c; + +return 1 ; +} +EXPORT_SYMBOL (trace_seq_putc);Mix of
EXPORT_SYMBOL()andEXPORT_SYMBOL_GPL()
That's a mistake. They all should be EXPORT_SYMBOL_GPL(). Hmm, I caught this once before and made a patch to fix it. That got lost somehow :-/
Quoted 11 lines
Quoted 11 lines
lib/trace_seq.c new file +10
-0+int trace_seq_putmem (struct trace_seq *s,const void *mem,size_t len) +{ +if (s->full) +return 0 ; + +if (len > ((PAGE_SIZE -1 ) - s->len)) { + s->full =1 ; +return 0 ; + } +
Quoted 8 lines
lib/trace_seq.c line 210
Quoted 6 lines
lib/trace_seq.c new file +6
-0+memcpy (s->buffer + s->len, mem, len); + s->len += len; + +return len; +} +Lotsa dittos
Ditto back at'cha
Quoted 14 lines
lib/trace_seq.c line 220
Quoted 11 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Comment? What? Git blame isn't good enough for ya? ;-)
Quoted 2 lines
Is this test really needed?
trace_seq_putmem()will handle this.
It was added as an optimization, because once it filled up, you could still have multiple calls to the trace_seq() functions that would waste time trying to write the buffer.
It seemed like a good idea at the time. I Cc'd Johannes Berg as he's the one that implemented.
Johannes, is this really needed, should we bother keeping it?
Quoted 15 lines
lib/trace_seq.c line 233
Quoted 13 lines
lib/trace_seq.c new file +12
-0+#ifdef __BIG_ENDIAN +for (i =0 , j =0 ; i < len; i++) { +#else +for (i = len-1 , j =0 ; i >=0 ; i--) { +#endif + hex[j++] =hex_asc_hi (data[i]); + hex[j++] =hex_asc_lo (data[i]); + } + hex[j++] =' ' ; + +return trace_seq_putmem (s, hex, j); +}
-ENODOC, missingEXPORT_SYMBOL.
Ditto.
Quoted 4 lines
lib/trace_seq.c line 235
Quoted 2 lines
lib/trace_seq.c new file +1
-0+void *trace_seq_reserve (struct trace_seq *s,size_t len)`len' is a
size_there, a uint intrace_seqand an int when it's a local.
We like to be diverse.
Quoted 11 lines
Quoted 11 lines
lib/trace_seq.c new file +10
-0+{ +void *ret; + +if (s->full) +return NULL ; + +if (len > ((PAGE_SIZE -1 ) - s->len)) { + s->full =1 ; +return NULL ; + }
Quoted 8 lines
lib/trace_seq.c line 251
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ + ret = s->buffer + s->len; + s->len += len; + +return ret; +}Dittos
Return dittos
Quoted 22 lines
Quoted 22 lines
lib/trace_seq.c new file +21
-0+int trace_seq_path (struct trace_seq *s,const struct path *path) +{ +unsigned char *p; + +if (s->full) +return 0 ; + +if (s->len >= (PAGE_SIZE -1 )) { + s->full =1 ; +return 0 ; + } + + p =d_path (path, s->buffer + s->len,PAGE_SIZE - s->len); +if (!IS_ERR (p)) { + p =mangle_path (s->buffer + s->len, p,"\n" ); +if (p) { + s->len = p - s->buffer; +return 1 ; + } + }else { + s->buffer[s->len++] ='?' ;
Quoted 8 lines
lib/trace_seq.c line 122
Quoted 6 lines
lib/trace_seq.c new file +6
-0+return 1 ; + } + + s->full =1 ; +return 0 ; +}Dittos
sottiD
Quoted 18 lines
Quoted 18 lines
lib/trace_seq.c new file +17
-0+ssize_t trace_seq_to_user (struct trace_seq *s,char __user *ubuf,size_t 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 ;
Quoted 8 lines
lib/trace_seq.c line 303
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ + cnt -= ret; + + s->readpos += cnt; +return cnt; +}Dittos
Tripple ditto!
Hey! Thanks for the review. Much appreciated. And maybe you should read those messages in your /dev/null folder that I cc you with. :-)
-- Steve
Andrew Morton
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 25 lines · On Fri, 20 Jun 2014 12:58:23 -0400 Steven Rostedt <[email protected]> wrote:
...
Quoted 13 lines
Quoted 13 lines
+ * Writes a ASCII representation of a bitmask string into @s.
+ */lib/trace_seq.c new file +10
-0+int +trace_seq_bitmask (struct trace_seq *s,const unsigned long *maskp, +int nmaskbits) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; +Quoted 8 lines
lib/trace_seq.c line 94
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ ret =bitmap_scnprintf (s->buffer, len, maskp, nmaskbits); + s->len += ret; + +return 1 ; +} +EXPORT_SYMBOL_GPL (trace_seq_bitmask);More dittos.
Quoted 1 line
Confused. What dittos is this dittoing?
Unneeded newline, poorly considered choice of types.
Quoted 31 lines
...
Quoted 19 lines
Quoted 19 lines
+ * buffer (@s). Then the output may be either used by
+ * the sequencer or pulled into another buffer.
+ */lib/trace_seq.c new file +16
-0+int +trace_seq_vprintf (struct trace_seq *s,const char *fmt,va_list args) +{ +int len = (PAGE_SIZE -1 ) - s->len; +int ret; + +if (s->full || !len) +return 0 ; + + ret =vsnprintf (s->buffer + s->len, len, fmt, args); + +/* If we can't write it all, don't bother writing anything */ +if (ret >= len) { + s->full =1 ; +return 0 ; + }Quoted 8 lines
lib/trace_seq.c line 128
Quoted 6 lines
lib/trace_seq.c new file +6
-0+ + s->len += ret; + +return len; +} +EXPORT_SYMBOL_GPL (trace_seq_vprintf);Several dittos.
Quoted 2 lines
Oh, just on the function. You're not dittoing a comment about the
EXPORT_SYMBOL_GPL()that you forgot to add, are you?
yup. Unneded newline, types, EXPORT_ confusion.
Quoted 18 lines
...
Quoted 14 lines
lib/trace_seq.c line 220
Quoted 11 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Quoted 1 line
Comment? What? Git blame isn't good enough for ya? ;-)
There's always that. There's also googling for the original list dicsussion. But it's a bit user-unfriendly, particularly when then code has aged was subsequently altered many times.
Quoted 6 lines
...
Hey! Thanks for the review. Much appreciated. And maybe you should read those messages in your
/dev/nullfolder that I cc you with. :-)
I sometimes do.
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 5 lines · On Fri, 20 Jun 2014 10:12:44 -0700 Andrew Morton <[email protected]>…
Quoted 1 line
Comment? What? Git blame isn't good enough for ya? ;-)
There's always that. There's also googling for the original list dicsussion. But it's a bit user-unfriendly, particularly when then code has aged was subsequently altered many times.
I was replying mostly in jest as that was what I did to remember why we added it.
Quoted 9 lines
Quoted 7 lines
...
Hey! Thanks for the review. Much appreciated. And maybe you should read those messages in your
/dev/nullfolder that I cc you with. :-)I sometimes do.
Ah, the lurker.
-- Steve
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
In reply to Andrew MortonQuoted 14 lines · On Fri, 20 Jun 2014 10:12:44 -0700 Andrew Morton <[email protected]>…
Quoted 14 lines
Quoted 13 lines
lib/trace_seq.c line 220
Quoted 10 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Quoted 5 lines
Quoted 1 line
Comment? What? Git blame isn't good enough for ya? ;-)
There's always that. There's also googling for the original list dicsussion. But it's a bit user-unfriendly, particularly when then code has aged was subsequently altered many times.
Although I did not address this because I'm waiting to hear back from Johannes Berg, I updated for your other comments. I hope I got them all.
Regardless of this patch series, I pulled out the code from trace_output.c and made a separate file for the trace_seq_*() functions in kernel/trace/trace_seq.c. I then updated the file with your comments. I found a bug or two and I will be dealing with them later as this will only be a clean up patch not a bug fix patch.
Anyway, here's the new file:
-- Steve
/*
* trace_seq.c
*
* Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt <[email protected]> *
* The trace_seq 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 trace_seq must be initialized with trace_seq_init().
* This will set up the counters within the descriptor. You can call
* trace_seq_init() more than once to reset the trace_seq to start
* from scratch.
*
* The buffer size is currently PAGE_SIZE, although it may become dynamic
* in the future.
*
* A write to the buffer will either succed or fail. That is, unlike
* sprintf() there will not be a partial write (well it may write into
* the buffer but it wont update the pointers). This allows users to
* try to write something into the trace_seq buffer and if it fails
* they can flush it and try again.
*
*/
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#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)
/* How much buffer is written? */
#define TRACE_SEQ_BUF_USED(s) min((s)->len, PAGE_SIZE - 1)
/**
* trace_print_seq - move the contents of trace_seq into a seq_file
* @m: the seq_file descriptor that is the destination
* @s: the trace_seq descriptor that is the source.
*
* Returns 0 on success and non zero on error. If it succeeds to
* write to the seq_file it will reset the trace_seq, otherwise
* it does not modify the trace_seq to let the caller try again. */
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);
/*
* Only reset this buffer if we successfully wrote to the
* seq_file buffer. This lets the caller try again or
* do something else with the contents.
*/
if (!ret)
trace_seq_init(s);
return ret;
}/**
* trace_seq_printf - sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
* trace_seq_printf() is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
va_list ap;
int ret;
if (s->full || !len)
return 0;
va_start(ap, fmt);
ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
va_end(ap);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_printf);
/**
* trace_seq_bitmask - write a bitmask array in its ASCII representation
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
int nmaskbits)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
s->len += ret;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_bitmask);
/**
* trace_seq_vprintf - sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
* trace_seq_printf is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = vsnprintf(s->buffer + s->len, len, fmt, args);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_vprintf);
/**
* trace_seq_bprintf - Write the printf string from binary arguments
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_bprintf);
/**
* trace_seq_puts - trace sequence printing of simple string
* @s: trace sequence descriptor
* @str: simple string to record
*
* The tracer may use either the sequence operations or its own
* copy to user routines. This function records a simple string
* into a special buffer (@s) for later retrieval by a sequencer
* or other mechanism.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_puts(struct trace_seq *s, const char *str)
{
unsigned int len = strlen(str);
if (s->full)
return 0;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}
memcpy(s->buffer + s->len, str, len);
s->len += len;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_puts);
/**
* trace_seq_putc - trace sequence printing of simple character
* @s: trace sequence descriptor
* @c: simple character to record
*
* The tracer may use either the sequence operations or its own
* copy to user routines. This function records a simple charater
* into a special buffer (@s) for later retrieval by a sequencer
* or other mechanism.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putc(struct trace_seq *s, unsigned char c)
{
if (s->full)
return 0;
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}
s->buffer[s->len++] = c;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_putc);
/**
* trace_seq_putmem - write raw data into the trace_seq buffer
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned len len)
{
if (s->full)
return 0;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}
memcpy(s->buffer + s->len, mem, len);
s->len += len;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_putmem);
#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1)
/**
* trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
* @s: trace sequence descriptor
* @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 trace_seq_putmem() except instead of just copying the
* raw memory into the buffer it writes its ASCII representation of it
* in hex characters.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
unsigned int len)
{
unsigned char hex[HEX_CHARS];
const unsigned char *data = mem;
int i, j;
if (s->full)
return 0;#ifdef __BIG_ENDIAN
for (i = 0, j = 0; i < len; i++) {
#else
for (i = len-1, j = 0; i >= 0; i--) {
#endif
hex[j++] = hex_asc_hi(data[i]);
hex[j++] = hex_asc_lo(data[i]);
}
hex[j++] = ' ';
return trace_seq_putmem(s, hex, j);
}EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
/**
* trace_seq_reserve - reserve space on the sequence buffer
* @s: trace sequence descriptor
* @len: The amount to reserver.
*
* If for some reason there is a need to save some space on the
* buffer to fill in later, this function is used for that purpose.
* The given length will be reserved and the pointer to that
* location on the buffer is returned, unless there is not enough
* buffer left to hold the given length then NULL is returned. */
void *trace_seq_reserve(struct trace_seq *s, unsigned int len)
{
void *ret;
if (s->full)
return NULL;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return NULL;
}
ret = s->buffer + s->len;
s->len += len;
return ret;
}/**
* trace_seq_path - copy a path into the sequence buffer
* @s: trace sequence descriptor
* @path: path to write into the sequence buffer.
*
* Write a path name into the sequence buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_path(struct trace_seq *s, const struct path *path)
{
unsigned char *p;
if (s->full)
return 0;
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}
p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
if (!IS_ERR(p)) {
p = mangle_path(s->buffer + s->len, p, "\n");
if (p) {
s->len = p - s->buffer;
return 1;
}
} else {
s->buffer[s->len++] = '?';
return 1;
}
s->full = 1;
return 0;
}/**
* trace_seq_to_user - copy the squence buffer to user space
* @s: trace sequence descriptor
* @ubuf: The userspace memory location to copy to
* @cnt: The amount to copy
*
* Copies the sequence buffer into the userspace memory pointed to
* by @ubuf. It starts from the last read position (@s->readpos)
* and writes up to @cnt characters or till it reaches the end of
* the content in the buffer (@s->len), which ever comes first. *
* On success, it returns a positive number of the number of bytes
* it copied.
*
* On failure it returns -EBUSY if all of the content in the
* sequence has been already read, which includes nothing in the
* sequenc (@s->len == @s->readpos).
*
* Returns -EFAULT if the copy to userspace fails.
*/
int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt)
{
int len;
int ret;
if (!cnt)
return 0;
if (s->len <= s->readpos)
return -EBUSY;
len = s->len - s->readpos;
if (cnt > len)
cnt = len;
ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
if (ret == cnt)
return -EFAULT;
cnt -= ret;
s->readpos += cnt;
return cnt;
}Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 27 lines · On Fri, 20 Jun 2014 16:28:45 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 14 lines · On Fri, 20 Jun 2014 10:12:44 -0700 Andrew Morton <[email protected]>…
Quoted 14 lines
Quoted 13 lines
lib/trace_seq.c line 220
Quoted 10 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Quoted 5 lines
Quoted 1 line
Comment? What? Git blame isn't good enough for ya? ;-)
There's always that. There's also googling for the original list dicsussion. But it's a bit user-unfriendly, particularly when then code has aged was subsequently altered many times.
Although I did not address this because I'm waiting to hear back from Johannes Berg, I updated for your other comments. I hope I got them all.
Quoted 7 lines
Regardless of this patch series, I pulled out the code from
trace_output.cand made a separate file for the trace_seq_*() functions inkernel/trace/trace_seq.c. I then updated the file with your comments. I found a bug or two and I will be dealing with them later as this will only be a clean up patch not a bug fix patch.Anyway, here's the new file:
Would be helpful if I compiled it before posting it :-)
Also, I missed a few EXPORT_SYMBOL_GPL()s along the way.
New update:
-- Steve
/*
* trace_seq.c
*
* Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt <[email protected]> *
* The trace_seq 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 trace_seq must be initialized with trace_seq_init().
* This will set up the counters within the descriptor. You can call
* trace_seq_init() more than once to reset the trace_seq to start
* from scratch.
*
* The buffer size is currently PAGE_SIZE, although it may become dynamic
* in the future.
*
* A write to the buffer will either succed or fail. That is, unlike
* sprintf() there will not be a partial write (well it may write into
* the buffer but it wont update the pointers). This allows users to
* try to write something into the trace_seq buffer and if it fails
* they can flush it and try again.
*
*/
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#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)
/* How much buffer is written? */
#define TRACE_SEQ_BUF_USED(s) min((s)->len, (unsigned int)(PAGE_SIZE - 1))
/**
* trace_print_seq - move the contents of trace_seq into a seq_file
* @m: the seq_file descriptor that is the destination
* @s: the trace_seq descriptor that is the source.
*
* Returns 0 on success and non zero on error. If it succeeds to
* write to the seq_file it will reset the trace_seq, otherwise
* it does not modify the trace_seq to let the caller try again. */
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);
/*
* Only reset this buffer if we successfully wrote to the
* seq_file buffer. This lets the caller try again or
* do something else with the contents.
*/
if (!ret)
trace_seq_init(s);
return ret;
}/**
* trace_seq_printf - sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
* trace_seq_printf() is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
va_list ap;
int ret;
if (s->full || !len)
return 0;
va_start(ap, fmt);
ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
va_end(ap);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_printf);
/**
* trace_seq_bitmask - write a bitmask array in its ASCII representation
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp,
int nmaskbits)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);
s->len += ret;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_bitmask);
/**
* trace_seq_vprintf - sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
* trace_seq_printf is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = vsnprintf(s->buffer + s->len, len, fmt, args);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_vprintf);
/**
* trace_seq_bprintf - Write the printf string from binary arguments
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary)
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
/* If we can't write it all, don't bother writing anything */
if (ret >= len) {
s->full = 1;
return 0;
}
s->len += ret;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_bprintf);
/**
* trace_seq_puts - trace sequence printing of simple string
* @s: trace sequence descriptor
* @str: simple string to record
*
* The tracer may use either the sequence operations or its own
* copy to user routines. This function records a simple string
* into a special buffer (@s) for later retrieval by a sequencer
* or other mechanism.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_puts(struct trace_seq *s, const char *str)
{
unsigned int len = strlen(str);
if (s->full)
return 0;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}
memcpy(s->buffer + s->len, str, len);
s->len += len;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_puts);
/**
* trace_seq_putc - trace sequence printing of simple character
* @s: trace sequence descriptor
* @c: simple character to record
*
* The tracer may use either the sequence operations or its own
* copy to user routines. This function records a simple charater
* into a special buffer (@s) for later retrieval by a sequencer
* or other mechanism.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putc(struct trace_seq *s, unsigned char c)
{
if (s->full)
return 0;
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}
s->buffer[s->len++] = c;
return 1;
}EXPORT_SYMBOL_GPL(trace_seq_putc);
/**
* trace_seq_putmem - write raw data into the trace_seq buffer
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len)
{
if (s->full)
return 0;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return 0;
}
memcpy(s->buffer + s->len, mem, len);
s->len += len;
return len;
}EXPORT_SYMBOL_GPL(trace_seq_putmem);
#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1)
/**
* trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex
* @s: trace sequence descriptor
* @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 trace_seq_putmem() except instead of just copying the
* raw memory into the buffer it writes its ASCII representation of it
* in hex characters.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem_hex(struct trace_seq *s, const void *mem,
unsigned int len)
{
unsigned char hex[HEX_CHARS];
const unsigned char *data = mem;
int i, j;
if (s->full)
return 0;#ifdef __BIG_ENDIAN
for (i = 0, j = 0; i < len; i++) {
#else
for (i = len-1, j = 0; i >= 0; i--) {
#endif
hex[j++] = hex_asc_hi(data[i]);
hex[j++] = hex_asc_lo(data[i]);
}
hex[j++] = ' ';
return trace_seq_putmem(s, hex, j);
}EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);
/**
* trace_seq_reserve - reserve space on the sequence buffer
* @s: trace sequence descriptor
* @len: The amount to reserver.
*
* If for some reason there is a need to save some space on the
* buffer to fill in later, this function is used for that purpose.
* The given length will be reserved and the pointer to that
* location on the buffer is returned, unless there is not enough
* buffer left to hold the given length then NULL is returned. */
void *trace_seq_reserve(struct trace_seq *s, unsigned int len)
{
void *ret;
if (s->full)
return NULL;
if (len > TRACE_SEQ_BUF_LEFT(s)) {
s->full = 1;
return NULL;
}
ret = s->buffer + s->len;
s->len += len;
return ret;
}EXPORT_SYMBOL_GPL(trace_seq_reserve);
/**
* trace_seq_path - copy a path into the sequence buffer
* @s: trace sequence descriptor
* @path: path to write into the sequence buffer.
*
* Write a path name into the sequence buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_path(struct trace_seq *s, const struct path *path)
{
unsigned char *p;
if (s->full)
return 0;
if (TRACE_SEQ_BUF_LEFT(s) < 1) {
s->full = 1;
return 0;
}
p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
if (!IS_ERR(p)) {
p = mangle_path(s->buffer + s->len, p, "\n");
if (p) {
s->len = p - s->buffer;
return 1;
}
} else {
s->buffer[s->len++] = '?';
return 1;
}
s->full = 1;
return 0;
}EXPORT_SYMBOL_GPL(trace_seq_path);
/**
* trace_seq_to_user - copy the squence buffer to user space
* @s: trace sequence descriptor
* @ubuf: The userspace memory location to copy to
* @cnt: The amount to copy
*
* Copies the sequence buffer into the userspace memory pointed to
* by @ubuf. It starts from the last read position (@s->readpos)
* and writes up to @cnt characters or till it reaches the end of
* the content in the buffer (@s->len), which ever comes first. *
* On success, it returns a positive number of the number of bytes
* it copied.
*
* On failure it returns -EBUSY if all of the content in the
* sequence has been already read, which includes nothing in the
* sequenc (@s->len == @s->readpos).
*
* Returns -EFAULT if the copy to userspace fails.
*/
int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt)
{
int len;
int ret;
if (!cnt)
return 0;
if (s->len <= s->readpos)
return -EBUSY;
len = s->len - s->readpos;
if (cnt > len)
cnt = len;
ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
if (ret == cnt)
return -EFAULT;
cnt -= ret;
s->readpos += cnt;
return cnt;
}EXPORT_SYMBOL_GPL(trace_seq_to_user);
Re: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 60 lines · Steven Rostedt
Quoted 27 lines · On Fri, 20 Jun 2014 16:28:45 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 14 lines · On Fri, 20 Jun 2014 10:12:44 -0700 Andrew Morton <[email protected]>…
Quoted 14 lines
Quoted 13 lines
lib/trace_seq.c line 220
Quoted 10 lines
lib/trace_seq.c new file +10
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ]; +const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Quoted 5 lines
Quoted 1 line
Comment? What? Git blame isn't good enough for ya? ;-)
There's always that. There's also googling for the original list dicsussion. But it's a bit user-unfriendly, particularly when then code has aged was subsequently altered many times.
Although I did not address this because I'm waiting to hear back from Johannes Berg, I updated for your other comments. I hope I got them all.
Quoted 7 lines
Regardless of this patch series, I pulled out the code from
trace_output.cand made a separate file for the trace_seq_*() functions inkernel/trace/trace_seq.c. I then updated the file with your comments. I found a bug or two and I will be dealing with them later as this will only be a clean up patch not a bug fix patch.Anyway, here's the new file:
Would be helpful if I compiled it before posting it :-)
Also, I missed a few EXPORT_SYMBOL_GPL()s along the way.
New update:
-- Steve
/*
*trace_seq.c
*
* Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt <[email protected]> *
* Thetrace_seqis 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_filefunctionality but has some differences.
*
* To use it, thetrace_seqmust be initialized withtrace_seq_init().
* This will set up the counters within the descriptor. You can call
*trace_seq_init()more than once to reset thetrace_seqto start
* from scratch.
*
Quoted 8 lines
* The buffer size is currently
PAGE_SIZE, although it may become dynamic
* in the future.
*
* A write to the buffer will either succed or fail. That is, unlike
*sprintf()there will not be a partial write (well it may write into
* the buffer but it wont update the pointers). This allows users to
* try to write something into thetrace_seqbuffer and if it fails
* they can flush it and try again.
We might want to write as much as possible to the buffer if it is used for the backtrace.
Well, if I understand it correctly, this is only about the last line. If the trace does not fit, we are in troubles anyway. We might solve this later when needed.
Quoted 10 lines
*/
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/trace_seq.h>/* How much buffer is left on the
trace_seq? */ #defineTRACE_SEQ_BUF_LEFT(s)((PAGE_SIZE- 1) - (s)->len)/* How much buffer is written? */
#defineTRACE_SEQ_BUF_USED(s)min((s)->len, (unsigned int)(PAGE_SIZE - 1))
[...]
Quoted 56 lines
/**
*trace_seq_printf- sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
*trace_seq_printf()is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
inttrace_seq_printf(struct trace_seq *s, const char *fmt, ...){ unsigned int len = TRACE_SEQ_BUF_LEFT(s); va_list ap; int ret; if (s->full || !len) return 0; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); /* If we can't write it all, don't bother writing anything */ if (ret >= len) { s->full = 1; return 0; } s->len += ret; return 1; }
EXPORT_SYMBOL_GPL(trace_seq_printf);/**
*trace_seq_bitmask- write a bitmask array in its ASCII representation
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bitmask(structtrace_seq*s, const unsigned long *maskp,
int nmaskbits)
Quoted 8 lines
{
unsigned int len = TRACE_SEQ_BUF_LEFT(s);
int ret;
if (s->full || !len)
return 0;
ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);We do not detect here if the whole bitmap is written. Hmm, the perfect solution is not easy. What about using the following workaround?
If we wrote until the end of the buffer, the write was most likely incomplete:
if (ret == len) {
s->full = 1;
return 0;
}It is more consistent with what we do in the other functions.
Quoted 6 lines
s->len += ret; return 1; }
EXPORT_SYMBOL_GPL(trace_seq_bitmask);
[...]
Quoted 25 lines
/**
*trace_seq_putmem_hex- write raw memory into the buffer in ASCII hex
* @s: trace sequence 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 totrace_seq_putmem()except instead of just copying the
* raw memory into the buffer it writes its ASCII representation of it
* in hex characters.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem_hex(structtrace_seq*s, const void *mem,
unsigned int len){ unsigned char hex[HEX_CHARS]; const unsigned char *data = mem; int i, j; if (s->full) return 0;
Quoted 5 lines
#ifdef __BIG_ENDIAN for (i = 0, j = 0; i < len; i++) { #else for (i = len-1, j = 0; i >= 0; i--) {#endif
This might overflow when "len" > (HEX_CHARS-1)/2.
I think that we should either limit "len" or make it in two cycles.
Hmm, if we use two cycles, it might be problem how to solve the endianity. IMHO, it depends on what type of data is written into the buffer (single vs. set of values).
Quoted 37 lines
hex[j++] = hex_asc_hi(data[i]); hex[j++] = hex_asc_lo(data[i]); } hex[j++] = ' '; return trace_seq_putmem(s, hex, j); }
EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);/**
*trace_seq_reserve- reserve space on the sequence buffer
* @s: trace sequence descriptor
* @len: The amount to reserver.
*
* If for some reason there is a need to save some space on the
* buffer to fill in later, this function is used for that purpose.
* The given length will be reserved and the pointer to that
* location on the buffer is returned, unless there is not enough
* buffer left to hold the given length then NULL is returned. */
void *trace_seq_reserve(structtrace_seq*s, unsigned int len){ void *ret; if (s->full) return NULL; if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return NULL; } ret = s->buffer + s->len; s->len += len; return ret; }
Quoted 1 line
EXPORT_SYMBOL_GPL(trace_seq_reserve);
This sounds like a slightly dangerous operation. The reserve is valid only until the buffer is re-initialized. Is this really intended? Do we really want to export it?
Well, the whole buffer need to be used carefully. IMHO, it can't be accessed in parallel without extra locks. So, this might be fine after all. We could only warn about this in the function description.
[...]
The rest looks fine to me.
Also the proposed name "seq_buf" sounds fine to me.
Best Regards,
Petr
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 8 lines · On Mon, 23 Jun 2014 18:33:06 +0200 Petr Mládek <[email protected]> wrote:
Quoted 5 lines
* A write to the buffer will either succed or fail. That is, unlike
*sprintf()there will not be a partial write (well it may write into
* the buffer but it wont update the pointers). This allows users to
* try to write something into thetrace_seqbuffer and if it fails
* they can flush it and try again.We might want to write as much as possible to the buffer if it is used for the backtrace.
Understood, but there is a reason for this way of doing things.
Quoted 78 lines
Well, if I understand it correctly, this is only about the last line. If the trace does not fit, we are in troubles anyway. We might solve this later when needed.
Quoted 10 lines
*/
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/trace_seq.h>/* How much buffer is left on the
trace_seq? */ #defineTRACE_SEQ_BUF_LEFT(s)((PAGE_SIZE- 1) - (s)->len)/* How much buffer is written? */
#defineTRACE_SEQ_BUF_USED(s)min((s)->len, (unsigned int)(PAGE_SIZE - 1))[...]
Quoted 60 lines
/**
*trace_seq_printf- sequence printing of trace information
* @s: trace sequence descriptor
* @fmt: printf format string
*
* The tracer may use either sequence operations or its own
* copy to user routines. To simplify formating of a trace
*trace_seq_printf()is used to store strings into a special
* buffer (@s). Then the output may be either used by
* the sequencer or pulled into another buffer.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
inttrace_seq_printf(struct trace_seq *s, const char *fmt, ...){ unsigned int len = TRACE_SEQ_BUF_LEFT(s); va_list ap; int ret; if (s->full || !len) return 0; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); /* If we can't write it all, don't bother writing anything */ if (ret >= len) { s->full = 1; return 0; } s->len += ret; return 1; }
EXPORT_SYMBOL_GPL(trace_seq_printf);/**
*trace_seq_bitmask- write a bitmask array in its ASCII representation
* @s: trace sequence 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 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_bitmask(structtrace_seq*s, const unsigned long *maskp,
int nmaskbits){ unsigned int len = TRACE_SEQ_BUF_LEFT(s); int ret;
Quoted 7 lines
Quoted 4 lines
if (s->full || !len) return 0; ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits);We do not detect here if the whole bitmap is written. Hmm, the perfect solution is not easy. What about using the following workaround?
Ah right. Nice catch.
Quoted 4 lines
If we wrote until the end of the buffer, the write was most likely incomplete:
Quoted 11 lines
if (ret == len) { s->full = 1; return 0; }It is more consistent with what we do in the other functions.
Quoted 4 lines
s->len += ret; return 1; }
I'll have to look into this too. Yeah, the results should be more consistent. Maybe have zero be it fit, and return how much buffer is left when it doesn't fit?
Quoted 30 lines
Quoted 2 lines
EXPORT_SYMBOL_GPL(trace_seq_bitmask);[...]
Quoted 25 lines
/**
*trace_seq_putmem_hex- write raw memory into the buffer in ASCII hex
* @s: trace sequence 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 totrace_seq_putmem()except instead of just copying the
* raw memory into the buffer it writes its ASCII representation of it
* in hex characters.
*
* Returns 1 if we successfully written all the contents to
* the buffer.
* Returns 0 if we the length to write is bigger than the
* reserved buffer space. In this case, nothing gets written.
*/
int trace_seq_putmem_hex(structtrace_seq*s, const void *mem,
unsigned int len){ unsigned char hex[HEX_CHARS]; const unsigned char *data = mem; int i, j; if (s->full) return 0;
Quoted 7 lines
Quoted 5 lines
#ifdef __BIG_ENDIAN for (i = 0, j = 0; i < len; i++) { #else for (i = len-1, j = 0; i >= 0; i--) {#endif
This might overflow when "len" > (HEX_CHARS-1)/2.
Yes I caught this while doing updates per Andrew's replies. Currently it's fine because the only user of it is wrapped in the macro. It originally had a BUG_ON() if len > HEX_CHARS but that was moved to the macro to make it a build bug on. I hate this function, but we need to keep it to keep backward compatibility for what was in the original -rt latency tracer :-p
Anyway, last week I rewrote this function to be much more robust. You can look at my branch ftrace/next (caution, it rebases constantly).
Quoted 42 lines
I think that we should either limit "len" or make it in two cycles.
Hmm, if we use two cycles, it might be problem how to solve the endianity. IMHO, it depends on what type of data is written into the buffer (single vs. set of values).
Quoted 35 lines
hex[j++] = hex_asc_hi(data[i]); hex[j++] = hex_asc_lo(data[i]); } hex[j++] = ' '; return trace_seq_putmem(s, hex, j); }
EXPORT_SYMBOL_GPL(trace_seq_putmem_hex);/**
*trace_seq_reserve- reserve space on the sequence buffer
* @s: trace sequence descriptor
* @len: The amount to reserver.
*
* If for some reason there is a need to save some space on the
* buffer to fill in later, this function is used for that purpose.
* The given length will be reserved and the pointer to that
* location on the buffer is returned, unless there is not enough
* buffer left to hold the given length then NULL is returned. */
void *trace_seq_reserve(structtrace_seq*s, unsigned int len){ void *ret; if (s->full) return NULL; if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return NULL; } ret = s->buffer + s->len; s->len += len;
Quoted 6 lines
Quoted 3 lines
return ret; }
EXPORT_SYMBOL_GPL(trace_seq_reserve);This sounds like a slightly dangerous operation. The reserve is valid only until the buffer is re-initialized. Is this really intended?
Well, the only on reserving it should be whoever is using it. It is like allocating memory and then letting something else free it.
Quoted 1 line
Do we really want to export it?
Honestly, I don't know why this was added. It was created by Eduard and pulled in by Ingo. There's no users in the kernel. I think I may just nuke it.
Quoted 5 lines
Well, the whole buffer need to be used carefully. IMHO, it can't be accessed in parallel without extra locks. So, this might be fine after all. We could only warn about this in the function description.
Well, neither can seq_file. It's based on that. Take a look at some of my changes in ftrace/next and see .
Quoted 5 lines
[...]
The rest looks fine to me.
Also the proposed name "
seq_buf" sounds fine to me.
Yeah, that may change soon too. Right now I'm working on cleaning up the code within the trace directory, and then I'll push it to lib if this is the way we are going.
-- Steve
Quoted 4 lines
Best Regards,
Petr
Re: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
In reply to Steven RostedtQuoted 19 lines · Steven Rostedt
Quoted 5 lines
Quoted 5 lines
lib/trace_seq.c new file +5
-0+#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex (struct trace_seq *s,const void *mem,size_t len) +{ +unsigned char hex[HEX_CHARS ];Quoted 8 lines
lib/trace_seq.c line 220
Quoted 5 lines
lib/trace_seq.c new file +5
-0+const unsigned char *data = mem; +int i, j; + +if (s->full) +return 0 ;What's this ->full thing all about anyway? Some central comment which explains the design is needed.
Comment? What? Git blame isn't good enough for ya? ;-)
Quoted 2 lines
Is this test really needed?
trace_seq_putmem()will handle this.
Quoted 8 lines
It was added as an optimization, because once it filled up, you could still have multiple calls to the
trace_seq()functions that would waste time trying to write the buffer.It seemed like a good idea at the time. I Cc'd Johannes Berg as he's the one that implemented.
Johannes, is this really needed, should we bother keeping it?
Honestly, I don't remember, sorry.
Looking at the code though, I'm not sure it's a pure optimisation - if you do say putc() after a failed puts(), without this code the putc() would succeed? I can't tell right now if that's really a problem, but it seems you could get some odd behaviour out of it.
johannes
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 4 lines · On Sun, 22 Jun 2014 09:38:05 +0200 Johannes Berg <[email protected]>…
Looking at the code though, I'm not sure it's a pure optimisation - if you do say
putc()after a failedputs(), without this code theputc()would succeed? I can't tell right now if that's really a problem, but it seems you could get some odd behaviour out of it.
How would putc() still succeed? We're just talking about the "full" field. It would still do the length check:
if (s->len >= (PAGE_SIZE - 1)) return 0;
-- Steve
Re: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 9 lines · Steven Rostedt
Quoted 4 lines · On Sun, 22 Jun 2014 09:38:05 +0200 Johannes Berg <[email protected]>…
Looking at the code though, I'm not sure it's a pure optimisation - if you do say
putc()after a failedputs(), without this code theputc()would succeed? I can't tell right now if that's really a problem, but it seems you could get some odd behaviour out of it.
Quoted 5 lines
How would
putc()still succeed? We're just talking about the "full" field. It would still do the length check:if (s->len >= (PAGE_SIZE - 1)) return 0;
Right, but the puts() could fail if not all of the string fits, and a subsequent putc() might fit.
johannes
Steven Rostedt
maintainerRe: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 10 lines · On Mon, 23 Jun 2014 19:38:14 +0200 Johannes Berg <[email protected]>…
Quoted 9 lines · Steven Rostedt
Quoted 4 lines · On Sun, 22 Jun 2014 09:38:05 +0200 Johannes Berg <[email protected]>…
Looking at the code though, I'm not sure it's a pure optimisation - if you do say
putc()after a failedputs(), without this code theputc()would succeed? I can't tell right now if that's really a problem, but it seems you could get some odd behaviour out of it.
Quoted 9 lines
Quoted 5 lines
How would
putc()still succeed? We're just talking about the "full" field. It would still do the length check:if (s->len >= (PAGE_SIZE - 1)) return 0;Right, but the
puts()could fail if not all of the string fits, and a subsequentputc()might fit.
Ah! I see what you are saying. You are saying that once you are full you need to flush. Honestly, the trace_puts() should check the return. Perhaps it wants to try again with a shorter string?
The full flag means that once we hit full, we are done, no more can be written till the buffer is flushed.
Is that what we would want from this utility? Or do we want to allow the user to try again with a smaller string?
I was thinking of reversing the error code and have the trace_seq_*() return 0 as success and the remaining buffer size on error.
-- Steve
Re: [RFC][PATCH 1/3] trace_seq: Move the trace_seq code to lib/
Quoted 3 lines · Steven Rostedt
Ah! I see what you are saying. You are saying that once you are full you need to flush. Honestly, the
trace_puts()should check the return. Perhaps it wants to try again with a shorter string?
Perhaps, but perhaps not? I don't remember exactly the issue that I saw without the full flag, and it may very well be a purely theoretical thing too. I don't think I mind either way.
Quoted 2 lines
The full flag means that once we hit full, we are done, no more can be written till the buffer is flushed.
Right. Arguably, it was already full or overflowed, but as it now stands we make sure the data is consistent with the input (or rejected if the input didn't fit.)
Quoted 2 lines
Is that what we would want from this utility? Or do we want to allow the user to try again with a smaller string?
That's a good question - right now of course the first attempt would make it full, and then any further attempt would fail.
Quoted 2 lines
I was thinking of reversing the error code and have the trace_seq_*() return 0 as success and the remaining buffer size on error.
That would make some sense if we wanted to make it post-determined (you fail, and then you can use the remaining buffer). Maybe it would make sense to pre-determine that, e.g. by having a function that can reserve a range, say
void trace_seq_put_buf(..., int min_len, int max_len,
int *out_avail, void *out_data_ptr)
(of course that prototype needs to be adjusted appropriately)
If less than min_len is available, we could still have the "full" semantics.
Either way, I don't really know or even care. I'm only using this indirectly through the tracing, where I don't think it makes a lot of sense without the full semantics since afaik the buffers need to be in the right order in the page(s), and where you likely don't want to cut a string short since you couldn't continue it on the next page, but you're clearly the expert on this and I'm probably wrong anyway :)
johannes
Steven Rostedt
maintainerRFC PATCH 2/3 printk: Add per_cpu printk func to allow printk to be diverted
In reply to Steven RostedtFrom: "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
@@ -802,4 +802,7 @@ do { __this_cpu_preempt_check("or"); \ (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)))#endif +/* 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
@@ -159,6 +159,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
@@ -1764,6 +1764,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
@@ -1787,19 +1811,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.0.0.rc2
Re: [RFC][PATCH 2/3] printk: Add per_cpu printk func to allow printk to be diverted
Quoted 7 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.
Quoted 7 lines
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 likeshow_regs().Signed-off-by Steven Rostedt <[email protected]>
Quoted 100 lines
--- 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@@ -802,4 +802,7 @@ do { __this_cpu_preempt_check("or"); \ (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)))#endif +/* 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@@ -159,6 +159,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@@ -1764,6 +1764,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 @@ -1787,19 +1811,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 · 8 lines
2.0.0.rc2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Steven Rostedt
maintainerRFC PATCH 3/3 x86/nmi: Perform a safe NMI stack trace on all CPUs
In reply to Steven RostedtFrom: "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 trace_seq 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 | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-)
arch/x86/kernel/apic/hw_nmi.c +62 -4
@@ -18,6 +18,7 @@#include <linux/nmi.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/trace_seq.h> #ifdef CONFIG_HARDLOCKUP_DETECTOR u64 hw_nmi_get_sample_period (int watchdog_thresh)
@@ -30,11 +31,30 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)/* For reliability, we're prepared to waste bits here. */ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct trace_seq, nmi_print_seq); +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; +static void print_seq_line (struct trace_seq *s,int last,int pos) +{ +const char *buf = s->buffer + last; + +/* Chop off the saved log level and update the length */ +if (printk_get_level (buf)) { + buf +=2 ; + last +=2 ; + } + +pr_emerg ("%.*s" , (pos - last) +1 , buf); +} +void arch_trigger_all_cpu_backtrace (void ) { +struct trace_seq *s; +int len; +int cpu;int i;if (test_and_set_bit (0 , &backtrace_flag))
@@ -44,6 +64,11 @@ void arch_trigger_all_cpu_backtrace(void)*/ return ; +for_each_possible_cpu (i) { + s = &per_cpu (nmi_print_seq, i); +trace_seq_init (s); + } +cpumask_copy (to_cpumask (backtrace_mask), cpu_online_mask);printk (KERN_INFO "sending NMI to all CPUs:\n" );
@@ -56,8 +81,39 @@ void arch_trigger_all_cpu_backtrace(void)mdelay (1 ); } +for_each_possible_cpu (cpu) { +int last_i =0 ; + + s = &per_cpu (nmi_print_seq, cpu); + len = s->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 (); +return true; +} + +static int nmi_vprintk (const char *fmt,va_list args) +{ +struct trace_seq *s =this_cpu_ptr (&nmi_print_seq); +int len = s->len; + +trace_seq_vprintf (s, fmt, args); +return s->len - len; } static int
@@ -68,12 +124,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); -printk (KERN_WARNING "NMI backtrace for cpu %d\n" , cpu); +/* Replace printk to write into the NMI seq */ +this_cpu_write (printk_func, nmi_vprintk); +printk ("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.0.0.rc2
Re: [RFC][PATCH 3/3] x86/nmi: Perform a safe NMI stack trace on all CPUs
Quoted 10 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 callshow_regs(). But this can lead to a hard lock up if the NMI comes in on anotherprintk().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_cputrace_seqdescriptor. After all NMIs have finished recording its data, thetrace_seqsare printed in a safe context.
Ah, if there is other places that need to call a printk from an NMI context, do we have to copy and paste this code there too? That would seem a little much. :-) I guess my only concern really is the seq_init and seq print stuff. The handler seems relatively simple.
Can we isolate this to a pr_nmi() routine or something? Or maybe a pr_nmi_queue and pr_nmi_dump or something?
Cheers,
Don
Quoted 129 lines
Signed-off-by Steven Rostedt <[email protected]>--- arch/x86/kernel/apic/hw_nmi.c | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-)arch/x86/kernel/apic/hw_nmi.c +62
-4@@ -18,6 +18,7 @@#include <linux/nmi.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/trace_seq.h> #ifdef CONFIG_HARDLOCKUP_DETECTOR u64 hw_nmi_get_sample_period (int watchdog_thresh)@@ -30,11 +31,30 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)/* For reliability, we're prepared to waste bits here. */ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct trace_seq, nmi_print_seq); +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; +static void print_seq_line (struct trace_seq *s,int last,int pos) +{ +const char *buf = s->buffer + last; + +/* Chop off the saved log level and update the length */ +if (printk_get_level (buf)) { + buf +=2 ; + last +=2 ; + } + +pr_emerg ("%.*s" , (pos - last) +1 , buf); +} +void arch_trigger_all_cpu_backtrace (void ) { +struct trace_seq *s; +int len; +int cpu;int i;if (test_and_set_bit (0 , &backtrace_flag))@@ -44,6 +64,11 @@ void arch_trigger_all_cpu_backtrace(void)*/ return ; +for_each_possible_cpu (i) { + s = &per_cpu (nmi_print_seq, i); +trace_seq_init (s); + } +cpumask_copy (to_cpumask (backtrace_mask), cpu_online_mask);printk (KERN_INFO "sending NMI to all CPUs:\n" );@@ -56,8 +81,39 @@ void arch_trigger_all_cpu_backtrace(void)mdelay (1 ); } +for_each_possible_cpu (cpu) { +int last_i =0 ; + + s = &per_cpu (nmi_print_seq, cpu); + len = s->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 (); +return true; +} + +static int nmi_vprintk (const char *fmt,va_list args) +{ +struct trace_seq *s =this_cpu_ptr (&nmi_print_seq); +int len = s->len; + +trace_seq_vprintf (s, fmt, args); +return s->len - len; } static int@@ -68,12 +124,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); -printk (KERN_WARNING "NMI backtrace for cpu %d\n" , cpu); +/* Replace printk to write into the NMI seq */ +this_cpu_write (printk_func, nmi_vprintk); +printk ("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 · 8 lines
2.0.0.rc2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Steven Rostedt
maintainerRe: [RFC][PATCH 3/3] x86/nmi: Perform a safe NMI stack trace on all CPUs
Quoted 12 lines · On Fri, 20 Jun 2014 09:58:15 -0400 Don Zickus <[email protected]> wrote:
Quoted 10 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 callshow_regs(). But this can lead to a hard lock up if the NMI comes in on anotherprintk().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_cputrace_seqdescriptor. After all NMIs have finished recording its data, thetrace_seqsare printed in a safe context.
Quoted 4 lines
Ah, if there is other places that need to call a printk from an NMI context, do we have to copy and paste this code there too? That would seem a little much. :-) I guess my only concern really is the
seq_initand seq print stuff. The handler seems relatively simple.
This is a special case for printing from NMI. As you can trigger it with sysrq-l. But any other prints from NMI should only be in the BUG() case, where we should just force the output and override any locks in printk.
Quoted 4 lines
Can we isolate this to a
pr_nmi()routine or something? Or maybe apr_nmi_queueandpr_nmi_dumpor something?
We'll have to see how traction this gets. It's just an RFC.
-- Steve
Re: [RFC][PATCH 3/3] x86/nmi: Perform a safe NMI stack trace on all CPUs
In reply to Steven RostedtQuoted 39 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 callshow_regs(). But this can lead to a hard lock up if the NMI comes in on anotherprintk().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_cputrace_seqdescriptor. After all NMIs have finished recording its data, thetrace_seqsare printed in a safe context.Signed-off-by Steven Rostedt <[email protected]>--- arch/x86/kernel/apic/hw_nmi.c | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-)arch/x86/kernel/apic/hw_nmi.c +5
-0@@ -18,6 +18,7 @@#include <linux/nmi.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/trace_seq.h> #ifdef CONFIG_HARDLOCKUP_DETECTOR u64 hw_nmi_get_sample_period (int watchdog_thresh)@@ -30,11 +31,30 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)/* For reliability, we're prepared to waste bits here. */ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct trace_seq, nmi_print_seq); +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; +static void print_seq_line (struct trace_seq *s,int last,int pos)
arch/x86/kernel/apic/hw_nmi.c u64 hw_nmi_get_sample_period(int watchdog_thresh) · line 50
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +9
-0+{ +const char *buf = s->buffer + last; + +/* Chop off the saved log level and update the length */ +if (printk_get_level (buf)) { + buf +=2 ; + last +=2 ; + } +pr_emerg ("%.*s" , (pos - last) +1 , buf);
I wonder why you chop off the log level and force the emergency one. I think that this should be better set by the caller. For example, drivers/tty/sysrq.c when it wants to push the output on console.
Quoted 23 lines
arch/x86/kernel/apic/hw_nmi.c +10
-0+} +void arch_trigger_all_cpu_backtrace (void ) { +struct trace_seq *s; +int len; +int cpu;int i;if (test_and_set_bit (0 , &backtrace_flag))@@ -44,6 +64,11 @@ void arch_trigger_all_cpu_backtrace(void)*/ return ; +for_each_possible_cpu (i) { + s = &per_cpu (nmi_print_seq, i); +trace_seq_init (s); + } +cpumask_copy (to_cpumask (backtrace_mask), cpu_online_mask);printk (KERN_INFO "sending NMI to all CPUs:\n" );
arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(void) · line 82
Quoted 2 lines
arch/x86/kernel/apic/hw_nmi.c +0
-0@@ -56,8 +81,39 @@ void arch_trigger_all_cpu_backtrace(void)mdelay (1 ); }
I would add a comment here. e.g.
/* * Finally, copy the backtraces to the main ring buffer in a safe * context, outside NMI. */
Quoted 14 lines
arch/x86/kernel/apic/hw_nmi.c +14
-0+for_each_possible_cpu (cpu) { +int last_i =0 ; + + s = &per_cpu (nmi_print_seq, cpu); + len = s->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 ; + }
arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(void) · line 107
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +8
-0+ } +if (last_i < i -1 ) { +print_seq_line (s, last_i, i); +pr_cont ("\n" ); + } + } +clear_bit (0 , &backtrace_flag);smp_mb__after_atomic (); +return true;
This function returns void ;-)
Quoted 14 lines
arch/x86/kernel/apic/hw_nmi.c +9
-0+} + +static int nmi_vprintk (const char *fmt,va_list args) +{ +struct trace_seq *s =this_cpu_ptr (&nmi_print_seq); +int len = s->len; + +trace_seq_vprintf (s, fmt, args); +return s->len - len; } static int@@ -68,12 +124,14 @@ arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs) cpu =smp_processor_id ();
arch/x86/kernel/apic/hw_nmi.c arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs) · line 131
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +4
-3if (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); -printk (KERN_WARNING "NMI backtrace for cpu %d\n" , cpu); +/* Replace printk to write into the NMI seq */ +this_cpu_write (printk_func, nmi_vprintk); +printk ("NMI backtrace for cpu %d\n" , cpu);
I would leave the log level as is.
Quoted 9 lines
arch/x86/kernel/apic/hw_nmi.c +2
-1show_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.0.0.rc2
Otherwise, I basically like the approach.
Best Regards,
Petr
Steven Rostedt
maintainerRe: [RFC][PATCH 3/3] x86/nmi: Perform a safe NMI stack trace on all CPUs
Quoted 49 lines · On Fri, 20 Jun 2014 16:55:05 +0200 Petr Mládek <[email protected]> wrote:
Quoted 48 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 callshow_regs(). But this can lead to a hard lock up if the NMI comes in on anotherprintk().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_cputrace_seqdescriptor. After all NMIs have finished recording its data, thetrace_seqsare printed in a safe context.Signed-off-by Steven Rostedt <[email protected]>--- arch/x86/kernel/apic/hw_nmi.c | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-)arch/x86/kernel/apic/hw_nmi.c +13
-0@@ -18,6 +18,7 @@#include <linux/nmi.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/trace_seq.h> #ifdef CONFIG_HARDLOCKUP_DETECTOR u64 hw_nmi_get_sample_period (int watchdog_thresh)@@ -30,11 +31,30 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)/* For reliability, we're prepared to waste bits here. */ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct trace_seq, nmi_print_seq); +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; +static void print_seq_line (struct trace_seq *s,int last,int pos) +{ +const char *buf = s->buffer + last; + +/* Chop off the saved log level and update the length */ +if (printk_get_level (buf)) { + buf +=2 ; + last +=2 ; + }
Quoted 5 lines
arch/x86/kernel/apic/hw_nmi.c u64 hw_nmi_get_sample_period(int watchdog_thresh) · line 50
Quoted 1 line
arch/x86/kernel/apic/hw_nmi.c +1
-0+pr_emerg ("%.*s" , (pos - last) +1 , buf);I wonder why you chop off the log level and force the emergency one. I think that this should be better set by the caller. For example,
drivers/tty/sysrq.cwhen it wants to push the output on console.
Ah, that's because I originally printed it as one buffer, and then parsed it up stripping all of them to be consistent. But I guess just calling printk() directly would work.
Quoted 29 lines
Quoted 24 lines
arch/x86/kernel/apic/hw_nmi.c +10
-0+} +void arch_trigger_all_cpu_backtrace (void ) { +struct trace_seq *s; +int len; +int cpu;int i;if (test_and_set_bit (0 , &backtrace_flag))@@ -44,6 +64,11 @@ void arch_trigger_all_cpu_backtrace(void)*/ return ; +for_each_possible_cpu (i) { + s = &per_cpu (nmi_print_seq, i); +trace_seq_init (s); + } +cpumask_copy (to_cpumask (backtrace_mask), cpu_online_mask);printk (KERN_INFO "sending NMI to all CPUs:\n" );arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(void) · line 82
Quoted 2 lines
arch/x86/kernel/apic/hw_nmi.c +0
-0@@ -56,8 +81,39 @@ void arch_trigger_all_cpu_backtrace(void)mdelay (1 ); }I would add a comment here. e.g.
Quoted 3 lines
/*
* Finally, copy the backtraces to the main ring buffer in a safe
* context, outside NMI.
Sure.
Quoted 20 lines
*/Quoted 18 lines
arch/x86/kernel/apic/hw_nmi.c +18
-0+for_each_possible_cpu (cpu) { +int last_i =0 ; + + s = &per_cpu (nmi_print_seq, cpu); + len = s->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" );
Quoted 8 lines
arch/x86/kernel/apic/hw_nmi.c void arch_trigger_all_cpu_backtrace(void) · line 107
Quoted 6 lines
arch/x86/kernel/apic/hw_nmi.c +4
-0+ } + } +clear_bit (0 , &backtrace_flag);smp_mb__after_atomic (); +return true;This function returns void ;-)
oops, I switched back and forth between changing the function to return bool because I was back and forth with allocating the trace-seq instead of having them around at boot up (just to save space). That was going to be an add on patch if we decide to allocate. But if we do allocate, we need to return whether or not it succeeded in allocation and if not, then trigger_all_cpu_backtrace() needs to fail. (this would need to update sparc as well).
Quoted 20 lines
Quoted 20 lines
arch/x86/kernel/apic/hw_nmi.c +10
-1+} + +static int nmi_vprintk (const char *fmt,va_list args) +{ +struct trace_seq *s =this_cpu_ptr (&nmi_print_seq); +int len = s->len; + +trace_seq_vprintf (s, fmt, args); +return s->len - len; } static int@@ -68,12 +124,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);
Quoted 7 lines
arch/x86/kernel/apic/hw_nmi.c arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs) · line 131
Quoted 5 lines
arch/x86/kernel/apic/hw_nmi.c +3
-2-arch_spin_lock (&lock); -printk (KERN_WARNING "NMI backtrace for cpu %d\n" , cpu); +/* Replace printk to write into the NMI seq */ +this_cpu_write (printk_func, nmi_vprintk); +printk ("NMI backtrace for cpu %d\n" , cpu);I would leave the log level as is.
Sure.
Quoted 12 lines
Quoted 10 lines
arch/x86/kernel/apic/hw_nmi.c +2
-1show_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.0.0.rc2Otherwise, I basically like the approach.
Thanks, I'll make some updates.
-- Steve
Re: [RFC][PATCH 3/3] x86/nmi: Perform a safe NMI stack trace on all CPUs
In reply to Steven RostedtQuoted 6 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 callshow_regs(). But this can lead to a hard lock up if the NMI comes in on anotherprintk().
Quoted 6 lines
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_cputrace_seqdescriptor. After all NMIs have finished recording its data, thetrace_seqsare printed in a safe context.Signed-off-by Steven Rostedt <[email protected]>
I do like the idea of safe printk from NMI, so given the changes later in this thread:
Quoted 127 lines
--- arch/x86/kernel/apic/hw_nmi.c | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-)arch/x86/kernel/apic/hw_nmi.c +62
-4@@ -18,6 +18,7 @@#include <linux/nmi.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/trace_seq.h> #ifdef CONFIG_HARDLOCKUP_DETECTOR u64 hw_nmi_get_sample_period (int watchdog_thresh)@@ -30,11 +31,30 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)/* For reliability, we're prepared to waste bits here. */ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; +/* Safe printing in NMI context */ +static DEFINE_PER_CPU(struct trace_seq, nmi_print_seq); +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; +static void print_seq_line (struct trace_seq *s,int last,int pos) +{ +const char *buf = s->buffer + last; + +/* Chop off the saved log level and update the length */ +if (printk_get_level (buf)) { + buf +=2 ; + last +=2 ; + } + +pr_emerg ("%.*s" , (pos - last) +1 , buf); +} +void arch_trigger_all_cpu_backtrace (void ) { +struct trace_seq *s; +int len; +int cpu;int i;if (test_and_set_bit (0 , &backtrace_flag))@@ -44,6 +64,11 @@ void arch_trigger_all_cpu_backtrace(void)*/ return ; +for_each_possible_cpu (i) { + s = &per_cpu (nmi_print_seq, i); +trace_seq_init (s); + } +cpumask_copy (to_cpumask (backtrace_mask), cpu_online_mask);printk (KERN_INFO "sending NMI to all CPUs:\n" );@@ -56,8 +81,39 @@ void arch_trigger_all_cpu_backtrace(void)mdelay (1 ); } +for_each_possible_cpu (cpu) { +int last_i =0 ; + + s = &per_cpu (nmi_print_seq, cpu); + len = s->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 (); +return true; +} + +static int nmi_vprintk (const char *fmt,va_list args) +{ +struct trace_seq *s =this_cpu_ptr (&nmi_print_seq); +int len = s->len; + +trace_seq_vprintf (s, fmt, args); +return s->len - len; } static int@@ -68,12 +124,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); -printk (KERN_WARNING "NMI backtrace for cpu %d\n" , cpu); +/* Replace printk to write into the NMI seq */ +this_cpu_write (printk_func, nmi_vprintk); +printk ("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 · 8 lines
2.0.0.rc2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Quoted 10 lines · Steven Rostedt
This is my proposal to print the NMI stack traces from an RCU stall safely. Here's the gist of it.
Patch 1: move the
trace_seqout of the tracing code. It's useful for other
purposes too. Like writing from an NMI context.Patch 2: 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 whatprintk()calls normally on a per cpu basis.
Quoted 6 lines
Patch 3: Have the NMI handler that dumps the stack trace just change the
printk_functo call a NMI safe printk function that writes to a per cputrace_seq. When all NMI handlers chimed in, the original caller prints out thetrace_seqsfor each CPU from a printk safe context.This is much less intrusive than the other versions out there.
I agree this is less intrusive than having printk() use two versions of the buffers and perform merging, OTOH, it doesn't really seem to be fully clean and systematic solution either.
I had a different idea earlier today, and Petr seems to have implemented it already; I guess he'll be sending it out as RFC tomorrow for comparision.
The idea basically is to *switch* what arch_trigger_all_cpu_backtrace() and arch_trigger_all_cpu_backtrace_handler() are doing; i.e. use the NMI as a way to stop all the CPUs (one by one), and let the CPU that is sending the NMIs around to actually walk and dump the stacks of the CPUs receiving the NMI IPI.
It's the most trivial aproach I've been able to come up with, and should be usable for everybody (RCU stall detector and sysrq). The only tricky part is: if we want pt_regs to be part of the dump as well, how to pass those cleanly between the 'stopped' CPU and the CPU that is doing the printing. Other than that, it's just moving a few lines of code around, I believe.
What do you think?
Signature · 2 lines
Jiri Kosina SUSE Labs
Steven Rostedt
maintainerQuoted 3 lines · On Thu, 19 Jun 2014 23:56:36 +0200 (CEST) Jiri Kosina <[email protected]> wrote:
I agree this is less intrusive than having
printk()use two versions of the buffers and perform merging, OTOH, it doesn't really seem to be fully clean and systematic solution either.
I'm not sure why you consider it not fully clean. I think it is quite elegant ;-)
Quoted 10 lines
I had a different idea earlier today, and Petr seems to have implemented it already; I guess he'll be sending it out as RFC tomorrow for comparision.
The idea basically is to *switch* what
arch_trigger_all_cpu_backtrace()andarch_trigger_all_cpu_backtrace_handler()are doing; i.e. use the NMI as a way to stop all the CPUs (one by one), and let the CPU that is sending the NMIs around to actually walk and dump the stacks of the CPUs receiving the NMI IPI.
And this is cleaner? Stopping a CPU via NMI and then what happens if something else goes wrong and that CPU never starts back up? This sounds like something that can cause more problems than it was reporting on.
The code had better handle all the strange cases where an NMI comes in after the code that sent it timed out waiting for the NMIs to return. Then you also need to print out the data while the NMIs still spin.
Quoted 7 lines
It's the most trivial aproach I've been able to come up with, and should be usable for everybody (RCU stall detector and sysrq). The only tricky part is: if we want
pt_regsto be part of the dump as well, how to pass those cleanly between the 'stopped' CPU and the CPU that is doing the printing. Other than that, it's just moving a few lines of code around, I believe.
Note, I first tried to just save the data and pass it to the calling functions, but found that rather messy, as there is a lot of stuff being dumped, and it's different if it is x86_64 vs i386 or if the NMI happened when in userspace or kernel. That's when I realized it would be trivial to just switch what printk does and then have the NMI do the printing to another NMI safe buffer.
It was even more convenient that trace_seq already existed and made it much easier to implement.
-- Steve
Quoted 10 lines · Steven Rostedt
Quoted 5 lines
The idea basically is to *switch* what
arch_trigger_all_cpu_backtrace()andarch_trigger_all_cpu_backtrace_handler()are doing; i.e. use the NMI as a way to stop all the CPUs (one by one), and let the CPU that is sending the NMIs around to actually walk and dump the stacks of the CPUs receiving the NMI IPI.And this is cleaner? Stopping a CPU via NMI and then what happens if something else goes wrong and that CPU never starts back up? This sounds like something that can cause more problems than it was reporting on.
It's going to get NMI in exactly the same situations it does with the current arch_trigger_all_cpu_backtrace(), the only difference being that it doesn't try to invoke printk() from inside NMI. The IPI-NMI is used solely as a point of synchronization for the stack dumping.
Quoted 1 line
Then you also need to print out the data while the NMIs still spin.
Exactly, that's actually the whole point.
Thanks,
Signature · 2 lines
Jiri Kosina SUSE Labs
Steven Rostedt
maintainerQuoted 13 lines · On Fri, 20 Jun 2014 01:03:28 +0200 (CEST) Jiri Kosina <[email protected]> wrote:
Quoted 10 lines · Steven Rostedt
Quoted 5 lines
The idea basically is to *switch* what
arch_trigger_all_cpu_backtrace()andarch_trigger_all_cpu_backtrace_handler()are doing; i.e. use the NMI as a way to stop all the CPUs (one by one), and let the CPU that is sending the NMIs around to actually walk and dump the stacks of the CPUs receiving the NMI IPI.And this is cleaner? Stopping a CPU via NMI and then what happens if something else goes wrong and that CPU never starts back up? This sounds like something that can cause more problems than it was reporting on.
Quoted 4 lines
It's going to get NMI in exactly the same situations it does with the current
arch_trigger_all_cpu_backtrace(), the only difference being that it doesn't try to invokeprintk()from inside NMI. The IPI-NMI is used solely as a point of synchronization for the stack dumping.
Well, all CPUs are going to be spinning until the main CPU prints everything out. That's not quite the same thing as what it use to do.
Quoted 4 lines
Quoted 2 lines
Then you also need to print out the data while the NMIs still spin.
Exactly, that's actually the whole point.
But this stops everything with a big hammer, until everything gets printed out, not just the one CPU that happens to be stuck.
-- Steve
Quoted 11 lines · Steven Rostedt
Quoted 11 lines
Quoted 10 lines
Quoted 5 lines
The idea basically is to *switch* what
arch_trigger_all_cpu_backtrace()andarch_trigger_all_cpu_backtrace_handler()are doing; i.e. use the NMI as a way to stop all the CPUs (one by one), and let the CPU that is sending the NMIs around to actually walk and dump the stacks of the CPUs receiving the NMI IPI.And this is cleaner? Stopping a CPU via NMI and then what happens if something else goes wrong and that CPU never starts back up? This sounds like something that can cause more problems than it was reporting on.
Quoted 7 lines
Quoted 4 lines
It's going to get NMI in exactly the same situations it does with the current
arch_trigger_all_cpu_backtrace(), the only difference being that it doesn't try to invokeprintk()from inside NMI. The IPI-NMI is used solely as a point of synchronization for the stack dumping.Well, all CPUs are going to be spinning until the main CPU prints everything out. That's not quite the same thing as what it use to do.
Is there a reason fo all CPUs to be spinning until everything is printed out? Every CPU will be spinning until his very stack is printed out, while other CPUs will be running uninterrupted.
I don't think there is a need for a global stop_machine()-like synchronization here. The printing CPU will be sending IPI to the CPU N+1 only after it has finished printing CPU N stacktrace.
Thanks,
Signature · 2 lines
Jiri Kosina SUSE Labs
Steven Rostedt
maintainerQuoted 4 lines · On Fri, 20 Jun 2014 01:27:26 +0200 (CEST) Jiri Kosina <[email protected]> wrote:
I don't think there is a need for a global stop_machine()-like synchronization here. The printing CPU will be sending IPI to the CPU N+1 only after it has finished printing CPU N stacktrace.
So you plan on sending an IPI to a CPU then wait for it to acknowledge that it is spinning, and then print out the data and then tell the CPU it can stop spinning?
-- Steve
Quoted 7 lines · Steven Rostedt
Quoted 3 lines
I don't think there is a need for a global stop_machine()-like synchronization here. The printing CPU will be sending IPI to the CPU N+1 only after it has finished printing CPU N stacktrace.
So you plan on sending an IPI to a CPU then wait for it to acknowledge that it is spinning, and then print out the data and then tell the CPU it can stop spinning?
Yes, that was exactly my idea. You have to be synchronized with the CPU receiving the NMI anyway in case you'd like to get its pt_regs and dump those as part of the dump.
Signature · 2 lines
Jiri Kosina SUSE Labs
Quoted 6 lines · Jiri Kosina
Quoted 4 lines · Steven Rostedt
Quoted 3 lines
I don't think there is a need for a global stop_machine()-like synchronization here. The printing CPU will be sending IPI to the CPU N+1 only after it has finished printing CPU N stacktrace.
Quoted 7 lines
Quoted 3 lines
So you plan on sending an IPI to a CPU then wait for it to acknowledge that it is spinning, and then print out the data and then tell the CPU it can stop spinning?
Yes, that was exactly my idea. You have to be synchronized with the CPU receiving the NMI anyway in case you'd like to get its
pt_regsand dump those as part of the dump.
This approach did not work after all. There was still the same race. If we stop a CPU in the middle of printk(), it does not help to move the printing task to another CPU ;-) We would need to make a copy of regs and all the stacks to unblock the CPU.
Hmm, in general, if we want a consistent snapshot, we need to temporary store the information in NMI context and put it into the main ring buffer in normal context. We either need to copy stacks or copy the printed text.
I start to like Steven's solution with the trace_seq buffer. I see the following advantages:
+ the snapshot is pretty good;
+ we still send NMI to all CPUs at the "same" time
+ only minimal time is spent in NMI context;
+ CPUs are not blocked by each other to get sequential output
+ minimum of new code
+ trace_seq buffer is already implemented and used
+ it might be even better after getting attention from new users
Of course, it has also some disadvantages:
+ needs quite big per-CPU buffer;
+ but we would need some extra space to copy the data anyway
+ trace might be shrunken;
+ but 1 page should be enough in most cases;
+ we could make it configurable
+ delay until the message appears in the ringbuffer and console
+ better than freezing
+ still saved in core file
+ crash tool could get improved to find the traces
Note that the above solution solves only printing of the stack. There are still other locations when printk is called in NMI context. IMHO, some of them are helpful:
./arch/x86/kernel/nmi.c: WARN(in_nmi(),
./arch/x86/mm/kmemcheck/kmemcheck.c: WARN_ON_ONCE(in_nmi());
./arch/x86/mm/fault.c: WARN_ON_ONCE(in_nmi());
./arch/x86/mm/fault.c: WARN_ON_ONCE(in_nmi());
./mm/vmalloc.c: BUG_ON(in_nmi());
./lib/genalloc.c: BUG_ON(in_nmi());
./lib/genalloc.c: BUG_ON(in_nmi());
./include/linux/hardirq.h: BUG_ON(in_nmi());And some are probably less important:
./arch/x86/platform/uv/uv_nmi.c several locations here
./arch/m68k/mac/macints.c- printk("... pausing, press NMI to resume ...");
Well, there are only few. Maybe, we could share the trace_seq buffer here.
Of course, there is still the possibility to implement a lockless buffer. But it will be much more complicated than the current one. I am not sure that we really want it.
Best Regards,
Petr
Quoted 74 lines · Petr Mládek
Quoted 6 lines · Jiri Kosina
Quoted 4 lines · Steven Rostedt
Quoted 3 lines
I don't think there is a need for a global stop_machine()-like synchronization here. The printing CPU will be sending IPI to the CPU N+1 only after it has finished printing CPU N stacktrace.
Quoted 7 lines
Quoted 3 lines
So you plan on sending an IPI to a CPU then wait for it to acknowledge that it is spinning, and then print out the data and then tell the CPU it can stop spinning?
Yes, that was exactly my idea. You have to be synchronized with the CPU receiving the NMI anyway in case you'd like to get its
pt_regsand dump those as part of the dump.This approach did not work after all. There was still the same race. If we stop a CPU in the middle of
printk(), it does not help to move the printing task to another CPU ;-) We would need to make a copy of regs and all the stacks to unblock the CPU.Hmm, in general, if we want a consistent snapshot, we need to temporary store the information in NMI context and put it into the main ring buffer in normal context. We either need to copy stacks or copy the printed text.
I start to like Steven's solution with the
trace_seqbuffer. I see the following advantages:+ the snapshot is pretty good;
+ we still send NMI to all CPUs at the "same" time+ only minimal time is spent in NMI context;
+ CPUs are not blocked by each other to get sequential output+ minimum of new code
+trace_seqbuffer is already implemented and used
+ it might be even better after getting attention from new usersOf course, it has also some disadvantages:
+ needs quite big per-CPU buffer;
+ but we would need some extra space to copy the data anyway+ trace might be shrunken;
+ but 1 page should be enough in most cases;
+ we could make it configurable+ delay until the message appears in the ringbuffer and console
+ better than freezing
+ still saved in core file
+ crash tool could get improved to find the tracesNote that the above solution solves only printing of the stack. There are still other locations when printk is called in NMI context. IMHO, some of them are helpful:
./arch/x86/kernel/nmi.c: WARN(in_nmi(), ./arch/x86/mm/kmemcheck/kmemcheck.c: WARN_ON_ONCE(in_nmi()); ./arch/x86/mm/fault.c: WARN_ON_ONCE(in_nmi()); ./arch/x86/mm/fault.c: WARN_ON_ONCE(in_nmi()); ./mm/vmalloc.c: BUG_ON(in_nmi()); ./lib/genalloc.c: BUG_ON(in_nmi()); ./lib/genalloc.c: BUG_ON(in_nmi()); ./include/linux/hardirq.h: BUG_ON(in_nmi());And some are probably less important:
./arch/x86/platform/uv/uv_nmi.cseveral locations here
./arch/m68k/mac/macints.c-printk("... pausing, press NMI to resume ...");
Quoted 6 lines
Well, there are only few. Maybe, we could share the
trace_seqbuffer here.Of course, there is still the possibility to implement a lockless buffer. But it will be much more complicated than the current one. I am not sure that we really want it.
Let me join the discussion.
In the past I've implemented similar feature for x86 in openvz kernel and now I'm thinking about it for arm.
Originally I thought that seizing all cpus one by one and printing from the initiator
is a best approach and I've started preparing arguments against over-engineered printk...
But it seems deferring printk output isn't that bad idea, it much easier and arch-independent.
Saving context is very arch-specific. Printing is a problem too:show_regs() is a horrible mess,
on most arches it always prints something from current context. I'm thinking about cleaning this
mess, but doing this for all arches takes some time for sure.
Instead of per-cpu buffers printk might use part of existing ring buffer -- initiator cpu allocates space for
target cpu and flushes it into common stream after it finish printing. Probably this kind of transactional model might be used on single cpu for multi-line KERN_CONT.
Quoted 9 lines
Best Regards,
Petr
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Quoted 3 lines · Konstantin Khlebnikov
Originally I thought that seizing all cpus one by one and printing from the initiator is a best approach and I've started preparing arguments against over-engineered printk...
By "seizing" I guess you mean sending IPI, right?
What do you do if you'd interrupt it in the middle of printk() in order not to deadlock by trying to take the buffer lock on the initiator CPU?
Signature · 2 lines
Jiri Kosina SUSE Labs
Quoted 10 lines · Jiri Kosina
Quoted 3 lines · Konstantin Khlebnikov
Originally I thought that seizing all cpus one by one and printing from the initiator is a best approach and I've started preparing arguments against over-engineered printk...
By "seizing" I guess you mean sending IPI, right?
What do you do if you'd interrupt it in the middle of
printk()in order not to deadlock by trying to take the buffer lock on the initiator CPU?
Yep, under "seize" I mean NMI IPI which gathering state of registers from cpu. Also it can keep cpu in nmi handler while initiator cpu unwinds its stack trace. Code of dump-all-stack in arch/sparc does something like that.
You're right, moving printk from one cpu to another doesn't fixes recursion, but we could send NMI IPI under logbuf_lock. I don't think that this is good approach, but it seems realistic. Of course all printing from other NMI handlers must be done in some weird way.
I like that context-depend printk routing from this patchset. I'm just not quite happy with this straight forward implementation. Rerouting flow into different buffers might serve different purposes, it must make code simpler instead of adding yet another knot. For example printk continuation might use it too.
Quoted 4 lines
--
Jiri Kosina
SUSE Labs
Quoted 8 lines · Konstantin Khlebnikov
Quoted 1 line · Jiri Kosina
On Tue, 24 Jun 2014, Konstantin Khlebnikov wrote:
I like that context-depend printk routing from this patchset. I'm just not quite happy with this straight forward implementation. Rerouting flow into different buffers might serve different purposes, it must make code simpler instead of adding yet another knot. For example printk continuation might use it too.
Note that we could not share the buffer between normal and NMI context easily. We would end with the same locking problems as we have now.
In fact, any sharing is complicated when NMI is involved, especially when we need to access the data from both NMI and normal context. And we need to copy them into the main ring buffer in a normal context.
This patchset works only because arch_trigger_all_cpu_backtrace() is called from normal context. Also all other calls are ignored until the previous call is finished, see "backtrace_flag".
Best Regards,
Petr
Quoted 6 lines · Konstantin Khlebnikov
Instead of per-cpu buffers printk might use part of existing ring buffer -- initiator cpu allocates space for
target cpu and flushes it into common stream after it finish printing. Probably this kind of transactional model might be used on single cpu for multi-lineKERN_CONT.
I wanted to think more about this.
The evident advantage is that we do not need extra buffers. Also it might be used instead of the cont buffer.
One challenge is that we do not know how much space is needed. If we do not reserve enough, we might not be able to reserve it later. This would happen when the main ring buffer is locked by some blocked process. If we reserve too much, it is a waste of information. Note that we must remove old messages to make the space.
Also we could not rotate the ring buffer over the reserved space. Then we might miss the last messages from the normal context when the system goes down.
Another question is if it could simplify the printk() code. The reservation logic would be probably more complicated than the current logic around the cont buffer.
Best Regards,
Petr