2013-11-24 18:54:58 +04:00
/*
* fs / kernfs / mount . c - kernfs mount implementation
*
* Copyright ( c ) 2001 - 3 Patrick Mochel
* Copyright ( c ) 2007 SUSE Linux Products GmbH
* Copyright ( c ) 2007 , 2013 Tejun Heo < tj @ kernel . org >
*
* This file is released under the GPLv2 .
*/
2013-11-28 23:54:44 +04:00
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/init.h>
# include <linux/magic.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include "kernfs-internal.h"
2013-12-11 23:11:57 +04:00
struct kmem_cache * kernfs_node_cache ;
2013-11-28 23:54:44 +04:00
2014-02-03 23:09:10 +04:00
static int kernfs_sop_remount_fs ( struct super_block * sb , int * flags , char * data )
{
struct kernfs_root * root = kernfs_info ( sb ) - > root ;
struct kernfs_syscall_ops * scops = root - > syscall_ops ;
if ( scops & & scops - > remount_fs )
return scops - > remount_fs ( root , flags , data ) ;
return 0 ;
}
static int kernfs_sop_show_options ( struct seq_file * sf , struct dentry * dentry )
{
struct kernfs_root * root = kernfs_root ( dentry - > d_fsdata ) ;
struct kernfs_syscall_ops * scops = root - > syscall_ops ;
if ( scops & & scops - > show_options )
return scops - > show_options ( sf , root ) ;
return 0 ;
}
2014-02-14 12:57:27 +04:00
const struct super_operations kernfs_sops = {
2013-11-28 23:54:44 +04:00
. statfs = simple_statfs ,
. drop_inode = generic_delete_inode ,
2013-12-11 23:11:58 +04:00
. evict_inode = kernfs_evict_inode ,
2014-02-03 23:09:10 +04:00
. remount_fs = kernfs_sop_remount_fs ,
. show_options = kernfs_sop_show_options ,
2013-11-28 23:54:44 +04:00
} ;
2014-02-03 23:09:15 +04:00
/**
* kernfs_root_from_sb - determine kernfs_root associated with a super_block
* @ sb : the super_block in question
*
* Return the kernfs_root associated with @ sb . If @ sb is not a kernfs one ,
* % NULL is returned .
*/
struct kernfs_root * kernfs_root_from_sb ( struct super_block * sb )
{
if ( sb - > s_op = = & kernfs_sops )
return kernfs_info ( sb ) - > root ;
return NULL ;
}
2014-04-26 11:40:28 +04:00
static int kernfs_fill_super ( struct super_block * sb , unsigned long magic )
2013-11-28 23:54:44 +04:00
{
2013-12-11 23:11:55 +04:00
struct kernfs_super_info * info = kernfs_info ( sb ) ;
2013-11-28 23:54:44 +04:00
struct inode * inode ;
struct dentry * root ;
2014-04-09 19:07:30 +04:00
info - > sb = sb ;
2013-11-28 23:54:44 +04:00
sb - > s_blocksize = PAGE_CACHE_SIZE ;
sb - > s_blocksize_bits = PAGE_CACHE_SHIFT ;
2014-04-26 11:40:28 +04:00
sb - > s_magic = magic ;
2013-12-11 23:11:57 +04:00
sb - > s_op = & kernfs_sops ;
2013-11-28 23:54:44 +04:00
sb - > s_time_gran = 1 ;
/* get root inode, initialize and unlock it */
2013-12-11 23:11:57 +04:00
mutex_lock ( & kernfs_mutex ) ;
2013-12-11 23:11:58 +04:00
inode = kernfs_get_inode ( sb , info - > root - > kn ) ;
2013-12-11 23:11:57 +04:00
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:44 +04:00
if ( ! inode ) {
2013-12-11 23:11:58 +04:00
pr_debug ( " kernfs: could not get root inode \n " ) ;
2013-11-28 23:54:44 +04:00
return - ENOMEM ;
}
/* instantiate and link root dentry */
root = d_make_root ( inode ) ;
if ( ! root ) {
pr_debug ( " %s: could not get root dentry! \n " , __func__ ) ;
return - ENOMEM ;
}
2013-12-11 23:11:53 +04:00
kernfs_get ( info - > root - > kn ) ;
root - > d_fsdata = info - > root - > kn ;
2013-11-28 23:54:44 +04:00
sb - > s_root = root ;
2013-12-11 23:11:57 +04:00
sb - > s_d_op = & kernfs_dops ;
2013-11-28 23:54:44 +04:00
return 0 ;
}
2013-12-11 23:11:58 +04:00
static int kernfs_test_super ( struct super_block * sb , void * data )
2013-11-28 23:54:44 +04:00
{
2013-12-11 23:11:55 +04:00
struct kernfs_super_info * sb_info = kernfs_info ( sb ) ;
struct kernfs_super_info * info = data ;
2013-11-28 23:54:44 +04:00
return sb_info - > root = = info - > root & & sb_info - > ns = = info - > ns ;
}
2013-12-11 23:11:58 +04:00
static int kernfs_set_super ( struct super_block * sb , void * data )
2013-11-28 23:54:44 +04:00
{
int error ;
error = set_anon_super ( sb , data ) ;
if ( ! error )
sb - > s_fs_info = data ;
return error ;
}
/**
* kernfs_super_ns - determine the namespace tag of a kernfs super_block
* @ sb : super_block of interest
*
* Return the namespace tag associated with kernfs super_block @ sb .
*/
const void * kernfs_super_ns ( struct super_block * sb )
{
2013-12-11 23:11:55 +04:00
struct kernfs_super_info * info = kernfs_info ( sb ) ;
2013-11-28 23:54:44 +04:00
return info - > ns ;
}
/**
* kernfs_mount_ns - kernfs mount helper
* @ fs_type : file_system_type of the fs being mounted
* @ flags : mount flags specified for the mount
* @ root : kernfs_root of the hierarchy being mounted
2014-04-26 11:40:28 +04:00
* @ magic : file system specific magic number
2014-02-25 15:28:44 +04:00
* @ new_sb_created : tell the caller if we allocated a new superblock
2013-11-28 23:54:44 +04:00
* @ ns : optional namespace tag of the mount
*
* This is to be called from each kernfs user ' s file_system_type - > mount ( )
* implementation , which should pass through the specified @ fs_type and
* @ flags , and specify the hierarchy and namespace tag to mount via @ root
* and @ ns , respectively .
*
* The return value can be passed to the vfs layer verbatim .
*/
struct dentry * kernfs_mount_ns ( struct file_system_type * fs_type , int flags ,
2014-04-26 11:40:28 +04:00
struct kernfs_root * root , unsigned long magic ,
bool * new_sb_created , const void * ns )
2013-11-28 23:54:44 +04:00
{
struct super_block * sb ;
2013-12-11 23:11:55 +04:00
struct kernfs_super_info * info ;
2013-11-28 23:54:44 +04:00
int error ;
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
if ( ! info )
return ERR_PTR ( - ENOMEM ) ;
info - > root = root ;
info - > ns = ns ;
2013-12-11 23:11:58 +04:00
sb = sget ( fs_type , kernfs_test_super , kernfs_set_super , flags , info ) ;
2013-11-28 23:54:44 +04:00
if ( IS_ERR ( sb ) | | sb - > s_fs_info ! = info )
kfree ( info ) ;
if ( IS_ERR ( sb ) )
return ERR_CAST ( sb ) ;
2014-02-25 15:28:44 +04:00
if ( new_sb_created )
* new_sb_created = ! sb - > s_root ;
2013-11-28 23:54:44 +04:00
if ( ! sb - > s_root ) {
2014-04-09 19:07:30 +04:00
struct kernfs_super_info * info = kernfs_info ( sb ) ;
2014-04-26 11:40:28 +04:00
error = kernfs_fill_super ( sb , magic ) ;
2013-11-28 23:54:44 +04:00
if ( error ) {
deactivate_locked_super ( sb ) ;
return ERR_PTR ( error ) ;
}
sb - > s_flags | = MS_ACTIVE ;
2014-04-09 19:07:30 +04:00
mutex_lock ( & kernfs_mutex ) ;
list_add ( & info - > node , & root - > supers ) ;
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:44 +04:00
}
return dget ( sb - > s_root ) ;
}
/**
* kernfs_kill_sb - kill_sb for kernfs
* @ sb : super_block being killed
*
* This can be used directly for file_system_type - > kill_sb ( ) . If a kernfs
* user needs extra cleanup , it can implement its own kill_sb ( ) and call
* this function at the end .
*/
void kernfs_kill_sb ( struct super_block * sb )
{
2013-12-11 23:11:55 +04:00
struct kernfs_super_info * info = kernfs_info ( sb ) ;
2013-12-11 23:11:53 +04:00
struct kernfs_node * root_kn = sb - > s_root - > d_fsdata ;
2013-11-28 23:54:44 +04:00
2014-04-09 19:07:30 +04:00
mutex_lock ( & kernfs_mutex ) ;
list_del ( & info - > node ) ;
mutex_unlock ( & kernfs_mutex ) ;
2013-11-28 23:54:44 +04:00
/*
* Remove the superblock from fs_supers / s_instances
2013-12-11 23:11:55 +04:00
* so we can ' t find it , before freeing kernfs_super_info .
2013-11-28 23:54:44 +04:00
*/
kill_anon_super ( sb ) ;
kfree ( info ) ;
2013-12-11 23:11:53 +04:00
kernfs_put ( root_kn ) ;
2013-11-28 23:54:44 +04:00
}
2014-06-30 07:50:28 +04:00
/**
* kernfs_pin_sb : try to pin the superblock associated with a kernfs_root
* @ kernfs_root : the kernfs_root in question
* @ ns : the namespace tag
*
* Pin the superblock so the superblock won ' t be destroyed in subsequent
* operations . This can be used to block - > kill_sb ( ) which may be useful
* for kernfs users which dynamically manage superblocks .
*
* Returns NULL if there ' s no superblock associated to this kernfs_root , or
* - EINVAL if the superblock is being freed .
*/
struct super_block * kernfs_pin_sb ( struct kernfs_root * root , const void * ns )
{
struct kernfs_super_info * info ;
struct super_block * sb = NULL ;
mutex_lock ( & kernfs_mutex ) ;
list_for_each_entry ( info , & root - > supers , node ) {
if ( info - > ns = = ns ) {
sb = info - > sb ;
if ( ! atomic_inc_not_zero ( & info - > sb - > s_active ) )
sb = ERR_PTR ( - EINVAL ) ;
break ;
}
}
mutex_unlock ( & kernfs_mutex ) ;
return sb ;
}
2013-11-28 23:54:44 +04:00
void __init kernfs_init ( void )
{
2013-12-11 23:11:57 +04:00
kernfs_node_cache = kmem_cache_create ( " kernfs_node_cache " ,
2013-12-11 23:11:53 +04:00
sizeof ( struct kernfs_node ) ,
2013-11-28 23:54:44 +04:00
0 , SLAB_PANIC , NULL ) ;
}