1ca01d6ddSTony Luck /* 2ca01d6ddSTony Luck * Persistent Storage - platform driver interface parts. 3ca01d6ddSTony Luck * 4f29e5956SAnton Vorontsov * Copyright (C) 2007-2008 Google, Inc. 5ca01d6ddSTony Luck * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> 6ca01d6ddSTony Luck * 7ca01d6ddSTony Luck * This program is free software; you can redistribute it and/or modify 8ca01d6ddSTony Luck * it under the terms of the GNU General Public License version 2 as 9ca01d6ddSTony Luck * published by the Free Software Foundation. 10ca01d6ddSTony Luck * 11ca01d6ddSTony Luck * This program is distributed in the hope that it will be useful, 12ca01d6ddSTony Luck * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ca01d6ddSTony Luck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ca01d6ddSTony Luck * GNU General Public License for more details. 15ca01d6ddSTony Luck * 16ca01d6ddSTony Luck * You should have received a copy of the GNU General Public License 17ca01d6ddSTony Luck * along with this program; if not, write to the Free Software 18ca01d6ddSTony Luck * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19ca01d6ddSTony Luck */ 20ca01d6ddSTony Luck 21ef748853SFabian Frederick #define pr_fmt(fmt) "pstore: " fmt 22ef748853SFabian Frederick 23ca01d6ddSTony Luck #include <linux/atomic.h> 24ca01d6ddSTony Luck #include <linux/types.h> 25ca01d6ddSTony Luck #include <linux/errno.h> 26ca01d6ddSTony Luck #include <linux/init.h> 27ca01d6ddSTony Luck #include <linux/kmsg_dump.h> 28f29e5956SAnton Vorontsov #include <linux/console.h> 29ca01d6ddSTony Luck #include <linux/module.h> 30ca01d6ddSTony Luck #include <linux/pstore.h> 3158eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS) 328cfc8ddcSGeliang Tang #include <linux/lzo.h> 338cfc8ddcSGeliang Tang #endif 3458eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS) 358cfc8ddcSGeliang Tang #include <linux/lz4.h> 368cfc8ddcSGeliang Tang #endif 37cb3bee03SGeliang Tang #include <linux/crypto.h> 38ca01d6ddSTony Luck #include <linux/string.h> 396dda9266SLuck, Tony #include <linux/timer.h> 40ca01d6ddSTony Luck #include <linux/slab.h> 41ca01d6ddSTony Luck #include <linux/uaccess.h> 42a3f5f075SAnton Vorontsov #include <linux/jiffies.h> 436dda9266SLuck, Tony #include <linux/workqueue.h> 44ca01d6ddSTony Luck 45ca01d6ddSTony Luck #include "internal.h" 46ca01d6ddSTony Luck 47ca01d6ddSTony Luck /* 486dda9266SLuck, Tony * We defer making "oops" entries appear in pstore - see 496dda9266SLuck, Tony * whether the system is actually still running well enough 506dda9266SLuck, Tony * to let someone see the entry 516dda9266SLuck, Tony */ 52521f7288SAnton Vorontsov static int pstore_update_ms = -1; 53a3f5f075SAnton Vorontsov module_param_named(update_ms, pstore_update_ms, int, 0600); 54a3f5f075SAnton Vorontsov MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content " 55521f7288SAnton Vorontsov "(default is -1, which means runtime updates are disabled; " 56521f7288SAnton Vorontsov "enabling this option is not safe, it may lead to further " 57521f7288SAnton Vorontsov "corruption on Oopses)"); 586dda9266SLuck, Tony 596dda9266SLuck, Tony static int pstore_new_entry; 606dda9266SLuck, Tony 6124ed960aSKees Cook static void pstore_timefunc(struct timer_list *); 621d27e3e2SKees Cook static DEFINE_TIMER(pstore_timer, pstore_timefunc); 636dda9266SLuck, Tony 646dda9266SLuck, Tony static void pstore_dowork(struct work_struct *); 656dda9266SLuck, Tony static DECLARE_WORK(pstore_work, pstore_dowork); 666dda9266SLuck, Tony 676dda9266SLuck, Tony /* 68ca01d6ddSTony Luck * pstore_lock just protects "psinfo" during 69ca01d6ddSTony Luck * calls to pstore_register() 70ca01d6ddSTony Luck */ 71ca01d6ddSTony Luck static DEFINE_SPINLOCK(pstore_lock); 72060287b8SAnton Vorontsov struct pstore_info *psinfo; 73ca01d6ddSTony Luck 74dee28e72SMatthew Garrett static char *backend; 75fe1d4758SKees Cook static char *compress = 76fe1d4758SKees Cook #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT 77fe1d4758SKees Cook CONFIG_PSTORE_COMPRESS_DEFAULT; 78fe1d4758SKees Cook #else 79fe1d4758SKees Cook NULL; 80fe1d4758SKees Cook #endif 81dee28e72SMatthew Garrett 82b0aad7a9SAruna Balakrishnaiah /* Compression parameters */ 83cb3bee03SGeliang Tang static struct crypto_comp *tfm; 848cfc8ddcSGeliang Tang 858cfc8ddcSGeliang Tang struct pstore_zbackend { 86cb3bee03SGeliang Tang int (*zbufsize)(size_t size); 878cfc8ddcSGeliang Tang const char *name; 888cfc8ddcSGeliang Tang }; 89b0aad7a9SAruna Balakrishnaiah 90b0aad7a9SAruna Balakrishnaiah static char *big_oops_buf; 91b0aad7a9SAruna Balakrishnaiah static size_t big_oops_buf_sz; 92b0aad7a9SAruna Balakrishnaiah 93366f7e7aSLuck, Tony /* How much of the console log to snapshot */ 94349d7438SDavid Howells unsigned long kmsg_bytes = PSTORE_DEFAULT_KMSG_BYTES; 95ca01d6ddSTony Luck 96366f7e7aSLuck, Tony void pstore_set_kmsg_bytes(int bytes) 97ca01d6ddSTony Luck { 98366f7e7aSLuck, Tony kmsg_bytes = bytes; 99ca01d6ddSTony Luck } 100ca01d6ddSTony Luck 101ca01d6ddSTony Luck /* Tag each group of saved records with a sequence number */ 102ca01d6ddSTony Luck static int oopscount; 103ca01d6ddSTony Luck 104381b872cSSeiji Aguchi static const char *get_reason_str(enum kmsg_dump_reason reason) 105381b872cSSeiji Aguchi { 106381b872cSSeiji Aguchi switch (reason) { 107381b872cSSeiji Aguchi case KMSG_DUMP_PANIC: 108381b872cSSeiji Aguchi return "Panic"; 109381b872cSSeiji Aguchi case KMSG_DUMP_OOPS: 110381b872cSSeiji Aguchi return "Oops"; 111381b872cSSeiji Aguchi case KMSG_DUMP_EMERG: 112381b872cSSeiji Aguchi return "Emergency"; 113381b872cSSeiji Aguchi case KMSG_DUMP_RESTART: 114381b872cSSeiji Aguchi return "Restart"; 115381b872cSSeiji Aguchi case KMSG_DUMP_HALT: 116381b872cSSeiji Aguchi return "Halt"; 117381b872cSSeiji Aguchi case KMSG_DUMP_POWEROFF: 118381b872cSSeiji Aguchi return "Poweroff"; 119381b872cSSeiji Aguchi default: 120381b872cSSeiji Aguchi return "Unknown"; 121381b872cSSeiji Aguchi } 122381b872cSSeiji Aguchi } 1239f6af27fSTony Luck 1249f244e9cSSeiji Aguchi bool pstore_cannot_block_path(enum kmsg_dump_reason reason) 1259f244e9cSSeiji Aguchi { 1269f244e9cSSeiji Aguchi /* 1279f244e9cSSeiji Aguchi * In case of NMI path, pstore shouldn't be blocked 1289f244e9cSSeiji Aguchi * regardless of reason. 1299f244e9cSSeiji Aguchi */ 1309f244e9cSSeiji Aguchi if (in_nmi()) 1319f244e9cSSeiji Aguchi return true; 1329f244e9cSSeiji Aguchi 1339f244e9cSSeiji Aguchi switch (reason) { 1349f244e9cSSeiji Aguchi /* In panic case, other cpus are stopped by smp_send_stop(). */ 1359f244e9cSSeiji Aguchi case KMSG_DUMP_PANIC: 1369f244e9cSSeiji Aguchi /* Emergency restart shouldn't be blocked by spin lock. */ 1379f244e9cSSeiji Aguchi case KMSG_DUMP_EMERG: 1389f244e9cSSeiji Aguchi return true; 1399f244e9cSSeiji Aguchi default: 1409f244e9cSSeiji Aguchi return false; 1419f244e9cSSeiji Aguchi } 1429f244e9cSSeiji Aguchi } 1439f244e9cSSeiji Aguchi EXPORT_SYMBOL_GPL(pstore_cannot_block_path); 1449f244e9cSSeiji Aguchi 14558eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS) 146cb3bee03SGeliang Tang static int zbufsize_deflate(size_t size) 147b0aad7a9SAruna Balakrishnaiah { 1487de8fe2fSAruna Balakrishnaiah size_t cmpr; 149b0aad7a9SAruna Balakrishnaiah 150cb3bee03SGeliang Tang switch (size) { 1517de8fe2fSAruna Balakrishnaiah /* buffer range for efivars */ 1527de8fe2fSAruna Balakrishnaiah case 1000 ... 2000: 1537de8fe2fSAruna Balakrishnaiah cmpr = 56; 1547de8fe2fSAruna Balakrishnaiah break; 1557de8fe2fSAruna Balakrishnaiah case 2001 ... 3000: 1567de8fe2fSAruna Balakrishnaiah cmpr = 54; 1577de8fe2fSAruna Balakrishnaiah break; 1587de8fe2fSAruna Balakrishnaiah case 3001 ... 3999: 1597de8fe2fSAruna Balakrishnaiah cmpr = 52; 1607de8fe2fSAruna Balakrishnaiah break; 1617de8fe2fSAruna Balakrishnaiah /* buffer range for nvram, erst */ 1627de8fe2fSAruna Balakrishnaiah case 4000 ... 10000: 1637de8fe2fSAruna Balakrishnaiah cmpr = 45; 1647de8fe2fSAruna Balakrishnaiah break; 1657de8fe2fSAruna Balakrishnaiah default: 1667de8fe2fSAruna Balakrishnaiah cmpr = 60; 1677de8fe2fSAruna Balakrishnaiah break; 1687de8fe2fSAruna Balakrishnaiah } 1697de8fe2fSAruna Balakrishnaiah 170cb3bee03SGeliang Tang return (size * 100) / cmpr; 1718cfc8ddcSGeliang Tang } 1728cfc8ddcSGeliang Tang #endif 1738cfc8ddcSGeliang Tang 17458eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS) 175cb3bee03SGeliang Tang static int zbufsize_lzo(size_t size) 1768cfc8ddcSGeliang Tang { 177cb3bee03SGeliang Tang return lzo1x_worst_compress(size); 1788cfc8ddcSGeliang Tang } 1798cfc8ddcSGeliang Tang #endif 1808cfc8ddcSGeliang Tang 18158eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS) 182cb3bee03SGeliang Tang static int zbufsize_lz4(size_t size) 1838cfc8ddcSGeliang Tang { 184cb3bee03SGeliang Tang return LZ4_compressBound(size); 185239b7161SGeliang Tang } 186239b7161SGeliang Tang #endif 187239b7161SGeliang Tang 18858eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS) 189cb3bee03SGeliang Tang static int zbufsize_842(size_t size) 190239b7161SGeliang Tang { 19155597406SKees Cook return size; 192239b7161SGeliang Tang } 193fe1d4758SKees Cook #endif 1948cfc8ddcSGeliang Tang 195fe1d4758SKees Cook static const struct pstore_zbackend *zbackend __ro_after_init; 196fe1d4758SKees Cook 197fe1d4758SKees Cook static const struct pstore_zbackend zbackends[] = { 19858eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS) 199fe1d4758SKees Cook { 200cb3bee03SGeliang Tang .zbufsize = zbufsize_deflate, 201cb3bee03SGeliang Tang .name = "deflate", 202fe1d4758SKees Cook }, 203fe1d4758SKees Cook #endif 20458eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS) 205fe1d4758SKees Cook { 206cb3bee03SGeliang Tang .zbufsize = zbufsize_lzo, 207fe1d4758SKees Cook .name = "lzo", 208fe1d4758SKees Cook }, 209fe1d4758SKees Cook #endif 21058eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) 211fe1d4758SKees Cook { 212cb3bee03SGeliang Tang .zbufsize = zbufsize_lz4, 213fe1d4758SKees Cook .name = "lz4", 214fe1d4758SKees Cook }, 215fe1d4758SKees Cook #endif 21658eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS) 217fe1d4758SKees Cook { 218cb3bee03SGeliang Tang .zbufsize = zbufsize_lz4, 219fe1d4758SKees Cook .name = "lz4hc", 220fe1d4758SKees Cook }, 221fe1d4758SKees Cook #endif 22258eb5b67SArnd Bergmann #if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS) 223fe1d4758SKees Cook { 224cb3bee03SGeliang Tang .zbufsize = zbufsize_842, 225239b7161SGeliang Tang .name = "842", 226fe1d4758SKees Cook }, 227fe1d4758SKees Cook #endif 228fe1d4758SKees Cook { } 2298cfc8ddcSGeliang Tang }; 2308cfc8ddcSGeliang Tang 2318cfc8ddcSGeliang Tang static int pstore_compress(const void *in, void *out, 232cb3bee03SGeliang Tang unsigned int inlen, unsigned int outlen) 2338cfc8ddcSGeliang Tang { 234cb3bee03SGeliang Tang int ret; 235cb3bee03SGeliang Tang 236cb3bee03SGeliang Tang ret = crypto_comp_compress(tfm, in, inlen, out, &outlen); 237cb3bee03SGeliang Tang if (ret) { 238cb3bee03SGeliang Tang pr_err("crypto_comp_compress failed, ret = %d!\n", ret); 239cb3bee03SGeliang Tang return ret; 2408cfc8ddcSGeliang Tang } 2418cfc8ddcSGeliang Tang 242cb3bee03SGeliang Tang return outlen; 243cb3bee03SGeliang Tang } 244cb3bee03SGeliang Tang 245cb3bee03SGeliang Tang static int pstore_decompress(void *in, void *out, 246cb3bee03SGeliang Tang unsigned int inlen, unsigned int outlen) 2478cfc8ddcSGeliang Tang { 248cb3bee03SGeliang Tang int ret; 249cb3bee03SGeliang Tang 250cb3bee03SGeliang Tang ret = crypto_comp_decompress(tfm, in, inlen, out, &outlen); 251cb3bee03SGeliang Tang if (ret) { 252cb3bee03SGeliang Tang pr_err("crypto_comp_decompress failed, ret = %d!\n", ret); 253cb3bee03SGeliang Tang return ret; 254cb3bee03SGeliang Tang } 255cb3bee03SGeliang Tang 256cb3bee03SGeliang Tang return outlen; 2578cfc8ddcSGeliang Tang } 2588cfc8ddcSGeliang Tang 2598cfc8ddcSGeliang Tang static void allocate_buf_for_compression(void) 2608cfc8ddcSGeliang Tang { 261e698aaf3STobias Regnery if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !zbackend) 262cb3bee03SGeliang Tang return; 263cb3bee03SGeliang Tang 264cb3bee03SGeliang Tang if (!crypto_has_comp(zbackend->name, 0, 0)) { 265cb3bee03SGeliang Tang pr_err("No %s compression\n", zbackend->name); 266cb3bee03SGeliang Tang return; 267cb3bee03SGeliang Tang } 268cb3bee03SGeliang Tang 269cb3bee03SGeliang Tang big_oops_buf_sz = zbackend->zbufsize(psinfo->bufsize); 270cb3bee03SGeliang Tang if (big_oops_buf_sz <= 0) 271cb3bee03SGeliang Tang return; 272cb3bee03SGeliang Tang 273cb3bee03SGeliang Tang big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); 274cb3bee03SGeliang Tang if (!big_oops_buf) { 2758cfc8ddcSGeliang Tang pr_err("allocate compression buffer error!\n"); 276cb3bee03SGeliang Tang return; 277cb3bee03SGeliang Tang } 278cb3bee03SGeliang Tang 279cb3bee03SGeliang Tang tfm = crypto_alloc_comp(zbackend->name, 0, 0); 280cb3bee03SGeliang Tang if (IS_ERR_OR_NULL(tfm)) { 281cb3bee03SGeliang Tang kfree(big_oops_buf); 282cb3bee03SGeliang Tang big_oops_buf = NULL; 283cb3bee03SGeliang Tang pr_err("crypto_alloc_comp() failed!\n"); 284cb3bee03SGeliang Tang return; 2858cfc8ddcSGeliang Tang } 2868cfc8ddcSGeliang Tang } 2878cfc8ddcSGeliang Tang 2888cfc8ddcSGeliang Tang static void free_buf_for_compression(void) 2898cfc8ddcSGeliang Tang { 290e698aaf3STobias Regnery if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && !IS_ERR_OR_NULL(tfm)) 291cb3bee03SGeliang Tang crypto_free_comp(tfm); 292cb3bee03SGeliang Tang kfree(big_oops_buf); 293cb3bee03SGeliang Tang big_oops_buf = NULL; 294cb3bee03SGeliang Tang big_oops_buf_sz = 0; 295ee1d2674SGeliang Tang } 296ee1d2674SGeliang Tang 297b0aad7a9SAruna Balakrishnaiah /* 298b0aad7a9SAruna Balakrishnaiah * Called when compression fails, since the printk buffer 299b0aad7a9SAruna Balakrishnaiah * would be fetched for compression calling it again when 300b0aad7a9SAruna Balakrishnaiah * compression fails would have moved the iterator of 301b0aad7a9SAruna Balakrishnaiah * printk buffer which results in fetching old contents. 302b0aad7a9SAruna Balakrishnaiah * Copy the recent messages from big_oops_buf to psinfo->buf 303b0aad7a9SAruna Balakrishnaiah */ 304b0aad7a9SAruna Balakrishnaiah static size_t copy_kmsg_to_buffer(int hsize, size_t len) 305b0aad7a9SAruna Balakrishnaiah { 306b0aad7a9SAruna Balakrishnaiah size_t total_len; 307b0aad7a9SAruna Balakrishnaiah size_t diff; 308b0aad7a9SAruna Balakrishnaiah 309b0aad7a9SAruna Balakrishnaiah total_len = hsize + len; 310b0aad7a9SAruna Balakrishnaiah 311b0aad7a9SAruna Balakrishnaiah if (total_len > psinfo->bufsize) { 312b0aad7a9SAruna Balakrishnaiah diff = total_len - psinfo->bufsize + hsize; 313b0aad7a9SAruna Balakrishnaiah memcpy(psinfo->buf, big_oops_buf, hsize); 314b0aad7a9SAruna Balakrishnaiah memcpy(psinfo->buf + hsize, big_oops_buf + diff, 315b0aad7a9SAruna Balakrishnaiah psinfo->bufsize - hsize); 316b0aad7a9SAruna Balakrishnaiah total_len = psinfo->bufsize; 317b0aad7a9SAruna Balakrishnaiah } else 318b0aad7a9SAruna Balakrishnaiah memcpy(psinfo->buf, big_oops_buf, total_len); 319b0aad7a9SAruna Balakrishnaiah 320b0aad7a9SAruna Balakrishnaiah return total_len; 321b0aad7a9SAruna Balakrishnaiah } 322b0aad7a9SAruna Balakrishnaiah 323e581ca81SKees Cook void pstore_record_init(struct pstore_record *record, 324e581ca81SKees Cook struct pstore_info *psinfo) 325e581ca81SKees Cook { 326e581ca81SKees Cook memset(record, 0, sizeof(*record)); 327e581ca81SKees Cook 328e581ca81SKees Cook record->psi = psinfo; 329c7f3c595SKees Cook 330c7f3c595SKees Cook /* Report zeroed timestamp if called before timekeeping has resumed. */ 331*7aaa822eSKees Cook record->time = ns_to_timespec64(ktime_get_real_fast_ns()); 332e581ca81SKees Cook } 333e581ca81SKees Cook 334ca01d6ddSTony Luck /* 335ca01d6ddSTony Luck * callback from kmsg_dump. (s2,l2) has the most recently 336ca01d6ddSTony Luck * written bytes, older bytes are in (s1,l1). Save as much 337ca01d6ddSTony Luck * as we can from the end of the buffer. 338ca01d6ddSTony Luck */ 339ca01d6ddSTony Luck static void pstore_dump(struct kmsg_dumper *dumper, 340e2ae715dSKay Sievers enum kmsg_dump_reason reason) 341ca01d6ddSTony Luck { 342e2ae715dSKay Sievers unsigned long total = 0; 343381b872cSSeiji Aguchi const char *why; 344b94fdd07SMatthew Garrett unsigned int part = 1; 345abd4d558SDon Zickus unsigned long flags = 0; 34698e44fdaSNamhyung Kim int is_locked; 347e2ae715dSKay Sievers int ret; 348ca01d6ddSTony Luck 349381b872cSSeiji Aguchi why = get_reason_str(reason); 3509f6af27fSTony Luck 3519f244e9cSSeiji Aguchi if (pstore_cannot_block_path(reason)) { 3529f244e9cSSeiji Aguchi is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags); 3539f244e9cSSeiji Aguchi if (!is_locked) { 3549f244e9cSSeiji Aguchi pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" 3559f244e9cSSeiji Aguchi , in_nmi() ? "NMI" : why); 356959217c8SLi Pengcheng return; 3579f244e9cSSeiji Aguchi } 35898e44fdaSNamhyung Kim } else { 359abd4d558SDon Zickus spin_lock_irqsave(&psinfo->buf_lock, flags); 36098e44fdaSNamhyung Kim is_locked = 1; 36198e44fdaSNamhyung Kim } 362ca01d6ddSTony Luck oopscount++; 363ca01d6ddSTony Luck while (total < kmsg_bytes) { 364e2ae715dSKay Sievers char *dst; 36576cc9580SKees Cook size_t dst_size; 36676cc9580SKees Cook int header_size; 367b0aad7a9SAruna Balakrishnaiah int zipped_len = -1; 36876cc9580SKees Cook size_t dump_size; 369e581ca81SKees Cook struct pstore_record record; 370e581ca81SKees Cook 371e581ca81SKees Cook pstore_record_init(&record, psinfo); 372e581ca81SKees Cook record.type = PSTORE_TYPE_DMESG; 373e581ca81SKees Cook record.count = oopscount; 374e581ca81SKees Cook record.reason = reason; 375e581ca81SKees Cook record.part = part; 376e581ca81SKees Cook record.buf = psinfo->buf; 377e2ae715dSKay Sievers 378f0e2efcfSKonstantin Khlebnikov if (big_oops_buf && is_locked) { 379b0aad7a9SAruna Balakrishnaiah dst = big_oops_buf; 38076cc9580SKees Cook dst_size = big_oops_buf_sz; 381235f6d15SNamhyung Kim } else { 382235f6d15SNamhyung Kim dst = psinfo->buf; 38376cc9580SKees Cook dst_size = psinfo->bufsize; 384235f6d15SNamhyung Kim } 385235f6d15SNamhyung Kim 38676cc9580SKees Cook /* Write dump header. */ 38776cc9580SKees Cook header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, 38876cc9580SKees Cook oopscount, part); 38976cc9580SKees Cook dst_size -= header_size; 390b0aad7a9SAruna Balakrishnaiah 39176cc9580SKees Cook /* Write dump contents. */ 39276cc9580SKees Cook if (!kmsg_dump_get_buffer(dumper, true, dst + header_size, 39376cc9580SKees Cook dst_size, &dump_size)) 394b0aad7a9SAruna Balakrishnaiah break; 395b0aad7a9SAruna Balakrishnaiah 396235f6d15SNamhyung Kim if (big_oops_buf && is_locked) { 397b0aad7a9SAruna Balakrishnaiah zipped_len = pstore_compress(dst, psinfo->buf, 39876cc9580SKees Cook header_size + dump_size, 39976cc9580SKees Cook psinfo->bufsize); 400b0aad7a9SAruna Balakrishnaiah 401b0aad7a9SAruna Balakrishnaiah if (zipped_len > 0) { 40276cc9580SKees Cook record.compressed = true; 40376cc9580SKees Cook record.size = zipped_len; 404b0aad7a9SAruna Balakrishnaiah } else { 40576cc9580SKees Cook record.size = copy_kmsg_to_buffer(header_size, 40676cc9580SKees Cook dump_size); 407b0aad7a9SAruna Balakrishnaiah } 408b0aad7a9SAruna Balakrishnaiah } else { 40976cc9580SKees Cook record.size = header_size + dump_size; 410b0aad7a9SAruna Balakrishnaiah } 411b0aad7a9SAruna Balakrishnaiah 41276cc9580SKees Cook ret = psinfo->write(&record); 413b238b8faSChen Gong if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) 4146dda9266SLuck, Tony pstore_new_entry = 1; 415e2ae715dSKay Sievers 41676cc9580SKees Cook total += record.size; 41756280682SMatthew Garrett part++; 418ca01d6ddSTony Luck } 419abd4d558SDon Zickus if (is_locked) 4209f244e9cSSeiji Aguchi spin_unlock_irqrestore(&psinfo->buf_lock, flags); 421ca01d6ddSTony Luck } 422ca01d6ddSTony Luck 423ca01d6ddSTony Luck static struct kmsg_dumper pstore_dumper = { 424ca01d6ddSTony Luck .dump = pstore_dump, 425ca01d6ddSTony Luck }; 426ca01d6ddSTony Luck 427306e5c2aSGeliang Tang /* 428306e5c2aSGeliang Tang * Register with kmsg_dump to save last part of console log on panic. 429306e5c2aSGeliang Tang */ 43018730411SGeliang Tang static void pstore_register_kmsg(void) 43118730411SGeliang Tang { 43218730411SGeliang Tang kmsg_dump_register(&pstore_dumper); 43318730411SGeliang Tang } 43418730411SGeliang Tang 435ee1d2674SGeliang Tang static void pstore_unregister_kmsg(void) 436ee1d2674SGeliang Tang { 437ee1d2674SGeliang Tang kmsg_dump_unregister(&pstore_dumper); 438ee1d2674SGeliang Tang } 439ee1d2674SGeliang Tang 440f29e5956SAnton Vorontsov #ifdef CONFIG_PSTORE_CONSOLE 441f29e5956SAnton Vorontsov static void pstore_console_write(struct console *con, const char *s, unsigned c) 442f29e5956SAnton Vorontsov { 443f29e5956SAnton Vorontsov const char *e = s + c; 444f29e5956SAnton Vorontsov 445f29e5956SAnton Vorontsov while (s < e) { 446e581ca81SKees Cook struct pstore_record record; 447f29e5956SAnton Vorontsov unsigned long flags; 448f29e5956SAnton Vorontsov 449e581ca81SKees Cook pstore_record_init(&record, psinfo); 450e581ca81SKees Cook record.type = PSTORE_TYPE_CONSOLE; 451e581ca81SKees Cook 452f29e5956SAnton Vorontsov if (c > psinfo->bufsize) 453f29e5956SAnton Vorontsov c = psinfo->bufsize; 45480c9d03cSChuansheng Liu 45580c9d03cSChuansheng Liu if (oops_in_progress) { 45680c9d03cSChuansheng Liu if (!spin_trylock_irqsave(&psinfo->buf_lock, flags)) 45780c9d03cSChuansheng Liu break; 45880c9d03cSChuansheng Liu } else { 459f29e5956SAnton Vorontsov spin_lock_irqsave(&psinfo->buf_lock, flags); 46080c9d03cSChuansheng Liu } 461b10b4711SKees Cook record.buf = (char *)s; 462b10b4711SKees Cook record.size = c; 4634c9ec219SKees Cook psinfo->write(&record); 464f29e5956SAnton Vorontsov spin_unlock_irqrestore(&psinfo->buf_lock, flags); 465f29e5956SAnton Vorontsov s += c; 466f29e5956SAnton Vorontsov c = e - s; 467f29e5956SAnton Vorontsov } 468f29e5956SAnton Vorontsov } 469f29e5956SAnton Vorontsov 470f29e5956SAnton Vorontsov static struct console pstore_console = { 471f29e5956SAnton Vorontsov .name = "pstore", 472f29e5956SAnton Vorontsov .write = pstore_console_write, 473f29e5956SAnton Vorontsov .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, 474f29e5956SAnton Vorontsov .index = -1, 475f29e5956SAnton Vorontsov }; 476f29e5956SAnton Vorontsov 477f29e5956SAnton Vorontsov static void pstore_register_console(void) 478f29e5956SAnton Vorontsov { 479f29e5956SAnton Vorontsov register_console(&pstore_console); 480f29e5956SAnton Vorontsov } 481ee1d2674SGeliang Tang 482ee1d2674SGeliang Tang static void pstore_unregister_console(void) 483ee1d2674SGeliang Tang { 484ee1d2674SGeliang Tang unregister_console(&pstore_console); 485ee1d2674SGeliang Tang } 486f29e5956SAnton Vorontsov #else 487f29e5956SAnton Vorontsov static void pstore_register_console(void) {} 488ee1d2674SGeliang Tang static void pstore_unregister_console(void) {} 489f29e5956SAnton Vorontsov #endif 490f29e5956SAnton Vorontsov 4914c9ec219SKees Cook static int pstore_write_user_compat(struct pstore_record *record, 492fdd03118SKees Cook const char __user *buf) 4935bf6d1b9SMark Salyzyn { 49430800d99SKees Cook int ret = 0; 4955bf6d1b9SMark Salyzyn 49630800d99SKees Cook if (record->buf) 49730800d99SKees Cook return -EINVAL; 4985bf6d1b9SMark Salyzyn 499077090afSGeliang Tang record->buf = memdup_user(buf, record->size); 500dfd6fa39SHirofumi Nakagawa if (IS_ERR(record->buf)) { 501077090afSGeliang Tang ret = PTR_ERR(record->buf); 50230800d99SKees Cook goto out; 5035bf6d1b9SMark Salyzyn } 50430800d99SKees Cook 5054c9ec219SKees Cook ret = record->psi->write(record); 50630800d99SKees Cook 50730800d99SKees Cook kfree(record->buf); 508077090afSGeliang Tang out: 50930800d99SKees Cook record->buf = NULL; 51030800d99SKees Cook 51130800d99SKees Cook return unlikely(ret < 0) ? ret : record->size; 5125bf6d1b9SMark Salyzyn } 5135bf6d1b9SMark Salyzyn 514ca01d6ddSTony Luck /* 515ca01d6ddSTony Luck * platform specific persistent storage driver registers with 516ca01d6ddSTony Luck * us here. If pstore is already mounted, call the platform 517ca01d6ddSTony Luck * read function right away to populate the file system. If not 518ca01d6ddSTony Luck * then the pstore mount code will call us later to fill out 519ca01d6ddSTony Luck * the file system. 520ca01d6ddSTony Luck */ 521ca01d6ddSTony Luck int pstore_register(struct pstore_info *psi) 522ca01d6ddSTony Luck { 523ca01d6ddSTony Luck struct module *owner = psi->owner; 524ca01d6ddSTony Luck 5250d7cd09aSKees Cook if (backend && strcmp(backend, psi->name)) { 5260d7cd09aSKees Cook pr_warn("ignoring unexpected backend '%s'\n", psi->name); 5278e48b1a8SLenny Szubowicz return -EPERM; 5280d7cd09aSKees Cook } 5298e48b1a8SLenny Szubowicz 5304c9ec219SKees Cook /* Sanity check flags. */ 5314c9ec219SKees Cook if (!psi->flags) { 5324c9ec219SKees Cook pr_warn("backend '%s' must support at least one frontend\n", 5334c9ec219SKees Cook psi->name); 5344c9ec219SKees Cook return -EINVAL; 5354c9ec219SKees Cook } 5364c9ec219SKees Cook 5374c9ec219SKees Cook /* Check for required functions. */ 5384c9ec219SKees Cook if (!psi->read || !psi->write) { 5394c9ec219SKees Cook pr_warn("backend '%s' must implement read() and write()\n", 5404c9ec219SKees Cook psi->name); 5414c9ec219SKees Cook return -EINVAL; 5424c9ec219SKees Cook } 5434c9ec219SKees Cook 544ca01d6ddSTony Luck spin_lock(&pstore_lock); 545ca01d6ddSTony Luck if (psinfo) { 5460d7cd09aSKees Cook pr_warn("backend '%s' already loaded: ignoring '%s'\n", 5470d7cd09aSKees Cook psinfo->name, psi->name); 548ca01d6ddSTony Luck spin_unlock(&pstore_lock); 549ca01d6ddSTony Luck return -EBUSY; 550ca01d6ddSTony Luck } 551dee28e72SMatthew Garrett 5524c9ec219SKees Cook if (!psi->write_user) 5534c9ec219SKees Cook psi->write_user = pstore_write_user_compat; 554ca01d6ddSTony Luck psinfo = psi; 555f6f82851SKees Cook mutex_init(&psinfo->read_mutex); 556ca01d6ddSTony Luck spin_unlock(&pstore_lock); 557ca01d6ddSTony Luck 558ca01d6ddSTony Luck if (owner && !try_module_get(owner)) { 559ca01d6ddSTony Luck psinfo = NULL; 560ca01d6ddSTony Luck return -EINVAL; 561ca01d6ddSTony Luck } 562ca01d6ddSTony Luck 563b0aad7a9SAruna Balakrishnaiah allocate_buf_for_compression(); 564b0aad7a9SAruna Balakrishnaiah 565ca01d6ddSTony Luck if (pstore_is_mounted()) 5666dda9266SLuck, Tony pstore_get_records(0); 567ca01d6ddSTony Luck 568c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_DMESG) 56918730411SGeliang Tang pstore_register_kmsg(); 570c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_CONSOLE) 571f29e5956SAnton Vorontsov pstore_register_console(); 572c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_FTRACE) 57365f8c95eSAnton Vorontsov pstore_register_ftrace(); 574c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_PMSG) 5759d5438f4SMark Salyzyn pstore_register_pmsg(); 576ca01d6ddSTony Luck 5776330d553SKees Cook /* Start watching for new records, if desired. */ 578a3f5f075SAnton Vorontsov if (pstore_update_ms >= 0) { 579a3f5f075SAnton Vorontsov pstore_timer.expires = jiffies + 580a3f5f075SAnton Vorontsov msecs_to_jiffies(pstore_update_ms); 5816dda9266SLuck, Tony add_timer(&pstore_timer); 582a3f5f075SAnton Vorontsov } 5836dda9266SLuck, Tony 58442222c2aSWang Long /* 58542222c2aSWang Long * Update the module parameter backend, so it is visible 58642222c2aSWang Long * through /sys/module/pstore/parameters/backend 58742222c2aSWang Long */ 58842222c2aSWang Long backend = psi->name; 58942222c2aSWang Long 590ef748853SFabian Frederick pr_info("Registered %s as persistent store backend\n", psi->name); 5918e48b1a8SLenny Szubowicz 5921344dd86SKees Cook module_put(owner); 5931344dd86SKees Cook 594ca01d6ddSTony Luck return 0; 595ca01d6ddSTony Luck } 596ca01d6ddSTony Luck EXPORT_SYMBOL_GPL(pstore_register); 597ca01d6ddSTony Luck 598ee1d2674SGeliang Tang void pstore_unregister(struct pstore_info *psi) 599ee1d2674SGeliang Tang { 6006330d553SKees Cook /* Stop timer and make sure all work has finished. */ 6016330d553SKees Cook pstore_update_ms = -1; 6026330d553SKees Cook del_timer_sync(&pstore_timer); 6036330d553SKees Cook flush_work(&pstore_work); 6046330d553SKees Cook 605c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_PMSG) 606ee1d2674SGeliang Tang pstore_unregister_pmsg(); 607c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_FTRACE) 608ee1d2674SGeliang Tang pstore_unregister_ftrace(); 609c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_CONSOLE) 610ee1d2674SGeliang Tang pstore_unregister_console(); 611c950fd6fSNamhyung Kim if (psi->flags & PSTORE_FLAGS_DMESG) 612ee1d2674SGeliang Tang pstore_unregister_kmsg(); 613ee1d2674SGeliang Tang 614ee1d2674SGeliang Tang free_buf_for_compression(); 615ee1d2674SGeliang Tang 616ee1d2674SGeliang Tang psinfo = NULL; 617ee1d2674SGeliang Tang backend = NULL; 618ee1d2674SGeliang Tang } 619ee1d2674SGeliang Tang EXPORT_SYMBOL_GPL(pstore_unregister); 620ee1d2674SGeliang Tang 621634f8f51SKees Cook static void decompress_record(struct pstore_record *record) 622634f8f51SKees Cook { 623634f8f51SKees Cook int unzipped_len; 6247e8cc8dcSKees Cook char *decompressed; 625634f8f51SKees Cook 6264a16d1cbSAnkit Kumar if (!record->compressed) 6274a16d1cbSAnkit Kumar return; 6284a16d1cbSAnkit Kumar 629634f8f51SKees Cook /* Only PSTORE_TYPE_DMESG support compression. */ 6304a16d1cbSAnkit Kumar if (record->type != PSTORE_TYPE_DMESG) { 631634f8f51SKees Cook pr_warn("ignored compressed record type %d\n", record->type); 632634f8f51SKees Cook return; 633634f8f51SKees Cook } 634634f8f51SKees Cook 635634f8f51SKees Cook /* No compression method has created the common buffer. */ 636634f8f51SKees Cook if (!big_oops_buf) { 637634f8f51SKees Cook pr_warn("no decompression buffer allocated\n"); 638634f8f51SKees Cook return; 639634f8f51SKees Cook } 640634f8f51SKees Cook 641634f8f51SKees Cook unzipped_len = pstore_decompress(record->buf, big_oops_buf, 642634f8f51SKees Cook record->size, big_oops_buf_sz); 6437e8cc8dcSKees Cook if (unzipped_len <= 0) { 6447e8cc8dcSKees Cook pr_err("decompression failed: %d\n", unzipped_len); 6457e8cc8dcSKees Cook return; 6467e8cc8dcSKees Cook } 6477e8cc8dcSKees Cook 6487e8cc8dcSKees Cook /* Build new buffer for decompressed contents. */ 6497e8cc8dcSKees Cook decompressed = kmalloc(unzipped_len + record->ecc_notice_size, 6507e8cc8dcSKees Cook GFP_KERNEL); 6517e8cc8dcSKees Cook if (!decompressed) { 6527e8cc8dcSKees Cook pr_err("decompression ran out of memory\n"); 6537e8cc8dcSKees Cook return; 6547e8cc8dcSKees Cook } 6557e8cc8dcSKees Cook memcpy(decompressed, big_oops_buf, unzipped_len); 6567e8cc8dcSKees Cook 6577e8cc8dcSKees Cook /* Append ECC notice to decompressed buffer. */ 6587e8cc8dcSKees Cook memcpy(decompressed + unzipped_len, record->buf + record->size, 659634f8f51SKees Cook record->ecc_notice_size); 6607e8cc8dcSKees Cook 6617e8cc8dcSKees Cook /* Swap out compresed contents with decompressed contents. */ 662634f8f51SKees Cook kfree(record->buf); 6637e8cc8dcSKees Cook record->buf = decompressed; 664634f8f51SKees Cook record->size = unzipped_len; 665634f8f51SKees Cook record->compressed = false; 666634f8f51SKees Cook } 667634f8f51SKees Cook 668ca01d6ddSTony Luck /* 6693a7d2fd1SKees Cook * Read all the records from one persistent store backend. Create 6706dda9266SLuck, Tony * files in our filesystem. Don't warn about -EEXIST errors 6716dda9266SLuck, Tony * when we are re-scanning the backing store looking to add new 6726dda9266SLuck, Tony * error records. 673ca01d6ddSTony Luck */ 6743a7d2fd1SKees Cook void pstore_get_backend_records(struct pstore_info *psi, 6753a7d2fd1SKees Cook struct dentry *root, int quiet) 676ca01d6ddSTony Luck { 6772a2b0acfSKees Cook int failed = 0; 678656de42eSKees Cook unsigned int stop_loop = 65536; 679ca01d6ddSTony Luck 6803a7d2fd1SKees Cook if (!psi || !root) 681ca01d6ddSTony Luck return; 682ca01d6ddSTony Luck 683f6f82851SKees Cook mutex_lock(&psi->read_mutex); 6842174f6dfSKees Cook if (psi->open && psi->open(psi)) 68506cf91b4SChen Gong goto out; 68606cf91b4SChen Gong 6871dfff7ddSKees Cook /* 6881dfff7ddSKees Cook * Backend callback read() allocates record.buf. decompress_record() 6891dfff7ddSKees Cook * may reallocate record.buf. On success, pstore_mkfile() will keep 6901dfff7ddSKees Cook * the record.buf, so free it only on failure. 6911dfff7ddSKees Cook */ 692656de42eSKees Cook for (; stop_loop; stop_loop--) { 6932a2b0acfSKees Cook struct pstore_record *record; 6942a2b0acfSKees Cook int rc; 6952a2b0acfSKees Cook 6962a2b0acfSKees Cook record = kzalloc(sizeof(*record), GFP_KERNEL); 6972a2b0acfSKees Cook if (!record) { 6982a2b0acfSKees Cook pr_err("out of memory creating record\n"); 6992a2b0acfSKees Cook break; 7002a2b0acfSKees Cook } 701e581ca81SKees Cook pstore_record_init(record, psi); 7022a2b0acfSKees Cook 7032a2b0acfSKees Cook record->size = psi->read(record); 7042a2b0acfSKees Cook 7052a2b0acfSKees Cook /* No more records left in backend? */ 706f6525b96SDouglas Anderson if (record->size <= 0) { 707f6525b96SDouglas Anderson kfree(record); 7082a2b0acfSKees Cook break; 709f6525b96SDouglas Anderson } 7102a2b0acfSKees Cook 7112a2b0acfSKees Cook decompress_record(record); 7123a7d2fd1SKees Cook rc = pstore_mkfile(root, record); 7131dfff7ddSKees Cook if (rc) { 71483f70f07SKees Cook /* pstore_mkfile() did not take record, so free it. */ 7152a2b0acfSKees Cook kfree(record->buf); 71683f70f07SKees Cook kfree(record); 7171dfff7ddSKees Cook if (rc != -EEXIST || !quiet) 7181dfff7ddSKees Cook failed++; 7191dfff7ddSKees Cook } 720ca01d6ddSTony Luck } 7212174f6dfSKees Cook if (psi->close) 72206cf91b4SChen Gong psi->close(psi); 72306cf91b4SChen Gong out: 724f6f82851SKees Cook mutex_unlock(&psi->read_mutex); 725ca01d6ddSTony Luck 726ca01d6ddSTony Luck if (failed) 727656de42eSKees Cook pr_warn("failed to create %d record(s) from '%s'\n", 728ca01d6ddSTony Luck failed, psi->name); 729656de42eSKees Cook if (!stop_loop) 730656de42eSKees Cook pr_err("looping? Too many records seen from '%s'\n", 731656de42eSKees Cook psi->name); 732ca01d6ddSTony Luck } 733ca01d6ddSTony Luck 7346dda9266SLuck, Tony static void pstore_dowork(struct work_struct *work) 7356dda9266SLuck, Tony { 7366dda9266SLuck, Tony pstore_get_records(1); 7376dda9266SLuck, Tony } 7386dda9266SLuck, Tony 73924ed960aSKees Cook static void pstore_timefunc(struct timer_list *unused) 7406dda9266SLuck, Tony { 7416dda9266SLuck, Tony if (pstore_new_entry) { 7426dda9266SLuck, Tony pstore_new_entry = 0; 7436dda9266SLuck, Tony schedule_work(&pstore_work); 7446dda9266SLuck, Tony } 7456dda9266SLuck, Tony 7466330d553SKees Cook if (pstore_update_ms >= 0) 7476330d553SKees Cook mod_timer(&pstore_timer, 7486330d553SKees Cook jiffies + msecs_to_jiffies(pstore_update_ms)); 7496dda9266SLuck, Tony } 7506dda9266SLuck, Tony 751fe1d4758SKees Cook void __init pstore_choose_compression(void) 752fe1d4758SKees Cook { 753fe1d4758SKees Cook const struct pstore_zbackend *step; 754fe1d4758SKees Cook 755fe1d4758SKees Cook if (!compress) 756fe1d4758SKees Cook return; 757fe1d4758SKees Cook 758fe1d4758SKees Cook for (step = zbackends; step->name; step++) { 759fe1d4758SKees Cook if (!strcmp(compress, step->name)) { 760fe1d4758SKees Cook zbackend = step; 761fe1d4758SKees Cook pr_info("using %s compression\n", zbackend->name); 762fe1d4758SKees Cook return; 763fe1d4758SKees Cook } 764fe1d4758SKees Cook } 765fe1d4758SKees Cook } 766fe1d4758SKees Cook 767fe1d4758SKees Cook module_param(compress, charp, 0444); 768fe1d4758SKees Cook MODULE_PARM_DESC(compress, "Pstore compression to use"); 769fe1d4758SKees Cook 770dee28e72SMatthew Garrett module_param(backend, charp, 0444); 771dee28e72SMatthew Garrett MODULE_PARM_DESC(backend, "Pstore backend to use"); 772