Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6:
  [IA64] tioca: Fix assignment from incompatible pointer warnings
  [IA64] mca.c: Fix cast from integer to pointer warning
  [IA64] setup.c Typo fix "Architechtuallly"
  [IA64] Add CONFIG_MISC_DEVICES=y to configs that need it.
  [IA64] disable interrupts at end of ia64_mca_cpe_int_handler()
  [IA64] Add DMA_ERROR_CODE define.
  pstore: fix build warning for unused return value from sysfs_create_file
  pstore: X86 platform interface using ACPI/APEI/ERST
  pstore: new filesystem interface to platform persistent storage
This commit is contained in:
Linus Torvalds 2011-03-16 19:01:29 -07:00
commit 242e5d06be
19 changed files with 766 additions and 3 deletions

View File

@ -0,0 +1,35 @@
Where: /dev/pstore/...
Date: January 2011
Kernel Version: 2.6.38
Contact: tony.luck@intel.com
Description: Generic interface to platform dependent persistent storage.
Platforms that provide a mechanism to preserve some data
across system reboots can register with this driver to
provide a generic interface to show records captured in
the dying moments. In the case of a panic the last part
of the console log is captured, but other interesting
data can also be saved.
# mount -t pstore - /dev/pstore
$ ls -l /dev/pstore
total 0
-r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1
Different users of this interface will result in different
filename prefixes. Currently two are defined:
"dmesg" - saved console log
"mce" - architecture dependent data from fatal h/w error
Once the information in a file has been read, removing
the file will signal to the underlying persistent storage
device that it can reclaim the space for later re-use.
$ rm /dev/pstore/dmesg-erst-1
The expectation is that all files in /dev/pstore
will be saved elsewhere and erased from persistent store
soon after boot to free up space ready for the next
catastrophe.

View File

@ -0,0 +1,7 @@
What: /sys/fs/pstore/kmsg_bytes
Date: January 2011
Kernel Version: 2.6.38
Contact: "Tony Luck" <tony.luck@intel.com>
Description:
Controls amount of console log that will be saved
to persistent store on oops/panic.

View File

@ -233,3 +233,4 @@ CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_MD5=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set # CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_T10DIF=y CONFIG_CRC_T10DIF=y
CONFIG_MISC_DEVICES=y

View File

@ -208,3 +208,4 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_MUTEXES=y
CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_MD5=y
CONFIG_MISC_DEVICES=y

View File

@ -12,6 +12,8 @@
#define ARCH_HAS_DMA_GET_REQUIRED_MASK #define ARCH_HAS_DMA_GET_REQUIRED_MASK
#define DMA_ERROR_CODE 0
extern struct dma_map_ops *dma_ops; extern struct dma_map_ops *dma_ops;
extern struct ia64_machine_vector ia64_mv; extern struct ia64_machine_vector ia64_mv;
extern void set_iommu_machvec(void); extern void set_iommu_machvec(void);

View File

@ -582,6 +582,8 @@ out:
/* Get the CPE error record and log it */ /* Get the CPE error record and log it */
ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE); ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);
local_irq_disable();
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -1859,7 +1861,8 @@ ia64_mca_cpu_init(void *cpu_data)
data = mca_bootmem(); data = mca_bootmem();
first_time = 0; first_time = 0;
} else } else
data = __get_free_pages(GFP_KERNEL, get_order(sz)); data = (void *)__get_free_pages(GFP_KERNEL,
get_order(sz));
if (!data) if (!data)
panic("Could not allocate MCA memory for cpu %d\n", panic("Could not allocate MCA memory for cpu %d\n",
cpu); cpu);

View File

