2005-06-24 09:03:52 +04:00
/*
* linux / fs / nfsd / nfs4recover . c
*
* Copyright ( c ) 2004 The Regents of the University of Michigan .
* All rights reserved .
*
* Andy Adamson < andros @ citi . umich . edu >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
* BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
*/
2006-08-24 13:10:20 +04:00
# include <linux/err.h>
2005-06-24 09:03:52 +04:00
# include <linux/sunrpc/svc.h>
# include <linux/nfsd/nfsd.h>
# include <linux/nfs4.h>
# include <linux/nfsd/state.h>
# include <linux/nfsd/xdr4.h>
2005-06-24 09:04:25 +04:00
# include <linux/param.h>
# include <linux/file.h>
# include <linux/namei.h>
2005-06-24 09:03:52 +04:00
# include <asm/uaccess.h>
2007-10-30 12:35:04 +03:00
# include <linux/scatterlist.h>
2005-06-24 09:03:52 +04:00
# include <linux/crypto.h>
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 01:22:52 +04:00
# include <linux/sched.h>
2008-02-16 01:37:34 +03:00
# include <linux/mount.h>
2005-06-24 09:03:52 +04:00
# define NFSDDBG_FACILITY NFSDDBG_PROC
2005-06-24 09:04:25 +04:00
/* Globals */
static struct nameidata rec_dir ;
static int rec_dir_init = 0 ;
static void
nfs4_save_user ( uid_t * saveuid , gid_t * savegid )
{
* saveuid = current - > fsuid ;
* savegid = current - > fsgid ;
current - > fsuid = 0 ;
current - > fsgid = 0 ;
}
static void
nfs4_reset_user ( uid_t saveuid , gid_t savegid )
{
current - > fsuid = saveuid ;
current - > fsgid = savegid ;
}
2005-06-24 09:03:52 +04:00
static void
md5_to_hex ( char * out , char * md5 )
{
int i ;
for ( i = 0 ; i < 16 ; i + + ) {
unsigned char c = md5 [ i ] ;
* out + + = ' 0 ' + ( ( c & 0xf0 ) > > 4 ) + ( c > = 0xa0 ) * ( ' a ' - ' 9 ' - 1 ) ;
* out + + = ' 0 ' + ( c & 0x0f ) + ( ( c & 0x0f ) > = 0x0a ) * ( ' a ' - ' 9 ' - 1 ) ;
}
* out = ' \0 ' ;
}
2006-10-20 10:28:59 +04:00
__be32
2005-06-24 09:03:52 +04:00
nfs4_make_rec_clidname ( char * dname , struct xdr_netobj * clname )
{
struct xdr_netobj cksum ;
2006-08-24 13:10:20 +04:00
struct hash_desc desc ;
2007-10-22 21:43:30 +04:00
struct scatterlist sg ;
2006-10-20 10:28:59 +04:00
__be32 status = nfserr_resource ;
2005-06-24 09:03:52 +04:00
dprintk ( " NFSD: nfs4_make_rec_clidname for %.*s \n " ,
clname - > len , clname - > data ) ;
2006-08-24 13:10:20 +04:00
desc . flags = CRYPTO_TFM_REQ_MAY_SLEEP ;
desc . tfm = crypto_alloc_hash ( " md5 " , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( desc . tfm ) )
goto out_no_tfm ;
cksum . len = crypto_hash_digestsize ( desc . tfm ) ;
2005-06-24 09:03:52 +04:00
cksum . data = kmalloc ( cksum . len , GFP_KERNEL ) ;
if ( cksum . data = = NULL )
goto out ;
2007-10-22 21:43:30 +04:00
sg_init_one ( & sg , clname - > data , clname - > len ) ;
2005-06-24 09:03:52 +04:00
2007-10-22 21:43:30 +04:00
if ( crypto_hash_digest ( & desc , & sg , sg . length , cksum . data ) )
2006-08-24 13:10:20 +04:00
goto out ;
2005-06-24 09:03:52 +04:00
md5_to_hex ( dname , cksum . data ) ;
kfree ( cksum . data ) ;
status = nfs_ok ;
out :
2006-08-24 13:10:20 +04:00
crypto_free_hash ( desc . tfm ) ;
out_no_tfm :
2005-06-24 09:03:52 +04:00
return status ;
}
2005-06-24 09:04:25 +04:00
2005-07-08 04:59:11 +04:00
static void
nfsd4_sync_rec_dir ( void )
2005-06-24 09:04:30 +04:00
{
2008-02-15 06:34:32 +03:00
mutex_lock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
nfsd_sync_dir ( rec_dir . path . dentry ) ;
mutex_unlock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
}
int
nfsd4_create_clid_dir ( struct nfs4_client * clp )
{
char * dname = clp - > cl_recdir ;
struct dentry * dentry ;
uid_t uid ;
gid_t gid ;
int status ;
dprintk ( " NFSD: nfsd4_create_clid_dir for \" %s \" \n " , dname ) ;
if ( ! rec_dir_init | | clp - > cl_firststate )
return 0 ;
nfs4_save_user ( & uid , & gid ) ;
/* lock the parent */
2008-02-15 06:34:32 +03:00
mutex_lock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
2008-02-15 06:34:32 +03:00
dentry = lookup_one_len ( dname , rec_dir . path . dentry , HEXDIR_LEN - 1 ) ;
2005-06-24 09:04:30 +04:00
if ( IS_ERR ( dentry ) ) {
status = PTR_ERR ( dentry ) ;
goto out_unlock ;
}
status = - EEXIST ;
if ( dentry - > d_inode ) {
dprintk ( " NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS \n " ) ;
goto out_put ;
}
2008-02-16 01:37:57 +03:00
status = mnt_want_write ( rec_dir . path . mnt ) ;
if ( status )
goto out_put ;
2008-02-15 06:34:32 +03:00
status = vfs_mkdir ( rec_dir . path . dentry - > d_inode , dentry , S_IRWXU ) ;
2008-02-16 01:37:57 +03:00
mnt_drop_write ( rec_dir . path . mnt ) ;
2005-06-24 09:04:30 +04:00
out_put :
dput ( dentry ) ;
out_unlock :
2008-02-15 06:34:32 +03:00
mutex_unlock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
if ( status = = 0 ) {
clp - > cl_firststate = 1 ;
2005-07-08 04:59:11 +04:00
nfsd4_sync_rec_dir ( ) ;
2005-06-24 09:04:30 +04:00
}
nfs4_reset_user ( uid , gid ) ;
dprintk ( " NFSD: nfsd4_create_clid_dir returns %d \n " , status ) ;
return status ;
}
2005-06-24 09:04:25 +04:00
typedef int ( recdir_func ) ( struct dentry * , struct dentry * ) ;
struct dentry_list {
struct dentry * dentry ;
struct list_head list ;
} ;
struct dentry_list_arg {
struct list_head dentries ;
struct dentry * parent ;
} ;
static int
nfsd4_build_dentrylist ( void * arg , const char * name , int namlen ,
2006-10-03 12:13:46 +04:00
loff_t offset , u64 ino , unsigned int d_type )
2005-06-24 09:04:25 +04:00
{
struct dentry_list_arg * dla = arg ;
struct list_head * dentries = & dla - > dentries ;
struct dentry * parent = dla - > parent ;
struct dentry * dentry ;
struct dentry_list * child ;
if ( name & & isdotent ( name , namlen ) )
2006-10-20 10:28:59 +04:00
return 0 ;
2005-06-24 09:04:25 +04:00
dentry = lookup_one_len ( name , parent , namlen ) ;
if ( IS_ERR ( dentry ) )
return PTR_ERR ( dentry ) ;
child = kmalloc ( sizeof ( * child ) , GFP_KERNEL ) ;
if ( child = = NULL )
return - ENOMEM ;
child - > dentry = dentry ;
list_add ( & child - > list , dentries ) ;
return 0 ;
}
static int
nfsd4_list_rec_dir ( struct dentry * dir , recdir_func * f )
{
struct file * filp ;
struct dentry_list_arg dla = {
. parent = dir ,
} ;
struct list_head * dentries = & dla . dentries ;
struct dentry_list * child ;
uid_t uid ;
gid_t gid ;
int status ;
if ( ! rec_dir_init )
return 0 ;
nfs4_save_user ( & uid , & gid ) ;
2008-02-15 06:34:32 +03:00
filp = dentry_open ( dget ( dir ) , mntget ( rec_dir . path . mnt ) , O_RDONLY ) ;
2005-06-24 09:04:25 +04:00
status = PTR_ERR ( filp ) ;
if ( IS_ERR ( filp ) )
goto out ;
INIT_LIST_HEAD ( dentries ) ;
status = vfs_readdir ( filp , nfsd4_build_dentrylist , & dla ) ;
fput ( filp ) ;
while ( ! list_empty ( dentries ) ) {
child = list_entry ( dentries - > next , struct dentry_list , list ) ;
status = f ( dir , child - > dentry ) ;
if ( status )
goto out ;
list_del ( & child - > list ) ;
dput ( child - > dentry ) ;
kfree ( child ) ;
}
out :
while ( ! list_empty ( dentries ) ) {
child = list_entry ( dentries - > next , struct dentry_list , list ) ;
list_del ( & child - > list ) ;
dput ( child - > dentry ) ;
kfree ( child ) ;
}
nfs4_reset_user ( uid , gid ) ;
return status ;
}
2005-06-24 09:04:30 +04:00
static int
nfsd4_remove_clid_file ( struct dentry * dir , struct dentry * dentry )
{
int status ;
if ( ! S_ISREG ( dir - > d_inode - > i_mode ) ) {
printk ( " nfsd4: non-file found in client recovery directory \n " ) ;
return - EINVAL ;
}
2006-12-08 13:39:38 +03:00
mutex_lock_nested ( & dir - > d_inode - > i_mutex , I_MUTEX_PARENT ) ;
2005-06-24 09:04:30 +04:00
status = vfs_unlink ( dir - > d_inode , dentry ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dir - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
return status ;
}
static int
nfsd4_clear_clid_dir ( struct dentry * dir , struct dentry * dentry )
{
int status ;
/* For now this directory should already be empty, but we empty it of
* any regular files anyway , just in case the directory was created by
* a kernel from the future . . . . */
nfsd4_list_rec_dir ( dentry , nfsd4_remove_clid_file ) ;
[PATCH] NFS4: fix for recursive locking problem
When I was performing some operations on NFS, I got below error on server
side.
=============================================
[ INFO: possible recursive locking detected ]
2.6.19-prep #1
---------------------------------------------
nfsd4/3525 is trying to acquire lock:
(&inode->i_mutex){--..}, at: [<c0611e5a>] mutex_lock+0x21/0x24
but task is already holding lock:
(&inode->i_mutex){--..}, at: [<c0611e5a>] mutex_lock+0x21/0x24
other info that might help us debug this:
2 locks held by nfsd4/3525:
#0: (client_mutex){--..}, at: [<c0611e5a>] mutex_lock+0x21/0x24
#1: (&inode->i_mutex){--..}, at: [<c0611e5a>] mutex_lock+0x21/0x24
stack backtrace:
[<c04051ed>] show_trace_log_lvl+0x58/0x16a
[<c04057fa>] show_trace+0xd/0x10
[<c0405913>] dump_stack+0x19/0x1b
[<c043b6f1>] __lock_acquire+0x778/0x99c
[<c043be86>] lock_acquire+0x4b/0x6d
[<c0611ceb>] __mutex_lock_slowpath+0xbc/0x20a
[<c0611e5a>] mutex_lock+0x21/0x24
[<c047fd7e>] vfs_rmdir+0x76/0xf8
[<f94b7ce9>] nfsd4_clear_clid_dir+0x2c/0x41 [nfsd]
[<f94b7de9>] nfsd4_remove_clid_dir+0xb1/0xe8 [nfsd]
[<f94b307b>] laundromat_main+0x9b/0x1c3 [nfsd]
[<c04333d6>] run_workqueue+0x7a/0xbb
[<c0433d0b>] worker_thread+0xd2/0x107
[<c0436285>] kthread+0xc3/0xf2
[<c0402005>] kernel_thread_helper+0x5/0xb
===================================================================
Cause for this problem was,2 successive mutex_lock calls on 2 diffrent inodes ,as shown below
static int
nfsd4_clear_clid_dir(struct dentry *dir, struct dentry *dentry)
{
int status;
/* For now this directory should already be empty, but we empty it of
* any regular files anyway, just in case the directory was created by
* a kernel from the future.... */
nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file);
mutex_lock(&dir->d_inode->i_mutex);
status = vfs_rmdir(dir->d_inode, dentry);
...
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error = may_delete(dir, dentry, 1);
if (error)
return error;
if (!dir->i_op || !dir->i_op->rmdir)
return -EPERM;
DQUOT_INIT(dir);
mutex_lock(&dentry->d_inode->i_mutex);
...
So I have developed the patch to overcome this problem.
Signed-off-by: Srinivasa DS <srinivasa@in.ibm.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-11-03 09:07:12 +03:00
mutex_lock_nested ( & dir - > d_inode - > i_mutex , I_MUTEX_PARENT ) ;
2005-06-24 09:04:30 +04:00
status = vfs_rmdir ( dir - > d_inode , dentry ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dir - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
return status ;
}
static int
nfsd4_unlink_clid_dir ( char * name , int namlen )
{
struct dentry * dentry ;
int status ;
dprintk ( " NFSD: nfsd4_unlink_clid_dir. name %.*s \n " , namlen , name ) ;
2008-02-15 06:34:32 +03:00
mutex_lock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
dentry = lookup_one_len ( name , rec_dir . path . dentry , namlen ) ;
mutex_unlock ( & rec_dir . path . dentry - > d_inode - > i_mutex ) ;
2005-06-24 09:04:30 +04:00
if ( IS_ERR ( dentry ) ) {
status = PTR_ERR ( dentry ) ;
return status ;
}
status = - ENOENT ;
if ( ! dentry - > d_inode )
goto out ;
2008-02-15 06:34:32 +03:00
status = nfsd4_clear_clid_dir ( rec_dir . path . dentry , dentry ) ;
2005-06-24 09:04:30 +04:00
out :
dput ( dentry ) ;
return status ;
}
void
nfsd4_remove_clid_dir ( struct nfs4_client * clp )
{
uid_t uid ;
gid_t gid ;
int status ;
if ( ! rec_dir_init | | ! clp - > cl_firststate )
return ;
2008-02-16 01:37:34 +03:00
status = mnt_want_write ( rec_dir . path . mnt ) ;
if ( status )
goto out ;
2005-07-08 04:59:13 +04:00
clp - > cl_firststate = 0 ;
2005-06-24 09:04:30 +04:00
nfs4_save_user ( & uid , & gid ) ;
status = nfsd4_unlink_clid_dir ( clp - > cl_recdir , HEXDIR_LEN - 1 ) ;
nfs4_reset_user ( uid , gid ) ;
if ( status = = 0 )
2005-07-08 04:59:11 +04:00
nfsd4_sync_rec_dir ( ) ;
2008-02-16 01:37:34 +03:00
mnt_drop_write ( rec_dir . path . mnt ) ;
out :
2005-06-24 09:04:30 +04:00
if ( status )
printk ( " NFSD: Failed to remove expired client state directory "
" %.*s \n " , HEXDIR_LEN , clp - > cl_recdir ) ;
return ;
}
static int
purge_old ( struct dentry * parent , struct dentry * child )
{
int status ;
if ( nfs4_has_reclaimed_state ( child - > d_name . name ) )
2006-10-20 10:28:59 +04:00
return 0 ;
2005-06-24 09:04:30 +04:00
status = nfsd4_clear_clid_dir ( parent , child ) ;
if ( status )
printk ( " failed to remove client recovery directory %s \n " ,
child - > d_name . name ) ;
/* Keep trying, success or failure: */
2006-10-20 10:28:59 +04:00
return 0 ;
2005-06-24 09:04:30 +04:00
}
void
nfsd4_recdir_purge_old ( void ) {
int status ;
if ( ! rec_dir_init )
return ;
2008-02-16 01:37:34 +03:00
status = mnt_want_write ( rec_dir . path . mnt ) ;
if ( status )
goto out ;
2008-02-15 06:34:32 +03:00
status = nfsd4_list_rec_dir ( rec_dir . path . dentry , purge_old ) ;
2005-06-24 09:04:30 +04:00
if ( status = = 0 )
2005-07-08 04:59:11 +04:00
nfsd4_sync_rec_dir ( ) ;
2008-02-16 01:37:34 +03:00
mnt_drop_write ( rec_dir . path . mnt ) ;
out :
2005-06-24 09:04:30 +04:00
if ( status )
printk ( " nfsd4: failed to purge old clients from recovery "
2008-02-15 06:34:32 +03:00
" directory %s \n " , rec_dir . path . dentry - > d_name . name ) ;
2005-06-24 09:04:30 +04:00
}
2005-06-24 09:04:25 +04:00
static int
load_recdir ( struct dentry * parent , struct dentry * child )
{
if ( child - > d_name . len ! = HEXDIR_LEN - 1 ) {
printk ( " nfsd4: illegal name %s in recovery directory \n " ,
child - > d_name . name ) ;
/* Keep trying; maybe the others are OK: */
2006-10-20 10:28:59 +04:00
return 0 ;
2005-06-24 09:04:25 +04:00
}
nfs4_client_to_reclaim ( child - > d_name . name ) ;
2006-10-20 10:28:59 +04:00
return 0 ;
2005-06-24 09:04:25 +04:00
}
int
nfsd4_recdir_load ( void ) {
int status ;
2008-02-15 06:34:32 +03:00
status = nfsd4_list_rec_dir ( rec_dir . path . dentry , load_recdir ) ;
2005-06-24 09:04:25 +04:00
if ( status )
printk ( " nfsd4: failed loading clients from recovery "
2008-02-15 06:34:32 +03:00
" directory %s \n " , rec_dir . path . dentry - > d_name . name ) ;
2005-06-24 09:04:25 +04:00
return status ;
}
/*
* Hold reference to the recovery directory .
*/
void
nfsd4_init_recdir ( char * rec_dirname )
{
uid_t uid = 0 ;
gid_t gid = 0 ;
int status ;
printk ( " NFSD: Using %s as the NFSv4 state recovery directory \n " ,
rec_dirname ) ;
BUG_ON ( rec_dir_init ) ;
nfs4_save_user ( & uid , & gid ) ;
2006-01-19 04:43:29 +03:00
status = path_lookup ( rec_dirname , LOOKUP_FOLLOW | LOOKUP_DIRECTORY ,
& rec_dir ) ;
if ( status )
printk ( " NFSD: unable to find recovery directory %s \n " ,
2005-06-24 09:04:25 +04:00
rec_dirname ) ;
if ( ! status )
rec_dir_init = 1 ;
nfs4_reset_user ( uid , gid ) ;
}
void
nfsd4_shutdown_recdir ( void )
{
if ( ! rec_dir_init )
return ;
rec_dir_init = 0 ;
2008-02-15 06:34:35 +03:00
path_put ( & rec_dir . path ) ;
2005-06-24 09:04:25 +04:00
}