Blame view
fs/pstore/platform.c
5.97 KB
ca01d6dd2 pstore: new files... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * 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> |
6dda92669 pstore: defer ins... |
28 |
#include <linux/timer.h> |
ca01d6dd2 pstore: new files... |
29 30 |
#include <linux/slab.h> #include <linux/uaccess.h> |
abd4d5587 pstore: change mu... |
31 |
#include <linux/hardirq.h> |
6dda92669 pstore: defer ins... |
32 |
#include <linux/workqueue.h> |
ca01d6dd2 pstore: new files... |
33 34 35 36 |
#include "internal.h" /* |
6dda92669 pstore: defer ins... |
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
* 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); /* |
ca01d6dd2 pstore: new files... |
52 53 54 55 56 |
* pstore_lock just protects "psinfo" during * calls to pstore_register() */ static DEFINE_SPINLOCK(pstore_lock); static struct pstore_info *psinfo; |
dee28e72b pstore: Allow the... |
57 |
static char *backend; |
366f7e7a7 pstore: use mount... |
58 |
/* How much of the console log to snapshot */ |
ca01d6dd2 pstore: new files... |
59 |
static unsigned long kmsg_bytes = 10240; |
366f7e7a7 pstore: use mount... |
60 |
void pstore_set_kmsg_bytes(int bytes) |
ca01d6dd2 pstore: new files... |
61 |
{ |
366f7e7a7 pstore: use mount... |
62 |
kmsg_bytes = bytes; |
ca01d6dd2 pstore: new files... |
63 |
} |
ca01d6dd2 pstore: new files... |
64 65 |
/* Tag each group of saved records with a sequence number */ static int oopscount; |
9f6af27fb pstore: cleanups ... |
66 67 68 |
static char *reason_str[] = { "Oops", "Panic", "Kexec", "Restart", "Halt", "Poweroff", "Emergency" }; |
ca01d6dd2 pstore: new files... |
69 70 71 72 73 74 75 76 77 78 79 80 81 |
/* * 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; |
9f6af27fb pstore: cleanups ... |
82 |
char *dst, *why; |
ca01d6dd2 pstore: new files... |
83 |
u64 id; |
b238b8fa9 pstore: make psto... |
84 |
int hsize, ret; |
b94fdd077 pstore: Make "par... |
85 |
unsigned int part = 1; |
abd4d5587 pstore: change mu... |
86 87 |
unsigned long flags = 0; int is_locked = 0; |
ca01d6dd2 pstore: new files... |
88 |
|
9f6af27fb pstore: cleanups ... |
89 90 91 92 |
if (reason < ARRAY_SIZE(reason_str)) why = reason_str[reason]; else why = "Unknown"; |
abd4d5587 pstore: change mu... |
93 94 95 96 97 98 99 |
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 "); } else spin_lock_irqsave(&psinfo->buf_lock, flags); |
ca01d6dd2 pstore: new files... |
100 101 102 |
oopscount++; while (total < kmsg_bytes) { dst = psinfo->buf; |
56280682c pstore: Add extra... |
103 104 |
hsize = sprintf(dst, "%s#%d Part%d ", why, oopscount, part); |
ca01d6dd2 pstore: new files... |
105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
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); |
3d6d8d20e pstore: pass reas... |
119 |
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, |
56280682c pstore: Add extra... |
120 |
hsize + l1_cpy + l2_cpy, psinfo); |
b238b8fa9 pstore: make psto... |
121 |
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) |
6dda92669 pstore: defer ins... |
122 |
pstore_new_entry = 1; |
ca01d6dd2 pstore: new files... |
123 124 125 |
l1 -= l1_cpy; l2 -= l2_cpy; total += l1_cpy + l2_cpy; |
56280682c pstore: Add extra... |
126 |
part++; |
ca01d6dd2 pstore: new files... |
127 |
} |
abd4d5587 pstore: change mu... |
128 129 130 131 132 |
if (in_nmi()) { if (is_locked) spin_unlock(&psinfo->buf_lock); } else spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
ca01d6dd2 pstore: new files... |
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
} 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; } |
dee28e72b pstore: Allow the... |
157 158 159 160 161 |
if (backend && strcmp(backend, psi->name)) { spin_unlock(&pstore_lock); return -EINVAL; } |
ca01d6dd2 pstore: new files... |
162 |
psinfo = psi; |
f6f828513 pstore: pass allo... |
163 |
mutex_init(&psinfo->read_mutex); |
ca01d6dd2 pstore: new files... |
164 165 166 167 168 169 170 171 |
spin_unlock(&pstore_lock); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } if (pstore_is_mounted()) |
6dda92669 pstore: defer ins... |
172 |
pstore_get_records(0); |
ca01d6dd2 pstore: new files... |
173 174 |
kmsg_dump_register(&pstore_dumper); |
6dda92669 pstore: defer ins... |
175 176 |
pstore_timer.expires = jiffies + PSTORE_INTERVAL; add_timer(&pstore_timer); |
ca01d6dd2 pstore: new files... |
177 178 179 180 181 |
return 0; } EXPORT_SYMBOL_GPL(pstore_register); /* |
6dda92669 pstore: defer ins... |
182 183 184 185 |
* 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. |
ca01d6dd2 pstore: new files... |
186 |
*/ |
6dda92669 pstore: defer ins... |
187 |
void pstore_get_records(int quiet) |
ca01d6dd2 pstore: new files... |
188 189 |
{ struct pstore_info *psi = psinfo; |
f6f828513 pstore: pass allo... |
190 |
char *buf = NULL; |
8d38d74b6 pstore: fix one t... |
191 |
ssize_t size; |
ca01d6dd2 pstore: new files... |
192 193 194 |
u64 id; enum pstore_type_id type; struct timespec time; |
06cf91b4b pstore: fix pstor... |
195 |
int failed = 0, rc; |
ca01d6dd2 pstore: new files... |
196 197 198 |
if (!psi) return; |
f6f828513 pstore: pass allo... |
199 |
mutex_lock(&psi->read_mutex); |
2174f6df7 pstore: gracefull... |
200 |
if (psi->open && psi->open(psi)) |
06cf91b4b pstore: fix pstor... |
201 |
goto out; |
f6f828513 pstore: pass allo... |
202 203 |
while ((size = psi->read(&id, &type, &time, &buf, psi)) > 0) { rc = pstore_mkfile(type, psi->name, id, buf, (size_t)size, |
6dda92669 pstore: defer ins... |
204 |
time, psi); |
f6f828513 pstore: pass allo... |
205 206 |
kfree(buf); buf = NULL; |
6dda92669 pstore: defer ins... |
207 |
if (rc && (rc != -EEXIST || !quiet)) |
ca01d6dd2 pstore: new files... |
208 209 |
failed++; } |
2174f6df7 pstore: gracefull... |
210 211 |
if (psi->close) psi->close(psi); |
06cf91b4b pstore: fix pstor... |
212 |
out: |
f6f828513 pstore: pass allo... |
213 |
mutex_unlock(&psi->read_mutex); |
ca01d6dd2 pstore: new files... |
214 215 216 217 218 219 |
if (failed) printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s' ", failed, psi->name); } |
6dda92669 pstore: defer ins... |
220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
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); } |
dee28e72b pstore: Allow the... |
234 235 |
module_param(backend, charp, 0444); MODULE_PARM_DESC(backend, "Pstore backend to use"); |