@ -592,7 +592,7 @@ void __cpuinit sn_cpu_init(void)
/* /*
* Don't check status. The SAL call is not supported on all PROMs * Don't check status. The SAL call is not supported on all PROMs
* but a failure is harmless. * but a failure is harmless.
* Architechtuallly, cpu_init is always called twice on cpu 0. We * Architecturally, cpu_init is always called twice on cpu 0. We
* should set cpu_number on cpu 0 once. * should set cpu_number on cpu 0 once.
*/ */
if (cpuid == 0) { if (cpuid == 0) {

View File

@ -509,7 +509,7 @@ tioca_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir)
* use the GART mapped mode. * use the GART mapped mode.
*/ */
static u64 static u64
tioca_dma_map(struct pci_dev *pdev, u64 paddr, size_t byte_count, int dma_flags) tioca_dma_map(struct pci_dev *pdev, unsigned long paddr, size_t byte_count, int dma_flags)
{ {
u64 mapaddr; u64 mapaddr;

View File

@ -1,5 +1,6 @@
config ACPI_APEI config ACPI_APEI
bool "ACPI Platform Error Interface (APEI)" bool "ACPI Platform Error Interface (APEI)"
select PSTORE
depends on X86 depends on X86
help help
APEI allows to report errors (for example from the chipset) APEI allows to report errors (for example from the chipset)

View File

@ -34,6 +34,7 @@
#include <linux/cper.h> #include <linux/cper.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/pstore.h>
#include <acpi/apei.h> #include <acpi/apei.h>
#include "apei-internal.h" #include "apei-internal.h"
@ -781,6 +782,128 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
return 0; return 0;
} }
static size_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time);
static u64 erst_writer(enum pstore_type_id type, size_t size);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
.name = "erst",
.read = erst_reader,
.write = erst_writer,
.erase = erst_clear
};
#define CPER_CREATOR_PSTORE \
UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \
0x64, 0x90, 0xb8, 0x9d)
#define CPER_SECTION_TYPE_DMESG \
UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
0x94, 0x19, 0xeb, 0x12)
#define CPER_SECTION_TYPE_MCE \
UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
0x04, 0x4a, 0x38, 0xfc)
struct cper_pstore_record {
struct cper_record_header hdr;
struct cper_section_descriptor sec_hdr;
char data[];
} __packed;
static size_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time)
{
int rc;
ssize_t len;
unsigned long flags;
u64 record_id;
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
if (erst_disable)
return -ENODEV;
raw_spin_lock_irqsave(&erst_lock, flags);
skip:
rc = __erst_get_next_record_id(&record_id);
if (rc) {
raw_spin_unlock_irqrestore(&erst_lock, flags);
return rc;
}
/* no more record */
if (record_id == APEI_ERST_INVALID_RECORD_ID) {
raw_spin_unlock_irqrestore(&erst_lock, flags);
return 0;
}
len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
erst_erange.size);
if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
goto skip;
raw_spin_unlock_irqrestore(&erst_lock, flags);
*id = record_id;
if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG) == 0)
*type = PSTORE_TYPE_DMESG;
else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_MCE) == 0)
*type = PSTORE_TYPE_MCE;
else
*type = PSTORE_TYPE_UNKNOWN;
if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
time->tv_sec = rcd->hdr.timestamp;
else
time->tv_sec = 0;
time->tv_nsec = 0;
return len - sizeof(*rcd);
}
static u64 erst_writer(enum pstore_type_id type, size_t size)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
memset(rcd, 0, sizeof(*rcd));
memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
rcd->hdr.revision = CPER_RECORD_REV;
rcd->hdr.signature_end = CPER_SIG_END;
rcd->hdr.section_count = 1;
rcd->hdr.error_severity = CPER_SEV_FATAL;
/* timestamp valid. platform_id, partition_id are invalid */
rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
rcd->hdr.timestamp = get_seconds();
rcd->hdr.record_length = sizeof(*rcd) + size;
rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
rcd->hdr.notification_type = CPER_NOTIFY_MCE;
rcd->hdr.record_id = cper_next_record_id();
rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
rcd->sec_hdr.section_offset = sizeof(*rcd);
rcd->sec_hdr.section_length = size;
rcd->sec_hdr.revision = CPER_SEC_REV;
/* fru_id and fru_text is invalid */
rcd->sec_hdr.validation_bits = 0;
rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
switch (type) {
case PSTORE_TYPE_DMESG:
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
break;
case PSTORE_TYPE_MCE:
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
break;
default:
return -EINVAL;
}
rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
erst_write(&rcd->hdr);
return rcd->hdr.record_id;
}
static int __init erst_init(void) static int __init erst_init(void)
{ {
int rc = 0; int rc = 0;
@ -788,6 +911,7 @@ static int __init erst_init(void)
struct apei_exec_context ctx; struct apei_exec_context ctx;
struct apei_resources erst_resources; struct apei_resources erst_resources;
struct resource *r; struct resource *r;
char *buf;
if (acpi_disabled) if (acpi_disabled)
goto err; goto err;
@ -854,6 +978,18 @@ static int __init erst_init(void)
if (!erst_erange.vaddr) if (!erst_erange.vaddr)
goto err_release_erange; goto err_release_erange;
buf = kmalloc(erst_erange.size, GFP_KERNEL);
mutex_init(&erst_info.buf_mutex);
if (buf) {
erst_info.buf = buf + sizeof(struct cper_pstore_record);
erst_info.bufsize = erst_erange.size -
sizeof(struct cper_pstore_record);
if (pstore_register(&erst_info)) {
pr_info(ERST_PFX "Could not register with persistent store\n");
kfree(buf);
}
}
pr_info(ERST_PFX pr_info(ERST_PFX
"Error Record Serialization Table (ERST) support is initialized.\n"); "Error Record Serialization Table (ERST) support is initialized.\n");

View File

@ -187,6 +187,7 @@ source "fs/omfs/Kconfig"
source "fs/hpfs/Kconfig" source "fs/hpfs/Kconfig"
source "fs/qnx4/Kconfig" source "fs/qnx4/Kconfig"
source "fs/romfs/Kconfig" source "fs/romfs/Kconfig"
source "fs/pstore/Kconfig"
source "fs/sysv/Kconfig" source "fs/sysv/Kconfig"
source "fs/ufs/Kconfig" source "fs/ufs/Kconfig"
source "fs/exofs/Kconfig" source "fs/exofs/Kconfig"

View File

@ -123,3 +123,4 @@ obj-$(CONFIG_BTRFS_FS) += btrfs/
obj-$(CONFIG_GFS2_FS) += gfs2/ obj-$(CONFIG_GFS2_FS) += gfs2/
obj-$(CONFIG_EXOFS_FS) += exofs/ obj-$(CONFIG_EXOFS_FS) += exofs/
obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/

13
fs/pstore/Kconfig Normal file
View File

@ -0,0 +1,13 @@
config PSTORE
bool "Persistant store support"
default n
help
This option enables generic access to platform level
persistent storage via "pstore" filesystem that can
be mounted as /dev/pstore. Only useful if you have
a platform level driver that registers with pstore to
provide the data, so you probably should just go say "Y"
(or "M") to a platform specific persistent store driver
(e.g. ACPI_APEI on X86) which will select this for you.
If you don't have a platform persistent store driver,
say N.

7
fs/pstore/Makefile Normal file
View File

@ -0,0 +1,7 @@
#
# Makefile for the linux pstorefs routines.
#
obj-y += pstore.o
pstore-objs += inode.o platform.o

285
fs/pstore/inode.c Normal file
View File

@ -0,0 +1,285 @@
/*
* Persistent Storage - ramfs parts.
*
* 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/module.h>
#include <linux/fs.h>
#include <linux/fsnotify.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/mount.h>
#include <linux/ramfs.h>
#include <linux/sched.h>
#include <linux/magic.h>
#include <linux/pstore.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "internal.h"
#define PSTORE_NAMELEN 64
struct pstore_private {
u64 id;
int (*erase)(u64);
};
#define pstore_get_inode ramfs_get_inode
/*
* When a file is unlinked from our file system we call the
* platform driver to erase the record from persistent store.
*/
static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
struct pstore_private *p = dentry->d_inode->i_private;
p->erase(p->id);
kfree(p);
return simple_unlink(dir, dentry);
}
static const struct inode_operations pstore_dir_inode_operations = {
.lookup = simple_lookup,
.unlink = pstore_unlink,
};
static const struct super_operations pstore_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.show_options = generic_show_options,
};
static struct super_block *pstore_sb;
static struct vfsmount *pstore_mnt;
int pstore_is_mounted(void)
{
return pstore_mnt != NULL;
}
/*
* Set up a file structure as if we had opened this file and
* write our data to it.
*/
static int pstore_writefile(struct inode *inode, struct dentry *dentry,
char *data, size_t size)
{
struct file f;
ssize_t n;
mm_segment_t old_fs = get_fs();
memset(&f, '0', sizeof f);
f.f_mapping = inode->i_mapping;
f.f_path.dentry = dentry;
f.f_path.mnt = pstore_mnt;
f.f_pos = 0;
f.f_op = inode->i_fop;
set_fs(KERNEL_DS);
n = do_sync_write(&f, data, size, &f.f_pos);
set_fs(old_fs);
fsnotify_modify(&f);
return n == size;
}
/*
* Make a regular file in the root directory of our file system.
* Load it up with "size" bytes of data from "buf".
* Set the mtime & ctime to the date that this record was originally stored.
*/
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
char *data, size_t size,
struct timespec time, int (*erase)(u64))
{
struct dentry *root = pstore_sb->s_root;
struct dentry *dentry;
struct inode *inode;
int rc;
char name[PSTORE_NAMELEN];
struct pstore_private *private;
rc = -ENOMEM;
inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0);
if (!inode)
goto fail;
inode->i_uid = inode->i_gid = 0;
private = kmalloc(sizeof *private, GFP_KERNEL);
if (!private)
goto fail_alloc;
private->id = id;
private->erase = erase;
switch (type) {
case PSTORE_TYPE_DMESG:
sprintf(name, "dmesg-%s-%lld", psname, id);
break;
case PSTORE_TYPE_MCE:
sprintf(name, "mce-%s-%lld", psname, id);
break;
case PSTORE_TYPE_UNKNOWN:
sprintf(name, "unknown-%s-%lld", psname, id);
break;
default:
sprintf(name, "type%d-%s-%lld", type, psname, id);
break;
}
mutex_lock(&root->d_inode->i_mutex);
rc = -ENOSPC;
dentry = d_alloc_name(root, name);
if (IS_ERR(dentry))
goto fail_lockedalloc;
d_add(dentry, inode);
mutex_unlock(&root->d_inode->i_mutex);
if (!pstore_writefile(inode, dentry, data, size))
goto fail_write;
inode->i_private = private;
if (time.tv_sec)
inode->i_mtime = inode->i_ctime = time;
return 0;
fail_write:
kfree(private);
inode->i_nlink--;
mutex_lock(&root->d_inode->i_mutex);
d_delete(dentry);
dput(dentry);
mutex_unlock(&root->d_inode->i_mutex);
goto fail;
fail_lockedalloc:
mutex_unlock(&root->d_inode->i_mutex);
kfree(private);
fail_alloc:
iput(inode);
fail:
return rc;
}
int pstore_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode = NULL;
struct dentry *root;
int err;
save_mount_options(sb, data);
pstore_sb = sb;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = PSTOREFS_MAGIC;
sb->s_op = &pstore_ops;
sb->s_time_gran = 1;
inode = pstore_get_inode(sb, NULL, S_IFDIR | 0755, 0);
if (!inode) {
err = -ENOMEM;
goto fail;
}
/* override ramfs "dir" options so we catch unlink(2) */
inode->i_op = &pstore_dir_inode_operations;
root = d_alloc_root(inode);
sb->s_root = root;
if (!root) {
err = -ENOMEM;
goto fail;
}
pstore_get_records();
return 0;
fail:
iput(inode);
return err;
}
static int pstore_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
struct dentry *root;
root = mount_nodev(fs_type, flags, data, pstore_fill_super);
if (IS_ERR(root))
return -ENOMEM;
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
pstore_mnt = mnt;
return 0;
}
static void pstore_kill_sb(struct super_block *sb)
{
kill_litter_super(sb);
pstore_sb = NULL;
pstore_mnt = NULL;
}
static struct file_system_type pstore_fs_type = {
.name = "pstore",
.get_sb = pstore_get_sb,
.kill_sb = pstore_kill_sb,
};
static int __init init_pstore_fs(void)
{
int rc = 0;
struct kobject *pstorefs_kobj;
pstorefs_kobj = kobject_create_and_add("pstore", fs_kobj);
if (!pstorefs_kobj) {
rc = -ENOMEM;
goto done;
}
rc = sysfs_create_file(pstorefs_kobj, &pstore_kmsg_bytes_attr.attr);
if (rc)
goto done1;
rc = register_filesystem(&pstore_fs_type);
if (rc == 0)
goto done;
sysfs_remove_file(pstorefs_kobj, &pstore_kmsg_bytes_attr.attr);
done1:
kobject_put(pstorefs_kobj);
done:
return rc;
}
module_init(init_pstore_fs)
MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>");
MODULE_LICENSE("GPL");

