Merge branch 'pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 1 Nov 2011 17:52:29 +0000 (10:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 1 Nov 2011 17:52:29 +0000 (10:52 -0700)
* '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

drivers/acpi/apei/erst.c
drivers/firmware/efivars.c
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
include/linux/pstore.h

index 2ca59dc..1274080 100644 (file)
@@ -933,7 +933,7 @@ static int erst_open_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,
                           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);
 static int erst_clearer(enum pstore_type_id type, u64 id,
                        struct pstore_info *psi);
@@ -1040,11 +1040,12 @@ out:
        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)
 {
        struct cper_pstore_record *rcd = (struct cper_pstore_record *)
                                        (erst_info.buf - sizeof(*rcd));
+       int ret;
 
        memset(rcd, 0, sizeof(*rcd));
        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,
        }
        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,
@@ -1165,7 +1167,7 @@ static int __init erst_init(void)
                goto err_release_erange;
 
        buf = kmalloc(erst_erange.size, GFP_KERNEL);
-       mutex_init(&erst_info.buf_mutex);
+       spin_lock_init(&erst_info.buf_lock);
        if (buf) {
                erst_info.buf = buf + sizeof(struct cper_pstore_record);
                erst_info.bufsize = erst_erange.size -
index eb80b54..8370f72 100644 (file)
@@ -490,8 +490,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
        return 0;
 }
 
-static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
-                           size_t size, struct pstore_info *psi)
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+               unsigned int part, size_t size, struct pstore_info *psi)
 {
        char 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,
        efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
        struct efivars *efivars = psi->data;
        struct efivar_entry *entry, *found = NULL;
-       int i;
+       int i, ret = 0;
 
        sprintf(stub_name, "dump-type%u-%u-", type, part);
        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,
                efivar_unregister(found);
 
        if (size)
-               efivar_create_sysfs_entry(efivars,
+               ret = efivar_create_sysfs_entry(efivars,
                                          utf16_strsize(efi_name,
                                                        DUMP_NAME_LEN * 2),
                                          efi_name, &vendor);
 
-       return part;
+       *id = part;
+       return ret;
 };
 
 static int efi_pstore_erase(enum pstore_type_id type, u64 id,
                            struct pstore_info *psi)
 {
-       efi_pstore_write(type, id, 0, psi);
+       efi_pstore_write(type, &id, (unsigned int)id, 0, psi);
 
        return 0;
 }
@@ -580,8 +581,8 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
        return -1;
 }
 
-static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
-                           size_t size, struct pstore_info *psi)
+static int efi_pstore_write(enum pstore_type_id type, u64 *id,
+               unsigned int part, size_t size, struct pstore_info *psi)
 {
        return 0;
 }
@@ -978,7 +979,7 @@ int register_efivars(struct efivars *efivars,
        if (efivars->efi_pstore_info.buf) {
                efivars->efi_pstore_info.bufsize = 1024;
                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);
        }
 
index 893b961..379a02d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/highmem.h>
 #include <linux/time.h>
 #include <linux/init.h>
+#include <linux/list.h>
 #include <linux/string.h>
 #include <linux/mount.h>
 #include <linux/ramfs.h>
 #include <linux/magic.h>
 #include <linux/pstore.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <linux/uaccess.h>
 
 #include "internal.h"
 
 #define        PSTORE_NAMELEN  64
 
+static DEFINE_SPINLOCK(allpstore_lock);
+static LIST_HEAD(allpstore);
+
 struct pstore_private {
+       struct list_head list;
        struct pstore_info *psi;
        enum pstore_type_id type;
        u64     id;
@@ -81,8 +87,16 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
 
 static void pstore_evict_inode(struct inode *inode)
 {
+       struct pstore_private   *p = inode->i_private;
+       unsigned long           flags;
+
        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 = {
@@ -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           *dentry;
        struct inode            *inode;
-       int                     rc;
+       int                     rc = 0;
        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;
        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,
 
        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);
 
        return 0;
@@ -277,7 +309,7 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent)
                goto fail;
        }
 
-       pstore_get_records();
+       pstore_get_records(0);
 
        return 0;
 fail:
index 611c1b3..3bde461 100644 (file)
@@ -1,5 +1,5 @@
 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,
                              char *data, size_t size,
                              struct timespec time, struct pstore_info *psi);
index c5300ec..2bd620f 100644 (file)
 #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/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
+ */
+#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
  * calls to pstore_register()
