Merge "explicitly read phy settings for ar934x ports"
diff --git a/arch/mips/configs/gfmn100_defconfig b/arch/mips/configs/gfmn100_defconfig
index 89302d8..4ebae82 100644
--- a/arch/mips/configs/gfmn100_defconfig
+++ b/arch/mips/configs/gfmn100_defconfig
@@ -9,10 +9,11 @@
CONFIG_EXPERIMENTAL=y
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
-CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_BUF_SHIFT=14
CONFIG_RELAY=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_KALLSYMS is not set
+CONFIG_PRINTK_PERSIST=y
# CONFIG_ELF_CORE is not set
# CONFIG_BASE_FULL is not set
# CONFIG_AIO is not set
@@ -198,7 +199,7 @@
CONFIG_DEBUG_INFO=y
# CONFIG_FTRACE is not set
CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="board=MN100"
+CONFIG_CMDLINE="board=MN100 log_buf_len=262144"
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_AES=y
diff --git a/init/Kconfig b/init/Kconfig
index 7e75262..42b4e8b 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1044,6 +1044,20 @@
very difficult to diagnose system problems, saying N here is
strongly discouraged.
+
+config PRINTK_PERSIST
+ default n
+ bool "printk log persists across reboots" if PRINTK
+ help
+ This option tries to keep the printk memory buffer in a well-known
+ location in physical memory. It isn't cleared on reboot (unless RAM
+ is wiped by your boot loader or BIOS) so if your system crashes
+ or panics, you might get to examine all the log messages next time you
+ boot. The persisted log messages show up in your 'dmesg' output.
+ Note: you must supply the log_buf_len= kernel parameter to
+ activate this feature.
+
+
config BUG
bool "BUG() support" if EXPERT
default y
diff --git a/kernel/printk.c b/kernel/printk.c
index ff14e1c..938b14d 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -42,6 +42,7 @@
#include <linux/notifier.h>
#include <linux/rculist.h>
#include <linux/poll.h>
+#include <linux/crc32.h>
#include <asm/uaccess.h>
@@ -64,6 +65,30 @@
DECLARE_WAIT_QUEUE_HEAD(log_wait);
+struct log {
+ u64 ts_nsec; /* timestamp in nanoseconds */
+ u16 len; /* length of entire record */
+ u16 text_len; /* length of text buffer */
+ u16 dict_len; /* length of dictionary buffer */
+ u8 facility; /* syslog facility */
+#ifdef CONFIG_PRINTK_PERSIST
+ u32 crc; /* The flags may change, so don't include them. */
+#endif
+ u8 flags:5; /* internal record flags */
+ u8 level:3; /* syslog level */
+};
+
+/* record buffer */
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#define LOG_ALIGN 4
+#else
+#define LOG_ALIGN __alignof__(struct log)
+#endif
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
+static char *log_buf = __log_buf;
+#define LOG_LINE_MAX 1024
+
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */
DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */
@@ -200,22 +225,14 @@
LOG_CONT = 8, /* text is a fragment of a continuation line */
};
-struct log {
- u64 ts_nsec; /* timestamp in nanoseconds */
- u16 len; /* length of entire record */
- u16 text_len; /* length of text buffer */
- u16 dict_len; /* length of dictionary buffer */
- u8 facility; /* syslog facility */
- u8 flags:5; /* internal record flags */
- u8 level:3; /* syslog level */
-};
-
/*
* The logbuf_lock protects kmsg buffer, indices, counters. It is also
* used in interesting ways to provide interlocking in console_unlock();
*/
static DEFINE_RAW_SPINLOCK(logbuf_lock);
+#ifdef CONFIG_PRINTK
+#ifndef CONFIG_PRINTK_PERSIST
/* the next printk record to read by syslog(READ) or /proc/kmsg */
static u64 syslog_seq;
static u32 syslog_idx;
@@ -228,25 +245,173 @@
/* index and sequence number of the next record to store in the buffer */
static u64 log_next_seq;
-#ifdef CONFIG_PRINTK
static u32 log_next_idx;
/* the next printk record to read after the last 'clear' command */
static u64 clear_seq;
static u32 clear_idx;
-#define LOG_LINE_MAX 1024
+/* the next printk record to write to the console */
+static u64 console_seq;
+static u32 console_idx;
+static enum log_flags console_prev;
-/* record buffer */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-#define LOG_ALIGN 4
-#else
-#define LOG_ALIGN __alignof__(struct log)
-#endif
-#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
+#else /* CONFIG_PRINTK_PERSIST */
+struct log_state {
+ u32 magic;
+ u32 _log_buf_len;
+ u64 _syslog_seq;
+ u32 _syslog_idx;
+ enum log_flags _syslog_prev;
+ size_t _syslog_partial;
+
+ u64 _log_first_seq;
+ u32 _log_first_idx;
+
+ u64 _log_next_seq;
+ u32 _log_next_idx;
+
+ u64 _console_seq;
+ u32 _console_idx;
+ enum log_flags _console_prev;
+
+ u64 _clear_seq;
+ u32 _clear_idx;
+};
+static struct log_state __log_state = {
+ ._log_buf_len = __LOG_BUF_LEN,
+};
+static struct log_state *log_state = &__log_state;
+#define log_buf_len log_state->_log_buf_len
+#define syslog_seq log_state->_syslog_seq
+#define syslog_idx log_state->_syslog_idx
+#define syslog_prev log_state->_syslog_prev
+#define syslog_partial log_state->_syslog_partial
+#define log_first_seq log_state->_log_first_seq
+#define log_first_idx log_state->_log_first_idx
+#define log_next_seq log_state->_log_next_seq
+#define log_next_idx log_state->_log_next_idx
+#define console_seq log_state->_console_seq
+#define console_idx log_state->_console_idx
+#define console_prev log_state->_console_prev
+#define clear_seq log_state->_clear_seq
+#define clear_idx log_state->_clear_idx
+
+#define PERSIST_SEARCH_START 0
+/* TODO(awdavies): Hard coded addr. Determined through guess/check.
+ *
+ * As such it is not likely that this address is entirely reliable. For a more
+ * portable solution, the bootloader should inform the kernel that there is a
+ * specific location of free memory that will it will not touch.
+ */
+#define PERSIST_SEARCH_END 0x17f0000
+#define PERSIST_SEARCH_JUMP 0x1000000
+#define PERSIST_MAGIC 0xcafef00d
+
+/*
+ * Size is a power of 2 so that the printk offset mask will work. A little
+ * extra space is added to the end of the buffer for extra data, but that
+ * doesn't change the offset of the buffers.
+ */
+static __init struct log_state *log_buf_alloc(unsigned long size,
+ char **new_logbuf)
+{
+ unsigned long where;
+ char *buf;
+ unsigned long full_size = size + sizeof(struct log_state);
+ struct log_state *new_log_state;
+ struct log *pklog;
+ u64 idx, lost_entries, seq;
+ u32 curr_crc;
+
+ for (where = PERSIST_SEARCH_END - size;
+ where >= PERSIST_SEARCH_START;
+ where -= PERSIST_SEARCH_JUMP) {
+ if (reserve_bootmem(where, full_size, BOOTMEM_EXCLUSIVE)) {
+ continue;
+ }
+ buf = phys_to_virt(where);
+ *new_logbuf = buf;
+ new_log_state = phys_to_virt(where + size);
+ pr_info("printk_persist: memory reserved @ 0x%08lx\n", where);
+ if ((new_log_state->magic != PERSIST_MAGIC) ||
+ (new_log_state->_log_buf_len != size) ||
+ (new_log_state->_log_first_seq >
+ new_log_state->_log_next_seq) ||
+ (new_log_state->_log_first_idx > size) ||
+ (new_log_state->_syslog_idx > size) ||
+ (new_log_state->_syslog_seq >
+ new_log_state->_log_next_seq) ||
+ (new_log_state->_log_next_idx > size) ||
+ (new_log_state->_console_seq >
+ new_log_state->_log_next_seq) ||
+ (new_log_state->_console_idx > size) ||
+ (new_log_state->_clear_seq >
+ new_log_state->_log_next_seq) ||
+ (new_log_state->_clear_idx > size)) {
+ pr_info("printk_persist: header invalid. Clearing.\n");
+ memset(buf, 0, full_size);
+ memset(new_log_state, 0, sizeof(*new_log_state));
+ new_log_state->magic = PERSIST_MAGIC;
+ new_log_state->_log_buf_len = size;
+ } else {
+ pr_info("printk_persist: Valid header: "
+ "\tlog_first_idx=%d\n"
+ "\tlog_next_idx=%d\n"
+ "\tlog_first_seq=%lld\n"
+ "\tlog_next_seq=%lld\n"
+ "\tconsole_seq=%lld\n"
+ "\tconsole_idx=%d\n"
+ "\tsyslog_seq=%lld\n"
+ "\tsyslog_idx=%d\n",
+ new_log_state->_log_first_idx,
+ new_log_state->_log_next_idx,
+ new_log_state->_log_first_seq,
+ new_log_state->_log_next_seq,
+ new_log_state->_console_seq,
+ new_log_state->_console_idx,
+ new_log_state->_syslog_seq,
+ new_log_state->_syslog_idx);
+ pr_info("printk_persist: validating records.\n");
+ for (seq = new_log_state->_log_first_seq,
+ idx = new_log_state->_log_first_idx;
+ seq < new_log_state->_log_next_seq;
+ ++seq) {
+ pklog = (struct log *) &buf[idx];
+ /* If crc doesn't match, move the next pointer to be here. */
+ curr_crc = crc32(~0, pklog, offsetof(struct log, crc));
+ if (pklog->crc != curr_crc) {
+ lost_entries = new_log_state->_log_next_seq - seq;
+ pr_info("printk_persist: corruption encountered. lost %llu entries\n",
+ lost_entries);
+ new_log_state->_log_next_seq = seq;
+ new_log_state->_log_next_idx = idx;
+ break;
+ }
+ if (pklog->len == 0) {
+ idx = 0;
+ /* Do not increment the sequence counter for the record that's used
+ * to indicate wrapping. We still want to verify its CRC above,
+ * however. */
+ --seq;
+ } else {
+ idx += pklog->len;
+ }
+ }
+ }
+ return new_log_state;
+ }
+ printk(KERN_ERR "printk_persist: failed to reserve bootmem "
+ "area. persistence disabled.\n");
+ buf = alloc_bootmem(full_size);
+ *new_logbuf = buf;
+ new_log_state = (struct log_state *)(buf + size);
+ memset(buf, 0, full_size);
+ return new_log_state;
+}
+
+#endif /* CONFIG_PRINTK_PERSIST */
/* cpu currently holding logbuf_lock */
static volatile unsigned int logbuf_cpu = UINT_MAX;
@@ -295,6 +460,33 @@
return idx + msg->len;
}
+static int logbuf_has_space(u32 msg_size, bool empty)
+{
+ u32 free;
+
+ if (log_next_idx > log_first_idx || empty)
+ free = max(log_buf_len - log_next_idx, log_first_idx);
+ else
+ free = log_first_idx - log_next_idx;
+
+ return free >= msg_size + sizeof(struct log);
+}
+
+static int log_make_free_space(u32 msg_size)
+{
+ while (log_first_seq < log_next_seq) {
+ if (logbuf_has_space(msg_size, false))
+ return 0;
+ log_first_idx = log_next(log_first_idx);
+ log_first_seq++;
+ }
+
+ if (logbuf_has_space(msg_size, true))
+ return 0;
+
+ return -ENOMEM;
+}
+
/* insert record into the buffer, discard old ones, update heads */
static void log_store(int facility, int level,
enum log_flags flags, u64 ts_nsec,
@@ -332,6 +524,10 @@
* to signify a wrap around.
*/
memset(log_buf + log_next_idx, 0, sizeof(struct log));
+#ifdef CONFIG_PRINTK_PERSIST
+ ((struct log *) (log_buf + log_next_idx))->crc =
+ crc32(~0, log_buf + log_next_idx, offsetof(struct log, crc));
+#endif
log_next_idx = 0;
}
@@ -350,6 +546,9 @@
msg->ts_nsec = local_clock();
memset(log_dict(msg) + dict_len, 0, pad_len);
msg->len = sizeof(struct log) + text_len + dict_len + pad_len;
+#ifdef CONFIG_PRINTK_PERSIST
+ msg->crc = crc32(~0, msg, offsetof(struct log, crc));
+#endif
/* insert message */
log_next_idx += msg->len;
@@ -671,13 +870,21 @@
unsigned long flags;
char *new_log_buf;
int free;
+#ifdef CONFIG_PRINTK_PERSIST
+ struct log_state *new_log_state;
+ struct log_state *old_log_state;
+ struct log *pklog;
+ u64 seq;
+ u32 idx;
+ int console_found=0, syslog_found=0;
+#endif
if (!new_log_buf_len)
return;
+#ifndef CONFIG_PRINTK_PERSIST
if (early) {
unsigned long mem;
-
mem = memblock_alloc(new_log_buf_len, PAGE_SIZE);
if (!mem)
return;
@@ -685,6 +892,9 @@
} else {
new_log_buf = alloc_bootmem_nopanic(new_log_buf_len);
}
+#else /* CONFIG_PRINTK_PERSIST */
+ new_log_state = log_buf_alloc(new_log_buf_len, &new_log_buf);
+#endif /* CONFIG_PRINTK_PERSIST */
if (unlikely(!new_log_buf)) {
pr_err("log_buf_len: %ld bytes not available\n",
@@ -697,7 +907,60 @@
log_buf = new_log_buf;
new_log_buf_len = 0;
free = __LOG_BUF_LEN - log_next_idx;
+#ifndef CONFIG_PRINTK_PERSIST
memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
+#else
+ old_log_state = log_state;
+ log_state = new_log_state;
+ for (seq = old_log_state->_log_first_seq, idx = old_log_state->_log_first_idx;
+ seq < old_log_state->_log_next_seq; ++seq) {
+ pklog = (struct log *)&__log_buf[idx];
+ if (pklog->len == 0) {
+ idx = 0;
+ pklog = (struct log *)&__log_buf[0];
+ }
+
+ if (log_make_free_space(pklog->len)) {
+ pr_err("not copying entry due to it being too large\n");
+ idx += pklog->len;
+ continue;
+ }
+
+ if (log_next_idx + pklog->len + sizeof(struct log) > log_buf_len) {
+ memset(log_buf + log_next_idx, 0, sizeof(struct log));
+ ((struct log *) (log_buf + log_next_idx))->crc =
+ crc32(~0, log_buf + log_next_idx, offsetof(struct log, crc));
+ log_next_idx = 0;
+ }
+ memcpy(&log_buf[log_next_idx], &__log_buf[idx], pklog->len);
+
+ if (old_log_state->_syslog_seq == seq) {
+ syslog_seq = log_next_seq;
+ syslog_idx = log_next_idx;
+ syslog_found = 1;
+ }
+
+ if (old_log_state->_console_seq == seq) {
+ console_seq = log_next_seq;
+ console_idx = log_next_idx;
+ console_found = 1;
+ }
+
+ idx += pklog->len;
+ ++log_next_seq;
+ log_next_idx += pklog->len;
+ }
+
+ if (!syslog_found) {
+ syslog_seq = log_next_seq;
+ syslog_idx = log_next_idx;
+ }
+
+ if (!console_found) {
+ console_seq = log_next_seq;
+ console_idx = log_next_idx;
+ }
+#endif /* CONFIG_PRINTK_PERSIST */
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
pr_info("log_buf_len: %d\n", log_buf_len);
@@ -1616,7 +1879,7 @@
}
EXPORT_SYMBOL(printk);
-#else
+#else /* CONFIG_PRINTK */
#define LOG_LINE_MAX 0
static struct cont {
@@ -1625,6 +1888,16 @@
u8 level;
bool flushed:1;
} cont;
+static u64 syslog_seq;
+static u32 syslog_idx;
+static enum log_flags syslog_prev;
+static size_t syslog_partial;
+static u64 log_first_seq;
+static u32 log_first_idx;
+static u64 log_next_seq;
+static u64 console_seq;
+static u32 console_idx;
+static enum log_flags console_prev;
static struct log *log_from_idx(u32 idx) { return NULL; }
static u32 log_next(u32 idx) { return 0; }
static void call_console_drivers(int level, const char *text, size_t len) {}
@@ -1896,11 +2169,6 @@
this_cpu_write(printk_pending, 1);
}
-/* the next printk record to write to the console */
-static u64 console_seq;
-static u32 console_idx;
-static enum log_flags console_prev;
-
/**
* console_unlock - unlock the console system
*
diff --git a/mm/bootmem.c b/mm/bootmem.c
index d5ba3ac..8a70c14 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -40,6 +40,7 @@
static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
static int bootmem_debug;
+static int bootmem_stopped = 0;
static int __init bootmem_debug_setup(char *buf)
{
@@ -184,6 +185,7 @@
bdebug("nid=%td start=%lx end=%lx\n",
bdata - bootmem_node_data, start, end);
+ bootmem_stopped = 1;
while (start < end) {
unsigned long *map, idx, vec;
@@ -281,6 +283,7 @@
unsigned long idx;
int exclusive = flags & BOOTMEM_EXCLUSIVE;
+ BUG_ON(bootmem_stopped);
bdebug("nid=%td start=%lx end=%lx flags=%x\n",
bdata - bootmem_node_data,
sidx + bdata->node_min_pfn,
@@ -350,7 +353,10 @@
return 0;
pos = bdata->node_low_pfn;
}
- BUG();
+ if (reserve && (flags & BOOTMEM_EXCLUSIVE))
+ return -ENOENT; /* not a bug as long as you use EXCLUSIVE. */
+ else
+ BUG();
}
/**