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>
# 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>
# 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 ;
2011-03-18 02:29:15 +03:00
ssize_t size ;
char data [ ] ;
2010-12-29 01:25:21 +03:00
} ;
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 )
{
struct pstore_private * ps = file - > private_data ;
return simple_read_from_buffer ( userbuf , count , ppos , ps - > data , ps - > size ) ;
}
static const struct file_operations pstore_file_operations = {
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2011-03-18 02:29:15 +03:00
. read = pstore_file_read ,
. llseek = default_llseek ,
} ;
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 )
{
struct pstore_private * p = dentry - > d_inode - > i_private ;
2011-11-19 01:49:00 +04:00
if ( p - > psi - > erase )
p - > psi - > erase ( p - > type , p - > id , p - > psi ) ;
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 ;
2011-03-18 21:44:48 +03:00
end_writeback ( 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 ,
} ;
2011-03-18 02:29:15 +03:00
static struct inode * pstore_get_inode ( struct super_block * sb ,
const struct inode * dir , int mode , dev_t dev )
{
struct inode * inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_ino = get_next_ino ( ) ;
inode - > i_uid = inode - > i_gid = 0 ;
inode - > i_mode = mode ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
switch ( mode & S_IFMT ) {
case S_IFREG :
inode - > i_fop = & pstore_file_operations ;
break ;
case S_IFDIR :
inode - > i_op = & pstore_dir_inode_operations ;
inode - > i_fop = & simple_dir_operations ;
inc_nlink ( inode ) ;
break ;
}
}
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 )
{
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 .
*/
int pstore_mkfile ( enum pstore_type_id type , char * psname , u64 id ,
2011-07-22 00:57:52 +04:00
char * data , 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 ;
inode = pstore_get_inode ( pstore_sb , root - > d_inode , S_IFREG | 0444 , 0 ) ;
if ( ! inode )
goto fail ;
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 ;
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 :
sprintf ( name , " dmesg-%s-%lld " , psname , id ) ;
break ;
case PSTORE_TYPE_MCE :
sprintf ( name , " mce-%s-%lld " , psname , id ) ;
break ;
case PSTORE_TYPE_UNKNOWN :
sprintf ( name , " unknown-%s-%lld " , psname , id ) ;
break ;
default :
sprintf ( name , " type%d-%s-%lld " , type , psname , id ) ;
break ;
}
mutex_lock ( & root - > d_inode - > i_mutex ) ;
rc = - ENOSPC ;
dentry = d_alloc_name ( root , name ) ;
if ( IS_ERR ( dentry ) )
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 ) ;
2010-12-29 01:25:21 +03:00
mutex_unlock ( & root - > d_inode - > i_mutex ) ;
2011-03-18 02:29:15 +03:00
return 0 ;
2010-12-29 01:25:21 +03:00
fail_lockedalloc :
mutex_unlock ( & root - > d_inode - > i_mutex ) ;
kfree ( private ) ;
fail_alloc :
iput ( inode ) ;
fail :
return rc ;
}
int pstore_fill_super ( struct super_block * sb , void * data , int silent )
{
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 ) ;
2010-12-29 01:25:21 +03:00
inode = pstore_get_inode ( sb , NULL , S_IFDIR | 0755 , 0 ) ;
2012-02-13 07:08:01 +04:00
if ( inode ) {
/* override ramfs "dir" options so we catch unlink(2) */
inode - > i_op = & pstore_dir_inode_operations ;
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 ,
} ;
static int __init init_pstore_fs ( void )
{
2011-03-19 01:33:43 +03:00
return register_filesystem ( & pstore_fs_type ) ;
2010-12-29 01:25:21 +03:00
}
module_init ( init_pstore_fs )
MODULE_AUTHOR ( " Tony Luck <tony.luck@intel.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;