1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * linux/fs/nfs/delegation.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (C) 2004 Trond Myklebust 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * NFS file delegation management 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds #include <linux/completion.h> 1158d9714aSTrond Myklebust #include <linux/kthread.h> 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/sched.h> 145a0e3ad6STejun Heo #include <linux/slab.h> 151da177e4SLinus Torvalds #include <linux/spinlock.h> 161eb5d98fSJeff Layton #include <linux/iversion.h> 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds #include <linux/nfs4.h> 191da177e4SLinus Torvalds #include <linux/nfs_fs.h> 201da177e4SLinus Torvalds #include <linux/nfs_xdr.h> 211da177e4SLinus Torvalds 224ce79717STrond Myklebust #include "nfs4_fs.h" 23c01d3645STrond Myklebust #include "nfs4session.h" 241da177e4SLinus Torvalds #include "delegation.h" 2524c8dbbbSDavid Howells #include "internal.h" 26ca8acf8dSTrond Myklebust #include "nfs4trace.h" 271da177e4SLinus Torvalds 2810717f45STrond Myklebust #define NFS_DEFAULT_DELEGATION_WATERMARK (5000U) 2910717f45STrond Myklebust 30d2269ea1STrond Myklebust static atomic_long_t nfs_active_delegations; 3110717f45STrond Myklebust static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK; 32d2269ea1STrond Myklebust 33d2269ea1STrond Myklebust static void __nfs_free_delegation(struct nfs_delegation *delegation) 34905f8d16STrond Myklebust { 35a52458b4SNeilBrown put_cred(delegation->cred); 36e00b8a24STrond Myklebust delegation->cred = NULL; 3726f04ddeSLai Jiangshan kfree_rcu(delegation, rcu); 388383e460STrond Myklebust } 398383e460STrond Myklebust 40d2269ea1STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) 41d2269ea1STrond Myklebust { 42d2269ea1STrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 43d2269ea1STrond Myklebust delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 44d2269ea1STrond Myklebust atomic_long_dec(&nfs_active_delegations); 45efeda80dSTrond Myklebust if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 46efeda80dSTrond Myklebust nfs_clear_verifier_delegated(delegation->inode); 47d2269ea1STrond Myklebust } 48d2269ea1STrond Myklebust } 49d2269ea1STrond Myklebust 508c75593cSTrond Myklebust static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation) 518c75593cSTrond Myklebust { 528c75593cSTrond Myklebust refcount_inc(&delegation->refcount); 538c75593cSTrond Myklebust return delegation; 548c75593cSTrond Myklebust } 558c75593cSTrond Myklebust 568c75593cSTrond Myklebust static void nfs_put_delegation(struct nfs_delegation *delegation) 578c75593cSTrond Myklebust { 588c75593cSTrond Myklebust if (refcount_dec_and_test(&delegation->refcount)) 598c75593cSTrond Myklebust __nfs_free_delegation(delegation); 608c75593cSTrond Myklebust } 618c75593cSTrond Myklebust 62d2269ea1STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 63d2269ea1STrond Myklebust { 64d2269ea1STrond Myklebust nfs_mark_delegation_revoked(delegation); 658c75593cSTrond Myklebust nfs_put_delegation(delegation); 66d2269ea1STrond Myklebust } 67d2269ea1STrond Myklebust 68d3978bb3SChuck Lever /** 69d3978bb3SChuck Lever * nfs_mark_delegation_referenced - set delegation's REFERENCED flag 70d3978bb3SChuck Lever * @delegation: delegation to process 71d3978bb3SChuck Lever * 72d3978bb3SChuck Lever */ 73b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 74b7391f44STrond Myklebust { 75b7391f44STrond Myklebust set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 76b7391f44STrond Myklebust } 77b7391f44STrond Myklebust 78aa05c87fSTrond Myklebust static bool 79aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation, 80aa05c87fSTrond Myklebust fmode_t flags) 81aa05c87fSTrond Myklebust { 82aa05c87fSTrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags && 83aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 84aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 85aa05c87fSTrond Myklebust return true; 86aa05c87fSTrond Myklebust return false; 87aa05c87fSTrond Myklebust } 88aa05c87fSTrond Myklebust 89be3df3ddSTrond Myklebust struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) 90be3df3ddSTrond Myklebust { 91be3df3ddSTrond Myklebust struct nfs_delegation *delegation; 92be3df3ddSTrond Myklebust 93be3df3ddSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 94be3df3ddSTrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) 95be3df3ddSTrond Myklebust return delegation; 96be3df3ddSTrond Myklebust return NULL; 97be3df3ddSTrond Myklebust } 98be3df3ddSTrond Myklebust 9915bb3afeSPeng Tao static int 10015bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark) 101b7391f44STrond Myklebust { 102b7391f44STrond Myklebust struct nfs_delegation *delegation; 103b7391f44STrond Myklebust int ret = 0; 104b7391f44STrond Myklebust 105b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 106b7391f44STrond Myklebust rcu_read_lock(); 107b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 108aa05c87fSTrond Myklebust if (nfs4_is_valid_delegation(delegation, flags)) { 10915bb3afeSPeng Tao if (mark) 110b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 111b7391f44STrond Myklebust ret = 1; 112b7391f44STrond Myklebust } 113b7391f44STrond Myklebust rcu_read_unlock(); 114b7391f44STrond Myklebust return ret; 115b7391f44STrond Myklebust } 11615bb3afeSPeng Tao /** 11715bb3afeSPeng Tao * nfs_have_delegation - check if inode has a delegation, mark it 11815bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if there is one. 11915bb3afeSPeng Tao * @inode: inode to check 12015bb3afeSPeng Tao * @flags: delegation types to check for 12115bb3afeSPeng Tao * 12215bb3afeSPeng Tao * Returns one if inode has the indicated delegation, otherwise zero. 12315bb3afeSPeng Tao */ 12415bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags) 12515bb3afeSPeng Tao { 12615bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, true); 12715bb3afeSPeng Tao } 12815bb3afeSPeng Tao 12915bb3afeSPeng Tao /* 13015bb3afeSPeng Tao * nfs4_check_delegation - check if inode has a delegation, do not mark 13115bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if it has one. 13215bb3afeSPeng Tao */ 13315bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags) 13415bb3afeSPeng Tao { 13515bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, false); 13615bb3afeSPeng Tao } 137b7391f44STrond Myklebust 13844f411c3SOlga Kornievskaia static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) 139888e694cSTrond Myklebust { 140888e694cSTrond Myklebust struct inode *inode = state->inode; 141888e694cSTrond Myklebust struct file_lock *fl; 142bd61e0a9SJeff Layton struct file_lock_context *flctx = inode->i_flctx; 143bd61e0a9SJeff Layton struct list_head *list; 144d5122201STrond Myklebust int status = 0; 145888e694cSTrond Myklebust 146bd61e0a9SJeff Layton if (flctx == NULL) 14765b62a29STrond Myklebust goto out; 148314d7cc0SJeff Layton 149bd61e0a9SJeff Layton list = &flctx->flc_posix; 1506109c850SJeff Layton spin_lock(&flctx->flc_lock); 151bd61e0a9SJeff Layton restart: 152bd61e0a9SJeff Layton list_for_each_entry(fl, list, fl_list) { 15344f411c3SOlga Kornievskaia if (nfs_file_open_context(fl->fl_file)->state != state) 154888e694cSTrond Myklebust continue; 1556109c850SJeff Layton spin_unlock(&flctx->flc_lock); 156db4f2e63STrond Myklebust status = nfs4_lock_delegation_recall(fl, state, stateid); 157d5122201STrond Myklebust if (status < 0) 1583f09df70STrond Myklebust goto out; 1596109c850SJeff Layton spin_lock(&flctx->flc_lock); 160888e694cSTrond Myklebust } 161bd61e0a9SJeff Layton if (list == &flctx->flc_posix) { 162bd61e0a9SJeff Layton list = &flctx->flc_flock; 163bd61e0a9SJeff Layton goto restart; 1645263e31eSJeff Layton } 1656109c850SJeff Layton spin_unlock(&flctx->flc_lock); 1663f09df70STrond Myklebust out: 167888e694cSTrond Myklebust return status; 168888e694cSTrond Myklebust } 169888e694cSTrond Myklebust 17024311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, 17124311f88STrond Myklebust const nfs4_stateid *stateid, fmode_t type) 1721da177e4SLinus Torvalds { 1731da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 1741da177e4SLinus Torvalds struct nfs_open_context *ctx; 175d25be546STrond Myklebust struct nfs4_state_owner *sp; 1761da177e4SLinus Torvalds struct nfs4_state *state; 177d25be546STrond Myklebust unsigned int seq; 178888e694cSTrond Myklebust int err; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds again: 1810de43976STrond Myklebust rcu_read_lock(); 1820de43976STrond Myklebust list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 1831da177e4SLinus Torvalds state = ctx->state; 1841da177e4SLinus Torvalds if (state == NULL) 1851da177e4SLinus Torvalds continue; 1861da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1871da177e4SLinus Torvalds continue; 188f8ebf7a8STrond Myklebust if (!nfs4_valid_open_stateid(state)) 189f8ebf7a8STrond Myklebust continue; 190f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 19190163027STrond Myklebust continue; 1920de43976STrond Myklebust if (!get_nfs_open_context(ctx)) 1930de43976STrond Myklebust continue; 1940de43976STrond Myklebust rcu_read_unlock(); 195d25be546STrond Myklebust sp = state->owner; 19665b62a29STrond Myklebust /* Block nfs4_proc_unlck */ 19765b62a29STrond Myklebust mutex_lock(&sp->so_delegreturn_mutex); 198d25be546STrond Myklebust seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 1995eb8d18cSTrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 200d25be546STrond Myklebust if (!err) 20144f411c3SOlga Kornievskaia err = nfs_delegation_claim_locks(state, stateid); 202d25be546STrond Myklebust if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 203d25be546STrond Myklebust err = -EAGAIN; 20465b62a29STrond Myklebust mutex_unlock(&sp->so_delegreturn_mutex); 2051da177e4SLinus Torvalds put_nfs_open_context(ctx); 206888e694cSTrond Myklebust if (err != 0) 207d18cc1fdSTrond Myklebust return err; 2081da177e4SLinus Torvalds goto again; 2091da177e4SLinus Torvalds } 2100de43976STrond Myklebust rcu_read_unlock(); 211d18cc1fdSTrond Myklebust return 0; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 214d3978bb3SChuck Lever /** 215d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 216d3978bb3SChuck Lever * @inode: inode to process 217d3978bb3SChuck Lever * @cred: credential to use for request 21835156bffSTrond Myklebust * @type: delegation type 21935156bffSTrond Myklebust * @stateid: delegation stateid 22035156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 221d3978bb3SChuck Lever * 2221da177e4SLinus Torvalds */ 223a52458b4SNeilBrown void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, 22435156bffSTrond Myklebust fmode_t type, 22535156bffSTrond Myklebust const nfs4_stateid *stateid, 22635156bffSTrond Myklebust unsigned long pagemod_limit) 2271da177e4SLinus Torvalds { 2288f649c37STrond Myklebust struct nfs_delegation *delegation; 229a52458b4SNeilBrown const struct cred *oldcred = NULL; 2301da177e4SLinus Torvalds 2318f649c37STrond Myklebust rcu_read_lock(); 2328f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 2338f649c37STrond Myklebust if (delegation != NULL) { 2348f649c37STrond Myklebust spin_lock(&delegation->lock); 2351deed572STrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) { 23635156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 23735156bffSTrond Myklebust delegation->type = type; 23835156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 23905c88babSTrond Myklebust oldcred = delegation->cred; 240a52458b4SNeilBrown delegation->cred = get_cred(cred); 2418f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 2428f649c37STrond Myklebust &delegation->flags); 2438f649c37STrond Myklebust spin_unlock(&delegation->lock); 2448f649c37STrond Myklebust rcu_read_unlock(); 245a52458b4SNeilBrown put_cred(oldcred); 24635156bffSTrond Myklebust trace_nfs4_reclaim_delegation(inode, type); 247b1a318deSTrond Myklebust return; 248b1a318deSTrond Myklebust } 2498f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 2508f649c37STrond Myklebust spin_unlock(&delegation->lock); 251b1a318deSTrond Myklebust } 2528f649c37STrond Myklebust rcu_read_unlock(); 25335156bffSTrond Myklebust nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit); 2548f649c37STrond Myklebust } 2551da177e4SLinus Torvalds 25657bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 25757bfa891STrond Myklebust { 25857bfa891STrond Myklebust int res = 0; 25957bfa891STrond Myklebust 260869f9dfaSTrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 261869f9dfaSTrond Myklebust res = nfs4_proc_delegreturn(inode, 262869f9dfaSTrond Myklebust delegation->cred, 263869f9dfaSTrond Myklebust &delegation->stateid, 264869f9dfaSTrond Myklebust issync); 26557bfa891STrond Myklebust return res; 26657bfa891STrond Myklebust } 26757bfa891STrond Myklebust 26886e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 26986e89489STrond Myklebust { 27086e89489STrond Myklebust struct inode *inode = NULL; 27186e89489STrond Myklebust 27286e89489STrond Myklebust spin_lock(&delegation->lock); 27386e89489STrond Myklebust if (delegation->inode != NULL) 27486e89489STrond Myklebust inode = igrab(delegation->inode); 2756f9449beSTrond Myklebust if (!inode) 2766f9449beSTrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 27786e89489STrond Myklebust spin_unlock(&delegation->lock); 27886e89489STrond Myklebust return inode; 27986e89489STrond Myklebust } 28086e89489STrond Myklebust 281dda4b225SChuck Lever static struct nfs_delegation * 282d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 28357bfa891STrond Myklebust { 284d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 285d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 28657bfa891STrond Myklebust 28757bfa891STrond Myklebust if (delegation == NULL) 288d25be546STrond Myklebust goto out; 289d25be546STrond Myklebust spin_lock(&delegation->lock); 2908c75593cSTrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 2918c75593cSTrond Myklebust /* Refcount matched in nfs_end_delegation_return() */ 2928c75593cSTrond Myklebust ret = nfs_get_delegation(delegation); 2938c75593cSTrond Myklebust } 294d25be546STrond Myklebust spin_unlock(&delegation->lock); 295efeda80dSTrond Myklebust if (ret) 296efeda80dSTrond Myklebust nfs_clear_verifier_delegated(&nfsi->vfs_inode); 297d25be546STrond Myklebust out: 298d25be546STrond Myklebust return ret; 299d25be546STrond Myklebust } 300d25be546STrond Myklebust 301d25be546STrond Myklebust static struct nfs_delegation * 302d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 303d25be546STrond Myklebust { 304d25be546STrond Myklebust struct nfs_delegation *delegation; 305d25be546STrond Myklebust 306d25be546STrond Myklebust rcu_read_lock(); 307d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 308d25be546STrond Myklebust rcu_read_unlock(); 309d25be546STrond Myklebust return delegation; 310d25be546STrond Myklebust } 311d25be546STrond Myklebust 312d25be546STrond Myklebust static void 313d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation, 314d25be546STrond Myklebust struct nfs_client *clp) 315d25be546STrond Myklebust { 316dda4b225SChuck Lever 31734310430STrond Myklebust spin_lock(&delegation->lock); 318d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 319d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 320d25be546STrond Myklebust spin_unlock(&delegation->lock); 321d25be546STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 322d25be546STrond Myklebust } 323d25be546STrond Myklebust 324d25be546STrond Myklebust static struct nfs_delegation * 325d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 326d25be546STrond Myklebust struct nfs_delegation *delegation, 327d25be546STrond Myklebust struct nfs_client *clp) 328d25be546STrond Myklebust { 329d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 330d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 331d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 332d25be546STrond Myklebust 333d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 334d25be546STrond Myklebust return NULL; 335d25be546STrond Myklebust 336d25be546STrond Myklebust spin_lock(&delegation->lock); 337f9e0cc9cSTrond Myklebust if (!delegation->inode) { 338f9e0cc9cSTrond Myklebust spin_unlock(&delegation->lock); 339f9e0cc9cSTrond Myklebust return NULL; 340f9e0cc9cSTrond Myklebust } 34157bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 34286e89489STrond Myklebust delegation->inode = NULL; 34357bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 34434310430STrond Myklebust spin_unlock(&delegation->lock); 34557bfa891STrond Myklebust return delegation; 34657bfa891STrond Myklebust } 34757bfa891STrond Myklebust 348dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 349d25be546STrond Myklebust struct nfs_delegation *delegation, 350d3978bb3SChuck Lever struct nfs_server *server) 351dda4b225SChuck Lever { 352d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 353dda4b225SChuck Lever 354dda4b225SChuck Lever spin_lock(&clp->cl_lock); 355d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 356dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 357dda4b225SChuck Lever return delegation; 358dda4b225SChuck Lever } 359dda4b225SChuck Lever 360d25be546STrond Myklebust static struct nfs_delegation * 361d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 362d25be546STrond Myklebust { 363d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 364d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 365d25be546STrond Myklebust struct nfs_delegation *delegation; 366d25be546STrond Myklebust 367ee05f456STrond Myklebust rcu_read_lock(); 368ee05f456STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 369ee05f456STrond Myklebust if (delegation != NULL) 370ee05f456STrond Myklebust delegation = nfs_detach_delegation(nfsi, delegation, server); 371ee05f456STrond Myklebust rcu_read_unlock(); 372ee05f456STrond Myklebust return delegation; 373d25be546STrond Myklebust } 374d25be546STrond Myklebust 375cf6726e2STrond Myklebust static void 376cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation, 377cf6726e2STrond Myklebust const struct nfs_delegation *update) 378cf6726e2STrond Myklebust { 379cf6726e2STrond Myklebust if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 380cf6726e2STrond Myklebust delegation->stateid.seqid = update->stateid.seqid; 381cf6726e2STrond Myklebust smp_wmb(); 382cf6726e2STrond Myklebust delegation->type = update->type; 383d2269ea1STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 384d2269ea1STrond Myklebust atomic_long_inc(&nfs_active_delegations); 385cf6726e2STrond Myklebust } 386cf6726e2STrond Myklebust } 387cf6726e2STrond Myklebust 388d3978bb3SChuck Lever /** 389d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 390d3978bb3SChuck Lever * @inode: inode to which delegation applies 391d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 39235156bffSTrond Myklebust * @type: delegation type 39335156bffSTrond Myklebust * @stateid: delegation stateid 39435156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 395d3978bb3SChuck Lever * 396d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 3971da177e4SLinus Torvalds */ 398a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, 39935156bffSTrond Myklebust fmode_t type, 40035156bffSTrond Myklebust const nfs4_stateid *stateid, 40135156bffSTrond Myklebust unsigned long pagemod_limit) 4021da177e4SLinus Torvalds { 403d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 404d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 4051da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 40617d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 40757bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 4081da177e4SLinus Torvalds int status = 0; 4091da177e4SLinus Torvalds 4108535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 4111da177e4SLinus Torvalds if (delegation == NULL) 4121da177e4SLinus Torvalds return -ENOMEM; 41335156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 4148c75593cSTrond Myklebust refcount_set(&delegation->refcount, 1); 41535156bffSTrond Myklebust delegation->type = type; 41635156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 4171eb5d98fSJeff Layton delegation->change_attr = inode_peek_iversion_raw(inode); 418a52458b4SNeilBrown delegation->cred = get_cred(cred); 4191da177e4SLinus Torvalds delegation->inode = inode; 420b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 42134310430STrond Myklebust spin_lock_init(&delegation->lock); 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 42417d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 42517d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 426ee05f456STrond Myklebust if (old_delegation == NULL) 427ee05f456STrond Myklebust goto add_new; 428cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 429cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 430cf6726e2STrond Myklebust &delegation->stateid)) { 431e0f07896STrond Myklebust spin_lock(&old_delegation->lock); 432cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 433cf6726e2STrond Myklebust delegation); 434e0f07896STrond Myklebust spin_unlock(&old_delegation->lock); 43557bfa891STrond Myklebust goto out; 43657bfa891STrond Myklebust } 437ee05f456STrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) { 43857bfa891STrond Myklebust /* 43957bfa891STrond Myklebust * Deal with broken servers that hand out two 44057bfa891STrond Myklebust * delegations for the same file. 44117280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 44217280175STrond Myklebust * nothing else. 44357bfa891STrond Myklebust */ 44457bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 44557bfa891STrond Myklebust "a duplicate delegation!\n", 4463110ff80SHarvey Harrison __func__, clp->cl_hostname); 44717280175STrond Myklebust if (delegation->type == old_delegation->type || 44817280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 44957bfa891STrond Myklebust freeme = delegation; 45057bfa891STrond Myklebust delegation = NULL; 45157bfa891STrond Myklebust goto out; 45257bfa891STrond Myklebust } 453ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 454ade04647STrond Myklebust &old_delegation->flags)) 455ade04647STrond Myklebust goto out; 456ee05f456STrond Myklebust } 457ee05f456STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); 458d25be546STrond Myklebust if (freeme == NULL) 459d25be546STrond Myklebust goto out; 460ee05f456STrond Myklebust add_new: 46138942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 4628383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 4631da177e4SLinus Torvalds delegation = NULL; 464412c77ceSTrond Myklebust 465d2269ea1STrond Myklebust atomic_long_inc(&nfs_active_delegations); 466d2269ea1STrond Myklebust 46735156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 468412c77ceSTrond Myklebust 46997c2c17aSTrond Myklebust spin_lock(&inode->i_lock); 47097c2c17aSTrond Myklebust if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 47197c2c17aSTrond Myklebust NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 47297c2c17aSTrond Myklebust spin_unlock(&inode->i_lock); 47357bfa891STrond Myklebust out: 4741da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 475603c83daSTrond Myklebust if (delegation != NULL) 476d2269ea1STrond Myklebust __nfs_free_delegation(delegation); 477ee05f456STrond Myklebust if (freeme != NULL) { 47857bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 479ee05f456STrond Myklebust nfs_free_delegation(freeme); 480ee05f456STrond Myklebust } 4811da177e4SLinus Torvalds return status; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds /* 4851da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 4861da177e4SLinus Torvalds */ 487d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 4881da177e4SLinus Torvalds { 489d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 490869f9dfaSTrond Myklebust int err = 0; 4911da177e4SLinus Torvalds 492d25be546STrond Myklebust if (delegation == NULL) 493d25be546STrond Myklebust return 0; 494d25be546STrond Myklebust do { 495869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 496869f9dfaSTrond Myklebust break; 49724311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 49824311f88STrond Myklebust delegation->type); 499d25be546STrond Myklebust if (!issync || err != -EAGAIN) 500d25be546STrond Myklebust break; 501d25be546STrond Myklebust /* 502d25be546STrond Myklebust * Guard against state recovery 503d25be546STrond Myklebust */ 504d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 505d25be546STrond Myklebust } while (err == 0); 506d25be546STrond Myklebust 507d25be546STrond Myklebust if (err) { 508d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 509d25be546STrond Myklebust goto out; 510d25be546STrond Myklebust } 5111da177e4SLinus Torvalds 512d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 513d18cc1fdSTrond Myklebust out: 5148c75593cSTrond Myklebust /* Refcount matched in nfs_start_delegation_return_locked() */ 5158c75593cSTrond Myklebust nfs_put_delegation(delegation); 516d18cc1fdSTrond Myklebust return err; 51790163027STrond Myklebust } 51890163027STrond Myklebust 519b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 520b757144fSTrond Myklebust { 521b757144fSTrond Myklebust bool ret = false; 522b757144fSTrond Myklebust 523b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 524b757144fSTrond Myklebust ret = true; 5250d104167STrond Myklebust else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { 526b757144fSTrond Myklebust struct inode *inode; 527b757144fSTrond Myklebust 528b757144fSTrond Myklebust spin_lock(&delegation->lock); 529b757144fSTrond Myklebust inode = delegation->inode; 530b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 531b757144fSTrond Myklebust ret = true; 532b757144fSTrond Myklebust spin_unlock(&delegation->lock); 533b757144fSTrond Myklebust } 5340d104167STrond Myklebust if (ret) 5350d104167STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 536af20b7b8STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || 537af20b7b8STrond Myklebust test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 538af20b7b8STrond Myklebust ret = false; 539af20b7b8STrond Myklebust 540b757144fSTrond Myklebust return ret; 541b757144fSTrond Myklebust } 542b757144fSTrond Myklebust 543d3978bb3SChuck Lever /** 544d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 545d3978bb3SChuck Lever * @clp: nfs_client to process 546d3978bb3SChuck Lever * 547dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 548dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 549dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 550dc327ed4STrond Myklebust * 551d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 552515d8611STrond Myklebust */ 553d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 554515d8611STrond Myklebust { 555515d8611STrond Myklebust struct nfs_delegation *delegation; 556e04bbf6bSNeilBrown struct nfs_delegation *prev; 557d3978bb3SChuck Lever struct nfs_server *server; 558515d8611STrond Myklebust struct inode *inode; 559e04bbf6bSNeilBrown struct inode *place_holder = NULL; 560e04bbf6bSNeilBrown struct nfs_delegation *place_holder_deleg = NULL; 561d18cc1fdSTrond Myklebust int err = 0; 562515d8611STrond Myklebust 563515d8611STrond Myklebust restart: 564e04bbf6bSNeilBrown /* 565e04bbf6bSNeilBrown * To avoid quadratic looping we hold a reference 566e04bbf6bSNeilBrown * to an inode place_holder. Each time we restart, we 567e04bbf6bSNeilBrown * list nfs_servers from the server of that inode, and 568e04bbf6bSNeilBrown * delegation in the server from the delegations of that 569e04bbf6bSNeilBrown * inode. 570e04bbf6bSNeilBrown * prev is an RCU-protected pointer to a delegation which 571e04bbf6bSNeilBrown * wasn't marked for return and might be a good choice for 572e04bbf6bSNeilBrown * the next place_holder. 573e04bbf6bSNeilBrown */ 574515d8611STrond Myklebust rcu_read_lock(); 575e04bbf6bSNeilBrown prev = NULL; 576e04bbf6bSNeilBrown if (place_holder) 577e04bbf6bSNeilBrown server = NFS_SERVER(place_holder); 578e04bbf6bSNeilBrown else 579e04bbf6bSNeilBrown server = list_entry_rcu(clp->cl_superblocks.next, 580e04bbf6bSNeilBrown struct nfs_server, client_link); 581e04bbf6bSNeilBrown list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { 582e04bbf6bSNeilBrown delegation = NULL; 583e04bbf6bSNeilBrown if (place_holder && server == NFS_SERVER(place_holder)) 584e04bbf6bSNeilBrown delegation = rcu_dereference(NFS_I(place_holder)->delegation); 585e04bbf6bSNeilBrown if (!delegation || delegation != place_holder_deleg) 586e04bbf6bSNeilBrown delegation = list_entry_rcu(server->delegations.next, 587e04bbf6bSNeilBrown struct nfs_delegation, super_list); 588e04bbf6bSNeilBrown list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 589e04bbf6bSNeilBrown struct inode *to_put = NULL; 590e04bbf6bSNeilBrown 591e04bbf6bSNeilBrown if (!nfs_delegation_need_return(delegation)) { 592e04bbf6bSNeilBrown prev = delegation; 593515d8611STrond Myklebust continue; 594e04bbf6bSNeilBrown } 5959f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 596f3893491SNeilBrown break; /* continue in outer loop */ 597e04bbf6bSNeilBrown 598e04bbf6bSNeilBrown if (prev) { 599e04bbf6bSNeilBrown struct inode *tmp; 600e04bbf6bSNeilBrown 601e04bbf6bSNeilBrown tmp = nfs_delegation_grab_inode(prev); 602e04bbf6bSNeilBrown if (tmp) { 603e04bbf6bSNeilBrown to_put = place_holder; 604e04bbf6bSNeilBrown place_holder = tmp; 605e04bbf6bSNeilBrown place_holder_deleg = prev; 606e04bbf6bSNeilBrown } 607e04bbf6bSNeilBrown } 608e04bbf6bSNeilBrown 6099f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 6109f0f8e12STrond Myklebust if (inode == NULL) { 6119f0f8e12STrond Myklebust rcu_read_unlock(); 612e04bbf6bSNeilBrown if (to_put) 613e04bbf6bSNeilBrown iput(to_put); 6149f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 6159f0f8e12STrond Myklebust goto restart; 6169f0f8e12STrond Myklebust } 617d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 618515d8611STrond Myklebust rcu_read_unlock(); 619d3978bb3SChuck Lever 620e04bbf6bSNeilBrown if (to_put) 621e04bbf6bSNeilBrown iput(to_put); 622e04bbf6bSNeilBrown 623d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 624515d8611STrond Myklebust iput(inode); 6259f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 6263ca951b6SNeilBrown cond_resched(); 627d18cc1fdSTrond Myklebust if (!err) 628515d8611STrond Myklebust goto restart; 629d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 630e04bbf6bSNeilBrown if (place_holder) 631e04bbf6bSNeilBrown iput(place_holder); 632d18cc1fdSTrond Myklebust return err; 633515d8611STrond Myklebust } 634d3978bb3SChuck Lever } 635515d8611STrond Myklebust rcu_read_unlock(); 636e04bbf6bSNeilBrown if (place_holder) 637e04bbf6bSNeilBrown iput(place_holder); 638d18cc1fdSTrond Myklebust return 0; 639515d8611STrond Myklebust } 640515d8611STrond Myklebust 641d3978bb3SChuck Lever /** 642b47e0e47STrond Myklebust * nfs_inode_evict_delegation - return delegation, don't reclaim opens 643d3978bb3SChuck Lever * @inode: inode to process 644d3978bb3SChuck Lever * 645d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 646b47e0e47STrond Myklebust * to be called from nfs4_clear_inode(). Guaranteed to always free 647b47e0e47STrond Myklebust * the delegation structure. 648e6f81075STrond Myklebust */ 649b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode) 650e6f81075STrond Myklebust { 651e6f81075STrond Myklebust struct nfs_delegation *delegation; 652e6f81075STrond Myklebust 653d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 654b47e0e47STrond Myklebust if (delegation != NULL) { 655f885ea64STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 656b47e0e47STrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 6575fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 658ee05f456STrond Myklebust nfs_free_delegation(delegation); 659e6f81075STrond Myklebust } 660b47e0e47STrond Myklebust } 661e6f81075STrond Myklebust 662d3978bb3SChuck Lever /** 663d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 664d3978bb3SChuck Lever * @inode: inode to process 665d3978bb3SChuck Lever * 666c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 667c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 668c57d1bc5STrond Myklebust * we should stop caching. 669c57d1bc5STrond Myklebust * 670d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 671d3978bb3SChuck Lever */ 67257ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 67390163027STrond Myklebust { 67490163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 67590163027STrond Myklebust struct nfs_delegation *delegation; 67690163027STrond Myklebust int err = 0; 67790163027STrond Myklebust 678c57d1bc5STrond Myklebust nfs_wb_all(inode); 679d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 680d25be546STrond Myklebust if (delegation != NULL) 681d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 68290163027STrond Myklebust return err; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 685c01d3645STrond Myklebust /** 686b7b7dac6STrond Myklebust * nfs_inode_return_delegation_on_close - asynchronously return a delegation 687b7b7dac6STrond Myklebust * @inode: inode to process 688b7b7dac6STrond Myklebust * 689b7b7dac6STrond Myklebust * This routine is called on file close in order to determine if the 690b7b7dac6STrond Myklebust * inode delegation needs to be returned immediately. 691b7b7dac6STrond Myklebust */ 692b7b7dac6STrond Myklebust void nfs4_inode_return_delegation_on_close(struct inode *inode) 693b7b7dac6STrond Myklebust { 694b7b7dac6STrond Myklebust struct nfs_delegation *delegation; 695b7b7dac6STrond Myklebust struct nfs_delegation *ret = NULL; 696b7b7dac6STrond Myklebust 697b7b7dac6STrond Myklebust if (!inode) 698b7b7dac6STrond Myklebust return; 699b7b7dac6STrond Myklebust rcu_read_lock(); 700b7b7dac6STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 701b7b7dac6STrond Myklebust if (!delegation) 702b7b7dac6STrond Myklebust goto out; 70310717f45STrond Myklebust if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || 70410717f45STrond Myklebust atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) { 705b7b7dac6STrond Myklebust spin_lock(&delegation->lock); 706b7b7dac6STrond Myklebust if (delegation->inode && 707b7b7dac6STrond Myklebust list_empty(&NFS_I(inode)->open_files) && 708b7b7dac6STrond Myklebust !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 709b7b7dac6STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 7108c75593cSTrond Myklebust /* Refcount matched in nfs_end_delegation_return() */ 7118c75593cSTrond Myklebust ret = nfs_get_delegation(delegation); 712b7b7dac6STrond Myklebust } 713b7b7dac6STrond Myklebust spin_unlock(&delegation->lock); 714efeda80dSTrond Myklebust if (ret) 715efeda80dSTrond Myklebust nfs_clear_verifier_delegated(inode); 716b7b7dac6STrond Myklebust } 717b7b7dac6STrond Myklebust out: 718b7b7dac6STrond Myklebust rcu_read_unlock(); 719b7b7dac6STrond Myklebust nfs_end_delegation_return(inode, ret, 0); 720b7b7dac6STrond Myklebust } 721b7b7dac6STrond Myklebust 722b7b7dac6STrond Myklebust /** 723c01d3645STrond Myklebust * nfs4_inode_make_writeable 724c01d3645STrond Myklebust * @inode: pointer to inode 725c01d3645STrond Myklebust * 726c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 727c01d3645STrond Myklebust * 728c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 729c01d3645STrond Myklebust */ 730c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 731c01d3645STrond Myklebust { 7323887ce1aSTrond Myklebust struct nfs_delegation *delegation; 7333887ce1aSTrond Myklebust 7343887ce1aSTrond Myklebust rcu_read_lock(); 7353887ce1aSTrond Myklebust delegation = nfs4_get_valid_delegation(inode); 7363887ce1aSTrond Myklebust if (delegation == NULL || 7373887ce1aSTrond Myklebust (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && 7383887ce1aSTrond Myklebust (delegation->type & FMODE_WRITE))) { 7393887ce1aSTrond Myklebust rcu_read_unlock(); 740c01d3645STrond Myklebust return 0; 741c01d3645STrond Myklebust } 7423887ce1aSTrond Myklebust rcu_read_unlock(); 7433887ce1aSTrond Myklebust return nfs4_inode_return_delegation(inode); 7443887ce1aSTrond Myklebust } 745c01d3645STrond Myklebust 746b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 747b757144fSTrond Myklebust struct nfs_delegation *delegation) 748b757144fSTrond Myklebust { 749b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 750b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 751b757144fSTrond Myklebust } 752b757144fSTrond Myklebust 753ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 754ed1e6211STrond Myklebust struct nfs_delegation *delegation) 7556411bd4aSTrond Myklebust { 7566411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 757ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 7586411bd4aSTrond Myklebust } 7596411bd4aSTrond Myklebust 7605c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 7615c31e236STrond Myklebust { 7625c31e236STrond Myklebust struct nfs_delegation *delegation; 7635c31e236STrond Myklebust bool ret = false; 7645c31e236STrond Myklebust 7655c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 7665c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 7675c31e236STrond Myklebust ret = true; 7685c31e236STrond Myklebust } 7695c31e236STrond Myklebust return ret; 7705c31e236STrond Myklebust } 7715c31e236STrond Myklebust 772b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 773b02ba0b6STrond Myklebust { 774b02ba0b6STrond Myklebust struct nfs_server *server; 775b02ba0b6STrond Myklebust 776b02ba0b6STrond Myklebust rcu_read_lock(); 777b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 778b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 779b02ba0b6STrond Myklebust rcu_read_unlock(); 780b02ba0b6STrond Myklebust } 781b02ba0b6STrond Myklebust 782b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 783b02ba0b6STrond Myklebust { 784b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 785b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 786b02ba0b6STrond Myklebust } 787b02ba0b6STrond Myklebust 788b02ba0b6STrond Myklebust /** 789b02ba0b6STrond Myklebust * nfs_expire_all_delegations 790b02ba0b6STrond Myklebust * @clp: client to process 791b02ba0b6STrond Myklebust * 792b02ba0b6STrond Myklebust */ 793b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 794b02ba0b6STrond Myklebust { 795b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 796b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 797b02ba0b6STrond Myklebust } 798b02ba0b6STrond Myklebust 799d3978bb3SChuck Lever /** 800d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 801302fad7bSTrond Myklebust * @server: pointer to nfs_server to process 802d3978bb3SChuck Lever * 8031da177e4SLinus Torvalds */ 804eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 8051da177e4SLinus Torvalds { 806d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 8075c31e236STrond Myklebust bool need_wait; 8081da177e4SLinus Torvalds 8091da177e4SLinus Torvalds if (clp == NULL) 8101da177e4SLinus Torvalds return; 811d3978bb3SChuck Lever 8128383e460STrond Myklebust rcu_read_lock(); 8135c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 8148383e460STrond Myklebust rcu_read_unlock(); 815d3978bb3SChuck Lever 8165c31e236STrond Myklebust if (need_wait) { 817d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 8185c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 8195c31e236STrond Myklebust } 820515d8611STrond Myklebust } 821515d8611STrond Myklebust 822826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 823d3978bb3SChuck Lever fmode_t flags) 824515d8611STrond Myklebust { 825515d8611STrond Myklebust struct nfs_delegation *delegation; 826515d8611STrond Myklebust 827d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 828c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 829c79571a5SAlexandros Batsakis continue; 830c79571a5SAlexandros Batsakis if (delegation->type & flags) 831826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 832707fb4b3STrond Myklebust } 833d3978bb3SChuck Lever } 834d3978bb3SChuck Lever 835826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 836d3978bb3SChuck Lever fmode_t flags) 837d3978bb3SChuck Lever { 838d3978bb3SChuck Lever struct nfs_server *server; 839d3978bb3SChuck Lever 840d3978bb3SChuck Lever rcu_read_lock(); 841d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 842826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 843515d8611STrond Myklebust rcu_read_unlock(); 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds 846ee05f456STrond Myklebust static void nfs_revoke_delegation(struct inode *inode, 84741020b67STrond Myklebust const nfs4_stateid *stateid) 848869f9dfaSTrond Myklebust { 849869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 8507f048831STrond Myklebust nfs4_stateid tmp; 85141020b67STrond Myklebust bool ret = false; 85241020b67STrond Myklebust 853869f9dfaSTrond Myklebust rcu_read_lock(); 854869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 85541020b67STrond Myklebust if (delegation == NULL) 85641020b67STrond Myklebust goto out; 8577f048831STrond Myklebust if (stateid == NULL) { 8587f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 8597f048831STrond Myklebust stateid = &tmp; 860f2d47b55STrond Myklebust } else { 861f2d47b55STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 86241020b67STrond Myklebust goto out; 863f2d47b55STrond Myklebust spin_lock(&delegation->lock); 864f2d47b55STrond Myklebust if (stateid->seqid) { 865f2d47b55STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { 866f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 867f2d47b55STrond Myklebust goto out; 868f2d47b55STrond Myklebust } 869f2d47b55STrond Myklebust delegation->stateid.seqid = stateid->seqid; 870f2d47b55STrond Myklebust } 871f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 872f2d47b55STrond Myklebust } 873d2269ea1STrond Myklebust nfs_mark_delegation_revoked(delegation); 87441020b67STrond Myklebust ret = true; 87541020b67STrond Myklebust out: 876869f9dfaSTrond Myklebust rcu_read_unlock(); 8777f048831STrond Myklebust if (ret) 8787f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 879869f9dfaSTrond Myklebust } 880869f9dfaSTrond Myklebust 88141020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 88241020b67STrond Myklebust const nfs4_stateid *stateid) 883a1d0b5eeSTrond Myklebust { 884ee05f456STrond Myklebust nfs_revoke_delegation(inode, stateid); 885a1d0b5eeSTrond Myklebust } 8869cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 887a1d0b5eeSTrond Myklebust 888d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode, 889d51f91d2STrond Myklebust const nfs4_stateid *stateid) 890d51f91d2STrond Myklebust { 891d51f91d2STrond Myklebust struct nfs_delegation *delegation; 892d51f91d2STrond Myklebust 893d51f91d2STrond Myklebust if (!inode) 894d51f91d2STrond Myklebust return; 895d51f91d2STrond Myklebust 896d51f91d2STrond Myklebust rcu_read_lock(); 897d51f91d2STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 898d51f91d2STrond Myklebust if (!delegation) 899d51f91d2STrond Myklebust goto out_rcu_unlock; 900d51f91d2STrond Myklebust 901d51f91d2STrond Myklebust spin_lock(&delegation->lock); 902d51f91d2STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 903d51f91d2STrond Myklebust goto out_spin_unlock; 904d51f91d2STrond Myklebust if (stateid->seqid) { 905d51f91d2STrond Myklebust /* If delegation->stateid is newer, dont mark as returned */ 906d51f91d2STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) 907d51f91d2STrond Myklebust goto out_clear_returning; 908d51f91d2STrond Myklebust if (delegation->stateid.seqid != stateid->seqid) 909d51f91d2STrond Myklebust delegation->stateid.seqid = stateid->seqid; 910d51f91d2STrond Myklebust } 911d51f91d2STrond Myklebust 912d2269ea1STrond Myklebust nfs_mark_delegation_revoked(delegation); 913d51f91d2STrond Myklebust 914d51f91d2STrond Myklebust out_clear_returning: 915d51f91d2STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 916d51f91d2STrond Myklebust out_spin_unlock: 917d51f91d2STrond Myklebust spin_unlock(&delegation->lock); 918d51f91d2STrond Myklebust out_rcu_unlock: 919d51f91d2STrond Myklebust rcu_read_unlock(); 920d51f91d2STrond Myklebust 921d51f91d2STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 922d51f91d2STrond Myklebust } 923d51f91d2STrond Myklebust 924d3978bb3SChuck Lever /** 925826e0013STrond Myklebust * nfs_expire_unused_delegation_types 926d3978bb3SChuck Lever * @clp: client to process 927d3978bb3SChuck Lever * @flags: delegation types to expire 928d3978bb3SChuck Lever * 929d3978bb3SChuck Lever */ 930826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 931c79571a5SAlexandros Batsakis { 932826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 933c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 934c79571a5SAlexandros Batsakis } 935c79571a5SAlexandros Batsakis 936d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 937b7391f44STrond Myklebust { 938b7391f44STrond Myklebust struct nfs_delegation *delegation; 939b7391f44STrond Myklebust 940d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 941b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 942b7391f44STrond Myklebust continue; 943b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 944b7391f44STrond Myklebust } 945b7391f44STrond Myklebust } 946b7391f44STrond Myklebust 947d3978bb3SChuck Lever /** 948d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 949d3978bb3SChuck Lever * @clp: nfs_client to process 950d3978bb3SChuck Lever * 951d3978bb3SChuck Lever */ 952b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 953b7391f44STrond Myklebust { 954d3978bb3SChuck Lever struct nfs_server *server; 955d3978bb3SChuck Lever 956d3978bb3SChuck Lever rcu_read_lock(); 957d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 958d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 959d3978bb3SChuck Lever rcu_read_unlock(); 960d3978bb3SChuck Lever 961b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 962b7391f44STrond Myklebust } 963b7391f44STrond Myklebust 964d3978bb3SChuck Lever /** 965d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 966d3978bb3SChuck Lever * @inode: inode to process 9678e663f0eSTrond Myklebust * @stateid: state ID information 968d3978bb3SChuck Lever * 969d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 9701da177e4SLinus Torvalds */ 971d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 972d3978bb3SChuck Lever const nfs4_stateid *stateid) 9731da177e4SLinus Torvalds { 974ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 975ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 9766411bd4aSTrond Myklebust struct nfs_delegation *delegation; 9771da177e4SLinus Torvalds 9786411bd4aSTrond Myklebust rcu_read_lock(); 979457a5042STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 980755a48a7STrond Myklebust if (delegation == NULL) 981755a48a7STrond Myklebust goto out_enoent; 9824816fdadSTrond Myklebust if (stateid != NULL && 9834816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 984755a48a7STrond Myklebust goto out_enoent; 985ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 9866411bd4aSTrond Myklebust rcu_read_unlock(); 987d3978bb3SChuck Lever 9886411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 9896411bd4aSTrond Myklebust return 0; 990755a48a7STrond Myklebust out_enoent: 991755a48a7STrond Myklebust rcu_read_unlock(); 992755a48a7STrond Myklebust return -ENOENT; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 995d3978bb3SChuck Lever static struct inode * 996d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 997d3978bb3SChuck Lever const struct nfs_fh *fhandle) 9981da177e4SLinus Torvalds { 9991da177e4SLinus Torvalds struct nfs_delegation *delegation; 1000e39d8a18STrond Myklebust struct inode *freeme, *res = NULL; 1001d3978bb3SChuck Lever 1002d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 100386e89489STrond Myklebust spin_lock(&delegation->lock); 100486e89489STrond Myklebust if (delegation->inode != NULL && 1005457a5042STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 100686e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 1007e39d8a18STrond Myklebust freeme = igrab(delegation->inode); 1008e39d8a18STrond Myklebust if (freeme && nfs_sb_active(freeme->i_sb)) 1009e39d8a18STrond Myklebust res = freeme; 101086e89489STrond Myklebust spin_unlock(&delegation->lock); 101186e89489STrond Myklebust if (res != NULL) 1012d3978bb3SChuck Lever return res; 1013e39d8a18STrond Myklebust if (freeme) { 1014e39d8a18STrond Myklebust rcu_read_unlock(); 1015e39d8a18STrond Myklebust iput(freeme); 1016e39d8a18STrond Myklebust rcu_read_lock(); 1017e39d8a18STrond Myklebust } 10186c342655STrond Myklebust return ERR_PTR(-EAGAIN); 10196c342655STrond Myklebust } 10206c342655STrond Myklebust spin_unlock(&delegation->lock); 10216c342655STrond Myklebust } 10226c342655STrond Myklebust return ERR_PTR(-ENOENT); 1023d3978bb3SChuck Lever } 1024d3978bb3SChuck Lever 1025d3978bb3SChuck Lever /** 1026d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 1027d3978bb3SChuck Lever * @clp: client state handle 1028d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 1029d3978bb3SChuck Lever * 1030d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 1031d3978bb3SChuck Lever * cannot be found. 1032d3978bb3SChuck Lever */ 1033d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 1034d3978bb3SChuck Lever const struct nfs_fh *fhandle) 1035d3978bb3SChuck Lever { 1036d3978bb3SChuck Lever struct nfs_server *server; 10376c342655STrond Myklebust struct inode *res; 1038d3978bb3SChuck Lever 1039d3978bb3SChuck Lever rcu_read_lock(); 1040d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1041d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 1042d5681f59SAnna Schumaker if (res != ERR_PTR(-ENOENT)) { 1043d5681f59SAnna Schumaker rcu_read_unlock(); 10446c342655STrond Myklebust return res; 1045d3978bb3SChuck Lever } 1046d5681f59SAnna Schumaker } 10478383e460STrond Myklebust rcu_read_unlock(); 10486c342655STrond Myklebust return ERR_PTR(-ENOENT); 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds 1051d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 1052d3978bb3SChuck Lever { 1053d3978bb3SChuck Lever struct nfs_delegation *delegation; 1054d3978bb3SChuck Lever 105545870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 105645870d69STrond Myklebust /* 105745870d69STrond Myklebust * If the delegation may have been admin revoked, then we 105845870d69STrond Myklebust * cannot reclaim it. 105945870d69STrond Myklebust */ 106045870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 106145870d69STrond Myklebust continue; 1062d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 1063d3978bb3SChuck Lever } 106445870d69STrond Myklebust } 1065d3978bb3SChuck Lever 1066d3978bb3SChuck Lever /** 1067d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 1068d3978bb3SChuck Lever * @clp: nfs_client to process 1069d3978bb3SChuck Lever * 10701da177e4SLinus Torvalds */ 1071adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 10721da177e4SLinus Torvalds { 1073d3978bb3SChuck Lever struct nfs_server *server; 1074d3978bb3SChuck Lever 10758383e460STrond Myklebust rcu_read_lock(); 1076d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1077d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 10788383e460STrond Myklebust rcu_read_unlock(); 10791da177e4SLinus Torvalds } 10801da177e4SLinus Torvalds 1081d3978bb3SChuck Lever /** 1082d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 1083d3978bb3SChuck Lever * @clp: nfs_client to process 1084d3978bb3SChuck Lever * 10851da177e4SLinus Torvalds */ 1086adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 10871da177e4SLinus Torvalds { 10888383e460STrond Myklebust struct nfs_delegation *delegation; 1089d3978bb3SChuck Lever struct nfs_server *server; 109086e89489STrond Myklebust struct inode *inode; 1091d3978bb3SChuck Lever 10928383e460STrond Myklebust restart: 10938383e460STrond Myklebust rcu_read_lock(); 1094d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1095d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 1096d3978bb3SChuck Lever super_list) { 10976f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 10986f9449beSTrond Myklebust &delegation->flags) || 10996f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 11006f9449beSTrond Myklebust &delegation->flags) || 11016f9449beSTrond Myklebust test_bit(NFS_DELEGATION_NEED_RECLAIM, 1102d3978bb3SChuck Lever &delegation->flags) == 0) 11031da177e4SLinus Torvalds continue; 11049f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 1105f3893491SNeilBrown break; /* continue in outer loop */ 11069f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 11079f0f8e12STrond Myklebust if (inode == NULL) { 11089f0f8e12STrond Myklebust rcu_read_unlock(); 11099f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 11109f0f8e12STrond Myklebust goto restart; 11119f0f8e12STrond Myklebust } 1112b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 1113b04b22f4STrond Myklebust rcu_read_unlock(); 1114b04b22f4STrond Myklebust if (delegation != NULL) { 11158c75593cSTrond Myklebust if (nfs_detach_delegation(NFS_I(inode), delegation, 11168c75593cSTrond Myklebust server) != NULL) 1117905f8d16STrond Myklebust nfs_free_delegation(delegation); 11188c75593cSTrond Myklebust /* Match nfs_start_delegation_return_locked */ 11198c75593cSTrond Myklebust nfs_put_delegation(delegation); 1120b04b22f4STrond Myklebust } 112186e89489STrond Myklebust iput(inode); 11229f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 11233ca951b6SNeilBrown cond_resched(); 11248383e460STrond Myklebust goto restart; 11251da177e4SLinus Torvalds } 1126d3978bb3SChuck Lever } 11278383e460STrond Myklebust rcu_read_unlock(); 11281da177e4SLinus Torvalds } 11293e4f6290STrond Myklebust 1130bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 1131bb3d1a3bSTrond Myklebust { 1132bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 1133bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 1134bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 1135bb3d1a3bSTrond Myklebust } 1136bb3d1a3bSTrond Myklebust 113745870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 113845870d69STrond Myklebust struct nfs_delegation *delegation) 113945870d69STrond Myklebust { 1140059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 1141059b43e9STrond Myklebust return; 114245870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 114345870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 114445870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 114545870d69STrond Myklebust } 114645870d69STrond Myklebust 1147bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 1148bb3d1a3bSTrond Myklebust struct inode *inode) 1149bb3d1a3bSTrond Myklebust { 1150bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 1151bb3d1a3bSTrond Myklebust 1152bb3d1a3bSTrond Myklebust rcu_read_lock(); 1153bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1154bb3d1a3bSTrond Myklebust if (delegation) 1155bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 1156bb3d1a3bSTrond Myklebust rcu_read_unlock(); 1157bb3d1a3bSTrond Myklebust 1158bb3d1a3bSTrond Myklebust } 1159bb3d1a3bSTrond Myklebust 116045870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 116145870d69STrond Myklebust { 116245870d69STrond Myklebust struct nfs_delegation *delegation; 116345870d69STrond Myklebust 116445870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 116545870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 116645870d69STrond Myklebust } 116745870d69STrond Myklebust 116845870d69STrond Myklebust /** 116945870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 117045870d69STrond Myklebust * @clp: nfs_client to process 117145870d69STrond Myklebust * 117245870d69STrond Myklebust * Iterates through all the delegations associated with this server and 117345870d69STrond Myklebust * marks them as needing to be checked for validity. 117445870d69STrond Myklebust */ 117545870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 117645870d69STrond Myklebust { 117745870d69STrond Myklebust struct nfs_server *server; 117845870d69STrond Myklebust 117945870d69STrond Myklebust rcu_read_lock(); 118045870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 118145870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 118245870d69STrond Myklebust rcu_read_unlock(); 118345870d69STrond Myklebust } 118445870d69STrond Myklebust 118545870d69STrond Myklebust /** 11868ca017c8SScott Mayhew * nfs_test_expired_all_delegations - test all delegations for a client 11878ca017c8SScott Mayhew * @clp: nfs_client to process 11888ca017c8SScott Mayhew * 11898ca017c8SScott Mayhew * Helper for handling "recallable state revoked" status from server. 11908ca017c8SScott Mayhew */ 11918ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp) 11928ca017c8SScott Mayhew { 11938ca017c8SScott Mayhew nfs_mark_test_expired_all_delegations(clp); 11948ca017c8SScott Mayhew nfs4_schedule_state_manager(clp); 11958ca017c8SScott Mayhew } 11968ca017c8SScott Mayhew 1197ad114089STrond Myklebust static void 1198ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode, 1199ad114089STrond Myklebust nfs4_stateid *stateid, 1200ad114089STrond Myklebust const struct cred *cred) 1201ad114089STrond Myklebust { 1202ad114089STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1203ad114089STrond Myklebust const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 1204ad114089STrond Myklebust int status; 1205ad114089STrond Myklebust 1206ad114089STrond Myklebust if (!cred) 1207ad114089STrond Myklebust return; 1208ad114089STrond Myklebust status = ops->test_and_free_expired(server, stateid, cred); 1209ad114089STrond Myklebust if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 1210ad114089STrond Myklebust nfs_remove_bad_delegation(inode, stateid); 1211ad114089STrond Myklebust } 1212ad114089STrond Myklebust 12138ca017c8SScott Mayhew /** 121445870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 121545870d69STrond Myklebust * @clp: nfs_client to process 121645870d69STrond Myklebust * 121745870d69STrond Myklebust * Iterates through all the delegations associated with this server and 121845870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 121945870d69STrond Myklebust * expected to be called in cases where the server may have lost its 122045870d69STrond Myklebust * lease. 122145870d69STrond Myklebust */ 122245870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 122345870d69STrond Myklebust { 122445870d69STrond Myklebust struct nfs_delegation *delegation; 122545870d69STrond Myklebust struct nfs_server *server; 122645870d69STrond Myklebust struct inode *inode; 1227a52458b4SNeilBrown const struct cred *cred; 122845870d69STrond Myklebust nfs4_stateid stateid; 122945870d69STrond Myklebust 123045870d69STrond Myklebust restart: 123145870d69STrond Myklebust rcu_read_lock(); 123245870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 123345870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, 123445870d69STrond Myklebust super_list) { 12356f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 12366f9449beSTrond Myklebust &delegation->flags) || 12376f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 12386f9449beSTrond Myklebust &delegation->flags) || 12396f9449beSTrond Myklebust test_bit(NFS_DELEGATION_TEST_EXPIRED, 124045870d69STrond Myklebust &delegation->flags) == 0) 124145870d69STrond Myklebust continue; 124245870d69STrond Myklebust if (!nfs_sb_active(server->super)) 1243f3893491SNeilBrown break; /* continue in outer loop */ 124445870d69STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 124545870d69STrond Myklebust if (inode == NULL) { 124645870d69STrond Myklebust rcu_read_unlock(); 124745870d69STrond Myklebust nfs_sb_deactive(server->super); 124845870d69STrond Myklebust goto restart; 124945870d69STrond Myklebust } 1250a52458b4SNeilBrown cred = get_cred_rcu(delegation->cred); 125145870d69STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 125245870d69STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 125345870d69STrond Myklebust rcu_read_unlock(); 1254ad114089STrond Myklebust nfs_delegation_test_free_expired(inode, &stateid, cred); 1255a52458b4SNeilBrown put_cred(cred); 1256bb3d1a3bSTrond Myklebust if (nfs4_server_rebooted(clp)) { 1257bb3d1a3bSTrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 1258bb3d1a3bSTrond Myklebust iput(inode); 1259bb3d1a3bSTrond Myklebust nfs_sb_deactive(server->super); 1260bb3d1a3bSTrond Myklebust return; 1261bb3d1a3bSTrond Myklebust } 126245870d69STrond Myklebust iput(inode); 126345870d69STrond Myklebust nfs_sb_deactive(server->super); 12643ca951b6SNeilBrown cond_resched(); 126545870d69STrond Myklebust goto restart; 126645870d69STrond Myklebust } 126745870d69STrond Myklebust } 126845870d69STrond Myklebust rcu_read_unlock(); 126945870d69STrond Myklebust } 127045870d69STrond Myklebust 12716c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 12726c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 12736c2d8f8dSTrond Myklebust { 12746c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 12756c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 12766c2d8f8dSTrond Myklebust bool found = false; 12776c2d8f8dSTrond Myklebust 12786c2d8f8dSTrond Myklebust rcu_read_lock(); 12796c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 12806c2d8f8dSTrond Myklebust if (delegation && 128142c304c3STrond Myklebust nfs4_stateid_match_or_older(&delegation->stateid, stateid) && 128242c304c3STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 12836c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 12846c2d8f8dSTrond Myklebust found = true; 12856c2d8f8dSTrond Myklebust } 12866c2d8f8dSTrond Myklebust rcu_read_unlock(); 12876c2d8f8dSTrond Myklebust if (found) 12886c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 12896c2d8f8dSTrond Myklebust } 12906c2d8f8dSTrond Myklebust 1291d3978bb3SChuck Lever /** 1292d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1293d3978bb3SChuck Lever * @clp: client state handle 1294d3978bb3SChuck Lever * 1295d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1296d3978bb3SChuck Lever * to this nfs_client. 1297d3978bb3SChuck Lever */ 1298d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1299d3978bb3SChuck Lever { 1300d3978bb3SChuck Lever struct nfs_server *server; 1301d3978bb3SChuck Lever int ret = 0; 1302d3978bb3SChuck Lever 1303d3978bb3SChuck Lever rcu_read_lock(); 1304d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1305d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1306d3978bb3SChuck Lever ret = 1; 1307d3978bb3SChuck Lever break; 1308d3978bb3SChuck Lever } 1309d3978bb3SChuck Lever rcu_read_unlock(); 1310d3978bb3SChuck Lever return ret; 1311d3978bb3SChuck Lever } 1312d3978bb3SChuck Lever 1313d3978bb3SChuck Lever /** 131412f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 131512f275cdSTrond Myklebust * @dst: stateid to refresh 131612f275cdSTrond Myklebust * @inode: inode to check 131712f275cdSTrond Myklebust * 131812f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 131912f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 132012f275cdSTrond Myklebust */ 132112f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 132212f275cdSTrond Myklebust { 132312f275cdSTrond Myklebust struct nfs_delegation *delegation; 132412f275cdSTrond Myklebust bool ret = false; 132512f275cdSTrond Myklebust if (!inode) 132612f275cdSTrond Myklebust goto out; 132712f275cdSTrond Myklebust 132812f275cdSTrond Myklebust rcu_read_lock(); 132912f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 133012f275cdSTrond Myklebust if (delegation != NULL && 1331b5756208STrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid) && 1332246afc0aSTrond Myklebust nfs4_stateid_is_newer(&delegation->stateid, dst) && 1333b5756208STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 133412f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 133579cc5542STrond Myklebust ret = true; 133612f275cdSTrond Myklebust } 133712f275cdSTrond Myklebust rcu_read_unlock(); 133812f275cdSTrond Myklebust out: 133912f275cdSTrond Myklebust return ret; 134012f275cdSTrond Myklebust } 134112f275cdSTrond Myklebust 134212f275cdSTrond Myklebust /** 1343d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1344d3978bb3SChuck Lever * @inode: inode to check 13450032a7a7STrond Myklebust * @flags: delegation type requirement 1346abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1347abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1348d3978bb3SChuck Lever * 13490032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 13500032a7a7STrond Myklebust * otherwise "false" is returned. 1351d3978bb3SChuck Lever */ 1352abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1353a52458b4SNeilBrown nfs4_stateid *dst, const struct cred **cred) 13543e4f6290STrond Myklebust { 13553e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 13563e4f6290STrond Myklebust struct nfs_delegation *delegation; 13570032a7a7STrond Myklebust bool ret; 13583e4f6290STrond Myklebust 13590032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 13608383e460STrond Myklebust rcu_read_lock(); 13618383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1362aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 13630032a7a7STrond Myklebust if (ret) { 1364f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 13650032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1366abf4e13cSTrond Myklebust if (cred) 1367a52458b4SNeilBrown *cred = get_cred(delegation->cred); 13683e4f6290STrond Myklebust } 13698383e460STrond Myklebust rcu_read_unlock(); 13708383e460STrond Myklebust return ret; 13713e4f6290STrond Myklebust } 13725445b1fbSTrond Myklebust 13735445b1fbSTrond Myklebust /** 13745445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 13755445b1fbSTrond Myklebust * @inode: inode to check 13765445b1fbSTrond Myklebust * 13775445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 13785445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 13795445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 13805445b1fbSTrond Myklebust */ 13815445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 13825445b1fbSTrond Myklebust { 13835445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 13845445b1fbSTrond Myklebust struct nfs_delegation *delegation; 13855445b1fbSTrond Myklebust bool ret = true; 13865445b1fbSTrond Myklebust 13875445b1fbSTrond Myklebust rcu_read_lock(); 13885445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 13895445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 13905445b1fbSTrond Myklebust goto out; 1391a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 13925445b1fbSTrond Myklebust ret = false; 13935445b1fbSTrond Myklebust out: 13945445b1fbSTrond Myklebust rcu_read_unlock(); 13955445b1fbSTrond Myklebust return ret; 13965445b1fbSTrond Myklebust } 139710717f45STrond Myklebust 139810717f45STrond Myklebust module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644); 1399