On (09/13/18 23:28), Sergey Senozhatsky wrote:
> Not that I see any problems with pr_line_flush(). But can drop it, sure.
> pr_line() is a replacement for pr_cont() and as such it's not for multi-line
> buffering.

OK, attached.
Let me know if anything needs to improved (including broken English).
Will we keep in the printk tree or shall I send a formal patch to Andrew?

===

From: Sergey Senozhatsky <[email protected]>
Subject: [PATCH] lib/seq_buf: add pr_line buffering API

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
 include/linux/kern_levels.h |  3 ++
 include/linux/seq_buf.h     | 60 +++++++++++++++++++++++++++++++++++++
 lib/seq_buf.c               | 57 +++++++++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+)

diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h
index d237fe854ad9..9c281ac745b3 100644
--- a/include/linux/kern_levels.h
+++ b/include/linux/kern_levels.h
@@ -20,6 +20,9 @@
  * Annotation for a "continued" line of log printout (only done after a
  * line that had no enclosing \n). Only to be used by core/arch code
  * during early bootup (a continued line is not SMP-safe otherwise).
+ *
+ * Please consider pr_line()/vpr_line() functions for SMP-safe continued
+ * line printing.
  */
 #define KERN_CONT      KERN_SOH "c"
 
diff --git a/include/linux/seq_buf.h b/include/linux/seq_buf.h
index aa5deb041c25..b33aeea14803 100644
--- a/include/linux/seq_buf.h
+++ b/include/linux/seq_buf.h
@@ -23,6 +23,62 @@ struct seq_buf {
        loff_t                  readpos;
 };
 
+#define __SEQ_BUF_INITIALIZER(buf, length)                     \
+{                                                              \
+       .buffer                 = (buf),                        \
+       .size                   = (length),                     \
+       .len                    = 0,                            \
+       .readpos                = 0,                            \
+}
+
+#ifdef CONFIG_PRINTK
+#define __PR_LINE_BUF_SZ       80
+#else
+#define __PR_LINE_BUF_SZ       0
+#endif
+
+/**
+ * pr_line - printk() line buffer structure
+ * @sb:        underlying seq buffer, which holds the data
+ * @level:     printk() log level (KERN_ERR, etc.)
+ */
+struct pr_line {
+       struct seq_buf          sb;
+       char                    *level;
+};
+
+/**
+ * DEFINE_PR_LINE - define a new pr_line variable
+ * @lev:       printk() log level
+ * @name:      variable name
+ *
+ * Defines a new pr_line varialbe, which would use an implicit
+ * stack buffer of size __PR_LINE_BUF_SZ.
+ */
+#define DEFINE_PR_LINE(lev, name)                              \
+       char            __line_##name[__PR_LINE_BUF_SZ];        \
+       struct pr_line  name = {                                \
+               .sb     = __SEQ_BUF_INITIALIZER(__line_##name,  \
+                                       __PR_LINE_BUF_SZ),      \
+               .level  = lev,                                  \
+       }
+
+/**
+ * DEFINE_PR_LINE_BUF - define a new pr_line variable
+ * @lev:       printk() log level
+ * @name:      variable name
+ * @buf:       external buffer
+ * @sz:        external buffer size
+ *
+ * Defines a new pr_line variable, which would use an external
+ * buffer for printk line.
+ */
+#define DEFINE_PR_LINE_BUF(lev, name, buf, sz)                 \
+       struct pr_line  name = {                                \
+               .sb     = __SEQ_BUF_INITIALIZER(buf, (sz)),     \
+               .level  = lev,                                  \
+       }
+
 static inline void seq_buf_clear(struct seq_buf *s)
 {
        s->len = 0;
@@ -131,4 +187,8 @@ extern int
 seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary);
 #endif
 
+extern __printf(2, 0)
+int vpr_line(struct pr_line *pl, const char *fmt, va_list args);
+extern __printf(2, 3)
+int pr_line(struct pr_line *pl, const char *fmt, ...);
 #endif /* _LINUX_SEQ_BUF_H */
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index 11f2ae0f9099..fada7623f168 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -324,3 +324,60 @@ int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, 
int cnt)
        s->readpos += cnt;
        return cnt;
 }
+
+/**
+ * vpr_line - Append data to the printk() line buffer
+ * @pl: the pr_line descriptor
+ * @fmt: printf format string
+ * @args: va_list of arguments from a printf() type function
+ *
+ * Writes a vnprintf() format into the printk() pr_line buffer.
+ * Terminating new-line symbol flushes (prints) the buffer.
+ *
+ * Unlike pr_cont() and printk(KERN_CONT), this function is SMP-safe
+ * and shall be used for continued line printing.
+ *
+ * Returns zero on success, -1 on overflow.
+ */
+int vpr_line(struct pr_line *pl, const char *fmt, va_list args)
+{
+       struct seq_buf *s = &pl->sb;
+       int ret, len;
+
+       ret = seq_buf_vprintf(s, fmt, args);
+
+       len = seq_buf_used(s);
+       if (len && s->buffer[len - 1] == '\n') {
+               printk("%s%.*s", pl->level ? : KERN_DEFAULT, len, s->buffer);
+               seq_buf_clear(s);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(vpr_line);
+
+/**
+ * pr_line - Append data to the printk() line buffer
+ * @pl: the pr_line descriptor
+ * @fmt: printf format string
+ *
+ * Writes a printf() format into the printk() pr_line buffer.
+ * Terminating new-line symbol flushes (prints) the buffer.
+ *
+ * Unlike pr_cont() and printk(KERN_CONT), this function is SMP-safe
+ * and shall be used for continued line printing.
+ *
+ * Returns zero on success, -1 on overflow.
+ */
+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);
-- 
2.19.0

Reply via email to