2010-12-29 01:25:21 +03:00
/*
* Persistent Storage - platform driver interface parts .
*
2012-05-26 17:20:19 +04:00
* Copyright ( C ) 2007 - 2008 Google , Inc .
2010-12-29 01:25:21 +03:00
* 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>
2012-05-26 17:20:19 +04:00
# include <linux/console.h>
2010-12-29 01:25:21 +03:00
# include <linux/module.h>
# include <linux/pstore.h>
# include <linux/string.h>
2011-08-12 02:14:39 +04:00
# include <linux/timer.h>
2010-12-29 01:25:21 +03:00
# include <linux/slab.h>
# include <linux/uaccess.h>
2011-08-12 21:54:51 +04:00
# include <linux/hardirq.h>
2011-08-12 02:14:39 +04:00
# include <linux/workqueue.h>
2010-12-29 01:25:21 +03:00
# include "internal.h"
2011-08-12 02:14:39 +04:00
/*
* 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 ) ;
2010-12-29 01:25:21 +03:00
/*
* pstore_lock just protects " psinfo " during
* calls to pstore_register ( )
*/
static DEFINE_SPINLOCK ( pstore_lock ) ;
static struct pstore_info * psinfo ;
2011-07-22 00:57:55 +04:00
static char * backend ;
2011-03-19 01:33:43 +03:00
/* How much of the console log to snapshot */
2010-12-29 01:25:21 +03:00
static unsigned long kmsg_bytes = 10240 ;
2011-03-19 01:33:43 +03:00
void pstore_set_kmsg_bytes ( int bytes )
2010-12-29 01:25:21 +03:00
{
2011-03-19 01:33:43 +03:00
kmsg_bytes = bytes ;
2010-12-29 01:25:21 +03:00
}
/* Tag each group of saved records with a sequence number */
static int oopscount ;
2012-03-17 02:36:59 +04:00
static const char * get_reason_str ( enum kmsg_dump_reason reason )
{
switch ( reason ) {
case KMSG_DUMP_PANIC :
return " Panic " ;
case KMSG_DUMP_OOPS :
return " Oops " ;
case KMSG_DUMP_EMERG :
return " Emergency " ;
case KMSG_DUMP_RESTART :
return " Restart " ;
case KMSG_DUMP_HALT :
return " Halt " ;
case KMSG_DUMP_POWEROFF :
return " Poweroff " ;
default :
return " Unknown " ;
}
}
2011-03-23 02:01:49 +03:00
2010-12-29 01:25:21 +03:00
/*
* 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 ;
2012-03-17 02:36:59 +04:00
char * dst ;
const char * why ;
2010-12-29 01:25:21 +03:00
u64 id ;
2011-10-12 20:17:24 +04:00
int hsize , ret ;
2011-07-22 00:57:54 +04:00
unsigned int part = 1 ;
2011-08-12 21:54:51 +04:00
unsigned long flags = 0 ;
int is_locked = 0 ;
2010-12-29 01:25:21 +03:00
2012-03-17 02:36:59 +04:00
why = get_reason_str ( reason ) ;
2011-03-23 02:01:49 +03:00
2011-08-12 21:54:51 +04:00
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 \n " ) ;
} else
spin_lock_irqsave ( & psinfo - > buf_lock , flags ) ;
2010-12-29 01:25:21 +03:00
oopscount + + ;
while ( total < kmsg_bytes ) {
dst = psinfo - > buf ;
2011-07-22 00:57:53 +04:00
hsize = sprintf ( dst , " %s#%d Part%d \n " , why , oopscount , part ) ;
2010-12-29 01:25:21 +03:00
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 ) ;
2011-11-18 01:13:29 +04:00
ret = psinfo - > write ( PSTORE_TYPE_DMESG , reason , & id , part ,
2011-07-22 00:57:53 +04:00
hsize + l1_cpy + l2_cpy , psinfo ) ;
2011-10-12 20:17:24 +04:00
if ( ret = = 0 & & reason = = KMSG_DUMP_OOPS & & pstore_is_mounted ( ) )
2011-08-12 02:14:39 +04:00
pstore_new_entry = 1 ;
2010-12-29 01:25:21 +03:00
l1 - = l1_cpy ;
l2 - = l2_cpy ;
total + = l1_cpy + l2_cpy ;
2011-07-22 00:57:53 +04:00
part + + ;
2010-12-29 01:25:21 +03:00
}
2011-08-12 21:54:51 +04:00
if ( in_nmi ( ) ) {
if ( is_locked )
spin_unlock ( & psinfo - > buf_lock ) ;
} else
spin_unlock_irqrestore ( & psinfo - > buf_lock , flags ) ;
2010-12-29 01:25:21 +03:00
}
static struct kmsg_dumper pstore_dumper = {
. dump = pstore_dump ,
} ;
2012-05-26 17:20:19 +04:00
# ifdef CONFIG_PSTORE_CONSOLE
static void pstore_console_write ( struct console * con , const char * s , unsigned c )
{
const char * e = s + c ;
while ( s < e ) {
unsigned long flags ;
if ( c > psinfo - > bufsize )
c = psinfo - > bufsize ;
spin_lock_irqsave ( & psinfo - > buf_lock , flags ) ;
memcpy ( psinfo - > buf , s , c ) ;
psinfo - > write ( PSTORE_TYPE_CONSOLE , 0 , NULL , 0 , c , psinfo ) ;
spin_unlock_irqrestore ( & psinfo - > buf_lock , flags ) ;
s + = c ;
c = e - s ;
}
}
static struct console pstore_console = {
. name = " pstore " ,
. write = pstore_console_write ,
. flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME ,
. index = - 1 ,
} ;
static void pstore_register_console ( void )
{
register_console ( & pstore_console ) ;
}
# else
static void pstore_register_console ( void ) { }
# endif
2010-12-29 01:25:21 +03:00
/*
* 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 ;
}
2011-07-22 00:57:55 +04:00
if ( backend & & strcmp ( backend , psi - > name ) ) {
spin_unlock ( & pstore_lock ) ;
return - EINVAL ;
}
2010-12-29 01:25:21 +03:00
psinfo = psi ;
2011-11-18 00:58:07 +04:00
mutex_init ( & psinfo - > read_mutex ) ;
2010-12-29 01:25:21 +03:00
spin_unlock ( & pstore_lock ) ;
if ( owner & & ! try_module_get ( owner ) ) {
psinfo = NULL ;
return - EINVAL ;
}
if ( pstore_is_mounted ( ) )
2011-08-12 02:14:39 +04:00
pstore_get_records ( 0 ) ;
2010-12-29 01:25:21 +03:00
kmsg_dump_register ( & pstore_dumper ) ;
2012-05-26 17:20:19 +04:00
pstore_register_console ( ) ;
2010-12-29 01:25:21 +03:00
2011-08-12 02:14:39 +04:00
pstore_timer . expires = jiffies + PSTORE_INTERVAL ;
add_timer ( & pstore_timer ) ;
2010-12-29 01:25:21 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( pstore_register ) ;
/*
2011-08-12 02:14:39 +04:00
* 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 .
2010-12-29 01:25:21 +03:00
*/
2011-08-12 02:14:39 +04:00
void pstore_get_records ( int quiet )
2010-12-29 01:25:21 +03:00
{
struct pstore_info * psi = psinfo ;
2011-11-18 00:58:07 +04:00
char * buf = NULL ;
2011-05-16 21:58:57 +04:00
ssize_t size ;
2010-12-29 01:25:21 +03:00
u64 id ;
enum pstore_type_id type ;
struct timespec time ;
2011-05-16 22:00:27 +04:00
int failed = 0 , rc ;
2010-12-29 01:25:21 +03:00
if ( ! psi )
return ;
2011-11-18 00:58:07 +04:00
mutex_lock ( & psi - > read_mutex ) ;
2011-11-19 01:49:00 +04:00
if ( psi - > open & & psi - > open ( psi ) )
2011-05-16 22:00:27 +04:00
goto out ;
2011-11-18 00:58:07 +04:00
while ( ( size = psi - > read ( & id , & type , & time , & buf , psi ) ) > 0 ) {
rc = pstore_mkfile ( type , psi - > name , id , buf , ( size_t ) size ,
2011-08-12 02:14:39 +04:00
time , psi ) ;
2011-11-18 00:58:07 +04:00
kfree ( buf ) ;
buf = NULL ;
2011-08-12 02:14:39 +04:00
if ( rc & & ( rc ! = - EEXIST | | ! quiet ) )
2010-12-29 01:25:21 +03:00
failed + + ;
}
2011-11-19 01:49:00 +04:00
if ( psi - > close )
psi - > close ( psi ) ;
2011-05-16 22:00:27 +04:00
out :
2011-11-18 00:58:07 +04:00
mutex_unlock ( & psi - > read_mutex ) ;
2010-12-29 01:25:21 +03:00
if ( failed )
printk ( KERN_WARNING " pstore: failed to load %d record(s) from '%s' \n " ,
failed , psi - > name ) ;
}
2011-08-12 02:14:39 +04:00
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 ) ;
}
2011-07-22 00:57:55 +04:00
module_param ( backend , charp , 0444 ) ;
MODULE_PARM_DESC ( backend , " Pstore backend to use " ) ;