11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/nfs/delegation.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2004 Trond Myklebust 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * NFS file delegation management 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds #include <linux/completion.h> 1058d9714aSTrond Myklebust #include <linux/kthread.h> 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/sched.h> 131da177e4SLinus Torvalds #include <linux/spinlock.h> 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include <linux/nfs4.h> 161da177e4SLinus Torvalds #include <linux/nfs_fs.h> 171da177e4SLinus Torvalds #include <linux/nfs_xdr.h> 181da177e4SLinus Torvalds 194ce79717STrond Myklebust #include "nfs4_fs.h" 201da177e4SLinus Torvalds #include "delegation.h" 2124c8dbbbSDavid Howells #include "internal.h" 221da177e4SLinus Torvalds 23905f8d16STrond Myklebust static void nfs_do_free_delegation(struct nfs_delegation *delegation) 241da177e4SLinus Torvalds { 251da177e4SLinus Torvalds kfree(delegation); 261da177e4SLinus Torvalds } 271da177e4SLinus Torvalds 288383e460STrond Myklebust static void nfs_free_delegation_callback(struct rcu_head *head) 298383e460STrond Myklebust { 308383e460STrond Myklebust struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu); 318383e460STrond Myklebust 32905f8d16STrond Myklebust nfs_do_free_delegation(delegation); 33905f8d16STrond Myklebust } 34905f8d16STrond Myklebust 35905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 36905f8d16STrond Myklebust { 37905f8d16STrond Myklebust struct rpc_cred *cred; 38905f8d16STrond Myklebust 39905f8d16STrond Myklebust cred = rcu_dereference(delegation->cred); 40905f8d16STrond Myklebust rcu_assign_pointer(delegation->cred, NULL); 41905f8d16STrond Myklebust call_rcu(&delegation->rcu, nfs_free_delegation_callback); 42905f8d16STrond Myklebust if (cred) 43905f8d16STrond Myklebust put_rpccred(cred); 448383e460STrond Myklebust } 458383e460STrond Myklebust 46888e694cSTrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) 47888e694cSTrond Myklebust { 48888e694cSTrond Myklebust struct inode *inode = state->inode; 49888e694cSTrond Myklebust struct file_lock *fl; 50888e694cSTrond Myklebust int status; 51888e694cSTrond Myklebust 5290dc7d27SHarvey Harrison for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { 53888e694cSTrond Myklebust if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) 54888e694cSTrond Myklebust continue; 55cd3758e3STrond Myklebust if (nfs_file_open_context(fl->fl_file) != ctx) 56888e694cSTrond Myklebust continue; 57888e694cSTrond Myklebust status = nfs4_lock_delegation_recall(state, fl); 58888e694cSTrond Myklebust if (status >= 0) 59888e694cSTrond Myklebust continue; 60888e694cSTrond Myklebust switch (status) { 61888e694cSTrond Myklebust default: 62888e694cSTrond Myklebust printk(KERN_ERR "%s: unhandled error %d.\n", 633110ff80SHarvey Harrison __func__, status); 64888e694cSTrond Myklebust case -NFS4ERR_EXPIRED: 65888e694cSTrond Myklebust /* kill_proc(fl->fl_pid, SIGLOST, 1); */ 66888e694cSTrond Myklebust case -NFS4ERR_STALE_CLIENTID: 677539bbabSDavid Howells nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client); 68888e694cSTrond Myklebust goto out_err; 69888e694cSTrond Myklebust } 70888e694cSTrond Myklebust } 71888e694cSTrond Myklebust return 0; 72888e694cSTrond Myklebust out_err: 73888e694cSTrond Myklebust return status; 74888e694cSTrond Myklebust } 75888e694cSTrond Myklebust 7690163027STrond Myklebust static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) 771da177e4SLinus Torvalds { 781da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 791da177e4SLinus Torvalds struct nfs_open_context *ctx; 801da177e4SLinus Torvalds struct nfs4_state *state; 81888e694cSTrond Myklebust int err; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds again: 841da177e4SLinus Torvalds spin_lock(&inode->i_lock); 851da177e4SLinus Torvalds list_for_each_entry(ctx, &nfsi->open_files, list) { 861da177e4SLinus Torvalds state = ctx->state; 871da177e4SLinus Torvalds if (state == NULL) 881da177e4SLinus Torvalds continue; 891da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 901da177e4SLinus Torvalds continue; 9190163027STrond Myklebust if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0) 9290163027STrond Myklebust continue; 931da177e4SLinus Torvalds get_nfs_open_context(ctx); 941da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 9513437e12STrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 96888e694cSTrond Myklebust if (err >= 0) 97888e694cSTrond Myklebust err = nfs_delegation_claim_locks(ctx, state); 981da177e4SLinus Torvalds put_nfs_open_context(ctx); 99888e694cSTrond Myklebust if (err != 0) 100888e694cSTrond Myklebust return; 1011da177e4SLinus Torvalds goto again; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* 1071da177e4SLinus Torvalds * Set up a delegation on an inode 1081da177e4SLinus Torvalds */ 1091da177e4SLinus Torvalds void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds struct nfs_delegation *delegation = NFS_I(inode)->delegation; 11205c88babSTrond Myklebust struct rpc_cred *oldcred; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds if (delegation == NULL) 1151da177e4SLinus Torvalds return; 1161da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 1171da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 1181da177e4SLinus Torvalds delegation->type = res->delegation_type; 1191da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 12005c88babSTrond Myklebust oldcred = delegation->cred; 1211da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1221da177e4SLinus Torvalds delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM; 1231da177e4SLinus Torvalds NFS_I(inode)->delegation_state = delegation->type; 1241da177e4SLinus Torvalds smp_wmb(); 12505c88babSTrond Myklebust put_rpccred(oldcred); 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 12857bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 12957bfa891STrond Myklebust { 13057bfa891STrond Myklebust int res = 0; 13157bfa891STrond Myklebust 13257bfa891STrond Myklebust res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 13357bfa891STrond Myklebust nfs_free_delegation(delegation); 13457bfa891STrond Myklebust return res; 13557bfa891STrond Myklebust } 13657bfa891STrond Myklebust 13757bfa891STrond Myklebust static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) 13857bfa891STrond Myklebust { 13957bfa891STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 14057bfa891STrond Myklebust 14157bfa891STrond Myklebust if (delegation == NULL) 14257bfa891STrond Myklebust goto nomatch; 143*34310430STrond Myklebust spin_lock(&delegation->lock); 14457bfa891STrond Myklebust if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, 14557bfa891STrond Myklebust sizeof(delegation->stateid.data)) != 0) 146*34310430STrond Myklebust goto nomatch_unlock; 14757bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 14857bfa891STrond Myklebust nfsi->delegation_state = 0; 14957bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 150*34310430STrond Myklebust spin_unlock(&delegation->lock); 15157bfa891STrond Myklebust return delegation; 152*34310430STrond Myklebust nomatch_unlock: 153*34310430STrond Myklebust spin_unlock(&delegation->lock); 15457bfa891STrond Myklebust nomatch: 15557bfa891STrond Myklebust return NULL; 15657bfa891STrond Myklebust } 15757bfa891STrond Myklebust 1581da177e4SLinus Torvalds /* 1591da177e4SLinus Torvalds * Set up a delegation on an inode 1601da177e4SLinus Torvalds */ 1611da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 1621da177e4SLinus Torvalds { 1637539bbabSDavid Howells struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 1641da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 1651da177e4SLinus Torvalds struct nfs_delegation *delegation; 16657bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 1671da177e4SLinus Torvalds int status = 0; 1681da177e4SLinus Torvalds 169f52720caSPanagiotis Issaris delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); 1701da177e4SLinus Torvalds if (delegation == NULL) 1711da177e4SLinus Torvalds return -ENOMEM; 1721da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 1731da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 1741da177e4SLinus Torvalds delegation->type = res->delegation_type; 1751da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 176beb2a5ecSTrond Myklebust delegation->change_attr = nfsi->change_attr; 1771da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1781da177e4SLinus Torvalds delegation->inode = inode; 179*34310430STrond Myklebust spin_lock_init(&delegation->lock); 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 18257bfa891STrond Myklebust if (rcu_dereference(nfsi->delegation) != NULL) { 18357bfa891STrond Myklebust if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, 18457bfa891STrond Myklebust sizeof(delegation->stateid)) == 0 && 18557bfa891STrond Myklebust delegation->type == nfsi->delegation->type) { 18657bfa891STrond Myklebust goto out; 18757bfa891STrond Myklebust } 18857bfa891STrond Myklebust /* 18957bfa891STrond Myklebust * Deal with broken servers that hand out two 19057bfa891STrond Myklebust * delegations for the same file. 19157bfa891STrond Myklebust */ 19257bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 19357bfa891STrond Myklebust "a duplicate delegation!\n", 1943110ff80SHarvey Harrison __func__, clp->cl_hostname); 19557bfa891STrond Myklebust if (delegation->type <= nfsi->delegation->type) { 19657bfa891STrond Myklebust freeme = delegation; 19757bfa891STrond Myklebust delegation = NULL; 19857bfa891STrond Myklebust goto out; 19957bfa891STrond Myklebust } 20057bfa891STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, NULL); 20157bfa891STrond Myklebust } 2028383e460STrond Myklebust list_add_rcu(&delegation->super_list, &clp->cl_delegations); 2031da177e4SLinus Torvalds nfsi->delegation_state = delegation->type; 2048383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 2051da177e4SLinus Torvalds delegation = NULL; 206412c77ceSTrond Myklebust 207412c77ceSTrond Myklebust /* Ensure we revalidate the attributes and page cache! */ 208412c77ceSTrond Myklebust spin_lock(&inode->i_lock); 209412c77ceSTrond Myklebust nfsi->cache_validity |= NFS_INO_REVAL_FORCED; 210412c77ceSTrond Myklebust spin_unlock(&inode->i_lock); 211412c77ceSTrond Myklebust 21257bfa891STrond Myklebust out: 2131da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 214603c83daSTrond Myklebust if (delegation != NULL) 215603c83daSTrond Myklebust nfs_free_delegation(delegation); 21657bfa891STrond Myklebust if (freeme != NULL) 21757bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 2181da177e4SLinus Torvalds return status; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds /* Sync all data to disk upon delegation return */ 2221da177e4SLinus Torvalds static void nfs_msync_inode(struct inode *inode) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds filemap_fdatawrite(inode->i_mapping); 2251da177e4SLinus Torvalds nfs_wb_all(inode); 2261da177e4SLinus Torvalds filemap_fdatawait(inode->i_mapping); 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds /* 2301da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 2311da177e4SLinus Torvalds */ 23290163027STrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) 2331da177e4SLinus Torvalds { 2347539bbabSDavid Howells struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 2351da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds nfs_msync_inode(inode); 2381da177e4SLinus Torvalds down_read(&clp->cl_sem); 2391da177e4SLinus Torvalds /* Guard against new delegated open calls */ 2401da177e4SLinus Torvalds down_write(&nfsi->rwsem); 24190163027STrond Myklebust nfs_delegation_claim_opens(inode, &delegation->stateid); 2421da177e4SLinus Torvalds up_write(&nfsi->rwsem); 2431da177e4SLinus Torvalds up_read(&clp->cl_sem); 2441da177e4SLinus Torvalds nfs_msync_inode(inode); 2451da177e4SLinus Torvalds 246e6f81075STrond Myklebust return nfs_do_return_delegation(inode, delegation, 1); 24790163027STrond Myklebust } 24890163027STrond Myklebust 249e6f81075STrond Myklebust /* 250e6f81075STrond Myklebust * This function returns the delegation without reclaiming opens 251e6f81075STrond Myklebust * or protecting against delegation reclaims. 252e6f81075STrond Myklebust * It is therefore really only safe to be called from 253e6f81075STrond Myklebust * nfs4_clear_inode() 254e6f81075STrond Myklebust */ 255e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 256e6f81075STrond Myklebust { 257e6f81075STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 258e6f81075STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 259e6f81075STrond Myklebust struct nfs_delegation *delegation; 260e6f81075STrond Myklebust 261e6f81075STrond Myklebust if (rcu_dereference(nfsi->delegation) != NULL) { 262e6f81075STrond Myklebust spin_lock(&clp->cl_lock); 263e6f81075STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, NULL); 264e6f81075STrond Myklebust spin_unlock(&clp->cl_lock); 265e6f81075STrond Myklebust if (delegation != NULL) 266e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 0); 267e6f81075STrond Myklebust } 268e6f81075STrond Myklebust } 269e6f81075STrond Myklebust 27090163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode) 27190163027STrond Myklebust { 27290163027STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 27390163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 27490163027STrond Myklebust struct nfs_delegation *delegation; 27590163027STrond Myklebust int err = 0; 27690163027STrond Myklebust 2778383e460STrond Myklebust if (rcu_dereference(nfsi->delegation) != NULL) { 27890163027STrond Myklebust spin_lock(&clp->cl_lock); 27990163027STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, NULL); 28090163027STrond Myklebust spin_unlock(&clp->cl_lock); 2811da177e4SLinus Torvalds if (delegation != NULL) 28290163027STrond Myklebust err = __nfs_inode_return_delegation(inode, delegation); 28390163027STrond Myklebust } 28490163027STrond Myklebust return err; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds /* 2881da177e4SLinus Torvalds * Return all delegations associated to a super block 2891da177e4SLinus Torvalds */ 2901da177e4SLinus Torvalds void nfs_return_all_delegations(struct super_block *sb) 2911da177e4SLinus Torvalds { 2927539bbabSDavid Howells struct nfs_client *clp = NFS_SB(sb)->nfs_client; 2931da177e4SLinus Torvalds struct nfs_delegation *delegation; 2941da177e4SLinus Torvalds struct inode *inode; 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds if (clp == NULL) 2971da177e4SLinus Torvalds return; 2981da177e4SLinus Torvalds restart: 2998383e460STrond Myklebust rcu_read_lock(); 3008383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 3011da177e4SLinus Torvalds if (delegation->inode->i_sb != sb) 3021da177e4SLinus Torvalds continue; 3031da177e4SLinus Torvalds inode = igrab(delegation->inode); 3041da177e4SLinus Torvalds if (inode == NULL) 3051da177e4SLinus Torvalds continue; 3068383e460STrond Myklebust spin_lock(&clp->cl_lock); 3078383e460STrond Myklebust delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); 3081da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 3098383e460STrond Myklebust rcu_read_unlock(); 3108383e460STrond Myklebust if (delegation != NULL) 31190163027STrond Myklebust __nfs_inode_return_delegation(inode, delegation); 3121da177e4SLinus Torvalds iput(inode); 3131da177e4SLinus Torvalds goto restart; 3141da177e4SLinus Torvalds } 3158383e460STrond Myklebust rcu_read_unlock(); 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 31810afec90STrond Myklebust static int nfs_do_expire_all_delegations(void *ptr) 31958d9714aSTrond Myklebust { 320adfa6f98SDavid Howells struct nfs_client *clp = ptr; 32158d9714aSTrond Myklebust struct nfs_delegation *delegation; 32258d9714aSTrond Myklebust struct inode *inode; 32358d9714aSTrond Myklebust 32458d9714aSTrond Myklebust allow_signal(SIGKILL); 32558d9714aSTrond Myklebust restart: 32658d9714aSTrond Myklebust if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) 32758d9714aSTrond Myklebust goto out; 32858d9714aSTrond Myklebust if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) 32958d9714aSTrond Myklebust goto out; 3308383e460STrond Myklebust rcu_read_lock(); 3318383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 33258d9714aSTrond Myklebust inode = igrab(delegation->inode); 33358d9714aSTrond Myklebust if (inode == NULL) 33458d9714aSTrond Myklebust continue; 3358383e460STrond Myklebust spin_lock(&clp->cl_lock); 3368383e460STrond Myklebust delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); 33758d9714aSTrond Myklebust spin_unlock(&clp->cl_lock); 3388383e460STrond Myklebust rcu_read_unlock(); 3398383e460STrond Myklebust if (delegation) 34090163027STrond Myklebust __nfs_inode_return_delegation(inode, delegation); 34158d9714aSTrond Myklebust iput(inode); 34258d9714aSTrond Myklebust goto restart; 34358d9714aSTrond Myklebust } 3448383e460STrond Myklebust rcu_read_unlock(); 34558d9714aSTrond Myklebust out: 34624c8dbbbSDavid Howells nfs_put_client(clp); 34758d9714aSTrond Myklebust module_put_and_exit(0); 34858d9714aSTrond Myklebust } 34958d9714aSTrond Myklebust 350adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp) 35158d9714aSTrond Myklebust { 35258d9714aSTrond Myklebust struct task_struct *task; 35358d9714aSTrond Myklebust 35458d9714aSTrond Myklebust __module_get(THIS_MODULE); 35558d9714aSTrond Myklebust atomic_inc(&clp->cl_count); 35658d9714aSTrond Myklebust task = kthread_run(nfs_do_expire_all_delegations, clp, 3575d8515caSChuck Lever "%s-delegreturn", 3585d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, 3595d8515caSChuck Lever RPC_DISPLAY_ADDR)); 36058d9714aSTrond Myklebust if (!IS_ERR(task)) 36158d9714aSTrond Myklebust return; 36224c8dbbbSDavid Howells nfs_put_client(clp); 36358d9714aSTrond Myklebust module_put(THIS_MODULE); 36458d9714aSTrond Myklebust } 36558d9714aSTrond Myklebust 3661da177e4SLinus Torvalds /* 3671da177e4SLinus Torvalds * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. 3681da177e4SLinus Torvalds */ 369adfa6f98SDavid Howells void nfs_handle_cb_pathdown(struct nfs_client *clp) 3701da177e4SLinus Torvalds { 3711da177e4SLinus Torvalds struct nfs_delegation *delegation; 3721da177e4SLinus Torvalds struct inode *inode; 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds if (clp == NULL) 3751da177e4SLinus Torvalds return; 3761da177e4SLinus Torvalds restart: 3778383e460STrond Myklebust rcu_read_lock(); 3788383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 3791da177e4SLinus Torvalds inode = igrab(delegation->inode); 3801da177e4SLinus Torvalds if (inode == NULL) 3811da177e4SLinus Torvalds continue; 3828383e460STrond Myklebust spin_lock(&clp->cl_lock); 3838383e460STrond Myklebust delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); 3841da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 3858383e460STrond Myklebust rcu_read_unlock(); 3868383e460STrond Myklebust if (delegation != NULL) 38790163027STrond Myklebust __nfs_inode_return_delegation(inode, delegation); 3881da177e4SLinus Torvalds iput(inode); 3891da177e4SLinus Torvalds goto restart; 3901da177e4SLinus Torvalds } 3918383e460STrond Myklebust rcu_read_unlock(); 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds struct recall_threadargs { 3951da177e4SLinus Torvalds struct inode *inode; 396adfa6f98SDavid Howells struct nfs_client *clp; 3971da177e4SLinus Torvalds const nfs4_stateid *stateid; 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds struct completion started; 4001da177e4SLinus Torvalds int result; 4011da177e4SLinus Torvalds }; 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds static int recall_thread(void *data) 4041da177e4SLinus Torvalds { 4051da177e4SLinus Torvalds struct recall_threadargs *args = (struct recall_threadargs *)data; 4061da177e4SLinus Torvalds struct inode *inode = igrab(args->inode); 4077539bbabSDavid Howells struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 4081da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 4091da177e4SLinus Torvalds struct nfs_delegation *delegation; 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds daemonize("nfsv4-delegreturn"); 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds nfs_msync_inode(inode); 4141da177e4SLinus Torvalds down_read(&clp->cl_sem); 4151da177e4SLinus Torvalds down_write(&nfsi->rwsem); 4161da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 41790163027STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, args->stateid); 41890163027STrond Myklebust if (delegation != NULL) 4191da177e4SLinus Torvalds args->result = 0; 42090163027STrond Myklebust else 4211da177e4SLinus Torvalds args->result = -ENOENT; 4221da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 4231da177e4SLinus Torvalds complete(&args->started); 42490163027STrond Myklebust nfs_delegation_claim_opens(inode, args->stateid); 4251da177e4SLinus Torvalds up_write(&nfsi->rwsem); 4261da177e4SLinus Torvalds up_read(&clp->cl_sem); 4271da177e4SLinus Torvalds nfs_msync_inode(inode); 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds if (delegation != NULL) 430e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 4311da177e4SLinus Torvalds iput(inode); 4321da177e4SLinus Torvalds module_put_and_exit(0); 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds /* 4361da177e4SLinus Torvalds * Asynchronous delegation recall! 4371da177e4SLinus Torvalds */ 4381da177e4SLinus Torvalds int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) 4391da177e4SLinus Torvalds { 4401da177e4SLinus Torvalds struct recall_threadargs data = { 4411da177e4SLinus Torvalds .inode = inode, 4421da177e4SLinus Torvalds .stateid = stateid, 4431da177e4SLinus Torvalds }; 4441da177e4SLinus Torvalds int status; 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds init_completion(&data.started); 4471da177e4SLinus Torvalds __module_get(THIS_MODULE); 4481da177e4SLinus Torvalds status = kernel_thread(recall_thread, &data, CLONE_KERNEL); 4491da177e4SLinus Torvalds if (status < 0) 4501da177e4SLinus Torvalds goto out_module_put; 4511da177e4SLinus Torvalds wait_for_completion(&data.started); 4521da177e4SLinus Torvalds return data.result; 4531da177e4SLinus Torvalds out_module_put: 4541da177e4SLinus Torvalds module_put(THIS_MODULE); 4551da177e4SLinus Torvalds return status; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds /* 4591da177e4SLinus Torvalds * Retrieve the inode associated with a delegation 4601da177e4SLinus Torvalds */ 461adfa6f98SDavid Howells struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle) 4621da177e4SLinus Torvalds { 4631da177e4SLinus Torvalds struct nfs_delegation *delegation; 4641da177e4SLinus Torvalds struct inode *res = NULL; 4658383e460STrond Myklebust rcu_read_lock(); 4668383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 4671da177e4SLinus Torvalds if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 4681da177e4SLinus Torvalds res = igrab(delegation->inode); 4691da177e4SLinus Torvalds break; 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds } 4728383e460STrond Myklebust rcu_read_unlock(); 4731da177e4SLinus Torvalds return res; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds /* 4771da177e4SLinus Torvalds * Mark all delegations as needing to be reclaimed 4781da177e4SLinus Torvalds */ 479adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 4801da177e4SLinus Torvalds { 4811da177e4SLinus Torvalds struct nfs_delegation *delegation; 4828383e460STrond Myklebust rcu_read_lock(); 4838383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) 4841da177e4SLinus Torvalds delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; 4858383e460STrond Myklebust rcu_read_unlock(); 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds /* 4891da177e4SLinus Torvalds * Reap all unclaimed delegations after reboot recovery is done 4901da177e4SLinus Torvalds */ 491adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 4921da177e4SLinus Torvalds { 4938383e460STrond Myklebust struct nfs_delegation *delegation; 4948383e460STrond Myklebust restart: 4958383e460STrond Myklebust rcu_read_lock(); 4968383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 4971da177e4SLinus Torvalds if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) 4981da177e4SLinus Torvalds continue; 4998383e460STrond Myklebust spin_lock(&clp->cl_lock); 5008383e460STrond Myklebust delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); 5011da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 5028383e460STrond Myklebust rcu_read_unlock(); 5038383e460STrond Myklebust if (delegation != NULL) 504905f8d16STrond Myklebust nfs_free_delegation(delegation); 5058383e460STrond Myklebust goto restart; 5061da177e4SLinus Torvalds } 5078383e460STrond Myklebust rcu_read_unlock(); 5081da177e4SLinus Torvalds } 5093e4f6290STrond Myklebust 5103e4f6290STrond Myklebust int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 5113e4f6290STrond Myklebust { 5123e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 5133e4f6290STrond Myklebust struct nfs_delegation *delegation; 5148383e460STrond Myklebust int ret = 0; 5153e4f6290STrond Myklebust 5168383e460STrond Myklebust rcu_read_lock(); 5178383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 5183e4f6290STrond Myklebust if (delegation != NULL) { 5193e4f6290STrond Myklebust memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); 5208383e460STrond Myklebust ret = 1; 5213e4f6290STrond Myklebust } 5228383e460STrond Myklebust rcu_read_unlock(); 5238383e460STrond Myklebust return ret; 5243e4f6290STrond Myklebust } 525