@@ -69,15 +87,22 @@ static void pstore_dump(struct kmsg_dumper *dumper,
        unsigned long   size, total = 0;
        char            *dst, *why;
        u64             id;
-       int             hsize;
+       int             hsize, ret;
        unsigned int    part = 1;
+       unsigned long   flags = 0;
+       int             is_locked = 0;
 
        if (reason < ARRAY_SIZE(reason_str))
                why = reason_str[reason];
        else
                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++;
        while (total < kmsg_bytes) {
                dst = psinfo->buf;
@@ -97,18 +122,20 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                memcpy(dst, s1 + s1_start, l1_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);
-               if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
-                       pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
-                                     psinfo->buf, hsize + l1_cpy + l2_cpy,
-                                     CURRENT_TIME, psinfo);
+               if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
+                       pstore_new_entry = 1;
                l1 -= l1_cpy;
                l2 -= l2_cpy;
                total += l1_cpy + l2_cpy;
                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 = {
@@ -148,19 +175,24 @@ int pstore_register(struct pstore_info *psi)
        }
 
        if (pstore_is_mounted())
-               pstore_get_records();
+               pstore_get_records(0);
 
        kmsg_dump_register(&pstore_dumper);
 
+       pstore_timer.expires = jiffies + PSTORE_INTERVAL;
+       add_timer(&pstore_timer);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(pstore_register);
 
 /*
- * Read all the records from the persistent store. Create and
- * file files in our filesystem.
+ * 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(void)
+void pstore_get_records(int quiet)
 {
        struct pstore_info *psi = psinfo;
        ssize_t                 size;
@@ -168,36 +200,55 @@ void pstore_get_records(void)
        enum pstore_type_id     type;
        struct timespec         time;
        int                     failed = 0, rc;
+       unsigned long           flags;
 
        if (!psi)
                return;
 
-       mutex_lock(&psinfo->buf_mutex);
+       spin_lock_irqsave(&psinfo->buf_lock, flags);
        rc = psi->open(psi);
        if (rc)
                goto out;
 
        while ((size = psi->read(&id, &type, &time, psi)) > 0) {
-               if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
-                                 time, psi))
+               rc = pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
+                                 time, psi);
+               if (rc && (rc != -EEXIST || !quiet))
                        failed++;
        }
        psi->close(psi);
 out:
-       mutex_unlock(&psinfo->buf_mutex);
+       spin_unlock_irqrestore(&psinfo->buf_lock, flags);
 
        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 + PSTORE_INTERVAL);
+}
+
 /*
  * 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;
+       u64             id;
+       int             ret;
+       unsigned long   flags;
 
        if (!psinfo)
                return -ENODEV;
@@ -205,13 +256,13 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size)
        if (size > psinfo->bufsize)
                return -EFBIG;
 
-       mutex_lock(&psinfo->buf_mutex);
+       spin_lock_irqsave(&psinfo->buf_lock, flags);
        memcpy(psinfo->buf, buf, size);
-       id = psinfo->write(type, 0, size, psinfo);
-       if (pstore_is_mounted())
+       ret = psinfo->write(type, &id, 0, size, psinfo);
+       if (ret == 0 && pstore_is_mounted())
                pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
                              size, CURRENT_TIME, psinfo);
-       mutex_unlock(&psinfo->buf_mutex);
+       spin_unlock_irqrestore(&psinfo->buf_lock, flags);
 
        return 0;
 }
index cc03bbf..ea56732 100644 (file)
@@ -32,15 +32,15 @@ enum pstore_type_id {
 struct pstore_info {
        struct module   *owner;
        char            *name;
-       struct mutex    buf_mutex;      /* serialize access to 'buf' */
+       spinlock_t      buf_lock;       /* serialize access to 'buf' */
        char            *buf;
        size_t          bufsize;
        int             (*open)(struct pstore_info *psi);
        int             (*close)(struct pstore_info *psi);
        ssize_t         (*read)(u64 *id, enum pstore_type_id *type,
                        struct timespec *time, struct pstore_info *psi);
-       u64             (*write)(enum pstore_type_id type, unsigned int part,
-                       size_t size, struct pstore_info *psi);
+       int             (*write)(enum pstore_type_id type, u64 *id,
+                       unsigned int part, size_t size, struct pstore_info *psi);
        int             (*erase)(enum pstore_type_id type, u64 id,
                        struct pstore_info *psi);
        void            *data;