platform.c 7.0 KB
Newer Older
1 2 3
/*
 * Persistent Storage - platform driver interface parts.
 *
4
 * Copyright (C) 2007-2008 Google, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * 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>
26
#include <linux/console.h>
27 28 29
#include <linux/module.h>
#include <linux/pstore.h>
#include <linux/string.h>
30
#include <linux/timer.h>
31 32
#include <linux/slab.h>
#include <linux/uaccess.h>
33
#include <linux/hardirq.h>
34
#include <linux/workqueue.h>
35 36 37

#include "internal.h"

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/*
 * 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);

53 54 55 56 57 58 59
/*
 * pstore_lock just protects "psinfo" during
 * calls to pstore_register()
 */
static DEFINE_SPINLOCK(pstore_lock);
static struct pstore_info *psinfo;

60 61
static char *backend;

62
/* How much of the console log to snapshot */
63 64
static unsigned long kmsg_bytes = 10240;

65
void pstore_set_kmsg_bytes(int bytes)
66
{
67
	kmsg_bytes = bytes;
68 69 70 71 72
}

/* Tag each group of saved records with a sequence number */
static int	oopscount;

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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";
	}
}
T
Tony Luck 已提交
92

93 94 95 96 97 98 99 100 101 102 103 104 105
/*
 * 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;
106 107
	char		*dst;
	const char	*why;
108
	u64		id;
109
	int		hsize, ret;
M
Matthew Garrett 已提交
110
	unsigned int	part = 1;
111 112
	unsigned long	flags = 0;
	int		is_locked = 0;
113

114
	why = get_reason_str(reason);
T
Tony Luck 已提交
115

116 117 118 119 120 121
	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);
122 123 124
	oopscount++;
	while (total < kmsg_bytes) {
		dst = psinfo->buf;
125
		hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
		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);

141
		ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
142
				   hsize + l1_cpy + l2_cpy, psinfo);
143
		if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
144
			pstore_new_entry = 1;
145 146 147
		l1 -= l1_cpy;
		l2 -= l2_cpy;
		total += l1_cpy + l2_cpy;
148
		part++;
149
	}
150 151 152 153 154
	if (in_nmi()) {
		if (is_locked)
			spin_unlock(&psinfo->buf_lock);
	} else
		spin_unlock_irqrestore(&psinfo->buf_lock, flags);
155 156 157 158 159 160
}

static struct kmsg_dumper pstore_dumper = {
	.dump = pstore_dump,
};

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
#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;

		if (c > psinfo->bufsize)
			c = psinfo->bufsize;
		spin_lock_irqsave(&psinfo->buf_lock, flags);
		memcpy(psinfo->buf, s, c);
		psinfo->write(PSTORE_TYPE_CONSOLE, 0, NULL, 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

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
/*
 * 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;
	}
213 214 215 216 217 218

	if (backend && strcmp(backend, psi->name)) {
		spin_unlock(&pstore_lock);
		return -EINVAL;
	}

219
	psinfo = psi;
220
	mutex_init(&psinfo->read_mutex);
221 222 223 224 225 226 227 228
	spin_unlock(&pstore_lock);

	if (owner && !try_module_get(owner)) {
		psinfo = NULL;
		return -EINVAL;
	}

	if (pstore_is_mounted())
229
		pstore_get_records(0);
230 231

	kmsg_dump_register(&pstore_dumper);
232
	pstore_register_console();
233

234 235 236
	pstore_timer.expires = jiffies + PSTORE_INTERVAL;
	add_timer(&pstore_timer);

237 238 239 240 241
	return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);

/*
242 243 244 245
 * 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.
246
 */
247
void pstore_get_records(int quiet)
248 249
{
	struct pstore_info *psi = psinfo;
250
	char			*buf = NULL;
251
	ssize_t			size;
252 253 254
	u64			id;
	enum pstore_type_id	type;
	struct timespec		time;
255
	int			failed = 0, rc;
256 257 258 259

	if (!psi)
		return;

260
	mutex_lock(&psi->read_mutex);
261
	if (psi->open && psi->open(psi))
262 263
		goto out;

264 265
	while ((size = psi->read(&id, &type, &time, &buf, psi)) > 0) {
		rc = pstore_mkfile(type, psi->name, id, buf, (size_t)size,
266
				  time, psi);
267 268
		kfree(buf);
		buf = NULL;
269
		if (rc && (rc != -EEXIST || !quiet))
270 271
			failed++;
	}
272 273
	if (psi->close)
		psi->close(psi);
274
out:
275
	mutex_unlock(&psi->read_mutex);
276 277 278 279 280 281

	if (failed)
		printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n",
		       failed, psi->name);
}

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
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);
}

297 298
module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");