[PATCH] printk: add pr_line buffering API

From: Sergey Senozhatsky
Date: Wed Sep 12 2018 - 02:55:03 EST


Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@xxxxxxxxx>
---
include/linux/printk.h | 63 ++++++++++++++++++++++++++++++++++++++++++
kernel/printk/printk.c | 55 ++++++++++++++++++++++++++++++++++++
2 files changed, 118 insertions(+)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index cf3eccfe1543..fc5f11c7579c 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -157,6 +157,15 @@ static inline void printk_nmi_direct_enter(void) { }
static inline void printk_nmi_direct_exit(void) { }
#endif /* PRINTK_NMI */

+#define PRINTK_PR_LINE_BUF_SZ 80
+
+struct pr_line {
+ char *buffer;
+ int size;
+ int len;
+ char *level;
+};
+
#ifdef CONFIG_PRINTK
asmlinkage __printf(5, 0)
int vprintk_emit(int facility, int level,
@@ -209,6 +218,30 @@ extern asmlinkage void dump_stack(void) __cold;
extern void printk_safe_init(void);
extern void printk_safe_flush(void);
extern void printk_safe_flush_on_panic(void);
+
+#define DEFINE_PR_LINE(lev, name) \
+ char __pr_line_buf[PRINTK_PR_LINE_BUF_SZ]; \
+ struct pr_line name = { \
+ .buffer = __pr_line_buf, \
+ .size = PRINTK_PR_LINE_BUF_SZ, \
+ .len = 0, \
+ .level = lev, \
+ }
+
+#define DEFINE_PR_LINE_BUF(lev, name, buf, sz) \
+ struct pr_line name = { \
+ .buffer = buf, \
+ .size = (sz), \
+ .len = 0, \
+ .level = lev, \
+ }
+
+extern __printf(2, 3)
+int pr_line(struct pr_line *pl, const char *fmt, ...);
+extern __printf(2, 0)
+int vpr_line(struct pr_line *pl, const char *fmt, va_list args);
+extern void pr_line_flush(struct pr_line *pl);
+
#else
static inline __printf(1, 0)
int vprintk(const char *s, va_list args)
@@ -284,6 +317,36 @@ static inline void printk_safe_flush(void)
static inline void printk_safe_flush_on_panic(void)
{
}
+
+#define DEFINE_PR_LINE(lev, name) \
+ struct pr_line name = { \
+ .buffer = NULL, \
+ .size = 0, \
+ .len = 0, \
+ .level = lev, \
+ }
+
+#define DEFINE_PR_LINE_BUF(lev, name, buf, sz) \
+ struct pr_line name = { \
+ .buffer = buf, \
+ .size = 0, \
+ .len = 0, \
+ .level = lev, \
+ }
+
+static inline __printf(2, 3)
+int pr_line(struct pr_line *pl, const char *fmt, ...)
+{
+ return 0;
+}
+static inline __printf(2, 0)
+int vpr_line(struct pr_line *pl, const char *fmt, va_list args)
+{
+ return 0;
+}
+static inline void pr_line_flush(struct pr_line *pl)
+{
+}
#endif

extern int kptr_restrict;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index fd6f8ed28e01..daeb41a57929 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2004,6 +2004,61 @@ asmlinkage __visible int printk(const char *fmt, ...)
}
EXPORT_SYMBOL(printk);

+#define PR_LINE_TRUNCATED_MSG "** truncated **\n"
+
+int vpr_line(struct pr_line *pl, const char *fmt, va_list args)
+{
+ int len;
+
+ if (unlikely(pl->size >= LOG_LINE_MAX))
+ pl->size = LOG_LINE_MAX - sizeof(PR_LINE_TRUNCATED_MSG);
+
+ if (fmt[0] == '\n') {
+ pr_line_flush(pl);
+ return 0;
+ }
+
+ if (pl->len >= pl->size)
+ return -1;
+
+ len = vsnprintf(pl->buffer + pl->len, pl->size - pl->len, fmt, args);
+ if (pl->len + len >= pl->size) {
+ pl->len = pl->size + 1;
+ return -1;
+ }
+
+ pl->len += len;
+ if (pl->len && pl->buffer[pl->len - 1] == '\n')
+ pr_line_flush(pl);
+ return 0;
+}
+EXPORT_SYMBOL(vpr_line);
+
+int pr_line(struct pr_line *pl, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vpr_line(pl, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+EXPORT_SYMBOL(pr_line);
+
+void pr_line_flush(struct pr_line *pl)
+{
+ if (!pl->len)
+ return;
+
+ if (pl->len < pl->size)
+ printk("%s%.*s", pl->level, pl->len, pl->buffer);
+ else
+ printk("%s%.*s%s", pl->level, pl->len, pl->buffer,
+ PR_LINE_TRUNCATED_MSG);
+ pl->len = 0;
+}
+EXPORT_SYMBOL(pr_line_flush);
#else /* CONFIG_PRINTK */

#define LOG_LINE_MAX 0
--
2.19.0