7
fs/pstore/internal.h Normal file
View File

@ -0,0 +1,7 @@
extern void pstore_get_records(void);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
char *data, size_t size,
struct timespec time, int (*erase)(u64));
extern int pstore_is_mounted(void);
extern struct kobj_attribute pstore_kmsg_bytes_attr;

202
fs/pstore/platform.c Normal file
View File

@ -0,0 +1,202 @@
/*
* Persistent Storage - platform driver interface parts.
*
* 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/module.h>
#include <linux/pstore.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "internal.h"
/*
* pstore_lock just protects "psinfo" during
* calls to pstore_register()
*/
static DEFINE_SPINLOCK(pstore_lock);
static struct pstore_info *psinfo;
/* How much of the console log to snapshot. /sys/fs/pstore/kmsg_bytes */
static unsigned long kmsg_bytes = 10240;
static ssize_t b_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%lu\n", kmsg_bytes);
}
static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
return (sscanf(buf, "%lu", &kmsg_bytes) > 0) ? count : 0;
}
struct kobj_attribute pstore_kmsg_bytes_attr =
__ATTR(kmsg_bytes, S_IRUGO | S_IWUSR, b_show, b_store);
/* Tag each group of saved records with a sequence number */
static int oopscount;
/*
* 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,
const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
{
unsigned long s1_start, s2_start;
unsigned long l1_cpy, l2_cpy;
unsigned long size, total = 0;
char *dst;
u64 id;
int hsize, part = 1;
mutex_lock(&psinfo->buf_mutex);
oopscount++;
while (total < kmsg_bytes) {
dst = psinfo->buf;
hsize = sprintf(dst, "Oops#%d Part%d\n", oopscount, part++);
size = psinfo->bufsize - hsize;
dst += hsize;
l2_cpy = min(l2, size);
l1_cpy = min(l1, size - l2_cpy);
if (l1_cpy + l2_cpy == 0)
break;
s2_start = l2 - l2_cpy;
s1_start = l1 - l1_cpy;
memcpy(dst, s1 + s1_start, l1_cpy);
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy);
if (pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
psinfo->buf, hsize + l1_cpy + l2_cpy,
CURRENT_TIME, psinfo->erase);
l1 -= l1_cpy;
l2 -= l2_cpy;
total += l1_cpy + l2_cpy;
}
mutex_unlock(&psinfo->buf_mutex);
}
static struct kmsg_dumper pstore_dumper = {
.dump = pstore_dump,
};
/*
* 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;
spin_lock(&pstore_lock);
if (psinfo) {
spin_unlock(&pstore_lock);
return -EBUSY;
}
psinfo = psi;
spin_unlock(&pstore_lock);
if (owner && !try_module_get(owner)) {
psinfo = NULL;
return -EINVAL;
}
if (pstore_is_mounted())
pstore_get_records();
kmsg_dump_register(&pstore_dumper);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
/*
* Read all the records from the persistent store. Create and
* file files in our filesystem.
*/
void pstore_get_records(void)
{
struct pstore_info *psi = psinfo;
size_t size;
u64 id;
enum pstore_type_id type;
struct timespec time;
int failed = 0;
if (!psi)
return;
mutex_lock(&psinfo->buf_mutex);
while ((size = psi->read(&id, &type, &time)) > 0) {
if (pstore_mkfile(type, psi->name, id, psi->buf, size,
time, psi->erase))
failed++;
}
mutex_unlock(&psinfo->buf_mutex);
if (failed)
printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n",
failed, psi->name);
}
/*
* Call platform driver to write a record to the
* persistent store.
*/
int pstore_write(enum pstore_type_id type, char *buf, size_t size)
{
u64 id;
if (!psinfo)
return -ENODEV;
if (size > psinfo->bufsize)
return -EFBIG;
mutex_lock(&psinfo->buf_mutex);
memcpy(psinfo->buf, buf, size);
id = psinfo->write(type, size);
if (pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
size, CURRENT_TIME, psinfo->erase);
mutex_unlock(&psinfo->buf_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_write);

View File

@ -27,6 +27,7 @@
#define ISOFS_SUPER_MAGIC 0x9660 #define ISOFS_SUPER_MAGIC 0x9660
#define JFFS2_SUPER_MAGIC 0x72b6 #define JFFS2_SUPER_MAGIC 0x72b6
#define ANON_INODE_FS_MAGIC 0x09041934 #define ANON_INODE_FS_MAGIC 0x09041934
#define PSTOREFS_MAGIC 0x6165676C
#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ #define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ #define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */

60
include/linux/pstore.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Persistent Storage - pstore.h
*
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This code is the generic layer to export data records from platform
* level persistent storage via a file system.
*
* 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
*/
#ifndef _LINUX_PSTORE_H
#define _LINUX_PSTORE_H
/* types */
enum pstore_type_id {
PSTORE_TYPE_DMESG = 0,
PSTORE_TYPE_MCE = 1,
PSTORE_TYPE_UNKNOWN = 255
};
struct pstore_info {
struct module *owner;
char *name;
struct mutex buf_mutex; /* serialize access to 'buf' */
char *buf;
size_t bufsize;
size_t (*read)(u64 *id, enum pstore_type_id *type,
struct timespec *time);
u64 (*write)(enum pstore_type_id type, size_t size);
int (*erase)(u64 id);
};
#ifdef CONFIG_PSTORE
extern int pstore_register(struct pstore_info *);
extern int pstore_write(enum pstore_type_id type, char *buf, size_t size);
#else
static inline int
pstore_register(struct pstore_info *psi)
{
return -ENODEV;
}
static inline int
pstore_write(enum pstore_type_id type, char *buf, size_t size)
{
return -ENODEV;
}
#endif
#endif /*_LINUX_PSTORE_H*/