alistair23-linux/fs/pstore/platform.c
Linus Torvalds 65b97fb730 Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull powerpc updates from Ben Herrenschmidt:
 "This is the powerpc changes for the 3.11 merge window.  In addition to
  the usual bug fixes and small updates, the main highlights are:

   - Support for transparent huge pages by Aneesh Kumar for 64-bit
     server processors.  This allows the use of 16M pages as transparent
     huge pages on kernels compiled with a 64K base page size.

   - Base VFIO support for KVM on power by Alexey Kardashevskiy

   - Wiring up of our nvram to the pstore infrastructure, including
     putting compressed oopses in there by Aruna Balakrishnaiah

   - Move, rework and improve our "EEH" (basically PCI error handling
     and recovery) infrastructure.  It is no longer specific to pseries
     but is now usable by the new "powernv" platform as well (no
     hypervisor) by Gavin Shan.

   - I fixed some bugs in our math-emu instruction decoding and made it
     usable to emulate some optional FP instructions on processors with
     hard FP that lack them (such as fsqrt on Freescale embedded
     processors).

   - Support for Power8 "Event Based Branch" facility by Michael
     Ellerman.  This facility allows what is basically "userspace
     interrupts" for performance monitor events.

   - A bunch of Transactional Memory vs.  Signals bug fixes and HW
     breakpoint/watchpoint fixes by Michael Neuling.

  And more ...  I appologize in advance if I've failed to highlight
  something that somebody deemed worth it."

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (156 commits)
  pstore: Add hsize argument in write_buf call of pstore_ftrace_call
  powerpc/fsl: add MPIC timer wakeup support
  powerpc/mpic: create mpic subsystem object
  powerpc/mpic: add global timer support
  powerpc/mpic: add irq_set_wake support
  powerpc/85xx: enable coreint for all the 64bit boards
  powerpc/8xx: Erroneous double irq_eoi() on CPM IRQ in MPC8xx
  powerpc/fsl: Enable CONFIG_E1000E in mpc85xx_smp_defconfig
  powerpc/mpic: Add get_version API both for internal and external use
  powerpc: Handle both new style and old style reserve maps
  powerpc/hw_brk: Fix off by one error when validating DAWR region end
  powerpc/pseries: Support compression of oops text via pstore
  powerpc/pseries: Re-organise the oops compression code
  pstore: Pass header size in the pstore write callback
  powerpc/powernv: Fix iommu initialization again
  powerpc/pseries: Inform the hypervisor we are using EBB regs
  powerpc/perf: Add power8 EBB support
  powerpc/perf: Core EBB support for 64-bit book3s
  powerpc/perf: Drop MMCRA from thread_struct
  powerpc/perf: Don't enable if we have zero events
  ...
2013-07-04 10:29:23 -07:00

343 lines
8.3 KiB
C

