2010-12-29 01:25:21 +03:00
/*
* Persistent Storage - ramfs parts .
*
* 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/module.h>
# include <linux/fs.h>
# include <linux/fsnotify.h>
# include <linux/pagemap.h>
# include <linux/highmem.h>
# include <linux/time.h>
# include <linux/init.h>
2011-08-12 02:14:39 +04:00
# include <linux/list.h>
2010-12-29 01:25:21 +03:00
# include <linux/string.h>
# include <linux/mount.h>
2012-07-10 04:10:41 +04:00
# include <linux/seq_file.h>
2010-12-29 01:25:21 +03:00
# include <linux/ramfs.h>
2011-03-19 01:33:43 +03:00
# include <linux/parser.h>
2010-12-29 01:25:21 +03:00
# include <linux/sched.h>
# include <linux/magic.h>
# include <linux/pstore.h>
# include <linux/slab.h>
2011-08-12 02:14:39 +04:00
# include <linux/spinlock.h>
2010-12-29 01:25:21 +03:00
# include <linux/uaccess.h>
2014-10-19 22:05:15 +04:00
# include <linux/syslog.h>
2010-12-29 01:25:21 +03:00
# include "internal.h"
# define PSTORE_NAMELEN 64
2011-08-12 02:14:39 +04:00
static DEFINE_SPINLOCK ( allpstore_lock ) ;
static LIST_HEAD ( allpstore ) ;
2010-12-29 01:25:21 +03:00
struct pstore_private {
2011-08-12 02:14:39 +04:00
struct list_head list ;
2011-07-22 00:57:52 +04:00
struct pstore_info * psi ;
2011-07-22 00:57:53 +04:00
enum pstore_type_id type ;
u64 id ;
efi_pstore: Add a sequence counter to a variable name
[Issue]
Currently, a variable name, which identifies each entry, consists of type, id and ctime.
But if multiple events happens in a short time, a second/third event may fail to log because
efi_pstore can't distinguish each event with current variable name.
[Solution]
A reasonable way to identify all events precisely is introducing a sequence counter to
the variable name.
The sequence counter has already supported in a pstore layer with "oopscount".
So, this patch adds it to a variable name.
Also, it is passed to read/erase callbacks of platform drivers in accordance with
the modification of the variable name.
<before applying this patch>
a variable name of first event: dump-type0-1-12345678
a variable name of second event: dump-type0-1-12345678
type:0
id:1
ctime:12345678
If multiple events happen in a short time, efi_pstore can't distinguish them because
variable names are same among them.
<after applying this patch>
it can be distinguishable by adding a sequence counter as follows.
a variable name of first event: dump-type0-1-1-12345678
a variable name of Second event: dump-type0-1-2-12345678
type:0
id:1
sequence counter: 1(first event), 2(second event)
ctime:12345678
In case of a write callback executed in pstore_console_write(), "0" is added to
an argument of the write callback because it just logs all kernel messages and
doesn't need to care about multiple events.
Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Mike Waychison <mikew@google.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
2012-11-27 04:07:44 +04:00
int count ;
2011-03-18 02:29:15 +03:00
ssize_t size ;
char data [ ] ;
2010-12-29 01:25:21 +03:00
} ;
2012-07-10 04:10:41 +04:00
struct pstore_ftrace_seq_data {
const void * ptr ;
size_t off ;
size_t size ;
} ;
# define REC_SIZE sizeof(struct pstore_ftrace_record)
static void * pstore_ftrace_seq_start ( struct seq_file * s , loff_t * pos )
{
struct pstore_private * ps = s - > private ;
struct pstore_ftrace_seq_data * data ;
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return NULL ;
data - > off = ps - > size % REC_SIZE ;
data - > off + = * pos * REC_SIZE ;
if ( data - > off + REC_SIZE > ps - > size ) {
kfree ( data ) ;
return NULL ;
}
return data ;
}
static void pstore_ftrace_seq_stop ( struct seq_file * s , void * v )
{
kfree ( v ) ;
}
static void * pstore_ftrace_seq_next ( struct seq_file * s , void * v , loff_t * pos )
{
struct pstore_private * ps = s - > private ;
struct pstore_ftrace_seq_data * data = v ;
data - > off + = REC_SIZE ;
if ( data - > off + REC_SIZE > ps - > size )
return NULL ;
( * pos ) + + ;
return data ;
}
static int pstore_ftrace_seq_show ( struct seq_file * s , void * v )
{
struct pstore_private * ps = s - > private ;
struct pstore_ftrace_seq_data * data = v ;
struct pstore_ftrace_record * rec = ( void * ) ( ps - > data + data - > off ) ;
seq_printf ( s , " %d %08lx %08lx %pf <- %pF \n " ,
pstore_ftrace_decode_cpu ( rec ) , rec - > ip , rec - > parent_ip ,
( void * ) rec - > ip , ( void * ) rec - > parent_ip ) ;
return 0 ;
}
static const struct seq_operations pstore_ftrace_seq_ops = {
. start = pstore_ftrace_seq_start ,
. next = pstore_ftrace_seq_next ,
. stop = pstore_ftrace_seq_stop ,
. show = pstore_ftrace_seq_show ,
} ;
2014-10-19 22:05:15 +04:00
static int pstore_check_syslog_permissions ( struct pstore_private * ps )
{
switch ( ps - > type ) {
case PSTORE_TYPE_DMESG :
case PSTORE_TYPE_CONSOLE :
return check_syslog_permissions ( SYSLOG_ACTION_READ_ALL ,
SYSLOG_FROM_READER ) ;
default :
return 0 ;
}
}
2011-03-18 02:29:15 +03:00
static ssize_t pstore_file_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
2012-07-10 04:10:41 +04:00
struct seq_file * sf = file - > private_data ;
struct pstore_private * ps = sf - > private ;
2011-03-18 02:29:15 +03:00
2012-07-10 04:10:41 +04:00
if ( ps - > type = = PSTORE_TYPE_FTRACE )
return seq_read ( file , userbuf , count , ppos ) ;
2011-03-18 02:29:15 +03:00
return simple_read_from_buffer ( userbuf , count , ppos , ps - > data , ps - > size ) ;
}
2012-07-10 04:10:41 +04:00
static int pstore_file_open ( struct inode * inode , struct file * file )
{
struct pstore_private * ps = inode - > i_private ;
struct seq_file * sf ;
int err ;
const struct seq_operations * sops = NULL ;
2014-10-19 22:05:15 +04:00
err = pstore_check_syslog_permissions ( ps ) ;
if ( err )
return err ;
2012-07-10 04:10:41 +04:00
if ( ps - > type = = PSTORE_TYPE_FTRACE )
sops = & pstore_ftrace_seq_ops ;
err = seq_open ( file , sops ) ;
if ( err < 0 )
return err ;
sf = file - > private_data ;
sf - > private = ps ;
return 0 ;
}
2012-12-18 03:59:39 +04:00
static loff_t pstore_file_llseek ( struct file * file , loff_t off , int whence )
2012-07-10 04:10:41 +04:00
{
struct seq_file * sf = file - > private_data ;
if ( sf - > op )
2012-12-18 03:59:39 +04:00
return seq_lseek ( file , off , whence ) ;
return default_llseek ( file , off , whence ) ;
2012-07-10 04:10:41 +04:00
}
2011-03-18 02:29:15 +03:00
static const struct file_operations pstore_file_operations = {
2012-07-10 04:10:41 +04:00
. open = pstore_file_open ,
. read = pstore_file_read ,
. llseek = pstore_file_llseek ,
. release = seq_release ,
2011-03-18 02:29:15 +03:00
} ;
2010-12-29 01:25:21 +03:00
/*
* When a file is unlinked from our file system we call the
* platform driver to erase the record from persistent store .
*/
static int pstore_unlink ( struct inode * dir , struct dentry * dentry )
{
2015-03-18 01:25:59 +03:00
struct pstore_private * p = d_inode ( dentry ) - > i_private ;
2014-10-19 22:05:15 +04:00
int err ;
err = pstore_check_syslog_permissions ( p ) ;
if ( err )
return err ;
2010-12-29 01:25:21 +03:00
2011-11-19 01:49:00 +04:00
if ( p - > psi - > erase )
efi_pstore: Add a sequence counter to a variable name
[Issue]
Currently, a variable name, which identifies each entry, consists of type, id and ctime.
But if multiple events happens in a short time, a second/third event may fail to log because
efi_pstore can't distinguish each event with current variable name.
[Solution]
A reasonable way to identify all events precisely is introducing a sequence counter to
the variable name.
The sequence counter has already supported in a pstore layer with "oopscount".
So, this patch adds it to a variable name.
Also, it is passed to read/erase callbacks of platform drivers in accordance with
the modification of the variable name.
<before applying this patch>
a variable name of first event: dump-type0-1-12345678
a variable name of second event: dump-type0-1-12345678
type:0
id:1
ctime:12345678
If multiple events happen in a short time, efi_pstore can't distinguish them because
variable names are same among them.
<after applying this patch>
it can be distinguishable by adding a sequence counter as follows.
a variable name of first event: dump-type0-1-1-12345678
a variable name of Second event: dump-type0-1-2-12345678
type:0
id:1
sequence counter: 1(first event), 2(second event)
ctime:12345678
In case of a write callback executed in pstore_console_write(), "0" is added to
an argument of the write callback because it just logs all kernel messages and
doesn't need to care about multiple events.
Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Mike Waychison <mikew@google.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
2012-11-27 04:07:44 +04:00
p - > psi - > erase ( p - > type , p - > id , p - > count ,
2015-03-18 01:25:59 +03:00
d_inode ( dentry ) - > i_ctime , p - > psi ) ;
2013-06-25 13:03:56 +04:00
else
return - EPERM ;
2010-12-29 01:25:21 +03:00
return simple_unlink ( dir , dentry ) ;
}
2011-03-18 21:44:48 +03:00
static void pstore_evict_inode ( struct inode * inode )
{
2011-08-12 02:14:39 +04:00
struct pstore_private * p = inode - > i_private ;
unsigned long flags ;
2012-05-03 16:48:02 +04:00
clear_inode ( inode ) ;
2011-08-12 02:14:39 +04:00
if ( p ) {
spin_lock_irqsave ( & allpstore_lock , flags ) ;
list_del ( & p - > list ) ;
spin_unlock_irqrestore ( & allpstore_lock , flags ) ;
kfree ( p ) ;
}
2011-03-18 21:44:48 +03:00
}
2010-12-29 01:25:21 +03:00
static const struct inode_operations pstore_dir_inode_operations = {
. lookup = simple_lookup ,
. unlink = pstore_unlink ,
} ;
2012-03-22 20:26:35 +04:00
static struct inode * pstore_get_inode ( struct super_block * sb )
2011-03-18 02:29:15 +03:00
{
struct inode * inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_ino = get_next_ino ( ) ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
}
return inode ;
}
2011-03-19 01:33:43 +03:00
enum {
Opt_kmsg_bytes , Opt_err
} ;
static const match_table_t tokens = {
{ Opt_kmsg_bytes , " kmsg_bytes=%u " } ,
{ Opt_err , NULL }
} ;
static void parse_options ( char * options )
{
char * p ;
substring_t args [ MAX_OPT_ARGS ] ;
int option ;
if ( ! options )
return ;
while ( ( p = strsep ( & options , " , " ) ) ! = NULL ) {
int token ;
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
switch ( token ) {
case Opt_kmsg_bytes :
if ( ! match_int ( & args [ 0 ] , & option ) )
pstore_set_kmsg_bytes ( option ) ;
break ;
}
}
}
static int pstore_remount ( struct super_block * sb , int * flags , char * data )
{
2014-03-13 18:14:33 +04:00
sync_filesystem ( sb ) ;
2011-03-19 01:33:43 +03:00
parse_options ( data ) ;
return 0 ;
}
2010-12-29 01:25:21 +03:00
static const struct super_operations pstore_ops = {
. statfs = simple_statfs ,
. drop_inode = generic_delete_inode ,
2011-03-18 21:44:48 +03:00
. evict_inode = pstore_evict_inode ,
2011-03-19 01:33:43 +03:00
. remount_fs = pstore_remount ,
2010-12-29 01:25:21 +03:00
. show_options = generic_show_options ,
} ;
static struct super_block * pstore_sb ;
int pstore_is_mounted ( void )
{
2011-03-18 02:29:15 +03:00
return pstore_sb ! = NULL ;
2010-12-29 01:25:21 +03:00
}
/*
* Make a regular file in the root directory of our file system .
* Load it up with " size " bytes of data from " buf " .
* Set the mtime & ctime to the date that this record was originally stored .
*/
efi_pstore: Add a sequence counter to a variable name
[Issue]
Currently, a variable name, which identifies each entry, consists of type, id and ctime.
But if multiple events happens in a short time, a second/third event may fail to log because
efi_pstore can't distinguish each event with current variable name.
[Solution]
A reasonable way to identify all events precisely is introducing a sequence counter to
the variable name.
The sequence counter has already supported in a pstore layer with "oopscount".
So, this patch adds it to a variable name.
Also, it is passed to read/erase callbacks of platform drivers in accordance with
the modification of the variable name.
<before applying this patch>
a variable name of first event: dump-type0-1-12345678
a variable name of second event: dump-type0-1-12345678
type:0
id:1
ctime:12345678
If multiple events happen in a short time, efi_pstore can't distinguish them because
variable names are same among them.
<after applying this patch>
it can be distinguishable by adding a sequence counter as follows.
a variable name of first event: dump-type0-1-1-12345678
a variable name of Second event: dump-type0-1-2-12345678
type:0
id:1
sequence counter: 1(first event), 2(second event)
ctime:12345678
In case of a write callback executed in pstore_console_write(), "0" is added to
an argument of the write callback because it just logs all kernel messages and
doesn't need to care about multiple events.
Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Mike Waychison <mikew@google.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
2012-11-27 04:07:44 +04:00
int pstore_mkfile ( enum pstore_type_id type , char * psname , u64 id , int count ,
2013-08-17 00:53:39 +04:00
char * data , bool compressed , size_t size ,
struct timespec time , struct pstore_info * psi )
2010-12-29 01:25:21 +03:00
{
struct dentry * root = pstore_sb - > s_root ;
struct dentry * dentry ;
struct inode * inode ;
2011-08-12 02:14:39 +04:00
int rc = 0 ;
2010-12-29 01:25:21 +03:00
char name [ PSTORE_NAMELEN ] ;
2011-08-12 02:14:39 +04:00
struct pstore_private * private , * pos ;
unsigned long flags ;
spin_lock_irqsave ( & allpstore_lock , flags ) ;
list_for_each_entry ( pos , & allpstore , list ) {
if ( pos - > type = = type & &
pos - > id = = id & &
pos - > psi = = psi ) {
rc = - EEXIST ;
break ;
}
}
spin_unlock_irqrestore ( & allpstore_lock , flags ) ;
if ( rc )
return rc ;
2010-12-29 01:25:21 +03:00
rc = - ENOMEM ;
2012-03-22 20:26:35 +04:00
inode = pstore_get_inode ( pstore_sb ) ;
2010-12-29 01:25:21 +03:00
if ( ! inode )
goto fail ;
2012-03-22 20:26:35 +04:00
inode - > i_mode = S_IFREG | 0444 ;
inode - > i_fop = & pstore_file_operations ;
2011-03-18 02:29:15 +03:00
private = kmalloc ( sizeof * private + size , GFP_KERNEL ) ;
2010-12-29 01:25:21 +03:00
if ( ! private )
goto fail_alloc ;
2011-07-22 00:57:53 +04:00
private - > type = type ;
2010-12-29 01:25:21 +03:00
private - > id = id ;
efi_pstore: Add a sequence counter to a variable name
[Issue]
Currently, a variable name, which identifies each entry, consists of type, id and ctime.
But if multiple events happens in a short time, a second/third event may fail to log because
efi_pstore can't distinguish each event with current variable name.
[Solution]
A reasonable way to identify all events precisely is introducing a sequence counter to
the variable name.
The sequence counter has already supported in a pstore layer with "oopscount".
So, this patch adds it to a variable name.
Also, it is passed to read/erase callbacks of platform drivers in accordance with
the modification of the variable name.
<before applying this patch>
a variable name of first event: dump-type0-1-12345678
a variable name of second event: dump-type0-1-12345678
type:0
id:1
ctime:12345678
If multiple events happen in a short time, efi_pstore can't distinguish them because
variable names are same among them.
<after applying this patch>
it can be distinguishable by adding a sequence counter as follows.
a variable name of first event: dump-type0-1-1-12345678
a variable name of Second event: dump-type0-1-2-12345678
type:0
id:1
sequence counter: 1(first event), 2(second event)
ctime:12345678
In case of a write callback executed in pstore_console_write(), "0" is added to
an argument of the write callback because it just logs all kernel messages and
doesn't need to care about multiple events.
Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Mike Waychison <mikew@google.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
2012-11-27 04:07:44 +04:00
private - > count = count ;
2011-07-22 00:57:52 +04:00
private - > psi = psi ;
2010-12-29 01:25:21 +03:00
switch ( type ) {
case PSTORE_TYPE_DMESG :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " dmesg-%s-%lld%s " ,
psname , id , compressed ? " .enc.z " : " " ) ;
2010-12-29 01:25:21 +03:00
break ;
2012-05-26 17:20:19 +04:00
case PSTORE_TYPE_CONSOLE :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " console-%s-%lld " , psname , id ) ;
2012-05-26 17:20:19 +04:00
break ;
2012-07-10 04:10:41 +04:00
case PSTORE_TYPE_FTRACE :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " ftrace-%s-%lld " , psname , id ) ;
2012-07-10 04:10:41 +04:00
break ;
2010-12-29 01:25:21 +03:00
case PSTORE_TYPE_MCE :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " mce-%s-%lld " , psname , id ) ;
2010-12-29 01:25:21 +03:00
break ;
2013-06-05 22:51:44 +04:00
case PSTORE_TYPE_PPC_RTAS :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " rtas-%s-%lld " , psname , id ) ;
2013-06-05 22:51:44 +04:00
break ;
2013-06-05 22:52:10 +04:00
case PSTORE_TYPE_PPC_OF :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " powerpc-ofw-%s-%lld " ,
psname , id ) ;
2013-06-05 22:52:10 +04:00
break ;
2013-06-05 22:52:20 +04:00
case PSTORE_TYPE_PPC_COMMON :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " powerpc-common-%s-%lld " ,
psname , id ) ;
2013-06-05 22:52:20 +04:00
break ;
2015-01-17 03:01:10 +03:00
case PSTORE_TYPE_PMSG :
scnprintf ( name , sizeof ( name ) , " pmsg-%s-%lld " , psname , id ) ;
break ;
2015-02-05 22:36:28 +03:00
case PSTORE_TYPE_PPC_OPAL :
sprintf ( name , " powerpc-opal-%s-%lld " , psname , id ) ;
break ;
2010-12-29 01:25:21 +03:00
case PSTORE_TYPE_UNKNOWN :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " unknown-%s-%lld " , psname , id ) ;
2010-12-29 01:25:21 +03:00
break ;
default :
2015-01-06 22:18:24 +03:00
scnprintf ( name , sizeof ( name ) , " type%d-%s-%lld " ,
type , psname , id ) ;
2010-12-29 01:25:21 +03:00
break ;
}
2015-03-18 01:25:59 +03:00
mutex_lock ( & d_inode ( root ) - > i_mutex ) ;
2010-12-29 01:25:21 +03:00
dentry = d_alloc_name ( root , name ) ;
2013-08-14 21:55:49 +04:00
if ( ! dentry )
2010-12-29 01:25:21 +03:00
goto fail_lockedalloc ;
2011-03-18 02:29:15 +03:00
memcpy ( private - > data , data , size ) ;
inode - > i_size = private - > size = size ;
2010-12-29 01:25:21 +03:00
inode - > i_private = private ;
if ( time . tv_sec )
inode - > i_mtime = inode - > i_ctime = time ;
2011-03-18 02:29:15 +03:00
d_add ( dentry , inode ) ;
2010-12-29 01:25:21 +03:00
2011-08-12 02:14:39 +04:00
spin_lock_irqsave ( & allpstore_lock , flags ) ;
list_add ( & private - > list , & allpstore ) ;
spin_unlock_irqrestore ( & allpstore_lock , flags ) ;
2015-03-18 01:25:59 +03:00
mutex_unlock ( & d_inode ( root ) - > i_mutex ) ;
2011-03-18 02:29:15 +03:00
return 0 ;
2010-12-29 01:25:21 +03:00
fail_lockedalloc :
2015-03-18 01:25:59 +03:00
mutex_unlock ( & d_inode ( root ) - > i_mutex ) ;
2010-12-29 01:25:21 +03:00
kfree ( private ) ;
fail_alloc :
iput ( inode ) ;
fail :
return rc ;
}
2012-05-26 17:07:53 +04:00
static int pstore_fill_super ( struct super_block * sb , void * data , int silent )
2010-12-29 01:25:21 +03:00
{
2012-02-13 07:08:01 +04:00
struct inode * inode ;
2010-12-29 01:25:21 +03:00
save_mount_options ( sb , data ) ;
pstore_sb = sb ;
sb - > s_maxbytes = MAX_LFS_FILESIZE ;
sb - > s_blocksize = PAGE_CACHE_SIZE ;
sb - > s_blocksize_bits = PAGE_CACHE_SHIFT ;
sb - > s_magic = PSTOREFS_MAGIC ;
sb - > s_op = & pstore_ops ;
sb - > s_time_gran = 1 ;
2011-03-19 01:33:43 +03:00
parse_options ( data ) ;
2012-03-22 20:26:35 +04:00
inode = pstore_get_inode ( sb ) ;
2012-02-13 07:08:01 +04:00
if ( inode ) {
2012-03-22 20:26:35 +04:00
inode - > i_mode = S_IFDIR | 0755 ;
2012-02-13 07:08:01 +04:00
inode - > i_op = & pstore_dir_inode_operations ;
2012-03-22 20:26:35 +04:00
inode - > i_fop = & simple_dir_operations ;
inc_nlink ( inode ) ;
2010-12-29 01:25:21 +03:00
}
2012-02-13 07:08:01 +04:00
sb - > s_root = d_make_root ( inode ) ;
if ( ! sb - > s_root )
return - ENOMEM ;
2010-12-29 01:25:21 +03:00
2011-08-12 02:14:39 +04:00
pstore_get_records ( 0 ) ;
2010-12-29 01:25:21 +03:00
return 0 ;
}
2011-03-18 02:29:15 +03:00
static struct dentry * pstore_mount ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
2010-12-29 01:25:21 +03:00
{
2011-03-18 02:29:15 +03:00
return mount_single ( fs_type , flags , data , pstore_fill_super ) ;
2010-12-29 01:25:21 +03:00
}
static void pstore_kill_sb ( struct super_block * sb )
{
kill_litter_super ( sb ) ;
pstore_sb = NULL ;
}
static struct file_system_type pstore_fs_type = {
. name = " pstore " ,
2011-03-18 02:29:15 +03:00
. mount = pstore_mount ,
2010-12-29 01:25:21 +03:00
. kill_sb = pstore_kill_sb ,
} ;
2013-02-13 01:07:22 +04:00
static struct kobject * pstore_kobj ;
2010-12-29 01:25:21 +03:00
static int __init init_pstore_fs ( void )
{
2013-02-13 01:07:22 +04:00
int err = 0 ;
/* Create a convenient mount point for people to access pstore */
pstore_kobj = kobject_create_and_add ( " pstore " , fs_kobj ) ;
if ( ! pstore_kobj ) {
err = - ENOMEM ;
goto out ;
}
err = register_filesystem ( & pstore_fs_type ) ;
if ( err < 0 )
kobject_put ( pstore_kobj ) ;
out :
return err ;
2010-12-29 01:25:21 +03:00
}
module_init ( init_pstore_fs )
MODULE_AUTHOR ( " Tony Luck <tony.luck@intel.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;