Blame view

fs/pstore/platform.c 5.97 KB
ca01d6dd2   Tony Luck   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   Tony Luck   pstore: defer ins...
28
  #include <linux/timer.h>
ca01d6dd2   Tony Luck   pstore: new files...
29
30
  #include <linux/slab.h>
  #include <linux/uaccess.h>
abd4d5587   Don Zickus   pstore: change mu...
31
  #include <linux/hardirq.h>
6dda92669   Tony Luck   pstore: defer ins...
32
  #include <linux/workqueue.h>
ca01d6dd2   Tony Luck   pstore: new files...
33
34
35
36
  
  #include "internal.h"
  
  /*
6dda92669   Tony Luck   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   Tony Luck   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   Matthew Garrett   pstore: Allow the...
57
  static char *backend;
366f7e7a7   Tony Luck   pstore: use mount...
58
  /* How much of the console log to snapshot */
ca01d6dd2   Tony Luck   pstore: new files...
59
  static unsigned long kmsg_bytes = 10240;
366f7e7a7   Tony Luck   pstore: use mount...
60
  void pstore_set_kmsg_bytes(int bytes)
ca01d6dd2   Tony Luck   pstore: new files...
61
  {
366f7e7a7   Tony Luck   pstore: use mount...
62
  	kmsg_bytes = bytes;
ca01d6dd2   Tony Luck   pstore: new files...
63
  }
ca01d6dd2   Tony Luck   pstore: new files...
64
65
  /* Tag each group of saved records with a sequence number */
  static int	oopscount;
9f6af27fb   Tony Luck   pstore: cleanups ...
66
67
68
  static char *reason_str[] = {
  	"Oops", "Panic", "Kexec", "Restart", "Halt", "Poweroff", "Emergency"
  };
ca01d6dd2   Tony Luck   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   Tony Luck   pstore: cleanups ...
82
  	char		*dst, *why;
ca01d6dd2   Tony Luck   pstore: new files...
83
  	u64		id;
b238b8fa9   Chen Gong   pstore: make psto...
84
  	int		hsize, ret;
b94fdd077   Matthew Garrett   pstore: Make "par...
85
  	unsigned int	part = 1;
abd4d5587   Don Zickus   pstore: change mu...
86
87
  	unsigned long	flags = 0;
  	int		is_locked = 0;
ca01d6dd2   Tony Luck   pstore: new files...
88

9f6af27fb   Tony Luck   pstore: cleanups ...
89
90
91
92
  	if (reason < ARRAY_SIZE(reason_str))
  		why = reason_str[reason];
  	else
  		why = "Unknown";
abd4d5587   Don Zickus   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   Tony Luck   pstore: new files...
100
101
102
  	oopscount++;
  	while (total < kmsg_bytes) {
  		dst = psinfo->buf;
56280682c   Matthew Garrett   pstore: Add extra...
103
104
  		hsize = sprintf(dst, "%s#%d Part%d
  ", why, oopscount, part);
ca01d6dd2   Tony Luck   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   Kees Cook   pstore: pass reas...
119
  		ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
56280682c   Matthew Garrett   pstore: Add extra...
120
  				   hsize + l1_cpy + l2_cpy, psinfo);
b238b8fa9   Chen Gong   pstore: make psto...
121
  		if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
6dda92669   Tony Luck   pstore: defer ins...
122
  			pstore_new_entry = 1;
ca01d6dd2   Tony Luck   pstore: new files...
123
124
125
  		l1 -= l1_cpy;
  		l2 -= l2_cpy;
  		total += l1_cpy + l2_cpy;
56280682c   Matthew Garrett   pstore: Add extra...
126
  		part++;
ca01d6dd2   Tony Luck   pstore: new files...
127
  	}
abd4d5587   Don Zickus   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   Tony Luck   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   Matthew Garrett   pstore: Allow the...
157
158
159
160
161
  
  	if (backend && strcmp(backend, psi->name)) {
  		spin_unlock(&pstore_lock);
  		return -EINVAL;
  	}
ca01d6dd2   Tony Luck   pstore: new files...
162
  	psinfo = psi;
f6f828513   Kees Cook   pstore: pass allo...
163
  	mutex_init(&psinfo->read_mutex);
ca01d6dd2   Tony Luck   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   Tony Luck   pstore: defer ins...
172
  		pstore_get_records(0);
ca01d6dd2   Tony Luck   pstore: new files...
173
174
  
  	kmsg_dump_register(&pstore_dumper);
6dda92669   Tony Luck   pstore: defer ins...
175
176
  	pstore_timer.expires = jiffies + PSTORE_INTERVAL;
  	add_timer(&pstore_timer);
ca01d6dd2   Tony Luck   pstore: new files...
177
178
179
180
181
  	return 0;
  }
  EXPORT_SYMBOL_GPL(pstore_register);
  
  /*
6dda92669   Tony Luck   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   Tony Luck   pstore: new files...
186
   */
6dda92669   Tony Luck   pstore: defer ins...
187
  void pstore_get_records(int quiet)
ca01d6dd2   Tony Luck   pstore: new files...
188
189
  {
  	struct pstore_info *psi = psinfo;
f6f828513   Kees Cook   pstore: pass allo...
190
  	char			*buf = NULL;
8d38d74b6   Chen Gong   pstore: fix one t...
191
  	ssize_t			size;
ca01d6dd2   Tony Luck   pstore: new files...
192
193
194
  	u64			id;
  	enum pstore_type_id	type;
  	struct timespec		time;
06cf91b4b   Chen Gong   pstore: fix pstor...
195
  	int			failed = 0, rc;
ca01d6dd2   Tony Luck   pstore: new files...
196
197
198
  
  	if (!psi)
  		return;
f6f828513   Kees Cook   pstore: pass allo...
199
  	mutex_lock(&psi->read_mutex);
2174f6df7   Kees Cook   pstore: gracefull...
200
  	if (psi->open && psi->open(psi))
06cf91b4b   Chen Gong   pstore: fix pstor...
201
  		goto out;
f6f828513   Kees Cook   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   Tony Luck   pstore: defer ins...
204
  				  time, psi);
f6f828513   Kees Cook   pstore: pass allo...
205
206
  		kfree(buf);
  		buf = NULL;
6dda92669   Tony Luck   pstore: defer ins...
207
  		if (rc && (rc != -EEXIST || !quiet))
ca01d6dd2   Tony Luck   pstore: new files...
208
209
  			failed++;
  	}
2174f6df7   Kees Cook   pstore: gracefull...
210
211
  	if (psi->close)
  		psi->close(psi);
06cf91b4b   Chen Gong   pstore: fix pstor...
212
  out:
f6f828513   Kees Cook   pstore: pass allo...
213
  	mutex_unlock(&psi->read_mutex);
ca01d6dd2   Tony Luck   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   Tony Luck   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   Matthew Garrett   pstore: Allow the...
234
235
  module_param(backend, charp, 0444);
  MODULE_PARM_DESC(backend, "Pstore backend to use");