diff options
author | Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> | 2013-08-16 16:53:10 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2013-08-19 13:18:11 -0400 |
commit | b0aad7a99c1df90c23ff4bac76eea9cf25049e9e (patch) | |
tree | 3b9c9bd902ac66b1597187b57aa6a754d6d0d846 /fs/pstore/platform.c | |
parent | 90ce4ca66864ee2de189b666e4e4230f452238a8 (diff) |
pstore: Add compression support to pstore
Add compression support to pstore which will help in capturing more data.
Initially, pstore will make a call to kmsg_dump with a bigger buffer
and will pass the size of bigger buffer to kmsg_dump and then compress
the data to registered buffer of registered size.
In case compression fails, pstore will capture the uncompressed
data by making a call again to kmsg_dump with registered_buffer
of registered size.
Pstore will indicate the data is compressed or not with a flag
in the write callback.
Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'fs/pstore/platform.c')
-rw-r--r-- | fs/pstore/platform.c | 148 |
1 files changed, 139 insertions, 9 deletions
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 20fa686f80fa..56218cb35267 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/console.h> | 26 | #include <linux/console.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/pstore.h> | 28 | #include <linux/pstore.h> |
29 | #include <linux/zlib.h> | ||
29 | #include <linux/string.h> | 30 | #include <linux/string.h> |
30 | #include <linux/timer.h> | 31 | #include <linux/timer.h> |
31 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
@@ -65,6 +66,15 @@ struct pstore_info *psinfo; | |||
65 | 66 | ||
66 | static char *backend; | 67 | static char *backend; |
67 | 68 | ||
69 | /* Compression parameters */ | ||
70 | #define COMPR_LEVEL 6 | ||
71 | #define WINDOW_BITS 12 | ||
72 | #define MEM_LEVEL 4 | ||
73 | static struct z_stream_s stream; | ||
74 | |||
75 | static char *big_oops_buf; | ||
76 | static size_t big_oops_buf_sz; | ||
77 | |||
68 | /* How much of the console log to snapshot */ | 78 | /* How much of the console log to snapshot */ |
69 | static unsigned long kmsg_bytes = 10240; | 79 | static unsigned long kmsg_bytes = 10240; |
70 | 80 | ||
@@ -117,6 +127,91 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) | |||
117 | } | 127 | } |
118 | EXPORT_SYMBOL_GPL(pstore_cannot_block_path); | 128 | EXPORT_SYMBOL_GPL(pstore_cannot_block_path); |
119 | 129 | ||
130 | /* Derived from logfs_compress() */ | ||
131 | static int pstore_compress(const void *in, void *out, size_t inlen, | ||
132 | size_t outlen) | ||
133 | { | ||
134 | int err, ret; | ||
135 | |||
136 | ret = -EIO; | ||
137 | err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, | ||
138 | MEM_LEVEL, Z_DEFAULT_STRATEGY); | ||
139 | if (err != Z_OK) | ||
140 | goto error; | ||
141 | |||
142 | stream.next_in = in; | ||
143 | stream.avail_in = inlen; | ||
144 | stream.total_in = 0; | ||
145 | stream.next_out = out; | ||
146 | stream.avail_out = outlen; | ||
147 | stream.total_out = 0; | ||
148 | |||
149 | err = zlib_deflate(&stream, Z_FINISH); | ||
150 | if (err != Z_STREAM_END) | ||
151 | goto error; | ||
152 | |||
153 | err = zlib_deflateEnd(&stream); | ||
154 | if (err != Z_OK) | ||
155 | goto error; | ||
156 | |||
157 | if (stream.total_out >= stream.total_in) | ||
158 | goto error; | ||
159 | |||
160 | ret = stream.total_out; | ||
161 | error: | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | static void allocate_buf_for_compression(void) | ||
166 | { | ||
167 | size_t size; | ||
168 | |||
169 | big_oops_buf_sz = (psinfo->bufsize * 100) / 45; | ||
170 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); | ||
171 | if (big_oops_buf) { | ||
172 | size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), | ||
173 | zlib_inflate_workspacesize()); | ||
174 | stream.workspace = kmalloc(size, GFP_KERNEL); | ||
175 | if (!stream.workspace) { | ||
176 | pr_err("pstore: No memory for compression workspace; " | ||
177 | "skipping compression\n"); | ||
178 | kfree(big_oops_buf); | ||
179 | big_oops_buf = NULL; | ||
180 | } | ||
181 | } else { | ||
182 | pr_err("No memory for uncompressed data; " | ||
183 | "skipping compression\n"); | ||
184 | stream.workspace = NULL; | ||
185 | } | ||
186 | |||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Called when compression fails, since the printk buffer | ||
191 | * would be fetched for compression calling it again when | ||
192 | * compression fails would have moved the iterator of | ||
193 | * printk buffer which results in fetching old contents. | ||
194 | * Copy the recent messages from big_oops_buf to psinfo->buf | ||
195 | */ | ||
196 | static size_t copy_kmsg_to_buffer(int hsize, size_t len) | ||
197 | { | ||
198 | size_t total_len; | ||
199 | size_t diff; | ||
200 | |||
201 | total_len = hsize + len; | ||
202 | |||
203 | if (total_len > psinfo->bufsize) { | ||
204 | diff = total_len - psinfo->bufsize + hsize; | ||
205 | memcpy(psinfo->buf, big_oops_buf, hsize); | ||
206 | memcpy(psinfo->buf + hsize, big_oops_buf + diff, | ||
207 | psinfo->bufsize - hsize); | ||
208 | total_len = psinfo->bufsize; | ||
209 | } else | ||
210 | memcpy(psinfo->buf, big_oops_buf, total_len); | ||
211 | |||
212 | return total_len; | ||
213 | } | ||
214 | |||
120 | /* | 215 | /* |
121 | * callback from kmsg_dump. (s2,l2) has the most recently | 216 | * callback from kmsg_dump. (s2,l2) has the most recently |
122 | * written bytes, older bytes are in (s1,l1). Save as much | 217 | * written bytes, older bytes are in (s1,l1). Save as much |
@@ -148,23 +243,56 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
148 | char *dst; | 243 | char *dst; |
149 | unsigned long size; | 244 | unsigned long size; |
150 | int hsize; | 245 | int hsize; |
246 | int zipped_len = -1; | ||
151 | size_t len; | 247 | size_t len; |
152 | bool compressed = false; | 248 | bool compressed; |
249 | size_t total_len; | ||
153 | 250 | ||
154 | dst = psinfo->buf; | 251 | if (big_oops_buf) { |
155 | hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); | 252 | dst = big_oops_buf; |
156 | size = psinfo->bufsize - hsize; | 253 | hsize = sprintf(dst, "%s#%d Part%d\n", why, |
157 | dst += hsize; | 254 | oopscount, part); |
255 | size = big_oops_buf_sz - hsize; | ||
158 | 256 | ||
159 | if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len)) | 257 | if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, |
160 | break; | 258 | size, &len)) |
259 | break; | ||
260 | |||
261 | zipped_len = pstore_compress(dst, psinfo->buf, | ||
262 | hsize + len, psinfo->bufsize); | ||
263 | |||
264 | if (zipped_len > 0) { | ||
265 | compressed = true; | ||
266 | total_len = zipped_len; | ||
267 | } else { | ||
268 | pr_err("pstore: compression failed for Part %d" | ||
269 | " returned %d\n", part, zipped_len); | ||
270 | pr_err("pstore: Capture uncompressed" | ||
271 | " oops/panic report of Part %d\n", part); | ||
272 | compressed = false; | ||
273 | total_len = copy_kmsg_to_buffer(hsize, len); | ||
274 | } | ||
275 | } else { | ||
276 | dst = psinfo->buf; | ||
277 | hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, | ||
278 | part); | ||
279 | size = psinfo->bufsize - hsize; | ||
280 | dst += hsize; | ||
281 | |||
282 | if (!kmsg_dump_get_buffer(dumper, true, dst, | ||
283 | size, &len)) | ||
284 | break; | ||
285 | |||
286 | compressed = false; | ||
287 | total_len = hsize + len; | ||
288 | } | ||
161 | 289 | ||
162 | ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, | 290 | ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, |
163 | oopscount, compressed, hsize + len, psinfo); | 291 | oopscount, compressed, total_len, psinfo); |
164 | if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) | 292 | if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) |
165 | pstore_new_entry = 1; | 293 | pstore_new_entry = 1; |
166 | 294 | ||
167 | total += hsize + len; | 295 | total += total_len; |
168 | part++; | 296 | part++; |
169 | } | 297 | } |
170 | if (pstore_cannot_block_path(reason)) { | 298 | if (pstore_cannot_block_path(reason)) { |
@@ -262,6 +390,8 @@ int pstore_register(struct pstore_info *psi) | |||
262 | return -EINVAL; | 390 | return -EINVAL; |
263 | } | 391 | } |
264 | 392 | ||
393 | allocate_buf_for_compression(); | ||
394 | |||
265 | if (pstore_is_mounted()) | 395 | if (pstore_is_mounted()) |
266 | pstore_get_records(0); | 396 | pstore_get_records(0); |
267 | 397 | ||