aboutsummaryrefslogtreecommitdiffstats
path: root/fs/pstore
diff options
context:
space:
mode:
authorLuck, Tony <tony.luck@intel.com>2011-08-11 18:14:39 -0400
committerTony Luck <tony.luck@intel.com>2011-08-16 14:53:01 -0400
commit6dda9266913ad57e09afc1a10d6473f10c806a63 (patch)
treec29258f6b3fd8a9bb080b395157f013f32c0f313 /fs/pstore
parent93ee7a9340d64f20295aacc3fb6a22b759323280 (diff)
pstore: defer inserting OOPS entries into pstore
Life is simple for all the kernel terminating types of kmsg_dump call backs - pstore just saves the tail end of the console log. But for "oops" the situation is more complex - the kernel may carry on running (possibly for ever). So we'd like to make the logged copy of the oops appear in the pstore filesystem - so that the user has a handle to clear the entry from the persistent backing store (if we don't, the store may fill with "oops" entries (that are also safely stashed in /var/log/messages) leaving no space for real errors. Current code calls pstore_mkfile() immediately. But this may not be safe. The oops could have happened with arbitrary locks held, or in interrupt or NMI context. So allocating memory and calling into generic filesystem code seems unwise. This patch defers making the entry appear. At the time of the oops, we merely set a flag "pstore_new_entry" noting that a new entry has been added. A periodic timer checks once a minute to see if the flag is set - if so, it schedules a work queue to rescan the backing store and make all new entries appear in the pstore filesystem. Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'fs/pstore')
-rw-r--r--fs/pstore/inode.c40
-rw-r--r--fs/pstore/internal.h2
-rw-r--r--fs/pstore/platform.c54
3 files changed, 82 insertions, 14 deletions
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 893b961dcfd8..379a02dc1217 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -24,6 +24,7 @@
24#include <linux/highmem.h> 24#include <linux/highmem.h>
25#include <linux/time.h> 25#include <linux/time.h>
26#include <linux/init.h> 26#include <linux/init.h>
27#include <linux/list.h>
27#include <linux/string.h> 28#include <linux/string.h>
28#include <linux/mount.h> 29#include <linux/mount.h>
29#include <linux/ramfs.h> 30#include <linux/ramfs.h>
@@ -32,13 +33,18 @@
32#include <linux/magic.h> 33#include <linux/magic.h>
33#include <linux/pstore.h> 34#include <linux/pstore.h>
34#include <linux/slab.h> 35#include <linux/slab.h>
36#include <linux/spinlock.h>
35#include <linux/uaccess.h> 37#include <linux/uaccess.h>
36 38
37#include "internal.h" 39#include "internal.h"
38 40
39#define PSTORE_NAMELEN 64 41#define PSTORE_NAMELEN 64
40 42
43static DEFINE_SPINLOCK(allpstore_lock);
44static LIST_HEAD(allpstore);
45
41struct pstore_private { 46struct pstore_private {
47 struct list_head list;
42 struct pstore_info *psi; 48 struct pstore_info *psi;
43 enum pstore_type_id type; 49 enum pstore_type_id type;
44 u64 id; 50 u64 id;
@@ -81,8 +87,16 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
81 87
82static void pstore_evict_inode(struct inode *inode) 88static void pstore_evict_inode(struct inode *inode)
83{ 89{
90 struct pstore_private *p = inode->i_private;
91 unsigned long flags;
92
84 end_writeback(inode); 93 end_writeback(inode);
85 kfree(inode->i_private); 94 if (p) {
95 spin_lock_irqsave(&allpstore_lock, flags);
96 list_del(&p->list);
97 spin_unlock_irqrestore(&allpstore_lock, flags);
98 kfree(p);
99 }
86} 100}
87 101
88static const struct inode_operations pstore_dir_inode_operations = { 102static 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 struct dentry *root = pstore_sb->s_root; 196 struct dentry *root = pstore_sb->s_root;
183 struct dentry *dentry; 197 struct dentry *dentry;
184 struct inode *inode; 198 struct inode *inode;
185 int rc; 199 int rc = 0;
186 char name[PSTORE_NAMELEN]; 200 char name[PSTORE_NAMELEN];
187 struct pstore_private *private; 201 struct pstore_private *private, *pos;
202 unsigned long flags;
203
204 spin_lock_irqsave(&allpstore_lock, flags);
205 list_for_each_entry(pos, &allpstore, list) {
206 if (pos->type == type &&
207 pos->id == id &&
208 pos->psi == psi) {
209 rc = -EEXIST;
210 break;
211 }
212 }
213 spin_unlock_irqrestore(&allpstore_lock, flags);
214 if (rc)
215 return rc;
188 216
189 rc = -ENOMEM; 217 rc = -ENOMEM;
190 inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0); 218 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 257
230 d_add(dentry, inode); 258 d_add(dentry, inode);
231 259
260 spin_lock_irqsave(&allpstore_lock, flags);
261 list_add(&private->list, &allpstore);
262 spin_unlock_irqrestore(&allpstore_lock, flags);
263
232 mutex_unlock(&root->d_inode->i_mutex); 264 mutex_unlock(&root->d_inode->i_mutex);
233 265
234 return 0; 266 return 0;
@@ -277,7 +309,7 @@ int pstore_fill_super(struct super_block *sb, void *data, int silent)
277 goto fail; 309 goto fail;
278 } 310 }
279 311
280 pstore_get_records(); 312 pstore_get_records(0);
281 313
282 return 0; 314 return 0;
283fail: 315fail:
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index 611c1b3c46fa..3bde461c3f34 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -1,5 +1,5 @@
1extern void pstore_set_kmsg_bytes(int); 1extern void pstore_set_kmsg_bytes(int);
2extern void pstore_get_records(void); 2extern void pstore_get_records(int);
3extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, 3extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
4 char *data, size_t size, 4 char *data, size_t size,
5 struct timespec time, struct pstore_info *psi); 5 struct timespec time, struct pstore_info *psi);
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index c5300ec31696..ca60ebcfb15f 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -25,12 +25,29 @@
25#include <linux/module.h> 25#include <linux/module.h>
26#include <linux/pstore.h> 26#include <linux/pstore.h>
27#include <linux/string.h> 27#include <linux/string.h>
28#include <linux/timer.h>
28#include <linux/slab.h> 29#include <linux/slab.h>
29#include <linux/uaccess.h> 30#include <linux/uaccess.h>
31#include <linux/workqueue.h>
30 32
31#include "internal.h" 33#include "internal.h"
32 34
33/* 35/*
36 * We defer making "oops" entries appear in pstore - see
37 * whether the system is actually still running well enough
38 * to let someone see the entry
39 */
40#define PSTORE_INTERVAL (60 * HZ)
41
42static int pstore_new_entry;
43
44static void pstore_timefunc(unsigned long);
45static DEFINE_TIMER(pstore_timer, pstore_timefunc, 0, 0);
46
47static void pstore_dowork(struct work_struct *);
48static DECLARE_WORK(pstore_work, pstore_dowork);
49
50/*
34 * pstore_lock just protects "psinfo" during 51 * pstore_lock just protects "psinfo" during
35 * calls to pstore_register() 52 * calls to pstore_register()
36 */ 53 */
@@ -100,9 +117,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
100 id = psinfo->write(PSTORE_TYPE_DMESG, part, 117 id = psinfo->write(PSTORE_TYPE_DMESG, part,
101 hsize + l1_cpy + l2_cpy, psinfo); 118 hsize + l1_cpy + l2_cpy, psinfo);
102 if (reason == KMSG_DUMP_OOPS && pstore_is_mounted()) 119 if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
103 pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, 120 pstore_new_entry = 1;
104 psinfo->buf, hsize + l1_cpy + l2_cpy,
105 CURRENT_TIME, psinfo);
106 l1 -= l1_cpy; 121 l1 -= l1_cpy;
107 l2 -= l2_cpy; 122 l2 -= l2_cpy;
108 total += l1_cpy + l2_cpy; 123 total += l1_cpy + l2_cpy;
@@ -148,19 +163,24 @@ int pstore_register(struct pstore_info *psi)
148 } 163 }
149 164
150 if (pstore_is_mounted()) 165 if (pstore_is_mounted())
151 pstore_get_records(); 166 pstore_get_records(0);
152 167
153 kmsg_dump_register(&pstore_dumper); 168 kmsg_dump_register(&pstore_dumper);
154 169
170 pstore_timer.expires = jiffies + PSTORE_INTERVAL;
171 add_timer(&pstore_timer);
172
155 return 0; 173 return 0;
156} 174}
157EXPORT_SYMBOL_GPL(pstore_register); 175EXPORT_SYMBOL_GPL(pstore_register);
158 176
159/* 177/*
160 * Read all the records from the persistent store. Create and 178 * Read all the records from the persistent store. Create
161 * file files in our filesystem. 179 * files in our filesystem. Don't warn about -EEXIST errors
180 * when we are re-scanning the backing store looking to add new
181 * error records.
162 */ 182 */
163void pstore_get_records(void) 183void pstore_get_records(int quiet)
164{ 184{
165 struct pstore_info *psi = psinfo; 185 struct pstore_info *psi = psinfo;
166 ssize_t size; 186 ssize_t size;
@@ -178,8 +198,9 @@ void pstore_get_records(void)
178 goto out; 198 goto out;
179 199
180 while ((size = psi->read(&id, &type, &time, psi)) > 0) { 200 while ((size = psi->read(&id, &type, &time, psi)) > 0) {
181 if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size, 201 rc = pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
182 time, psi)) 202 time, psi);
203 if (rc && (rc != -EEXIST || !quiet))
183 failed++; 204 failed++;
184 } 205 }
185 psi->close(psi); 206 psi->close(psi);
@@ -191,6 +212,21 @@ out:
191 failed, psi->name); 212 failed, psi->name);
192} 213}
193 214
215static void pstore_dowork(struct work_struct *work)
216{
217 pstore_get_records(1);
218}
219
220static void pstore_timefunc(unsigned long dummy)
221{
222 if (pstore_new_entry) {
223 pstore_new_entry = 0;
224 schedule_work(&pstore_work);
225 }
226
227 mod_timer(&pstore_timer, jiffies + PSTORE_INTERVAL);
228}
229
194/* 230/*
195 * Call platform driver to write a record to the 231 * Call platform driver to write a record to the
196 * persistent store. 232 * persistent store.