/*
* Persistent Storage - platform driver interface parts.
*
* Copyright (C) 2007-2008 Google, Inc.
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/atomic.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kmsg_dump.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pstore.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/hardirq.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include "internal.h"
/*
* We defer making "oops" entries appear in pstore - see
* whether the system is actually still running well enough
* to let someone see the entry
*/
static int pstore_update_ms = -1;
module_param_named(update_ms, pstore_update_ms, int, 0600);
MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content "
"(default is -1, which means runtime updates are disabled; "
"enabling this option is not safe, it may lead to further "
"corruption on Oopses)");
static int pstore_new_entry;
static void pstore_timefunc(unsigned long);
static DEFINE_TIMER(pstore_timer, pstore_timefunc, 0, 0);
static void pstore_dowork(struct work_struct *);
static DECLARE_WORK(pstore_work, pstore_dowork);
/*
* pstore_lock just protects "psinfo" during
* calls to pstore_register()
*/
static DEFINE_SPINLOCK(pstore_lock);
struct pstore_info *psinfo;
static char *backend;
/* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240;
void pstore_set_kmsg_bytes(int bytes)
{
kmsg_bytes = bytes;
}
/* Tag each group of saved records with a sequence number */
static int oopscount;
static const char *get_reason_str(enum kmsg_dump_reason reason)
{
switch (reason) {
case KMSG_DUMP_PANIC:
return "Panic";
case KMSG_DUMP_OOPS:
return "Oops";
case KMSG_DUMP_EMERG:
return "Emergency";
case KMSG_DUMP_RESTART:
return "Restart";
case KMSG_DUMP_HALT:
return "Halt";
case KMSG_DUMP_POWEROFF:
return "Poweroff";
default:
return "Unknown";
}
}
bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
{
/*
* In case of NMI path, pstore shouldn't be blocked
* regardless of reason.
*/
if (in_nmi())
return true;
switch (reason) {
/* In panic case, other cpus are stopped by smp_send_stop(). */
case KMSG_DUMP_PANIC:
/* Emergency restart shouldn't be blocked by spin lock. */
case KMSG_DUMP_EMERG:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
/*
* callback from kmsg_dump. (s2,l2) has the most recently
* written bytes, older bytes are in (s1,l1). Save as much
* as we can from the end of the buffer.
*/
static void pstore_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason)
{
unsigned long total = 0;
const char *why;
u64 id;
unsigned int part = 1;
unsigned long flags = 0;
int is_locked = 0;
int ret;
why = get_reason_str(reason);
if (pstore_cannot_block_path(reason)) {
is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags);
if (!is_locked) {
pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
, in_nmi() ? "NMI" : why);
}
} else
spin_lock_irqsave(&psinfo->buf_lock, flags);
oopscount++;
while (total < kmsg_bytes) {
char *dst;
unsigned long size;
int hsize;
size_t len;
dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
size = psinfo->bufsize - hsize;
dst += hsize;
if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
break;
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
oopscount, hsize, hsize + len, psinfo);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1;
total += hsize + len;
part++;
}
if (pstore_cannot_block_path(reason)) {
if (is_locked)
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
} else
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
}
static struct kmsg_dumper pstore_dumper = {
.dump = pstore_dump,
};
#ifdef CONFIG_PSTORE_CONSOLE
static void pstore_console_write(struct console *con, const char *s, unsigned c)
{
const char *e = s + c;
while (s < e) {
unsigned long flags;
u64 id;
if (c > psinfo->bufsize)
c = psinfo->bufsize;
if (oops_in_progress) {
if (!spin_trylock_irqsave(&psinfo->buf_lock, flags))
break;
} else {
spin_lock_irqsave(&psinfo->buf_lock, flags);
}
memcpy(psinfo->buf, s, c);
psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, 0, 0, c, psinfo);
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
s += c;
c = e - s;
}
}
static struct console pstore_console = {
.name = "pstore",
.write = pstore_console_write,
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
.index = -1,
};
static void pstore_register_console(void)
{
register_console(&pstore_console);
}
#else
static void pstore_register_console(void) {}
#endif
static int pstore_write_compat(enum pstore_type_id type,
enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count,
size_t hsize, size_t size,
struct pstore_info *psi)
{
return psi->write_buf(type, reason, id, part, psinfo->buf, hsize,
size, psi);
}
/*
* platform specific persistent storage driver registers with
* us here. If pstore is already mounted, call the platform
* read function right away to populate the file system. If not
* then the pstore mount code will call us later to fill out
* the file system.
*
* Register with kmsg_dump to save last part of console log on panic.
*/
int pstore_register(struct pstore_info *psi)
{
struct module *owner = psi->owner;
if (backend && strcmp(backend, psi->name))
return -EPERM;
spin_lock(&pstore_lock);
if (psinfo) {
spin_unlock(&pstore_lock);
return -EBUSY;
}
if (!psi->write)
psi->write = pstore_write_compat;
psinfo = psi;
mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock);
if (owner && !try_module_get(owner)) {
psinfo = NULL;
return -EINVAL;
}
if (pstore_is_mounted())
pstore_get_records(0);
kmsg_dump_register(&pstore_dumper);
pstore_register_console();
pstore_register_ftrace();
if (pstore_update_ms >= 0) {
pstore_timer.expires = jiffies +
msecs_to_jiffies(pstore_update_ms);
add_timer(&pstore_timer);
}
pr_info("pstore: Registered %s as persistent store backend\n",
psi->name);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
/*
* Read all the records from the persistent store. Create
* files in our filesystem. Don't warn about -EEXIST errors
* when we are re-scanning the backing store looking to add new
* error records.
*/
void pstore_get_records(int quiet)
{
struct pstore_info *psi = psinfo;
char *buf = NULL;
ssize_t size;
u64 id;
int count;
enum pstore_type_id type;
struct timespec time;
int failed = 0, rc;
if (!psi)
return;
mutex_lock(&psi->read_mutex);
if (psi->open && psi->open(psi))
goto out;
while ((size = psi->read(&id, &type, &count, &time, &buf, psi)) > 0) {
rc = pstore_mkfile(type, psi->name, id, count, buf,
(size_t)size, time, psi);
kfree(buf);
buf = NULL;
if (rc && (rc != -EEXIST || !quiet))
failed++;
}
if (psi->close)
psi->close(psi);
out:
mutex_unlock(&psi->read_mutex);
if (failed)
printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n",
failed, psi->name);
}
static void pstore_dowork(struct work_struct *work)
{
pstore_get_records(1);
}
static void pstore_timefunc(unsigned long dummy)
{
if (pstore_new_entry) {
pstore_new_entry = 0;
schedule_work(&pstore_work);
}
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
}
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");