|
26 | 26 | #include <linux/console.h>
|
27 | 27 | #include <linux/module.h>
|
28 | 28 | #include <linux/pstore.h>
|
| 29 | +#include <linux/zlib.h> |
29 | 30 | #include <linux/string.h>
|
30 | 31 | #include <linux/timer.h>
|
31 | 32 | #include <linux/slab.h>
|
@@ -65,6 +66,15 @@ struct pstore_info *psinfo;
|
65 | 66 |
|
66 | 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 | 78 | /* How much of the console log to snapshot */
|
69 | 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 | 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 | 216 | * callback from kmsg_dump. (s2,l2) has the most recently
|
122 | 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 | 243 | char *dst;
|
149 | 244 | unsigned long size;
|
150 | 245 | int hsize;
|
| 246 | + int zipped_len = -1; |
151 | 247 | size_t len;
|
152 |
| - bool compressed = false; |
| 248 | + bool compressed; |
| 249 | + size_t total_len; |
153 | 250 |
|
154 |
| - dst = psinfo->buf; |
155 |
| - hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); |
156 |
| - size = psinfo->bufsize - hsize; |
157 |
| - dst += hsize; |
| 251 | + if (big_oops_buf) { |
| 252 | + dst = big_oops_buf; |
| 253 | + hsize = sprintf(dst, "%s#%d Part%d\n", why, |
| 254 | + oopscount, part); |
| 255 | + size = big_oops_buf_sz - hsize; |
158 | 256 |
|
159 |
| - if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len)) |
160 |
| - break; |
| 257 | + if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, |
| 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 | 290 | ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
|
163 |
| - oopscount, compressed, hsize + len, psinfo); |
| 291 | + oopscount, compressed, total_len, psinfo); |
164 | 292 | if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
|
165 | 293 | pstore_new_entry = 1;
|
166 | 294 |
|
167 |
| - total += hsize + len; |
| 295 | + total += total_len; |
168 | 296 | part++;
|
169 | 297 | }
|
170 | 298 | if (pstore_cannot_block_path(reason)) {
|
@@ -262,6 +390,8 @@ int pstore_register(struct pstore_info *psi)
|
262 | 390 | return -EINVAL;
|
263 | 391 | }
|
264 | 392 |
|
| 393 | + allocate_buf_for_compression(); |
| 394 | + |
265 | 395 | if (pstore_is_mounted())
|
266 | 396 | pstore_get_records(0);
|
267 | 397 |
|
|
0 commit comments