1*ca01d6ddSTony Luck /* 2*ca01d6ddSTony Luck * Persistent Storage - platform driver interface parts. 3*ca01d6ddSTony Luck * 4*ca01d6ddSTony Luck * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> 5*ca01d6ddSTony Luck * 6*ca01d6ddSTony Luck * This program is free software; you can redistribute it and/or modify 7*ca01d6ddSTony Luck * it under the terms of the GNU General Public License version 2 as 8*ca01d6ddSTony Luck * published by the Free Software Foundation. 9*ca01d6ddSTony Luck * 10*ca01d6ddSTony Luck * This program is distributed in the hope that it will be useful, 11*ca01d6ddSTony Luck * but WITHOUT ANY WARRANTY; without even the implied warranty of 12*ca01d6ddSTony Luck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*ca01d6ddSTony Luck * GNU General Public License for more details. 14*ca01d6ddSTony Luck * 15*ca01d6ddSTony Luck * You should have received a copy of the GNU General Public License 16*ca01d6ddSTony Luck * along with this program; if not, write to the Free Software 17*ca01d6ddSTony Luck * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18*ca01d6ddSTony Luck */ 19*ca01d6ddSTony Luck 20*ca01d6ddSTony Luck #include <linux/atomic.h> 21*ca01d6ddSTony Luck #include <linux/types.h> 22*ca01d6ddSTony Luck #include <linux/errno.h> 23*ca01d6ddSTony Luck #include <linux/init.h> 24*ca01d6ddSTony Luck #include <linux/kmsg_dump.h> 25*ca01d6ddSTony Luck #include <linux/module.h> 26*ca01d6ddSTony Luck #include <linux/pstore.h> 27*ca01d6ddSTony Luck #include <linux/string.h> 28*ca01d6ddSTony Luck #include <linux/slab.h> 29*ca01d6ddSTony Luck #include <linux/uaccess.h> 30*ca01d6ddSTony Luck 31*ca01d6ddSTony Luck #include "internal.h" 32*ca01d6ddSTony Luck 33*ca01d6ddSTony Luck /* 34*ca01d6ddSTony Luck * pstore_lock just protects "psinfo" during 35*ca01d6ddSTony Luck * calls to pstore_register() 36*ca01d6ddSTony Luck */ 37*ca01d6ddSTony Luck static DEFINE_SPINLOCK(pstore_lock); 38*ca01d6ddSTony Luck static struct pstore_info *psinfo; 39*ca01d6ddSTony Luck 40*ca01d6ddSTony Luck /* How much of the console log to snapshot. /sys/fs/pstore/kmsg_bytes */ 41*ca01d6ddSTony Luck static unsigned long kmsg_bytes = 10240; 42*ca01d6ddSTony Luck 43*ca01d6ddSTony Luck static ssize_t b_show(struct kobject *kobj, 44*ca01d6ddSTony Luck struct kobj_attribute *attr, char *buf) 45*ca01d6ddSTony Luck { 46*ca01d6ddSTony Luck return snprintf(buf, PAGE_SIZE, "%lu\n", kmsg_bytes); 47*ca01d6ddSTony Luck } 48*ca01d6ddSTony Luck 49*ca01d6ddSTony Luck static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr, 50*ca01d6ddSTony Luck const char *buf, size_t count) 51*ca01d6ddSTony Luck { 52*ca01d6ddSTony Luck return (sscanf(buf, "%lu", &kmsg_bytes) > 0) ? count : 0; 53*ca01d6ddSTony Luck } 54*ca01d6ddSTony Luck 55*ca01d6ddSTony Luck struct kobj_attribute pstore_kmsg_bytes_attr = 56*ca01d6ddSTony Luck __ATTR(kmsg_bytes, S_IRUGO | S_IWUSR, b_show, b_store); 57*ca01d6ddSTony Luck 58*ca01d6ddSTony Luck /* Tag each group of saved records with a sequence number */ 59*ca01d6ddSTony Luck static int oopscount; 60*ca01d6ddSTony Luck 61*ca01d6ddSTony Luck /* 62*ca01d6ddSTony Luck * callback from kmsg_dump. (s2,l2) has the most recently 63*ca01d6ddSTony Luck * written bytes, older bytes are in (s1,l1). Save as much 64*ca01d6ddSTony Luck * as we can from the end of the buffer. 65*ca01d6ddSTony Luck */ 66*ca01d6ddSTony Luck static void pstore_dump(struct kmsg_dumper *dumper, 67*ca01d6ddSTony Luck enum kmsg_dump_reason reason, 68*ca01d6ddSTony Luck const char *s1, unsigned long l1, 69*ca01d6ddSTony Luck const char *s2, unsigned long l2) 70*ca01d6ddSTony Luck { 71*ca01d6ddSTony Luck unsigned long s1_start, s2_start; 72*ca01d6ddSTony Luck unsigned long l1_cpy, l2_cpy; 73*ca01d6ddSTony Luck unsigned long size, total = 0; 74*ca01d6ddSTony Luck char *dst; 75*ca01d6ddSTony Luck u64 id; 76*ca01d6ddSTony Luck int hsize, part = 1; 77*ca01d6ddSTony Luck 78*ca01d6ddSTony Luck mutex_lock(&psinfo->buf_mutex); 79*ca01d6ddSTony Luck oopscount++; 80*ca01d6ddSTony Luck while (total < kmsg_bytes) { 81*ca01d6ddSTony Luck dst = psinfo->buf; 82*ca01d6ddSTony Luck hsize = sprintf(dst, "Oops#%d Part%d\n", oopscount, part++); 83*ca01d6ddSTony Luck size = psinfo->bufsize - hsize; 84*ca01d6ddSTony Luck dst += hsize; 85*ca01d6ddSTony Luck 86*ca01d6ddSTony Luck l2_cpy = min(l2, size); 87*ca01d6ddSTony Luck l1_cpy = min(l1, size - l2_cpy); 88*ca01d6ddSTony Luck 89*ca01d6ddSTony Luck if (l1_cpy + l2_cpy == 0) 90*ca01d6ddSTony Luck break; 91*ca01d6ddSTony Luck 92*ca01d6ddSTony Luck s2_start = l2 - l2_cpy; 93*ca01d6ddSTony Luck s1_start = l1 - l1_cpy; 94*ca01d6ddSTony Luck 95*ca01d6ddSTony Luck memcpy(dst, s1 + s1_start, l1_cpy); 96*ca01d6ddSTony Luck memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); 97*ca01d6ddSTony Luck 98*ca01d6ddSTony Luck id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy); 99*ca01d6ddSTony Luck if (pstore_is_mounted()) 100*ca01d6ddSTony Luck pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, 101*ca01d6ddSTony Luck psinfo->buf, hsize + l1_cpy + l2_cpy, 102*ca01d6ddSTony Luck CURRENT_TIME, psinfo->erase); 103*ca01d6ddSTony Luck l1 -= l1_cpy; 104*ca01d6ddSTony Luck l2 -= l2_cpy; 105*ca01d6ddSTony Luck total += l1_cpy + l2_cpy; 106*ca01d6ddSTony Luck } 107*ca01d6ddSTony Luck mutex_unlock(&psinfo->buf_mutex); 108*ca01d6ddSTony Luck } 109*ca01d6ddSTony Luck 110*ca01d6ddSTony Luck static struct kmsg_dumper pstore_dumper = { 111*ca01d6ddSTony Luck .dump = pstore_dump, 112*ca01d6ddSTony Luck }; 113*ca01d6ddSTony Luck 114*ca01d6ddSTony Luck /* 115*ca01d6ddSTony Luck * platform specific persistent storage driver registers with 116*ca01d6ddSTony Luck * us here. If pstore is already mounted, call the platform 117*ca01d6ddSTony Luck * read function right away to populate the file system. If not 118*ca01d6ddSTony Luck * then the pstore mount code will call us later to fill out 119*ca01d6ddSTony Luck * the file system. 120*ca01d6ddSTony Luck * 121*ca01d6ddSTony Luck * Register with kmsg_dump to save last part of console log on panic. 122*ca01d6ddSTony Luck */ 123*ca01d6ddSTony Luck int pstore_register(struct pstore_info *psi) 124*ca01d6ddSTony Luck { 125*ca01d6ddSTony Luck struct module *owner = psi->owner; 126*ca01d6ddSTony Luck 127*ca01d6ddSTony Luck spin_lock(&pstore_lock); 128*ca01d6ddSTony Luck if (psinfo) { 129*ca01d6ddSTony Luck spin_unlock(&pstore_lock); 130*ca01d6ddSTony Luck return -EBUSY; 131*ca01d6ddSTony Luck } 132*ca01d6ddSTony Luck psinfo = psi; 133*ca01d6ddSTony Luck spin_unlock(&pstore_lock); 134*ca01d6ddSTony Luck 135*ca01d6ddSTony Luck if (owner && !try_module_get(owner)) { 136*ca01d6ddSTony Luck psinfo = NULL; 137*ca01d6ddSTony Luck return -EINVAL; 138*ca01d6ddSTony Luck } 139*ca01d6ddSTony Luck 140*ca01d6ddSTony Luck if (pstore_is_mounted()) 141*ca01d6ddSTony Luck pstore_get_records(); 142*ca01d6ddSTony Luck 143*ca01d6ddSTony Luck kmsg_dump_register(&pstore_dumper); 144*ca01d6ddSTony Luck 145*ca01d6ddSTony Luck return 0; 146*ca01d6ddSTony Luck } 147*ca01d6ddSTony Luck EXPORT_SYMBOL_GPL(pstore_register); 148*ca01d6ddSTony Luck 149*ca01d6ddSTony Luck /* 150*ca01d6ddSTony Luck * Read all the records from the persistent store. Create and 151*ca01d6ddSTony Luck * file files in our filesystem. 152*ca01d6ddSTony Luck */ 153*ca01d6ddSTony Luck void pstore_get_records(void) 154*ca01d6ddSTony Luck { 155*ca01d6ddSTony Luck struct pstore_info *psi = psinfo; 156*ca01d6ddSTony Luck size_t size; 157*ca01d6ddSTony Luck u64 id; 158*ca01d6ddSTony Luck enum pstore_type_id type; 159*ca01d6ddSTony Luck struct timespec time; 160*ca01d6ddSTony Luck int failed = 0; 161*ca01d6ddSTony Luck 162*ca01d6ddSTony Luck if (!psi) 163*ca01d6ddSTony Luck return; 164*ca01d6ddSTony Luck 165*ca01d6ddSTony Luck mutex_lock(&psinfo->buf_mutex); 166*ca01d6ddSTony Luck while ((size = psi->read(&id, &type, &time)) > 0) { 167*ca01d6ddSTony Luck if (pstore_mkfile(type, psi->name, id, psi->buf, size, 168*ca01d6ddSTony Luck time, psi->erase)) 169*ca01d6ddSTony Luck failed++; 170*ca01d6ddSTony Luck } 171*ca01d6ddSTony Luck mutex_unlock(&psinfo->buf_mutex); 172*ca01d6ddSTony Luck 173*ca01d6ddSTony Luck if (failed) 174*ca01d6ddSTony Luck printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", 175*ca01d6ddSTony Luck failed, psi->name); 176*ca01d6ddSTony Luck } 177*ca01d6ddSTony Luck 178*ca01d6ddSTony Luck /* 179*ca01d6ddSTony Luck * Call platform driver to write a record to the 180*ca01d6ddSTony Luck * persistent store. 181*ca01d6ddSTony Luck */ 182*ca01d6ddSTony Luck int pstore_write(enum pstore_type_id type, char *buf, size_t size) 183*ca01d6ddSTony Luck { 184*ca01d6ddSTony Luck u64 id; 185*ca01d6ddSTony Luck 186*ca01d6ddSTony Luck if (!psinfo) 187*ca01d6ddSTony Luck return -ENODEV; 188*ca01d6ddSTony Luck 189*ca01d6ddSTony Luck if (size > psinfo->bufsize) 190*ca01d6ddSTony Luck return -EFBIG; 191*ca01d6ddSTony Luck 192*ca01d6ddSTony Luck mutex_lock(&psinfo->buf_mutex); 193*ca01d6ddSTony Luck memcpy(psinfo->buf, buf, size); 194*ca01d6ddSTony Luck id = psinfo->write(type, size); 195*ca01d6ddSTony Luck if (pstore_is_mounted()) 196*ca01d6ddSTony Luck pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, 197*ca01d6ddSTony Luck size, CURRENT_TIME, psinfo->erase); 198*ca01d6ddSTony Luck mutex_unlock(&psinfo->buf_mutex); 199*ca01d6ddSTony Luck 200*ca01d6ddSTony Luck return 0; 201*ca01d6ddSTony Luck } 202*ca01d6ddSTony Luck EXPORT_SYMBOL_GPL(pstore_write); 203