Add two new functions for reading the kernel log buffer.  Their intention
is to be used by recovery/dump/debug code so the kernel log can be easily
retrieved/parsed in a crash scenario, but they are generic enough for other
people to dream up other fun uses.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 4300bb4..3fcc09e 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -156,6 +156,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
 	__attribute__ ((format (printf, 1, 0)));
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2))) __cold;
+extern int log_buf_read(int len, char *buf, int grab_lock);
+extern int log_buf_read_byte(int idx, char *byte, int grab_lock);
 #else
 static inline int vprintk(const char *s, va_list args)
 	__attribute__ ((format (printf, 1, 0)));
@@ -163,6 +165,8 @@ static inline int vprintk(const char *s, va_list args) { return 0; }
 static inline int printk(const char *s, ...)
 	__attribute__ ((format (printf, 1, 2)));
 static inline int __cold printk(const char *s, ...) { return 0; }
+static inline int log_buf_read(int len, char *buf, int grab_lock) { return 0; }
+static inline int log_buf_read_byte(int len, char *buf, int grab_lock) { return 0; }
 #endif
 
 unsigned long int_sqrt(unsigned long);
diff --git a/kernel/printk.c b/kernel/printk.c
index 051d27e..f08dbf5 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -163,6 +163,71 @@ out:
 __setup("log_buf_len=", log_buf_len_setup);
 
 /*
+ * Read the log buffer into the supplied buffer.  The len option
+ * tells whether to copy from the beginning (> 0), the end (< 0), or
+ * just query the number of existing chars.   The number of bytes
+ * actually copied is returned.
+ */
+int log_buf_read(int len, char *buf, int grab_lock)
+{
+	unsigned long start, end;
+	int num_to_copy;
+
+	if (len == 0)
+		return log_end - log_start;
+
+	if (grab_lock)
+		spin_lock_irq(&logbuf_lock);
+
+	num_to_copy = min(abs(len), abs(log_end - log_start));
+
+	if (len < 0) {
+		start = log_end - num_to_copy;
+		end = log_end;
+	} else {
+		start = log_start;
+		end = log_start + num_to_copy;
+	}
+
+	while (start != end)
+		*buf++ = LOG_BUF(start++);
+
+	if (grab_lock)
+		spin_unlock_irq(&logbuf_lock);
+
+	return num_to_copy;
+}
+
+/*
+ * Grab a single byte out of the log buffer.  The idx option
+ * tells whether to index from the beginning (>= 0) or the
+ * end (< 0) of the buffer.
+ */
+int log_buf_read_byte(int idx, char *byte, int grab_lock)
+{
+	int ret = 1;
+
+	if (grab_lock)
+		spin_lock_irq(&logbuf_lock);
+
+	if (abs(idx) >= log_buf_len || abs(idx) >= logged_chars) {
+		ret = -1;
+		goto done;
+	}
+
+	if (idx < 0)
+		*byte = LOG_BUF(log_end + idx);
+	else
+		*byte = LOG_BUF(log_start + idx);
+
+ done:
+	if (grab_lock)
+		spin_unlock_irq(&logbuf_lock);
+
+	return ret;
+}
+
+/*
  * Commands to do_syslog:
  *
  * 	0 -- Close the log.  Currently a NOP.
