Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c398651 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -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 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
	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 -
+10 −9
Original line number Diff line number Diff line
@@ -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);
	}

+36 −4
Original line number Diff line number Diff line
@@ -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>
@@ -32,13 +33,18 @@
#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:
+1 −1
Original line number Diff line number Diff line
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);
+72 −21
Original line number Diff line number Diff line
@@ -25,11 +25,29 @@
#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,29 +200,46 @@ 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.
@@ -198,6 +247,8 @@ void pstore_get_records(void)
int pstore_write(enum pstore_type_id type, char *buf, size_t size)
{
	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;
}
Loading