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> 135a0e3ad6STejun Heo #include <linux/slab.h> 141da177e4SLinus Torvalds #include <linux/spinlock.h> 151eb5d98fSJeff Layton #include <linux/iversion.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/nfs4.h> 181da177e4SLinus Torvalds #include <linux/nfs_fs.h> 191da177e4SLinus Torvalds #include <linux/nfs_xdr.h> 201da177e4SLinus Torvalds 214ce79717STrond Myklebust #include "nfs4_fs.h" 22c01d3645STrond Myklebust #include "nfs4session.h" 231da177e4SLinus Torvalds #include "delegation.h" 2424c8dbbbSDavid Howells #include "internal.h" 25ca8acf8dSTrond Myklebust #include "nfs4trace.h" 261da177e4SLinus Torvalds 27905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 28905f8d16STrond Myklebust { 29e00b8a24STrond Myklebust if (delegation->cred) { 30e00b8a24STrond Myklebust put_rpccred(delegation->cred); 31e00b8a24STrond Myklebust delegation->cred = NULL; 32e00b8a24STrond Myklebust } 3326f04ddeSLai Jiangshan kfree_rcu(delegation, rcu); 348383e460STrond Myklebust } 358383e460STrond Myklebust 36d3978bb3SChuck Lever /** 37d3978bb3SChuck Lever * nfs_mark_delegation_referenced - set delegation's REFERENCED flag 38d3978bb3SChuck Lever * @delegation: delegation to process 39d3978bb3SChuck Lever * 40d3978bb3SChuck Lever */ 41b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 42b7391f44STrond Myklebust { 43b7391f44STrond Myklebust set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 44b7391f44STrond Myklebust } 45b7391f44STrond Myklebust 46aa05c87fSTrond Myklebust static bool 47aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation, 48aa05c87fSTrond Myklebust fmode_t flags) 49aa05c87fSTrond Myklebust { 50aa05c87fSTrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags && 51aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 52aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 53aa05c87fSTrond Myklebust return true; 54aa05c87fSTrond Myklebust return false; 55aa05c87fSTrond Myklebust } 56aa05c87fSTrond Myklebust 5715bb3afeSPeng Tao static int 5815bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark) 59b7391f44STrond Myklebust { 60b7391f44STrond Myklebust struct nfs_delegation *delegation; 61b7391f44STrond Myklebust int ret = 0; 62b7391f44STrond Myklebust 63b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 64b7391f44STrond Myklebust rcu_read_lock(); 65b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 66aa05c87fSTrond Myklebust if (nfs4_is_valid_delegation(delegation, flags)) { 6715bb3afeSPeng Tao if (mark) 68b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 69b7391f44STrond Myklebust ret = 1; 70b7391f44STrond Myklebust } 71b7391f44STrond Myklebust rcu_read_unlock(); 72b7391f44STrond Myklebust return ret; 73b7391f44STrond Myklebust } 7415bb3afeSPeng Tao /** 7515bb3afeSPeng Tao * nfs_have_delegation - check if inode has a delegation, mark it 7615bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if there is one. 7715bb3afeSPeng Tao * @inode: inode to check 7815bb3afeSPeng Tao * @flags: delegation types to check for 7915bb3afeSPeng Tao * 8015bb3afeSPeng Tao * Returns one if inode has the indicated delegation, otherwise zero. 8115bb3afeSPeng Tao */ 8215bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags) 8315bb3afeSPeng Tao { 8415bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, true); 8515bb3afeSPeng Tao } 8615bb3afeSPeng Tao 8715bb3afeSPeng Tao /* 8815bb3afeSPeng Tao * nfs4_check_delegation - check if inode has a delegation, do not mark 8915bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if it has one. 9015bb3afeSPeng Tao */ 9115bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags) 9215bb3afeSPeng Tao { 9315bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, false); 9415bb3afeSPeng Tao } 95b7391f44STrond Myklebust 96db4f2e63STrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) 97888e694cSTrond Myklebust { 98888e694cSTrond Myklebust struct inode *inode = state->inode; 99888e694cSTrond Myklebust struct file_lock *fl; 100bd61e0a9SJeff Layton struct file_lock_context *flctx = inode->i_flctx; 101bd61e0a9SJeff Layton struct list_head *list; 102d5122201STrond Myklebust int status = 0; 103888e694cSTrond Myklebust 104bd61e0a9SJeff Layton if (flctx == NULL) 10565b62a29STrond Myklebust goto out; 106314d7cc0SJeff Layton 107bd61e0a9SJeff Layton list = &flctx->flc_posix; 1086109c850SJeff Layton spin_lock(&flctx->flc_lock); 109bd61e0a9SJeff Layton restart: 110bd61e0a9SJeff Layton list_for_each_entry(fl, list, fl_list) { 111cd3758e3STrond Myklebust if (nfs_file_open_context(fl->fl_file) != ctx) 112888e694cSTrond Myklebust continue; 1136109c850SJeff Layton spin_unlock(&flctx->flc_lock); 114db4f2e63STrond Myklebust status = nfs4_lock_delegation_recall(fl, state, stateid); 115d5122201STrond Myklebust if (status < 0) 1163f09df70STrond Myklebust goto out; 1176109c850SJeff Layton spin_lock(&flctx->flc_lock); 118888e694cSTrond Myklebust } 119bd61e0a9SJeff Layton if (list == &flctx->flc_posix) { 120bd61e0a9SJeff Layton list = &flctx->flc_flock; 121bd61e0a9SJeff Layton goto restart; 1225263e31eSJeff Layton } 1236109c850SJeff Layton spin_unlock(&flctx->flc_lock); 1243f09df70STrond Myklebust out: 125888e694cSTrond Myklebust return status; 126888e694cSTrond Myklebust } 127888e694cSTrond Myklebust 12824311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, 12924311f88STrond Myklebust const nfs4_stateid *stateid, fmode_t type) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 1321da177e4SLinus Torvalds struct nfs_open_context *ctx; 133d25be546STrond Myklebust struct nfs4_state_owner *sp; 1341da177e4SLinus Torvalds struct nfs4_state *state; 135d25be546STrond Myklebust unsigned int seq; 136888e694cSTrond Myklebust int err; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds again: 1391da177e4SLinus Torvalds spin_lock(&inode->i_lock); 1401da177e4SLinus Torvalds list_for_each_entry(ctx, &nfsi->open_files, list) { 1411da177e4SLinus Torvalds state = ctx->state; 1421da177e4SLinus Torvalds if (state == NULL) 1431da177e4SLinus Torvalds continue; 1441da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1451da177e4SLinus Torvalds continue; 146f8ebf7a8STrond Myklebust if (!nfs4_valid_open_stateid(state)) 147f8ebf7a8STrond Myklebust continue; 148f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 14990163027STrond Myklebust continue; 1501da177e4SLinus Torvalds get_nfs_open_context(ctx); 1511da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 152d25be546STrond Myklebust sp = state->owner; 15365b62a29STrond Myklebust /* Block nfs4_proc_unlck */ 15465b62a29STrond Myklebust mutex_lock(&sp->so_delegreturn_mutex); 155d25be546STrond Myklebust seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 15624311f88STrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid, type); 157d25be546STrond Myklebust if (!err) 158db4f2e63STrond Myklebust err = nfs_delegation_claim_locks(ctx, state, stateid); 159d25be546STrond Myklebust if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 160d25be546STrond Myklebust err = -EAGAIN; 16165b62a29STrond Myklebust mutex_unlock(&sp->so_delegreturn_mutex); 1621da177e4SLinus Torvalds put_nfs_open_context(ctx); 163888e694cSTrond Myklebust if (err != 0) 164d18cc1fdSTrond Myklebust return err; 1651da177e4SLinus Torvalds goto again; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 168d18cc1fdSTrond Myklebust return 0; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 171d3978bb3SChuck Lever /** 172d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 173d3978bb3SChuck Lever * @inode: inode to process 174d3978bb3SChuck Lever * @cred: credential to use for request 17535156bffSTrond Myklebust * @type: delegation type 17635156bffSTrond Myklebust * @stateid: delegation stateid 17735156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 178d3978bb3SChuck Lever * 1791da177e4SLinus Torvalds */ 180d3978bb3SChuck Lever void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, 18135156bffSTrond Myklebust fmode_t type, 18235156bffSTrond Myklebust const nfs4_stateid *stateid, 18335156bffSTrond Myklebust unsigned long pagemod_limit) 1841da177e4SLinus Torvalds { 1858f649c37STrond Myklebust struct nfs_delegation *delegation; 1868f649c37STrond Myklebust struct rpc_cred *oldcred = NULL; 1871da177e4SLinus Torvalds 1888f649c37STrond Myklebust rcu_read_lock(); 1898f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1908f649c37STrond Myklebust if (delegation != NULL) { 1918f649c37STrond Myklebust spin_lock(&delegation->lock); 1928f649c37STrond Myklebust if (delegation->inode != NULL) { 19335156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 19435156bffSTrond Myklebust delegation->type = type; 19535156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 19605c88babSTrond Myklebust oldcred = delegation->cred; 1971da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1988f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 1998f649c37STrond Myklebust &delegation->flags); 2008f649c37STrond Myklebust spin_unlock(&delegation->lock); 2018f649c37STrond Myklebust rcu_read_unlock(); 2027c0af9ffSTrond Myklebust put_rpccred(oldcred); 20335156bffSTrond Myklebust trace_nfs4_reclaim_delegation(inode, type); 204b1a318deSTrond Myklebust return; 205b1a318deSTrond Myklebust } 2068f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 2078f649c37STrond Myklebust spin_unlock(&delegation->lock); 208b1a318deSTrond Myklebust } 2098f649c37STrond Myklebust rcu_read_unlock(); 21035156bffSTrond Myklebust nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit); 2118f649c37STrond Myklebust } 2121da177e4SLinus Torvalds 21357bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 21457bfa891STrond Myklebust { 21557bfa891STrond Myklebust int res = 0; 21657bfa891STrond Myklebust 217869f9dfaSTrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 218869f9dfaSTrond Myklebust res = nfs4_proc_delegreturn(inode, 219869f9dfaSTrond Myklebust delegation->cred, 220869f9dfaSTrond Myklebust &delegation->stateid, 221869f9dfaSTrond Myklebust issync); 22257bfa891STrond Myklebust nfs_free_delegation(delegation); 22357bfa891STrond Myklebust return res; 22457bfa891STrond Myklebust } 22557bfa891STrond Myklebust 22686e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 22786e89489STrond Myklebust { 22886e89489STrond Myklebust struct inode *inode = NULL; 22986e89489STrond Myklebust 23086e89489STrond Myklebust spin_lock(&delegation->lock); 23186e89489STrond Myklebust if (delegation->inode != NULL) 23286e89489STrond Myklebust inode = igrab(delegation->inode); 23386e89489STrond Myklebust spin_unlock(&delegation->lock); 23486e89489STrond Myklebust return inode; 23586e89489STrond Myklebust } 23686e89489STrond Myklebust 237dda4b225SChuck Lever static struct nfs_delegation * 238d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 23957bfa891STrond Myklebust { 240d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 241d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 24257bfa891STrond Myklebust 24357bfa891STrond Myklebust if (delegation == NULL) 244d25be546STrond Myklebust goto out; 245d25be546STrond Myklebust spin_lock(&delegation->lock); 246d25be546STrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 247d25be546STrond Myklebust ret = delegation; 248d25be546STrond Myklebust spin_unlock(&delegation->lock); 249d25be546STrond Myklebust out: 250d25be546STrond Myklebust return ret; 251d25be546STrond Myklebust } 252d25be546STrond Myklebust 253d25be546STrond Myklebust static struct nfs_delegation * 254d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 255d25be546STrond Myklebust { 256d25be546STrond Myklebust struct nfs_delegation *delegation; 257d25be546STrond Myklebust 258d25be546STrond Myklebust rcu_read_lock(); 259d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 260d25be546STrond Myklebust rcu_read_unlock(); 261d25be546STrond Myklebust return delegation; 262d25be546STrond Myklebust } 263d25be546STrond Myklebust 264d25be546STrond Myklebust static void 265d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation, 266d25be546STrond Myklebust struct nfs_client *clp) 267d25be546STrond Myklebust { 268dda4b225SChuck Lever 26934310430STrond Myklebust spin_lock(&delegation->lock); 270d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 271d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 272d25be546STrond Myklebust spin_unlock(&delegation->lock); 273d25be546STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 274d25be546STrond Myklebust } 275d25be546STrond Myklebust 276d25be546STrond Myklebust static struct nfs_delegation * 277d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 278d25be546STrond Myklebust struct nfs_delegation *delegation, 279d25be546STrond Myklebust struct nfs_client *clp) 280d25be546STrond Myklebust { 281d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 282d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 283d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 284d25be546STrond Myklebust 285d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 286d25be546STrond Myklebust return NULL; 287d25be546STrond Myklebust 288d25be546STrond Myklebust spin_lock(&delegation->lock); 289d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 29057bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 29186e89489STrond Myklebust delegation->inode = NULL; 29257bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 29334310430STrond Myklebust spin_unlock(&delegation->lock); 29457bfa891STrond Myklebust return delegation; 29557bfa891STrond Myklebust } 29657bfa891STrond Myklebust 297dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 298d25be546STrond Myklebust struct nfs_delegation *delegation, 299d3978bb3SChuck Lever struct nfs_server *server) 300dda4b225SChuck Lever { 301d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 302dda4b225SChuck Lever 303dda4b225SChuck Lever spin_lock(&clp->cl_lock); 304d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 305dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 306dda4b225SChuck Lever return delegation; 307dda4b225SChuck Lever } 308dda4b225SChuck Lever 309d25be546STrond Myklebust static struct nfs_delegation * 310d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 311d25be546STrond Myklebust { 312d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 313d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 314d25be546STrond Myklebust struct nfs_delegation *delegation; 315d25be546STrond Myklebust 316d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 317d25be546STrond Myklebust if (delegation == NULL) 318d25be546STrond Myklebust return NULL; 319d25be546STrond Myklebust return nfs_detach_delegation(nfsi, delegation, server); 320d25be546STrond Myklebust } 321d25be546STrond Myklebust 322cf6726e2STrond Myklebust static void 323cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation, 324cf6726e2STrond Myklebust const struct nfs_delegation *update) 325cf6726e2STrond Myklebust { 326cf6726e2STrond Myklebust if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 327cf6726e2STrond Myklebust delegation->stateid.seqid = update->stateid.seqid; 328cf6726e2STrond Myklebust smp_wmb(); 329cf6726e2STrond Myklebust delegation->type = update->type; 330cf6726e2STrond Myklebust } 331cf6726e2STrond Myklebust } 332cf6726e2STrond Myklebust 333d3978bb3SChuck Lever /** 334d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 335d3978bb3SChuck Lever * @inode: inode to which delegation applies 336d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 33735156bffSTrond Myklebust * @type: delegation type 33835156bffSTrond Myklebust * @stateid: delegation stateid 33935156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 340d3978bb3SChuck Lever * 341d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 3421da177e4SLinus Torvalds */ 34335156bffSTrond Myklebust int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, 34435156bffSTrond Myklebust fmode_t type, 34535156bffSTrond Myklebust const nfs4_stateid *stateid, 34635156bffSTrond Myklebust unsigned long pagemod_limit) 3471da177e4SLinus Torvalds { 348d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 349d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 3501da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 35117d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 35257bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 3531da177e4SLinus Torvalds int status = 0; 3541da177e4SLinus Torvalds 3558535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 3561da177e4SLinus Torvalds if (delegation == NULL) 3571da177e4SLinus Torvalds return -ENOMEM; 35835156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 35935156bffSTrond Myklebust delegation->type = type; 36035156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 3611eb5d98fSJeff Layton delegation->change_attr = inode_peek_iversion_raw(inode); 3621da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 3631da177e4SLinus Torvalds delegation->inode = inode; 364b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 36534310430STrond Myklebust spin_lock_init(&delegation->lock); 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 36817d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 36917d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 37017d2c0a0SDavid Howells if (old_delegation != NULL) { 371cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 372cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 373cf6726e2STrond Myklebust &delegation->stateid)) { 374cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 375cf6726e2STrond Myklebust delegation); 37657bfa891STrond Myklebust goto out; 37757bfa891STrond Myklebust } 37857bfa891STrond Myklebust /* 37957bfa891STrond Myklebust * Deal with broken servers that hand out two 38057bfa891STrond Myklebust * delegations for the same file. 38117280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 38217280175STrond Myklebust * nothing else. 38357bfa891STrond Myklebust */ 38457bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 38557bfa891STrond Myklebust "a duplicate delegation!\n", 3863110ff80SHarvey Harrison __func__, clp->cl_hostname); 38717280175STrond Myklebust if (delegation->type == old_delegation->type || 38817280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 38957bfa891STrond Myklebust freeme = delegation; 39057bfa891STrond Myklebust delegation = NULL; 39157bfa891STrond Myklebust goto out; 39257bfa891STrond Myklebust } 393ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 394ade04647STrond Myklebust &old_delegation->flags)) 395ade04647STrond Myklebust goto out; 396d25be546STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, 397d25be546STrond Myklebust old_delegation, clp); 398d25be546STrond Myklebust if (freeme == NULL) 399d25be546STrond Myklebust goto out; 40057bfa891STrond Myklebust } 40138942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 4028383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 4031da177e4SLinus Torvalds delegation = NULL; 404412c77ceSTrond Myklebust 40535156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 406412c77ceSTrond Myklebust 40797c2c17aSTrond Myklebust spin_lock(&inode->i_lock); 40897c2c17aSTrond Myklebust if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 40997c2c17aSTrond Myklebust NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 41097c2c17aSTrond Myklebust spin_unlock(&inode->i_lock); 41157bfa891STrond Myklebust out: 4121da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 413603c83daSTrond Myklebust if (delegation != NULL) 414603c83daSTrond Myklebust nfs_free_delegation(delegation); 41557bfa891STrond Myklebust if (freeme != NULL) 41657bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 4171da177e4SLinus Torvalds return status; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds /* 4211da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 4221da177e4SLinus Torvalds */ 423d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 4241da177e4SLinus Torvalds { 425d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 4261da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 427869f9dfaSTrond Myklebust int err = 0; 4281da177e4SLinus Torvalds 429d25be546STrond Myklebust if (delegation == NULL) 430d25be546STrond Myklebust return 0; 431d25be546STrond Myklebust do { 432869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 433869f9dfaSTrond Myklebust break; 43424311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 43524311f88STrond Myklebust delegation->type); 436d25be546STrond Myklebust if (!issync || err != -EAGAIN) 437d25be546STrond Myklebust break; 438d25be546STrond Myklebust /* 439d25be546STrond Myklebust * Guard against state recovery 440d25be546STrond Myklebust */ 441d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 442d25be546STrond Myklebust } while (err == 0); 443d25be546STrond Myklebust 444d25be546STrond Myklebust if (err) { 445d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 446d25be546STrond Myklebust goto out; 447d25be546STrond Myklebust } 448d25be546STrond Myklebust if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode))) 449d18cc1fdSTrond Myklebust goto out; 4501da177e4SLinus Torvalds 451d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 452d18cc1fdSTrond Myklebust out: 453d18cc1fdSTrond Myklebust return err; 45490163027STrond Myklebust } 45590163027STrond Myklebust 456b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 457b757144fSTrond Myklebust { 458b757144fSTrond Myklebust bool ret = false; 459b757144fSTrond Myklebust 460ec3ca4e5STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 461ec3ca4e5STrond Myklebust goto out; 462b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 463b757144fSTrond Myklebust ret = true; 464b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { 465b757144fSTrond Myklebust struct inode *inode; 466b757144fSTrond Myklebust 467b757144fSTrond Myklebust spin_lock(&delegation->lock); 468b757144fSTrond Myklebust inode = delegation->inode; 469b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 470b757144fSTrond Myklebust ret = true; 471b757144fSTrond Myklebust spin_unlock(&delegation->lock); 472b757144fSTrond Myklebust } 473ec3ca4e5STrond Myklebust out: 474b757144fSTrond Myklebust return ret; 475b757144fSTrond Myklebust } 476b757144fSTrond Myklebust 477d3978bb3SChuck Lever /** 478d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 479d3978bb3SChuck Lever * @clp: nfs_client to process 480d3978bb3SChuck Lever * 481dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 482dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 483dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 484dc327ed4STrond Myklebust * 485d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 486515d8611STrond Myklebust */ 487d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 488515d8611STrond Myklebust { 489515d8611STrond Myklebust struct nfs_delegation *delegation; 490e04bbf6bSNeilBrown struct nfs_delegation *prev; 491d3978bb3SChuck Lever struct nfs_server *server; 492515d8611STrond Myklebust struct inode *inode; 493e04bbf6bSNeilBrown struct inode *place_holder = NULL; 494e04bbf6bSNeilBrown struct nfs_delegation *place_holder_deleg = NULL; 495d18cc1fdSTrond Myklebust int err = 0; 496515d8611STrond Myklebust 497515d8611STrond Myklebust restart: 498e04bbf6bSNeilBrown /* 499e04bbf6bSNeilBrown * To avoid quadratic looping we hold a reference 500e04bbf6bSNeilBrown * to an inode place_holder. Each time we restart, we 501e04bbf6bSNeilBrown * list nfs_servers from the server of that inode, and 502e04bbf6bSNeilBrown * delegation in the server from the delegations of that 503e04bbf6bSNeilBrown * inode. 504e04bbf6bSNeilBrown * prev is an RCU-protected pointer to a delegation which 505e04bbf6bSNeilBrown * wasn't marked for return and might be a good choice for 506e04bbf6bSNeilBrown * the next place_holder. 507e04bbf6bSNeilBrown */ 508515d8611STrond Myklebust rcu_read_lock(); 509e04bbf6bSNeilBrown prev = NULL; 510e04bbf6bSNeilBrown if (place_holder) 511e04bbf6bSNeilBrown server = NFS_SERVER(place_holder); 512e04bbf6bSNeilBrown else 513e04bbf6bSNeilBrown server = list_entry_rcu(clp->cl_superblocks.next, 514e04bbf6bSNeilBrown struct nfs_server, client_link); 515e04bbf6bSNeilBrown list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { 516e04bbf6bSNeilBrown delegation = NULL; 517e04bbf6bSNeilBrown if (place_holder && server == NFS_SERVER(place_holder)) 518e04bbf6bSNeilBrown delegation = rcu_dereference(NFS_I(place_holder)->delegation); 519e04bbf6bSNeilBrown if (!delegation || delegation != place_holder_deleg) 520e04bbf6bSNeilBrown delegation = list_entry_rcu(server->delegations.next, 521e04bbf6bSNeilBrown struct nfs_delegation, super_list); 522e04bbf6bSNeilBrown list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 523e04bbf6bSNeilBrown struct inode *to_put = NULL; 524e04bbf6bSNeilBrown 525e04bbf6bSNeilBrown if (!nfs_delegation_need_return(delegation)) { 526e04bbf6bSNeilBrown prev = delegation; 527515d8611STrond Myklebust continue; 528e04bbf6bSNeilBrown } 5299f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 530f3893491SNeilBrown break; /* continue in outer loop */ 531e04bbf6bSNeilBrown 532e04bbf6bSNeilBrown if (prev) { 533e04bbf6bSNeilBrown struct inode *tmp; 534e04bbf6bSNeilBrown 535e04bbf6bSNeilBrown tmp = nfs_delegation_grab_inode(prev); 536e04bbf6bSNeilBrown if (tmp) { 537e04bbf6bSNeilBrown to_put = place_holder; 538e04bbf6bSNeilBrown place_holder = tmp; 539e04bbf6bSNeilBrown place_holder_deleg = prev; 540e04bbf6bSNeilBrown } 541e04bbf6bSNeilBrown } 542e04bbf6bSNeilBrown 5439f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 5449f0f8e12STrond Myklebust if (inode == NULL) { 5459f0f8e12STrond Myklebust rcu_read_unlock(); 546e04bbf6bSNeilBrown if (to_put) 547e04bbf6bSNeilBrown iput(to_put); 5489f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5499f0f8e12STrond Myklebust goto restart; 5509f0f8e12STrond Myklebust } 551d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 552515d8611STrond Myklebust rcu_read_unlock(); 553d3978bb3SChuck Lever 554e04bbf6bSNeilBrown if (to_put) 555e04bbf6bSNeilBrown iput(to_put); 556e04bbf6bSNeilBrown 557d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 558515d8611STrond Myklebust iput(inode); 5599f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5603ca951b6SNeilBrown cond_resched(); 561d18cc1fdSTrond Myklebust if (!err) 562515d8611STrond Myklebust goto restart; 563d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 564e04bbf6bSNeilBrown if (place_holder) 565e04bbf6bSNeilBrown iput(place_holder); 566d18cc1fdSTrond Myklebust return err; 567515d8611STrond Myklebust } 568d3978bb3SChuck Lever } 569515d8611STrond Myklebust rcu_read_unlock(); 570e04bbf6bSNeilBrown if (place_holder) 571e04bbf6bSNeilBrown iput(place_holder); 572d18cc1fdSTrond Myklebust return 0; 573515d8611STrond Myklebust } 574515d8611STrond Myklebust 575d3978bb3SChuck Lever /** 576d3978bb3SChuck Lever * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens 577d3978bb3SChuck Lever * @inode: inode to process 578d3978bb3SChuck Lever * 579d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 580d3978bb3SChuck Lever * to be called from nfs4_clear_inode(). 581e6f81075STrond Myklebust */ 582e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 583e6f81075STrond Myklebust { 584e6f81075STrond Myklebust struct nfs_delegation *delegation; 585e6f81075STrond Myklebust 586d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 587e6f81075STrond Myklebust if (delegation != NULL) 5885fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 589e6f81075STrond Myklebust } 590e6f81075STrond Myklebust 591d3978bb3SChuck Lever /** 592d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 593d3978bb3SChuck Lever * @inode: inode to process 594d3978bb3SChuck Lever * 595c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 596c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 597c57d1bc5STrond Myklebust * we should stop caching. 598c57d1bc5STrond Myklebust * 599d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 600d3978bb3SChuck Lever */ 60157ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 60290163027STrond Myklebust { 60390163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 60490163027STrond Myklebust struct nfs_delegation *delegation; 60590163027STrond Myklebust int err = 0; 60690163027STrond Myklebust 607c57d1bc5STrond Myklebust nfs_wb_all(inode); 608d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 609d25be546STrond Myklebust if (delegation != NULL) 610d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 61190163027STrond Myklebust return err; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 614c01d3645STrond Myklebust /** 615c01d3645STrond Myklebust * nfs4_inode_make_writeable 616c01d3645STrond Myklebust * @inode: pointer to inode 617c01d3645STrond Myklebust * 618c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 619c01d3645STrond Myklebust * 620c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 621c01d3645STrond Myklebust */ 622c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 623c01d3645STrond Myklebust { 624c01d3645STrond Myklebust if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || 625c01d3645STrond Myklebust !nfs4_check_delegation(inode, FMODE_WRITE)) 626c01d3645STrond Myklebust return nfs4_inode_return_delegation(inode); 627c01d3645STrond Myklebust return 0; 628c01d3645STrond Myklebust } 629c01d3645STrond Myklebust 630b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 631b757144fSTrond Myklebust struct nfs_delegation *delegation) 632b757144fSTrond Myklebust { 633b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 634b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 635b757144fSTrond Myklebust } 636b757144fSTrond Myklebust 637ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 638ed1e6211STrond Myklebust struct nfs_delegation *delegation) 6396411bd4aSTrond Myklebust { 6406411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 641ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 6426411bd4aSTrond Myklebust } 6436411bd4aSTrond Myklebust 6445c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 6455c31e236STrond Myklebust { 6465c31e236STrond Myklebust struct nfs_delegation *delegation; 6475c31e236STrond Myklebust bool ret = false; 6485c31e236STrond Myklebust 6495c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 6505c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 6515c31e236STrond Myklebust ret = true; 6525c31e236STrond Myklebust } 6535c31e236STrond Myklebust return ret; 6545c31e236STrond Myklebust } 6555c31e236STrond Myklebust 656b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 657b02ba0b6STrond Myklebust { 658b02ba0b6STrond Myklebust struct nfs_server *server; 659b02ba0b6STrond Myklebust 660b02ba0b6STrond Myklebust rcu_read_lock(); 661b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 662b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 663b02ba0b6STrond Myklebust rcu_read_unlock(); 664b02ba0b6STrond Myklebust } 665b02ba0b6STrond Myklebust 666b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 667b02ba0b6STrond Myklebust { 668b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 669b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 670b02ba0b6STrond Myklebust } 671b02ba0b6STrond Myklebust 672b02ba0b6STrond Myklebust /** 673b02ba0b6STrond Myklebust * nfs_expire_all_delegations 674b02ba0b6STrond Myklebust * @clp: client to process 675b02ba0b6STrond Myklebust * 676b02ba0b6STrond Myklebust */ 677b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 678b02ba0b6STrond Myklebust { 679b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 680b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 681b02ba0b6STrond Myklebust } 682b02ba0b6STrond Myklebust 683d3978bb3SChuck Lever /** 684d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 685d3978bb3SChuck Lever * @sb: sb to process 686d3978bb3SChuck Lever * 6871da177e4SLinus Torvalds */ 688eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 6891da177e4SLinus Torvalds { 690d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 6915c31e236STrond Myklebust bool need_wait; 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds if (clp == NULL) 6941da177e4SLinus Torvalds return; 695d3978bb3SChuck Lever 6968383e460STrond Myklebust rcu_read_lock(); 6975c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 6988383e460STrond Myklebust rcu_read_unlock(); 699d3978bb3SChuck Lever 7005c31e236STrond Myklebust if (need_wait) { 701d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 7025c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 7035c31e236STrond Myklebust } 704515d8611STrond Myklebust } 705515d8611STrond Myklebust 706826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 707d3978bb3SChuck Lever fmode_t flags) 708515d8611STrond Myklebust { 709515d8611STrond Myklebust struct nfs_delegation *delegation; 710515d8611STrond Myklebust 711d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 712c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 713c79571a5SAlexandros Batsakis continue; 714c79571a5SAlexandros Batsakis if (delegation->type & flags) 715826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 716707fb4b3STrond Myklebust } 717d3978bb3SChuck Lever } 718d3978bb3SChuck Lever 719826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 720d3978bb3SChuck Lever fmode_t flags) 721d3978bb3SChuck Lever { 722d3978bb3SChuck Lever struct nfs_server *server; 723d3978bb3SChuck Lever 724d3978bb3SChuck Lever rcu_read_lock(); 725d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 726826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 727515d8611STrond Myklebust rcu_read_unlock(); 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 73041020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server, 73141020b67STrond Myklebust struct nfs_delegation *delegation) 73241020b67STrond Myklebust { 73341020b67STrond Myklebust set_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 734059b43e9STrond Myklebust delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 73541020b67STrond Myklebust nfs_mark_return_delegation(server, delegation); 73641020b67STrond Myklebust } 73741020b67STrond Myklebust 73841020b67STrond Myklebust static bool nfs_revoke_delegation(struct inode *inode, 73941020b67STrond Myklebust const nfs4_stateid *stateid) 740869f9dfaSTrond Myklebust { 741869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 7427f048831STrond Myklebust nfs4_stateid tmp; 74341020b67STrond Myklebust bool ret = false; 74441020b67STrond Myklebust 745869f9dfaSTrond Myklebust rcu_read_lock(); 746869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 74741020b67STrond Myklebust if (delegation == NULL) 74841020b67STrond Myklebust goto out; 7497f048831STrond Myklebust if (stateid == NULL) { 7507f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 7517f048831STrond Myklebust stateid = &tmp; 7527f048831STrond Myklebust } else if (!nfs4_stateid_match(stateid, &delegation->stateid)) 75341020b67STrond Myklebust goto out; 75441020b67STrond Myklebust nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation); 75541020b67STrond Myklebust ret = true; 75641020b67STrond Myklebust out: 757869f9dfaSTrond Myklebust rcu_read_unlock(); 7587f048831STrond Myklebust if (ret) 7597f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 76041020b67STrond Myklebust return ret; 761869f9dfaSTrond Myklebust } 762869f9dfaSTrond Myklebust 76341020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 76441020b67STrond Myklebust const nfs4_stateid *stateid) 765a1d0b5eeSTrond Myklebust { 766a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 767a1d0b5eeSTrond Myklebust 76841020b67STrond Myklebust if (!nfs_revoke_delegation(inode, stateid)) 76941020b67STrond Myklebust return; 770d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 7717f048831STrond Myklebust if (delegation) 772a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 773a1d0b5eeSTrond Myklebust } 7749cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 775a1d0b5eeSTrond Myklebust 776d3978bb3SChuck Lever /** 777826e0013STrond Myklebust * nfs_expire_unused_delegation_types 778d3978bb3SChuck Lever * @clp: client to process 779d3978bb3SChuck Lever * @flags: delegation types to expire 780d3978bb3SChuck Lever * 781d3978bb3SChuck Lever */ 782826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 783c79571a5SAlexandros Batsakis { 784826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 785c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 786c79571a5SAlexandros Batsakis } 787c79571a5SAlexandros Batsakis 788d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 789b7391f44STrond Myklebust { 790b7391f44STrond Myklebust struct nfs_delegation *delegation; 791b7391f44STrond Myklebust 792d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 793b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 794b7391f44STrond Myklebust continue; 795b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 796b7391f44STrond Myklebust } 797b7391f44STrond Myklebust } 798b7391f44STrond Myklebust 799d3978bb3SChuck Lever /** 800d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 801d3978bb3SChuck Lever * @clp: nfs_client to process 802d3978bb3SChuck Lever * 803d3978bb3SChuck Lever */ 804b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 805b7391f44STrond Myklebust { 806d3978bb3SChuck Lever struct nfs_server *server; 807d3978bb3SChuck Lever 808d3978bb3SChuck Lever rcu_read_lock(); 809d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 810d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 811d3978bb3SChuck Lever rcu_read_unlock(); 812d3978bb3SChuck Lever 813b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 814b7391f44STrond Myklebust } 815b7391f44STrond Myklebust 816d3978bb3SChuck Lever /** 817d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 818d3978bb3SChuck Lever * @inode: inode to process 8198e663f0eSTrond Myklebust * @stateid: state ID information 820d3978bb3SChuck Lever * 821d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 8221da177e4SLinus Torvalds */ 823d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 824d3978bb3SChuck Lever const nfs4_stateid *stateid) 8251da177e4SLinus Torvalds { 826ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 827ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 8286411bd4aSTrond Myklebust struct nfs_delegation *delegation; 8291da177e4SLinus Torvalds 8306411bd4aSTrond Myklebust rcu_read_lock(); 8316411bd4aSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 832755a48a7STrond Myklebust if (delegation == NULL) 833755a48a7STrond Myklebust goto out_enoent; 8344816fdadSTrond Myklebust if (stateid != NULL && 8354816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 836755a48a7STrond Myklebust goto out_enoent; 837ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 8386411bd4aSTrond Myklebust rcu_read_unlock(); 839d3978bb3SChuck Lever 8406411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 8416411bd4aSTrond Myklebust return 0; 842755a48a7STrond Myklebust out_enoent: 843755a48a7STrond Myklebust rcu_read_unlock(); 844755a48a7STrond Myklebust return -ENOENT; 8451da177e4SLinus Torvalds } 8461da177e4SLinus Torvalds 847d3978bb3SChuck Lever static struct inode * 848d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 849d3978bb3SChuck Lever const struct nfs_fh *fhandle) 8501da177e4SLinus Torvalds { 8511da177e4SLinus Torvalds struct nfs_delegation *delegation; 8521da177e4SLinus Torvalds struct inode *res = NULL; 853d3978bb3SChuck Lever 854d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 85586e89489STrond Myklebust spin_lock(&delegation->lock); 85686e89489STrond Myklebust if (delegation->inode != NULL && 85786e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 8581da177e4SLinus Torvalds res = igrab(delegation->inode); 8591da177e4SLinus Torvalds } 86086e89489STrond Myklebust spin_unlock(&delegation->lock); 86186e89489STrond Myklebust if (res != NULL) 86286e89489STrond Myklebust break; 8631da177e4SLinus Torvalds } 864d3978bb3SChuck Lever return res; 865d3978bb3SChuck Lever } 866d3978bb3SChuck Lever 867d3978bb3SChuck Lever /** 868d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 869d3978bb3SChuck Lever * @clp: client state handle 870d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 871d3978bb3SChuck Lever * 872d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 873d3978bb3SChuck Lever * cannot be found. 874d3978bb3SChuck Lever */ 875d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 876d3978bb3SChuck Lever const struct nfs_fh *fhandle) 877d3978bb3SChuck Lever { 878d3978bb3SChuck Lever struct nfs_server *server; 879d3978bb3SChuck Lever struct inode *res = NULL; 880d3978bb3SChuck Lever 881d3978bb3SChuck Lever rcu_read_lock(); 882d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 883d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 884d3978bb3SChuck Lever if (res != NULL) 885d3978bb3SChuck Lever break; 886d3978bb3SChuck Lever } 8878383e460STrond Myklebust rcu_read_unlock(); 8881da177e4SLinus Torvalds return res; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds 891d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 892d3978bb3SChuck Lever { 893d3978bb3SChuck Lever struct nfs_delegation *delegation; 894d3978bb3SChuck Lever 89545870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 89645870d69STrond Myklebust /* 89745870d69STrond Myklebust * If the delegation may have been admin revoked, then we 89845870d69STrond Myklebust * cannot reclaim it. 89945870d69STrond Myklebust */ 90045870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 90145870d69STrond Myklebust continue; 902d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 903d3978bb3SChuck Lever } 90445870d69STrond Myklebust } 905d3978bb3SChuck Lever 906d3978bb3SChuck Lever /** 907d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 908d3978bb3SChuck Lever * @clp: nfs_client to process 909d3978bb3SChuck Lever * 9101da177e4SLinus Torvalds */ 911adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 9121da177e4SLinus Torvalds { 913d3978bb3SChuck Lever struct nfs_server *server; 914d3978bb3SChuck Lever 9158383e460STrond Myklebust rcu_read_lock(); 916d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 917d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 9188383e460STrond Myklebust rcu_read_unlock(); 9191da177e4SLinus Torvalds } 9201da177e4SLinus Torvalds 921d3978bb3SChuck Lever /** 922d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 923d3978bb3SChuck Lever * @clp: nfs_client to process 924d3978bb3SChuck Lever * 9251da177e4SLinus Torvalds */ 926adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 9271da177e4SLinus Torvalds { 9288383e460STrond Myklebust struct nfs_delegation *delegation; 929d3978bb3SChuck Lever struct nfs_server *server; 93086e89489STrond Myklebust struct inode *inode; 931d3978bb3SChuck Lever 9328383e460STrond Myklebust restart: 9338383e460STrond Myklebust rcu_read_lock(); 934d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 935d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 936d3978bb3SChuck Lever super_list) { 937ec3ca4e5STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, 938ec3ca4e5STrond Myklebust &delegation->flags)) 939ec3ca4e5STrond Myklebust continue; 940d3978bb3SChuck Lever if (test_bit(NFS_DELEGATION_NEED_RECLAIM, 941d3978bb3SChuck Lever &delegation->flags) == 0) 9421da177e4SLinus Torvalds continue; 9439f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 944f3893491SNeilBrown break; /* continue in outer loop */ 9459f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 9469f0f8e12STrond Myklebust if (inode == NULL) { 9479f0f8e12STrond Myklebust rcu_read_unlock(); 9489f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 9499f0f8e12STrond Myklebust goto restart; 9509f0f8e12STrond Myklebust } 951b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 952b04b22f4STrond Myklebust rcu_read_unlock(); 953b04b22f4STrond Myklebust if (delegation != NULL) { 954d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 955d25be546STrond Myklebust delegation, server); 9568383e460STrond Myklebust if (delegation != NULL) 957905f8d16STrond Myklebust nfs_free_delegation(delegation); 958b04b22f4STrond Myklebust } 95986e89489STrond Myklebust iput(inode); 9609f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 9613ca951b6SNeilBrown cond_resched(); 9628383e460STrond Myklebust goto restart; 9631da177e4SLinus Torvalds } 964d3978bb3SChuck Lever } 9658383e460STrond Myklebust rcu_read_unlock(); 9661da177e4SLinus Torvalds } 9673e4f6290STrond Myklebust 968bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 969bb3d1a3bSTrond Myklebust { 970bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 971bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 972bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 973bb3d1a3bSTrond Myklebust } 974bb3d1a3bSTrond Myklebust 97545870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 97645870d69STrond Myklebust struct nfs_delegation *delegation) 97745870d69STrond Myklebust { 978059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 979059b43e9STrond Myklebust return; 98045870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 98145870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 98245870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 98345870d69STrond Myklebust } 98445870d69STrond Myklebust 985bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 986bb3d1a3bSTrond Myklebust struct inode *inode) 987bb3d1a3bSTrond Myklebust { 988bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 989bb3d1a3bSTrond Myklebust 990bb3d1a3bSTrond Myklebust rcu_read_lock(); 991bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 992bb3d1a3bSTrond Myklebust if (delegation) 993bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 994bb3d1a3bSTrond Myklebust rcu_read_unlock(); 995bb3d1a3bSTrond Myklebust 996bb3d1a3bSTrond Myklebust } 997bb3d1a3bSTrond Myklebust 99845870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 99945870d69STrond Myklebust { 100045870d69STrond Myklebust struct nfs_delegation *delegation; 100145870d69STrond Myklebust 100245870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 100345870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 100445870d69STrond Myklebust } 100545870d69STrond Myklebust 100645870d69STrond Myklebust /** 100745870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 100845870d69STrond Myklebust * @clp: nfs_client to process 100945870d69STrond Myklebust * 101045870d69STrond Myklebust * Iterates through all the delegations associated with this server and 101145870d69STrond Myklebust * marks them as needing to be checked for validity. 101245870d69STrond Myklebust */ 101345870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 101445870d69STrond Myklebust { 101545870d69STrond Myklebust struct nfs_server *server; 101645870d69STrond Myklebust 101745870d69STrond Myklebust rcu_read_lock(); 101845870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 101945870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 102045870d69STrond Myklebust rcu_read_unlock(); 102145870d69STrond Myklebust } 102245870d69STrond Myklebust 102345870d69STrond Myklebust /** 102445870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 102545870d69STrond Myklebust * @clp: nfs_client to process 102645870d69STrond Myklebust * 102745870d69STrond Myklebust * Iterates through all the delegations associated with this server and 102845870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 102945870d69STrond Myklebust * expected to be called in cases where the server may have lost its 103045870d69STrond Myklebust * lease. 103145870d69STrond Myklebust */ 103245870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 103345870d69STrond Myklebust { 103445870d69STrond Myklebust const struct nfs4_minor_version_ops *ops = clp->cl_mvops; 103545870d69STrond Myklebust struct nfs_delegation *delegation; 103645870d69STrond Myklebust struct nfs_server *server; 103745870d69STrond Myklebust struct inode *inode; 103845870d69STrond Myklebust struct rpc_cred *cred; 103945870d69STrond Myklebust nfs4_stateid stateid; 104045870d69STrond Myklebust 104145870d69STrond Myklebust restart: 104245870d69STrond Myklebust rcu_read_lock(); 104345870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 104445870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, 104545870d69STrond Myklebust super_list) { 104645870d69STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, 104745870d69STrond Myklebust &delegation->flags)) 104845870d69STrond Myklebust continue; 104945870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, 105045870d69STrond Myklebust &delegation->flags) == 0) 105145870d69STrond Myklebust continue; 105245870d69STrond Myklebust if (!nfs_sb_active(server->super)) 1053f3893491SNeilBrown break; /* continue in outer loop */ 105445870d69STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 105545870d69STrond Myklebust if (inode == NULL) { 105645870d69STrond Myklebust rcu_read_unlock(); 105745870d69STrond Myklebust nfs_sb_deactive(server->super); 105845870d69STrond Myklebust goto restart; 105945870d69STrond Myklebust } 106045870d69STrond Myklebust cred = get_rpccred_rcu(delegation->cred); 106145870d69STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 106245870d69STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 106345870d69STrond Myklebust rcu_read_unlock(); 106445870d69STrond Myklebust if (cred != NULL && 106545870d69STrond Myklebust ops->test_and_free_expired(server, &stateid, cred) < 0) { 106645870d69STrond Myklebust nfs_revoke_delegation(inode, &stateid); 106745870d69STrond Myklebust nfs_inode_find_state_and_recover(inode, &stateid); 106845870d69STrond Myklebust } 106945870d69STrond Myklebust put_rpccred(cred); 1070bb3d1a3bSTrond Myklebust if (nfs4_server_rebooted(clp)) { 1071bb3d1a3bSTrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 1072bb3d1a3bSTrond Myklebust iput(inode); 1073bb3d1a3bSTrond Myklebust nfs_sb_deactive(server->super); 1074bb3d1a3bSTrond Myklebust return; 1075bb3d1a3bSTrond Myklebust } 107645870d69STrond Myklebust iput(inode); 107745870d69STrond Myklebust nfs_sb_deactive(server->super); 10783ca951b6SNeilBrown cond_resched(); 107945870d69STrond Myklebust goto restart; 108045870d69STrond Myklebust } 108145870d69STrond Myklebust } 108245870d69STrond Myklebust rcu_read_unlock(); 108345870d69STrond Myklebust } 108445870d69STrond Myklebust 10856c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 10866c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 10876c2d8f8dSTrond Myklebust { 10886c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 10896c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 10906c2d8f8dSTrond Myklebust bool found = false; 10916c2d8f8dSTrond Myklebust 10926c2d8f8dSTrond Myklebust rcu_read_lock(); 10936c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 10946c2d8f8dSTrond Myklebust if (delegation && 10956c2d8f8dSTrond Myklebust nfs4_stateid_match_other(&delegation->stateid, stateid)) { 10966c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 10976c2d8f8dSTrond Myklebust found = true; 10986c2d8f8dSTrond Myklebust } 10996c2d8f8dSTrond Myklebust rcu_read_unlock(); 11006c2d8f8dSTrond Myklebust if (found) 11016c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 11026c2d8f8dSTrond Myklebust } 11036c2d8f8dSTrond Myklebust 1104d3978bb3SChuck Lever /** 1105d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1106d3978bb3SChuck Lever * @clp: client state handle 1107d3978bb3SChuck Lever * 1108d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1109d3978bb3SChuck Lever * to this nfs_client. 1110d3978bb3SChuck Lever */ 1111d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1112d3978bb3SChuck Lever { 1113d3978bb3SChuck Lever struct nfs_server *server; 1114d3978bb3SChuck Lever int ret = 0; 1115d3978bb3SChuck Lever 1116d3978bb3SChuck Lever rcu_read_lock(); 1117d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1118d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1119d3978bb3SChuck Lever ret = 1; 1120d3978bb3SChuck Lever break; 1121d3978bb3SChuck Lever } 1122d3978bb3SChuck Lever rcu_read_unlock(); 1123d3978bb3SChuck Lever return ret; 1124d3978bb3SChuck Lever } 1125d3978bb3SChuck Lever 1126d3978bb3SChuck Lever /** 112712f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 112812f275cdSTrond Myklebust * @dst: stateid to refresh 112912f275cdSTrond Myklebust * @inode: inode to check 113012f275cdSTrond Myklebust * 113112f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 113212f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 113312f275cdSTrond Myklebust */ 113412f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 113512f275cdSTrond Myklebust { 113612f275cdSTrond Myklebust struct nfs_delegation *delegation; 113712f275cdSTrond Myklebust bool ret = false; 113812f275cdSTrond Myklebust if (!inode) 113912f275cdSTrond Myklebust goto out; 114012f275cdSTrond Myklebust 114112f275cdSTrond Myklebust rcu_read_lock(); 114212f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 114312f275cdSTrond Myklebust if (delegation != NULL && 114412f275cdSTrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid)) { 114512f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 114612f275cdSTrond Myklebust return ret; 114712f275cdSTrond Myklebust } 114812f275cdSTrond Myklebust rcu_read_unlock(); 114912f275cdSTrond Myklebust out: 115012f275cdSTrond Myklebust return ret; 115112f275cdSTrond Myklebust } 115212f275cdSTrond Myklebust 115312f275cdSTrond Myklebust /** 1154d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1155d3978bb3SChuck Lever * @inode: inode to check 11560032a7a7STrond Myklebust * @flags: delegation type requirement 1157abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1158abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1159d3978bb3SChuck Lever * 11600032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 11610032a7a7STrond Myklebust * otherwise "false" is returned. 1162d3978bb3SChuck Lever */ 1163abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1164abf4e13cSTrond Myklebust nfs4_stateid *dst, struct rpc_cred **cred) 11653e4f6290STrond Myklebust { 11663e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 11673e4f6290STrond Myklebust struct nfs_delegation *delegation; 11680032a7a7STrond Myklebust bool ret; 11693e4f6290STrond Myklebust 11700032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 11718383e460STrond Myklebust rcu_read_lock(); 11728383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1173aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 11740032a7a7STrond Myklebust if (ret) { 1175f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 11760032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1177abf4e13cSTrond Myklebust if (cred) 1178abf4e13cSTrond Myklebust *cred = get_rpccred(delegation->cred); 11793e4f6290STrond Myklebust } 11808383e460STrond Myklebust rcu_read_unlock(); 11818383e460STrond Myklebust return ret; 11823e4f6290STrond Myklebust } 11835445b1fbSTrond Myklebust 11845445b1fbSTrond Myklebust /** 11855445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 11865445b1fbSTrond Myklebust * @inode: inode to check 11875445b1fbSTrond Myklebust * 11885445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 11895445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 11905445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 11915445b1fbSTrond Myklebust */ 11925445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 11935445b1fbSTrond Myklebust { 11945445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 11955445b1fbSTrond Myklebust struct nfs_delegation *delegation; 11965445b1fbSTrond Myklebust bool ret = true; 11975445b1fbSTrond Myklebust 11985445b1fbSTrond Myklebust rcu_read_lock(); 11995445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 12005445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 12015445b1fbSTrond Myklebust goto out; 1202a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 12035445b1fbSTrond Myklebust ret = false; 12045445b1fbSTrond Myklebust out: 12055445b1fbSTrond Myklebust rcu_read_unlock(); 12065445b1fbSTrond Myklebust return ret; 12075445b1fbSTrond Myklebust } 1208