Commit 1c398651 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux

* 'pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  pstore: make pstore write function return normal success/fail value
  pstore: change mutex locking to spin_locks
  pstore: defer inserting OOPS entries into pstore
parents f470f8d4 b238b8fa
...@@ -933,7 +933,7 @@ static int erst_open_pstore(struct pstore_info *psi); ...@@ -933,7 +933,7 @@ static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time, struct pstore_info *psi); struct timespec *time, struct pstore_info *psi);
static u64 erst_writer(enum pstore_type_id type, unsigned int part, static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
size_t size, struct pstore_info *psi); size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id, static int erst_clearer(enum pstore_type_id type, u64 id,
struct pstore_info *psi); struct pstore_info *psi);
...@@ -1040,11 +1040,12 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, ...@@ -1040,11 +1040,12 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
return (rc < 0) ? rc : (len - sizeof(*rcd)); return (rc < 0) ? rc : (len - sizeof(*rcd));
} }
static u64 erst_writer(enum pstore_type_id type, unsigned int part, static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part,
size_t size, struct pstore_info *psi) size_t size, struct pstore_info *psi)
{ {
struct cper_pstore_record *rcd = (struct cper_pstore_record *) struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd)); (erst_info.buf - sizeof(*rcd));
int ret;
memset(rcd, 0, sizeof(*rcd)); memset(rcd, 0, sizeof(*rcd));
memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
...@@ -1079,9 +1080,10 @@ static u64 erst_writer(enum pstore_type_id type, unsigned int part, ...@@ -1079,9 +1080,10 @@ static u64 erst_writer(enum pstore_type_id type, unsigned int part,
} }
rcd->sec_hdr.section_severity = CPER_SEV_FATAL; rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
erst_write(&rcd->hdr); ret = erst_write(&rcd->hdr);
*id = rcd->hdr.record_id;
return rcd->hdr.record_id; return ret;
} }
static int erst_clearer(enum pstore_type_id type, u64 id, static int erst_clearer(enum pstore_type_id type, u64 id,
...@@ -1165,7 +1167,7 @@ static int __init erst_init(void) ...@@ -1165,7 +1167,7 @@ static int __init erst_init(void)
goto err_release_erange; goto err_release_erange;
buf = kmalloc(erst_erange.size, GFP_KERNEL); buf = kmalloc(erst_erange.size, GFP_KERNEL);
mutex_init(&erst_info.buf_mutex); spin_lock_init(&erst_info.buf_lock);
if (buf) { if (buf) {
erst_info.buf = buf + sizeof(struct cper_pstore_record); erst_info.buf = buf + sizeof(struct cper_pstore_record);
erst_info.bufsize = erst_erange.size - erst_info.bufsize = erst_erange.size -
......
...@@ -490,8 +490,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -490,8 +490,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
return 0; return 0;
} }
static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part, static int efi_pstore_write(enum pstore_type_id type, u64 *id,
size_t size, struct pstore_info *psi) unsigned int part, size_t size, struct pstore_info *psi)
{ {
char name[DUMP_NAME_LEN]; char name[DUMP_NAME_LEN];
char stub_name[DUMP_NAME_LEN]; char stub_name[DUMP_NAME_LEN];
...@@ -499,7 +499,7 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part, ...@@ -499,7 +499,7 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct efivars *efivars = psi->data; struct efivars *efivars = psi->data;
struct efivar_entry *entry, *found = NULL; struct efivar_entry *entry, *found = NULL;
int i; int i, ret = 0;
sprintf(stub_name, "dump-type%u-%u-", type, part); sprintf(stub_name, "dump-type%u-%u-", type, part);
sprintf(name, "%s%lu", stub_name, get_seconds()); sprintf(name, "%s%lu", stub_name, get_seconds());
...@@ -548,18 +548,19 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part, ...@@ -548,18 +548,19 @@ static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
efivar_unregister(found); efivar_unregister(found);
if (size) if (size)
efivar_create_sysfs_entry(efivars, ret = efivar_create_sysfs_entry(efivars,
utf16_strsize(efi_name, utf16_strsize(efi_name,
DUMP_NAME_LEN * 2), DUMP_NAME_LEN * 2),
efi_name, &vendor); efi_name, &vendor);
return part; *id = part;
return ret;
}; };
static int efi_pstore_erase(enum pstore_type_id type, u64 id, static int efi_pstore_erase(enum pstore_type_id type, u64 id,
struct pstore_info *psi) struct pstore_info *psi)
{ {
efi_pstore_write(type, id, 0, psi); efi_pstore_write(type, &id, (unsigned int)id, 0, psi);
return 0; return 0;
} }
...@@ -580,8 +581,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -580,8 +581,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
return -1; return -1;
} }
static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part, static int efi_pstore_write(enum pstore_type_id type, u64 *id,
size_t size, struct pstore_info *psi) unsigned int part, size_t size, struct pstore_info *psi)
{ {
return 0; return 0;
} }
...@@ -978,7 +979,7 @@ int register_efivars(struct efivars *efivars, ...@@ -978,7 +979,7 @@ int register_efivars(struct efivars *efivars,
if (efivars->efi_pstore_info.buf) { if (efivars->efi_pstore_info.buf) {
efivars->efi_pstore_info.bufsize = 1024; efivars->efi_pstore_info.bufsize = 1024;
efivars->efi_pstore_info.data = efivars; efivars->efi_pstore_info.data = efivars;
mutex_init(&efivars->efi_pstore_info.buf_mutex); spin_lock_init(&efivars->efi_pstore_info.buf_lock);
pstore_register(&efivars->efi_pstore_info); pstore_register(&efivars->efi_pstore_info);
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/ramfs.h> #include <linux/ramfs.h>
...@@ -32,13 +33,18 @@ ...@@ -32,13 +33,18 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "internal.h" #include "internal.h"
#define PSTORE_NAMELEN 64 #define PSTORE_NAMELEN 64
static DEFINE_SPINLOCK(allpstore_lock);
static LIST_HEAD(allpstore);
struct pstore_private { struct pstore_private {
struct list_head list;
struct pstore_info *psi; struct pstore_info *psi;
enum pstore_type_id type; enum pstore_type_id type;
u64 id; u64 id;
...@@ -81,8 +87,16 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry) ...@@ -81,8 +87,16 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
static void pstore_evict_inode(struct inode *inode) static void pstore_evict_inode(struct inode *inode)
{ {
struct pstore_private *p = inode->i_private;
unsigned long flags;
end_writeback(inode); end_writeback(inode);
kfree(inode->i_private); if (p) {
spin_lock_irqsave(&allpstore_lock, flags);
list_del(&p->list);
spin_unlock_irqrestore(&allpstore_lock, flags);
kfree(p);
}
} }
static const struct inode_operations pstore_dir_inode_operations = { static const struct inode_operations pstore_dir_inode_operations = {
...@@ -182,9 +196,23 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, ...@@ -182,9 +196,23 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
struct dentry *root = pstore_sb->s_root; struct dentry *root = pstore_sb->s_root;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
int rc; int rc = 0;
char name[PSTORE_NAMELEN]; char name[PSTORE_NAMELEN];
struct pstore_private *private; struct pstore_private *private, *pos;
unsigned long flags;
spin_lock_irqsave(&allpstore_lock, flags);
list_for_each_entry(pos, &allpstore, list) {
if (pos->type == type &&
pos->id == id &&
pos->psi == psi) {
rc = -EEXIST;
break;
}
}
spin_unlock_irqrestore(&allpstore_lock, flags);
if (rc)
return rc;
rc = -ENOMEM; rc = -ENOMEM;
inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0); inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0);
...@@ -229,6 +257,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, ...@@ -229,6 +257,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
d_add(dentry, inode); d_add(dentry, inode);
spin_lock_irqsave(&allpstore_lock, flags);
list_add(&private->list, &allpstore);
spin_unlock_irqrestore(&allpstore_lock, flags);
mutex_unlock(&root->d_inode->i_mutex); mutex_unlock(&root->d_inode->i_mutex);
return 0; return 0;
...@@ -277,7 +309,7 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent) ...@@ -277,7 +309,7 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent)
goto fail; goto fail;
} }
pstore_get_records(); pstore_get_records(0);
return 0; return 0;
fail: fail:
......
extern void pstore_set_kmsg_bytes(int); extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(void); extern void pstore_get_records(int);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
char *data, size_t size, char *data, size_t size,
struct timespec time, struct pstore_info *psi); struct timespec time, struct pstore_info *psi);
......
...@@ -25,11 +25,29 @@ ...@@ -25,11 +25,29 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pstore.h> #include <linux/pstore.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/timer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/hardirq.h>
#include <linux/workqueue.h>
#include "internal.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
*/
#define PSTORE_INTERVAL (60 * HZ)
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 * pstore_lock just protects "psinfo" during
* calls to pstore_register() * calls to pstore_register()
...@@ -69,15 +87,22 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -69,15 +87,22 @@ static void pstore_dump(struct kmsg_dumper *dumper,
unsigned long size, total = 0; unsigned long size, total = 0;
char *dst, *why; char *dst, *why;
u64 id; u64 id;
int hsize; int hsize, ret;
unsigned int part = 1; unsigned int part = 1;
unsigned long flags = 0;
int is_locked = 0;
if (reason < ARRAY_SIZE(reason_str)) if (reason < ARRAY_SIZE(reason_str))
why = reason_str[reason]; why = reason_str[reason];
else else
why = "Unknown"; why = "Unknown";
mutex_lock(&psinfo->buf_mutex); if (in_nmi()) {
is_locked = spin_trylock(&psinfo->buf_lock);
if (!is_locked)
pr_err("pstore dump routine blocked in NMI, may corrupt error record\n");
} else
spin_lock_irqsave(&psinfo->buf_lock, flags);
oopscount++; oopscount++;
while (total < kmsg_bytes) { while (total < kmsg_bytes) {
dst = psinfo->buf; dst = psinfo->buf;
...@@ -97,18 +122,20 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -97,18 +122,20 @@ static void pstore_dump(struct kmsg_dumper *dumper,
memcpy(dst, s1 + s1_start, l1_cpy); memcpy(dst, s1 + s1_start, l1_cpy);
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
id = psinfo->write(PSTORE_TYPE_DMESG, part, ret = psinfo->write(PSTORE_TYPE_DMESG, &id, part,
hsize + l1_cpy + l2_cpy, psinfo); hsize + l1_cpy + l2_cpy, psinfo);
if (reason == KMSG_DUMP_OOPS && pstore_is_mounted()) if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, pstore_new_entry = 1;
psinfo->buf, hsize + l1_cpy + l2_cpy,
CURRENT_TIME, psinfo);
l1 -= l1_cpy; l1 -= l1_cpy;
l2 -= l2_cpy; l2 -= l2_cpy;
total += l1_cpy + l2_cpy; total += l1_cpy + l2_cpy;
part++; part++;
} }
mutex_unlock(&psinfo->buf_mutex); if (in_nmi()) {
if (is_locked)
spin_unlock(&psinfo->buf_lock);
} else
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
} }
static struct kmsg_dumper pstore_dumper = { static struct kmsg_dumper pstore_dumper = {
...@@ -148,19 +175,24 @@ int pstore_register(struct pstore_info *psi) ...@@ -148,19 +175,24 @@ int pstore_register(struct pstore_info *psi)
} }
if (pstore_is_mounted()) if (pstore_is_mounted())
pstore_get_records(); pstore_get_records(0);
kmsg_dump_register(&pstore_dumper); kmsg_dump_register(&pstore_dumper);
pstore_timer.expires = jiffies + PSTORE_INTERVAL;
add_timer(&pstore_timer);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(pstore_register); EXPORT_SYMBOL_GPL(pstore_register);
/* /*
* Read all the records from the persistent store. Create and * Read all the records from the persistent store. Create
* file files in our filesystem. * 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(void) void pstore_get_records(int quiet)
{ {
struct pstore_info *psi = psinfo; struct pstore_info *psi = psinfo;
ssize_t size; ssize_t size;
...@@ -168,36 +200,55 @@ void pstore_get_records(void) ...@@ -168,36 +200,55 @@ void pstore_get_records(void)
enum pstore_type_id type; enum pstore_type_id type;
struct timespec time; struct timespec time;
int failed = 0, rc; int failed = 0, rc;
unsigned long flags;
if (!psi) if (!psi)
return; return;
mutex_lock(&psinfo->buf_mutex); spin_lock_irqsave(&psinfo->buf_lock, flags);
rc = psi->open(psi); rc = psi->open(psi);
if (rc) if (rc)
goto out; goto out;
while ((size = psi->read(&id, &type, &time, psi)) > 0) { while ((size = psi->read(&id, &type, &time, psi)) > 0) {
if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size, rc = pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
time, psi)) time, psi);
if (rc && (rc != -EEXIST || !quiet))
failed++; failed++;
} }
psi->close(psi); psi->close(psi);
out: out:
mutex_unlock(&psinfo->buf_mutex); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
if (failed) if (failed)
printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n",
failed, psi->name); 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 + PSTORE_INTERVAL);
}
/* /*
* Call platform driver to write a record to the * Call platform driver to write a record to the
* persistent store. * persistent store.
*/ */
int pstore_write(enum pstore_type_id type, char *buf, size_t size) int pstore_write(enum pstore_type_id type, char *buf, size_t size)
{ {
u64 id; u64 id;
int ret;
unsigned long flags;
if (!psinfo) if (!psinfo)
return -ENODEV; return -ENODEV;
...@@ -205,13 +256,13 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size) ...@@ -205,13 +256,13 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size)
if (size > psinfo->bufsize) if (size > psinfo->bufsize)
return -EFBIG; return -EFBIG;
mutex_lock(&psinfo->buf_mutex); spin_lock_irqsave(&psinfo->buf_lock, flags);
memcpy(psinfo->buf, buf, size); memcpy(psinfo->buf, buf, size);
id = psinfo->write(type, 0, size, psinfo); ret = psinfo->write(type, &id, 0, size, psinfo);
if (pstore_is_mounted()) if (ret == 0 && pstore_is_mounted())
pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
size, CURRENT_TIME, psinfo); size, CURRENT_TIME, psinfo);
mutex_unlock(&psinfo->buf_mutex); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
return 0; return 0;
} }
......
...@@ -32,15 +32,15 @@ enum pstore_type_id { ...@@ -32,15 +32,15 @@ enum pstore_type_id {
struct pstore_info { struct pstore_info {
struct module *owner; struct module *owner;
char *name; char *name;
struct mutex buf_mutex; /* serialize access to 'buf' */ spinlock_t buf_lock; /* serialize access to 'buf' */
char *buf; char *buf;
size_t bufsize; size_t bufsize;
int (*open)(struct pstore_info *psi); int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi); int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type, ssize_t (*read)(u64 *id, enum pstore_type_id *type,
struct timespec *time, struct pstore_info *psi); struct timespec *time, struct pstore_info *psi);
u64 (*write)(enum pstore_type_id type, unsigned int part, int (*write)(enum pstore_type_id type, u64 *id,
size_t size, struct pstore_info *psi); unsigned int part, size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id, int (*erase)(enum pstore_type_id type, u64 id,
struct pstore_info *psi); struct pstore_info *psi);
void *data; void *data;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment