2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / delegation . c
*
* Copyright ( C ) 2004 Trond Myklebust
*
* NFS file delegation management
*
*/
# include <linux/completion.h>
2006-01-03 11:55:24 +03:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/sched.h>
2009-07-11 22:08:37 +04:00
# include <linux/smp_lock.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
# include <linux/nfs4.h>
# include <linux/nfs_fs.h>
# include <linux/nfs_xdr.h>
2005-06-22 21:16:21 +04:00
# include "nfs4_fs.h"
2005-04-17 02:20:36 +04:00
# include "delegation.h"
2006-08-23 04:06:10 +04:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
2007-08-06 20:18:34 +04:00
static void nfs_do_free_delegation ( struct nfs_delegation * delegation )
2005-04-17 02:20:36 +04:00
{
kfree ( delegation ) ;
}
2007-07-06 23:12:04 +04:00
static void nfs_free_delegation_callback ( struct rcu_head * head )
{
struct nfs_delegation * delegation = container_of ( head , struct nfs_delegation , rcu ) ;
2007-08-06 20:18:34 +04:00
nfs_do_free_delegation ( delegation ) ;
}
static void nfs_free_delegation ( struct nfs_delegation * delegation )
{
struct rpc_cred * cred ;
cred = rcu_dereference ( delegation - > cred ) ;
rcu_assign_pointer ( delegation - > cred , NULL ) ;
call_rcu ( & delegation - > rcu , nfs_free_delegation_callback ) ;
if ( cred )
put_rpccred ( cred ) ;
2007-07-06 23:12:04 +04:00
}
2008-12-23 23:21:52 +03:00
void nfs_mark_delegation_referenced ( struct nfs_delegation * delegation )
{
set_bit ( NFS_DELEGATION_REFERENCED , & delegation - > flags ) ;
}
2008-12-23 23:21:53 +03:00
int nfs_have_delegation ( struct inode * inode , fmode_t flags )
2008-12-23 23:21:52 +03:00
{
struct nfs_delegation * delegation ;
int ret = 0 ;
flags & = FMODE_READ | FMODE_WRITE ;
rcu_read_lock ( ) ;
delegation = rcu_dereference ( NFS_I ( inode ) - > delegation ) ;
if ( delegation ! = NULL & & ( delegation - > type & flags ) = = flags ) {
nfs_mark_delegation_referenced ( delegation ) ;
ret = 1 ;
}
rcu_read_unlock ( ) ;
return ret ;
}
2005-11-04 23:38:11 +03:00
static int nfs_delegation_claim_locks ( struct nfs_open_context * ctx , struct nfs4_state * state )
{
struct inode * inode = state - > inode ;
struct file_lock * fl ;
2009-06-18 00:22:58 +04:00
int status = 0 ;
2005-11-04 23:38:11 +03:00
2009-06-18 00:23:00 +04:00
if ( inode - > i_flock = = NULL )
goto out ;
/* Protect inode->i_flock using the BKL */
lock_kernel ( ) ;
2008-02-21 00:03:05 +03:00
for ( fl = inode - > i_flock ; fl ! = NULL ; fl = fl - > fl_next ) {
2005-11-04 23:38:11 +03:00
if ( ! ( fl - > fl_flags & ( FL_POSIX | FL_FLOCK ) ) )
continue ;
2007-08-11 01:44:32 +04:00
if ( nfs_file_open_context ( fl - > fl_file ) ! = ctx )
2005-11-04 23:38:11 +03:00
continue ;
2009-06-18 00:23:00 +04:00
unlock_kernel ( ) ;
2005-11-04 23:38:11 +03:00
status = nfs4_lock_delegation_recall ( state , fl ) ;
2009-06-18 00:22:58 +04:00
if ( status < 0 )
2009-06-18 00:23:00 +04:00
goto out ;
lock_kernel ( ) ;
2005-11-04 23:38:11 +03:00
}
2009-06-18 00:23:00 +04:00
unlock_kernel ( ) ;
out :
2005-11-04 23:38:11 +03:00
return status ;
}
2009-12-03 16:10:17 +03:00
static int nfs_delegation_claim_opens ( struct inode * inode , const nfs4_stateid * stateid )
2005-04-17 02:20:36 +04:00
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct nfs_open_context * ctx ;
struct nfs4_state * state ;
2005-11-04 23:38:11 +03:00
int err ;
2005-04-17 02:20:36 +04:00
again :
spin_lock ( & inode - > i_lock ) ;
list_for_each_entry ( ctx , & nfsi - > open_files , list ) {
state = ctx - > state ;
if ( state = = NULL )
continue ;
if ( ! test_bit ( NFS_DELEGATED_STATE , & state - > flags ) )
continue ;
2007-07-05 22:55:18 +04:00
if ( memcmp ( state - > stateid . data , stateid - > data , sizeof ( state - > stateid . data ) ) ! = 0 )
continue ;
2005-04-17 02:20:36 +04:00
get_nfs_open_context ( ctx ) ;
spin_unlock ( & inode - > i_lock ) ;
2007-07-06 23:10:43 +04:00
err = nfs4_open_delegation_recall ( ctx , state , stateid ) ;
2005-11-04 23:38:11 +03:00
if ( err > = 0 )
err = nfs_delegation_claim_locks ( ctx , state ) ;
2005-04-17 02:20:36 +04:00
put_nfs_open_context ( ctx ) ;
2005-11-04 23:38:11 +03:00
if ( err ! = 0 )
2009-12-03 16:10:17 +03:00
return err ;
2005-04-17 02:20:36 +04:00
goto again ;
}
spin_unlock ( & inode - > i_lock ) ;
2009-12-03 16:10:17 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Set up a delegation on an inode
*/
void nfs_inode_reclaim_delegation ( struct inode * inode , struct rpc_cred * cred , struct nfs_openres * res )
{
struct nfs_delegation * delegation = NFS_I ( inode ) - > delegation ;
2007-10-11 23:11:51 +04:00
struct rpc_cred * oldcred ;
2005-04-17 02:20:36 +04:00
if ( delegation = = NULL )
return ;
memcpy ( delegation - > stateid . data , res - > delegation . data ,
sizeof ( delegation - > stateid . data ) ) ;
delegation - > type = res - > delegation_type ;
delegation - > maxsize = res - > maxsize ;
2007-10-11 23:11:51 +04:00
oldcred = delegation - > cred ;
2005-04-17 02:20:36 +04:00
delegation - > cred = get_rpccred ( cred ) ;
2008-12-23 23:21:39 +03:00
clear_bit ( NFS_DELEGATION_NEED_RECLAIM , & delegation - > flags ) ;
2005-04-17 02:20:36 +04:00
NFS_I ( inode ) - > delegation_state = delegation - > type ;
smp_wmb ( ) ;
2007-10-11 23:11:51 +04:00
put_rpccred ( oldcred ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 00:38:18 +03:00
static int nfs_do_return_delegation ( struct inode * inode , struct nfs_delegation * delegation , int issync )
{
int res = 0 ;
res = nfs4_proc_delegreturn ( inode , delegation - > cred , & delegation - > stateid , issync ) ;
nfs_free_delegation ( delegation ) ;
return res ;
}
2008-12-23 23:21:39 +03:00
static struct inode * nfs_delegation_grab_inode ( struct nfs_delegation * delegation )
{
struct inode * inode = NULL ;
spin_lock ( & delegation - > lock ) ;
if ( delegation - > inode ! = NULL )
inode = igrab ( delegation - > inode ) ;
spin_unlock ( & delegation - > lock ) ;
return inode ;
}
2008-01-26 00:38:18 +03:00
static struct nfs_delegation * nfs_detach_delegation_locked ( struct nfs_inode * nfsi , const nfs4_stateid * stateid )
{
struct nfs_delegation * delegation = rcu_dereference ( nfsi - > delegation ) ;
if ( delegation = = NULL )
goto nomatch ;
2008-12-23 23:21:38 +03:00
spin_lock ( & delegation - > lock ) ;
2008-01-26 00:38:18 +03:00
if ( stateid ! = NULL & & memcmp ( delegation - > stateid . data , stateid - > data ,
sizeof ( delegation - > stateid . data ) ) ! = 0 )
2008-12-23 23:21:38 +03:00
goto nomatch_unlock ;
2008-01-26 00:38:18 +03:00
list_del_rcu ( & delegation - > super_list ) ;
2008-12-23 23:21:39 +03:00
delegation - > inode = NULL ;
2008-01-26 00:38:18 +03:00
nfsi - > delegation_state = 0 ;
rcu_assign_pointer ( nfsi - > delegation , NULL ) ;
2008-12-23 23:21:38 +03:00
spin_unlock ( & delegation - > lock ) ;
2008-01-26 00:38:18 +03:00
return delegation ;
2008-12-23 23:21:38 +03:00
nomatch_unlock :
spin_unlock ( & delegation - > lock ) ;
2008-01-26 00:38:18 +03:00
nomatch :
return NULL ;
}
2005-04-17 02:20:36 +04:00
/*
* Set up a delegation on an inode
*/
int nfs_inode_set_delegation ( struct inode * inode , struct rpc_cred * cred , struct nfs_openres * res )
{
2006-08-23 04:06:09 +04:00
struct nfs_client * clp = NFS_SERVER ( inode ) - > nfs_client ;
2005-04-17 02:20:36 +04:00
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct nfs_delegation * delegation ;
2008-01-26 00:38:18 +03:00
struct nfs_delegation * freeme = NULL ;
2005-04-17 02:20:36 +04:00
int status = 0 ;
2006-09-27 12:49:39 +04:00
delegation = kmalloc ( sizeof ( * delegation ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( delegation = = NULL )
return - ENOMEM ;
memcpy ( delegation - > stateid . data , res - > delegation . data ,
sizeof ( delegation - > stateid . data ) ) ;
delegation - > type = res - > delegation_type ;
delegation - > maxsize = res - > maxsize ;
2006-01-03 11:55:37 +03:00
delegation - > change_attr = nfsi - > change_attr ;
2005-04-17 02:20:36 +04:00
delegation - > cred = get_rpccred ( cred ) ;
delegation - > inode = inode ;
2008-12-23 23:21:52 +03:00
delegation - > flags = 1 < < NFS_DELEGATION_REFERENCED ;
2008-12-23 23:21:38 +03:00
spin_lock_init ( & delegation - > lock ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & clp - > cl_lock ) ;
2008-01-26 00:38:18 +03:00
if ( rcu_dereference ( nfsi - > delegation ) ! = NULL ) {
2005-04-17 02:20:36 +04:00
if ( memcmp ( & delegation - > stateid , & nfsi - > delegation - > stateid ,
2008-01-26 00:38:18 +03:00
sizeof ( delegation - > stateid ) ) = = 0 & &
delegation - > type = = nfsi - > delegation - > type ) {
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 00:38:18 +03:00
/*
* Deal with broken servers that hand out two
* delegations for the same file .
*/
dfprintk ( FILE , " %s: server %s handed out "
" a duplicate delegation! \n " ,
2008-05-03 00:42:44 +04:00
__func__ , clp - > cl_hostname ) ;
2008-01-26 00:38:18 +03:00
if ( delegation - > type < = nfsi - > delegation - > type ) {
freeme = delegation ;
delegation = NULL ;
goto out ;
}
freeme = nfs_detach_delegation_locked ( nfsi , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 00:38:18 +03:00
list_add_rcu ( & delegation - > super_list , & clp - > cl_delegations ) ;
nfsi - > delegation_state = delegation - > type ;
rcu_assign_pointer ( nfsi - > delegation , delegation ) ;
delegation = NULL ;
2007-07-04 00:10:55 +04:00
/* Ensure we revalidate the attributes and page cache! */
spin_lock ( & inode - > i_lock ) ;
nfsi - > cache_validity | = NFS_INO_REVAL_FORCED ;
spin_unlock ( & inode - > i_lock ) ;
2008-01-26 00:38:18 +03:00
out :
2005-04-17 02:20:36 +04:00
spin_unlock ( & clp - > cl_lock ) ;
2007-10-19 03:59:20 +04:00
if ( delegation ! = NULL )
nfs_free_delegation ( delegation ) ;
2008-01-26 00:38:18 +03:00
if ( freeme ! = NULL )
nfs_do_return_delegation ( inode , freeme , 0 ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
/* Sync all data to disk upon delegation return */
static void nfs_msync_inode ( struct inode * inode )
{
filemap_fdatawrite ( inode - > i_mapping ) ;
nfs_wb_all ( inode ) ;
filemap_fdatawait ( inode - > i_mapping ) ;
}
/*
* Basic procedure for returning a delegation to the server
*/
2009-12-03 16:10:17 +03:00
static int __nfs_inode_return_delegation ( struct inode * inode , struct nfs_delegation * delegation , int issync )
2005-04-17 02:20:36 +04:00
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
2009-12-03 16:10:17 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2009-06-18 00:23:00 +04:00
/*
* Guard against new delegated open / lock / unlock calls and against
* state recovery
*/
2005-04-17 02:20:36 +04:00
down_write ( & nfsi - > rwsem ) ;
2009-12-03 16:10:17 +03:00
err = nfs_delegation_claim_opens ( inode , & delegation - > stateid ) ;
2005-04-17 02:20:36 +04:00
up_write ( & nfsi - > rwsem ) ;
2009-12-03 16:10:17 +03:00
if ( err )
goto out ;
2005-04-17 02:20:36 +04:00
2009-12-03 16:10:17 +03:00
err = nfs_do_return_delegation ( inode , delegation , issync ) ;
out :
return err ;
2007-07-05 22:55:18 +04:00
}
2008-12-23 23:21:46 +03:00
/*
* Return all delegations that have been marked for return
*/
2009-12-03 16:10:17 +03:00
int nfs_client_return_marked_delegations ( struct nfs_client * clp )
2008-12-23 23:21:46 +03:00
{
struct nfs_delegation * delegation ;
struct inode * inode ;
2009-12-03 16:10:17 +03:00
int err = 0 ;
2008-12-23 23:21:46 +03:00
restart :
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
if ( ! test_and_clear_bit ( NFS_DELEGATION_RETURN , & delegation - > flags ) )
continue ;
inode = nfs_delegation_grab_inode ( delegation ) ;
if ( inode = = NULL )
continue ;
spin_lock ( & clp - > cl_lock ) ;
delegation = nfs_detach_delegation_locked ( NFS_I ( inode ) , NULL ) ;
spin_unlock ( & clp - > cl_lock ) ;
rcu_read_unlock ( ) ;
2009-12-03 16:10:17 +03:00
if ( delegation ! = NULL ) {
filemap_flush ( inode - > i_mapping ) ;
err = __nfs_inode_return_delegation ( inode , delegation , 0 ) ;
}
2008-12-23 23:21:46 +03:00
iput ( inode ) ;
2009-12-03 16:10:17 +03:00
if ( ! err )
goto restart ;
set_bit ( NFS4CLNT_DELEGRETURN , & clp - > cl_state ) ;
return err ;
2008-12-23 23:21:46 +03:00
}
rcu_read_unlock ( ) ;
2009-12-03 16:10:17 +03:00
return 0 ;
2008-12-23 23:21:46 +03:00
}
2008-01-25 02:14:34 +03:00
/*
* This function returns the delegation without reclaiming opens
* or protecting against delegation reclaims .
* It is therefore really only safe to be called from
* nfs4_clear_inode ( )
*/
void nfs_inode_return_delegation_noreclaim ( struct inode * inode )
{
struct nfs_client * clp = NFS_SERVER ( inode ) - > nfs_client ;
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct nfs_delegation * delegation ;
if ( rcu_dereference ( nfsi - > delegation ) ! = NULL ) {
spin_lock ( & clp - > cl_lock ) ;
delegation = nfs_detach_delegation_locked ( nfsi , NULL ) ;
spin_unlock ( & clp - > cl_lock ) ;
if ( delegation ! = NULL )
nfs_do_return_delegation ( inode , delegation , 0 ) ;
}
}
2007-07-05 22:55:18 +04:00
int nfs_inode_return_delegation ( struct inode * inode )
{
struct nfs_client * clp = NFS_SERVER ( inode ) - > nfs_client ;
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct nfs_delegation * delegation ;
int err = 0 ;
2007-07-06 23:12:04 +04:00
if ( rcu_dereference ( nfsi - > delegation ) ! = NULL ) {
2007-07-05 22:55:18 +04:00
spin_lock ( & clp - > cl_lock ) ;
delegation = nfs_detach_delegation_locked ( nfsi , NULL ) ;
spin_unlock ( & clp - > cl_lock ) ;
2009-12-03 16:10:17 +03:00
if ( delegation ! = NULL ) {
nfs_msync_inode ( inode ) ;
err = __nfs_inode_return_delegation ( inode , delegation , 1 ) ;
}
2007-07-05 22:55:18 +04:00
}
return err ;
2005-04-17 02:20:36 +04:00
}
2008-12-23 23:21:51 +03:00
static void nfs_mark_return_delegation ( struct nfs_client * clp , struct nfs_delegation * delegation )
{
set_bit ( NFS_DELEGATION_RETURN , & delegation - > flags ) ;
set_bit ( NFS4CLNT_DELEGRETURN , & clp - > cl_state ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Return all delegations associated to a super block
*/
2008-12-23 23:21:46 +03:00
void nfs_super_return_all_delegations ( struct super_block * sb )
2005-04-17 02:20:36 +04:00
{
2006-08-23 04:06:09 +04:00
struct nfs_client * clp = NFS_SB ( sb ) - > nfs_client ;
2005-04-17 02:20:36 +04:00
struct nfs_delegation * delegation ;
if ( clp = = NULL )
return ;
2007-07-06 23:12:04 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
2008-12-23 23:21:39 +03:00
spin_lock ( & delegation - > lock ) ;
if ( delegation - > inode ! = NULL & & delegation - > inode - > i_sb = = sb )
2008-12-23 23:21:46 +03:00
set_bit ( NFS_DELEGATION_RETURN , & delegation - > flags ) ;
2008-12-23 23:21:39 +03:00
spin_unlock ( & delegation - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-06 23:12:04 +04:00
rcu_read_unlock ( ) ;
2009-12-03 16:10:17 +03:00
if ( nfs_client_return_marked_delegations ( clp ) ! = 0 )
nfs4_schedule_state_manager ( clp ) ;
2008-12-23 23:21:46 +03:00
}
2009-12-05 21:20:52 +03:00
static
void nfs_client_mark_return_all_delegation_types ( struct nfs_client * clp , fmode_t flags )
2008-12-23 23:21:46 +03:00
{
struct nfs_delegation * delegation ;
rcu_read_lock ( ) ;
2008-12-23 23:21:47 +03:00
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
2009-12-05 21:20:52 +03:00
if ( ( delegation - > type = = ( FMODE_READ | FMODE_WRITE ) ) & & ! ( flags & FMODE_WRITE ) )
continue ;
if ( delegation - > type & flags )
nfs_mark_return_delegation ( clp , delegation ) ;
2008-12-23 23:21:47 +03:00
}
2008-12-23 23:21:46 +03:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
}
2009-12-05 21:20:52 +03:00
static void nfs_client_mark_return_all_delegations ( struct nfs_client * clp )
{
nfs_client_mark_return_all_delegation_types ( clp , FMODE_READ | FMODE_WRITE ) ;
}
2008-12-23 23:21:50 +03:00
static void nfs_delegation_run_state_manager ( struct nfs_client * clp )
2006-01-03 11:55:24 +03:00
{
2008-12-23 23:21:50 +03:00
if ( test_bit ( NFS4CLNT_DELEGRETURN , & clp - > cl_state ) )
nfs4_schedule_state_manager ( clp ) ;
2006-01-03 11:55:24 +03:00
}
2009-12-05 21:27:02 +03:00
void nfs_expire_all_delegation_types ( struct nfs_client * clp , fmode_t flags )
2006-01-03 11:55:24 +03:00
{
2009-12-05 21:20:52 +03:00
nfs_client_mark_return_all_delegation_types ( clp , flags ) ;
2008-12-23 23:21:50 +03:00
nfs_delegation_run_state_manager ( clp ) ;
2006-01-03 11:55:24 +03:00
}
2009-12-05 21:20:52 +03:00
void nfs_expire_all_delegations ( struct nfs_client * clp )
{
nfs_expire_all_delegation_types ( clp , FMODE_READ | FMODE_WRITE ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error .
*/
2006-08-23 04:06:08 +04:00
void nfs_handle_cb_pathdown ( struct nfs_client * clp )
2005-04-17 02:20:36 +04:00
{
if ( clp = = NULL )
return ;
2008-12-23 23:21:47 +03:00
nfs_client_mark_return_all_delegations ( clp ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-23 23:21:52 +03:00
static void nfs_client_mark_return_unreferenced_delegations ( struct nfs_client * clp )
{
struct nfs_delegation * delegation ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
if ( test_and_clear_bit ( NFS_DELEGATION_REFERENCED , & delegation - > flags ) )
continue ;
2009-12-05 21:19:11 +03:00
nfs_mark_return_delegation ( clp , delegation ) ;
2008-12-23 23:21:52 +03:00
}
rcu_read_unlock ( ) ;
}
void nfs_expire_unreferenced_delegations ( struct nfs_client * clp )
{
nfs_client_mark_return_unreferenced_delegations ( clp ) ;
nfs_delegation_run_state_manager ( clp ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Asynchronous delegation recall !
*/
2009-12-05 21:48:55 +03:00
int nfs_async_inode_return_delegation ( struct inode * inode , const nfs4_stateid * stateid ,
int ( * validate_stateid ) ( struct nfs_delegation * delegation ,
const nfs4_stateid * stateid ) )
2005-04-17 02:20:36 +04:00
{
2008-12-23 23:21:51 +03:00
struct nfs_client * clp = NFS_SERVER ( inode ) - > nfs_client ;
struct nfs_delegation * delegation ;
2005-04-17 02:20:36 +04:00
2008-12-23 23:21:51 +03:00
rcu_read_lock ( ) ;
delegation = rcu_dereference ( NFS_I ( inode ) - > delegation ) ;
2009-12-05 21:48:55 +03:00
if ( ! validate_stateid ( delegation , stateid ) ) {
2008-12-23 23:21:51 +03:00
rcu_read_unlock ( ) ;
return - ENOENT ;
}
2009-12-05 21:48:55 +03:00
2008-12-23 23:21:51 +03:00
nfs_mark_return_delegation ( clp , delegation ) ;
rcu_read_unlock ( ) ;
nfs_delegation_run_state_manager ( clp ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Retrieve the inode associated with a delegation
*/
2006-08-23 04:06:08 +04:00
struct inode * nfs_delegation_find_inode ( struct nfs_client * clp , const struct nfs_fh * fhandle )
2005-04-17 02:20:36 +04:00
{
struct nfs_delegation * delegation ;
struct inode * res = NULL ;
2007-07-06 23:12:04 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
2008-12-23 23:21:39 +03:00
spin_lock ( & delegation - > lock ) ;
if ( delegation - > inode ! = NULL & &
nfs_compare_fh ( fhandle , & NFS_I ( delegation - > inode ) - > fh ) = = 0 ) {
2005-04-17 02:20:36 +04:00
res = igrab ( delegation - > inode ) ;
}
2008-12-23 23:21:39 +03:00
spin_unlock ( & delegation - > lock ) ;
if ( res ! = NULL )
break ;
2005-04-17 02:20:36 +04:00
}
2007-07-06 23:12:04 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
/*
* Mark all delegations as needing to be reclaimed
*/
2006-08-23 04:06:08 +04:00
void nfs_delegation_mark_reclaim ( struct nfs_client * clp )
2005-04-17 02:20:36 +04:00
{
struct nfs_delegation * delegation ;
2007-07-06 23:12:04 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list )
2008-12-23 23:21:39 +03:00
set_bit ( NFS_DELEGATION_NEED_RECLAIM , & delegation - > flags ) ;
2007-07-06 23:12:04 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Reap all unclaimed delegations after reboot recovery is done
*/
2006-08-23 04:06:08 +04:00
void nfs_delegation_reap_unclaimed ( struct nfs_client * clp )
2005-04-17 02:20:36 +04:00
{
2007-07-06 23:12:04 +04:00
struct nfs_delegation * delegation ;
2008-12-23 23:21:39 +03:00
struct inode * inode ;
2007-07-06 23:12:04 +04:00
restart :
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( delegation , & clp - > cl_delegations , super_list ) {
2008-12-23 23:21:39 +03:00
if ( test_bit ( NFS_DELEGATION_NEED_RECLAIM , & delegation - > flags ) = = 0 )
2005-04-17 02:20:36 +04:00
continue ;
2008-12-23 23:21:39 +03:00
inode = nfs_delegation_grab_inode ( delegation ) ;
if ( inode = = NULL )
continue ;
2007-07-06 23:12:04 +04:00
spin_lock ( & clp - > cl_lock ) ;
2008-12-23 23:21:39 +03:00
delegation = nfs_detach_delegation_locked ( NFS_I ( inode ) , NULL ) ;
2007-07-06 23:12:04 +04:00
spin_unlock ( & clp - > cl_lock ) ;
rcu_read_unlock ( ) ;
if ( delegation ! = NULL )
2007-08-06 20:18:34 +04:00
nfs_free_delegation ( delegation ) ;
2008-12-23 23:21:39 +03:00
iput ( inode ) ;
2007-07-06 23:12:04 +04:00
goto restart ;
2005-04-17 02:20:36 +04:00
}
2007-07-06 23:12:04 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-20 21:44:46 +03:00
int nfs4_copy_delegation_stateid ( nfs4_stateid * dst , struct inode * inode )
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct nfs_delegation * delegation ;
2007-07-06 23:12:04 +04:00
int ret = 0 ;
2006-03-20 21:44:46 +03:00
2007-07-06 23:12:04 +04:00
rcu_read_lock ( ) ;
delegation = rcu_dereference ( nfsi - > delegation ) ;
2006-03-20 21:44:46 +03:00
if ( delegation ! = NULL ) {
memcpy ( dst - > data , delegation - > stateid . data , sizeof ( dst - > data ) ) ;
2007-07-06 23:12:04 +04:00
ret = 1 ;
2006-03-20 21:44:46 +03:00
}
2007-07-06 23:12:04 +04:00
rcu_read_unlock ( ) ;
return ret ;
2006-03-20 21:44:46 +03:00
}