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 78be200377STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 79be200377STrond Myklebust struct nfs_delegation *delegation) 80be200377STrond Myklebust { 81be200377STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 82be200377STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 83be200377STrond Myklebust } 84be200377STrond Myklebust 85aa05c87fSTrond Myklebust static bool 86aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation, 87aa05c87fSTrond Myklebust fmode_t flags) 88aa05c87fSTrond Myklebust { 89aa05c87fSTrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags && 90aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 91aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 92aa05c87fSTrond Myklebust return true; 93aa05c87fSTrond Myklebust return false; 94aa05c87fSTrond Myklebust } 95aa05c87fSTrond Myklebust 96be3df3ddSTrond Myklebust struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) 97be3df3ddSTrond Myklebust { 98be3df3ddSTrond Myklebust struct nfs_delegation *delegation; 99be3df3ddSTrond Myklebust 100be3df3ddSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 101be3df3ddSTrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) 102be3df3ddSTrond Myklebust return delegation; 103be3df3ddSTrond Myklebust return NULL; 104be3df3ddSTrond Myklebust } 105be3df3ddSTrond Myklebust 10615bb3afeSPeng Tao static int 10715bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark) 108b7391f44STrond Myklebust { 109b7391f44STrond Myklebust struct nfs_delegation *delegation; 110b7391f44STrond Myklebust int ret = 0; 111b7391f44STrond Myklebust 112b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 113b7391f44STrond Myklebust rcu_read_lock(); 114b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 115aa05c87fSTrond Myklebust if (nfs4_is_valid_delegation(delegation, flags)) { 11615bb3afeSPeng Tao if (mark) 117b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 118b7391f44STrond Myklebust ret = 1; 119b7391f44STrond Myklebust } 120b7391f44STrond Myklebust rcu_read_unlock(); 121b7391f44STrond Myklebust return ret; 122b7391f44STrond Myklebust } 12315bb3afeSPeng Tao /** 1246453bcd0STrond Myklebust * nfs4_have_delegation - check if inode has a delegation, mark it 12515bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if there is one. 12615bb3afeSPeng Tao * @inode: inode to check 12715bb3afeSPeng Tao * @flags: delegation types to check for 12815bb3afeSPeng Tao * 12915bb3afeSPeng Tao * Returns one if inode has the indicated delegation, otherwise zero. 13015bb3afeSPeng Tao */ 13115bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags) 13215bb3afeSPeng Tao { 13315bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, true); 13415bb3afeSPeng Tao } 13515bb3afeSPeng Tao 13615bb3afeSPeng Tao /* 13715bb3afeSPeng Tao * nfs4_check_delegation - check if inode has a delegation, do not mark 13815bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if it has one. 13915bb3afeSPeng Tao */ 14015bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags) 14115bb3afeSPeng Tao { 14215bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, false); 14315bb3afeSPeng Tao } 144b7391f44STrond Myklebust 14544f411c3SOlga Kornievskaia static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) 146888e694cSTrond Myklebust { 147888e694cSTrond Myklebust struct inode *inode = state->inode; 148888e694cSTrond Myklebust struct file_lock *fl; 149bd61e0a9SJeff Layton struct file_lock_context *flctx = inode->i_flctx; 150bd61e0a9SJeff Layton struct list_head *list; 151d5122201STrond Myklebust int status = 0; 152888e694cSTrond Myklebust 153bd61e0a9SJeff Layton if (flctx == NULL) 15465b62a29STrond Myklebust goto out; 155314d7cc0SJeff Layton 156bd61e0a9SJeff Layton list = &flctx->flc_posix; 1576109c850SJeff Layton spin_lock(&flctx->flc_lock); 158bd61e0a9SJeff Layton restart: 159bd61e0a9SJeff Layton list_for_each_entry(fl, list, fl_list) { 16044f411c3SOlga Kornievskaia if (nfs_file_open_context(fl->fl_file)->state != state) 161888e694cSTrond Myklebust continue; 1626109c850SJeff Layton spin_unlock(&flctx->flc_lock); 163db4f2e63STrond Myklebust status = nfs4_lock_delegation_recall(fl, state, stateid); 164d5122201STrond Myklebust if (status < 0) 1653f09df70STrond Myklebust goto out; 1666109c850SJeff Layton spin_lock(&flctx->flc_lock); 167888e694cSTrond Myklebust } 168bd61e0a9SJeff Layton if (list == &flctx->flc_posix) { 169bd61e0a9SJeff Layton list = &flctx->flc_flock; 170bd61e0a9SJeff Layton goto restart; 1715263e31eSJeff Layton } 1726109c850SJeff Layton spin_unlock(&flctx->flc_lock); 1733f09df70STrond Myklebust out: 174888e694cSTrond Myklebust return status; 175888e694cSTrond Myklebust } 176888e694cSTrond Myklebust 17724311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, 17824311f88STrond Myklebust const nfs4_stateid *stateid, fmode_t type) 1791da177e4SLinus Torvalds { 1801da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 1811da177e4SLinus Torvalds struct nfs_open_context *ctx; 182d25be546STrond Myklebust struct nfs4_state_owner *sp; 1831da177e4SLinus Torvalds struct nfs4_state *state; 184d25be546STrond Myklebust unsigned int seq; 185888e694cSTrond Myklebust int err; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds again: 1880de43976STrond Myklebust rcu_read_lock(); 1890de43976STrond Myklebust list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 1901da177e4SLinus Torvalds state = ctx->state; 1911da177e4SLinus Torvalds if (state == NULL) 1921da177e4SLinus Torvalds continue; 1931da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1941da177e4SLinus Torvalds continue; 195f8ebf7a8STrond Myklebust if (!nfs4_valid_open_stateid(state)) 196f8ebf7a8STrond Myklebust continue; 197f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 19890163027STrond Myklebust continue; 1990de43976STrond Myklebust if (!get_nfs_open_context(ctx)) 2000de43976STrond Myklebust continue; 2010de43976STrond Myklebust rcu_read_unlock(); 202d25be546STrond Myklebust sp = state->owner; 20365b62a29STrond Myklebust /* Block nfs4_proc_unlck */ 20465b62a29STrond Myklebust mutex_lock(&sp->so_delegreturn_mutex); 205d25be546STrond Myklebust seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 2065eb8d18cSTrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 207d25be546STrond Myklebust if (!err) 20844f411c3SOlga Kornievskaia err = nfs_delegation_claim_locks(state, stateid); 209d25be546STrond Myklebust if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 210d25be546STrond Myklebust err = -EAGAIN; 21165b62a29STrond Myklebust mutex_unlock(&sp->so_delegreturn_mutex); 2121da177e4SLinus Torvalds put_nfs_open_context(ctx); 213888e694cSTrond Myklebust if (err != 0) 214d18cc1fdSTrond Myklebust return err; 2151da177e4SLinus Torvalds goto again; 2161da177e4SLinus Torvalds } 2170de43976STrond Myklebust rcu_read_unlock(); 218d18cc1fdSTrond Myklebust return 0; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 221d3978bb3SChuck Lever /** 222d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 223d3978bb3SChuck Lever * @inode: inode to process 224d3978bb3SChuck Lever * @cred: credential to use for request 22535156bffSTrond Myklebust * @type: delegation type 22635156bffSTrond Myklebust * @stateid: delegation stateid 22735156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 228d3978bb3SChuck Lever * 2291da177e4SLinus Torvalds */ 230a52458b4SNeilBrown void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, 231*1ba04394STrond Myklebust fmode_t type, const nfs4_stateid *stateid, 23235156bffSTrond Myklebust unsigned long pagemod_limit) 2331da177e4SLinus Torvalds { 2348f649c37STrond Myklebust struct nfs_delegation *delegation; 235a52458b4SNeilBrown const struct cred *oldcred = NULL; 2361da177e4SLinus Torvalds 2378f649c37STrond Myklebust rcu_read_lock(); 2388f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 2398f649c37STrond Myklebust if (delegation != NULL) { 2408f649c37STrond Myklebust spin_lock(&delegation->lock); 24135156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 24235156bffSTrond Myklebust delegation->type = type; 24335156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 24405c88babSTrond Myklebust oldcred = delegation->cred; 245a52458b4SNeilBrown delegation->cred = get_cred(cred); 246*1ba04394STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 247*1ba04394STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REVOKED, 248*1ba04394STrond Myklebust &delegation->flags)) 249*1ba04394STrond Myklebust atomic_long_inc(&nfs_active_delegations); 2508f649c37STrond Myklebust spin_unlock(&delegation->lock); 2518f649c37STrond Myklebust rcu_read_unlock(); 252a52458b4SNeilBrown put_cred(oldcred); 25335156bffSTrond Myklebust trace_nfs4_reclaim_delegation(inode, type); 254*1ba04394STrond Myklebust } else { 2558f649c37STrond Myklebust rcu_read_unlock(); 256*1ba04394STrond Myklebust nfs_inode_set_delegation(inode, cred, type, stateid, 257*1ba04394STrond Myklebust pagemod_limit); 258*1ba04394STrond Myklebust } 2598f649c37STrond Myklebust } 2601da177e4SLinus Torvalds 26157bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 26257bfa891STrond Myklebust { 2635d63944fSTrond Myklebust const struct cred *cred; 26457bfa891STrond Myklebust int res = 0; 26557bfa891STrond Myklebust 2665d63944fSTrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 2675d63944fSTrond Myklebust spin_lock(&delegation->lock); 2685d63944fSTrond Myklebust cred = get_cred(delegation->cred); 2695d63944fSTrond Myklebust spin_unlock(&delegation->lock); 2705d63944fSTrond Myklebust res = nfs4_proc_delegreturn(inode, cred, 271869f9dfaSTrond Myklebust &delegation->stateid, 272869f9dfaSTrond Myklebust issync); 2735d63944fSTrond Myklebust put_cred(cred); 2745d63944fSTrond Myklebust } 27557bfa891STrond Myklebust return res; 27657bfa891STrond Myklebust } 27757bfa891STrond Myklebust 27886e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 27986e89489STrond Myklebust { 28086e89489STrond Myklebust struct inode *inode = NULL; 28186e89489STrond Myklebust 28286e89489STrond Myklebust spin_lock(&delegation->lock); 28386e89489STrond Myklebust if (delegation->inode != NULL) 28486e89489STrond Myklebust inode = igrab(delegation->inode); 2856f9449beSTrond Myklebust if (!inode) 2866f9449beSTrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 28786e89489STrond Myklebust spin_unlock(&delegation->lock); 28886e89489STrond Myklebust return inode; 28986e89489STrond Myklebust } 29086e89489STrond Myklebust 291dda4b225SChuck Lever static struct nfs_delegation * 292d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 29357bfa891STrond Myklebust { 294d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 295d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 29657bfa891STrond Myklebust 29757bfa891STrond Myklebust if (delegation == NULL) 298d25be546STrond Myklebust goto out; 299d25be546STrond Myklebust spin_lock(&delegation->lock); 3008c75593cSTrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 301be200377STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); 3028c75593cSTrond Myklebust /* Refcount matched in nfs_end_delegation_return() */ 3038c75593cSTrond Myklebust ret = nfs_get_delegation(delegation); 3048c75593cSTrond Myklebust } 305d25be546STrond Myklebust spin_unlock(&delegation->lock); 306efeda80dSTrond Myklebust if (ret) 307efeda80dSTrond Myklebust nfs_clear_verifier_delegated(&nfsi->vfs_inode); 308d25be546STrond Myklebust out: 309d25be546STrond Myklebust return ret; 310d25be546STrond Myklebust } 311d25be546STrond Myklebust 312d25be546STrond Myklebust static struct nfs_delegation * 313d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 314d25be546STrond Myklebust { 315d25be546STrond Myklebust struct nfs_delegation *delegation; 316d25be546STrond Myklebust 317d25be546STrond Myklebust rcu_read_lock(); 318d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 319d25be546STrond Myklebust rcu_read_unlock(); 320d25be546STrond Myklebust return delegation; 321d25be546STrond Myklebust } 322d25be546STrond Myklebust 323be200377STrond Myklebust static void nfs_abort_delegation_return(struct nfs_delegation *delegation, 324be200377STrond Myklebust struct nfs_client *clp, int err) 325d25be546STrond Myklebust { 326dda4b225SChuck Lever 32734310430STrond Myklebust spin_lock(&delegation->lock); 328d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 329be200377STrond Myklebust if (err == -EAGAIN) { 330be200377STrond Myklebust set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); 331be200377STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state); 332be200377STrond Myklebust } 333d25be546STrond Myklebust spin_unlock(&delegation->lock); 334d25be546STrond Myklebust } 335d25be546STrond Myklebust 336d25be546STrond Myklebust static struct nfs_delegation * 337d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 338d25be546STrond Myklebust struct nfs_delegation *delegation, 339d25be546STrond Myklebust struct nfs_client *clp) 340d25be546STrond Myklebust { 341d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 342d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 343d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 344d25be546STrond Myklebust 345d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 346d25be546STrond Myklebust return NULL; 347d25be546STrond Myklebust 348d25be546STrond Myklebust spin_lock(&delegation->lock); 349f9e0cc9cSTrond Myklebust if (!delegation->inode) { 350f9e0cc9cSTrond Myklebust spin_unlock(&delegation->lock); 351f9e0cc9cSTrond Myklebust return NULL; 352f9e0cc9cSTrond Myklebust } 35357bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 35486e89489STrond Myklebust delegation->inode = NULL; 35557bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 35634310430STrond Myklebust spin_unlock(&delegation->lock); 35757bfa891STrond Myklebust return delegation; 35857bfa891STrond Myklebust } 35957bfa891STrond Myklebust 360dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 361d25be546STrond Myklebust struct nfs_delegation *delegation, 362d3978bb3SChuck Lever struct nfs_server *server) 363dda4b225SChuck Lever { 364d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 365dda4b225SChuck Lever 366dda4b225SChuck Lever spin_lock(&clp->cl_lock); 367d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 368dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 369dda4b225SChuck Lever return delegation; 370dda4b225SChuck Lever } 371dda4b225SChuck Lever 372d25be546STrond Myklebust static struct nfs_delegation * 373d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 374d25be546STrond Myklebust { 375d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 376d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 377d25be546STrond Myklebust struct nfs_delegation *delegation; 378d25be546STrond Myklebust 379ee05f456STrond Myklebust rcu_read_lock(); 380ee05f456STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 381ee05f456STrond Myklebust if (delegation != NULL) 382ee05f456STrond Myklebust delegation = nfs_detach_delegation(nfsi, delegation, server); 383ee05f456STrond Myklebust rcu_read_unlock(); 384ee05f456STrond Myklebust return delegation; 385d25be546STrond Myklebust } 386d25be546STrond Myklebust 387cf6726e2STrond Myklebust static void 38857f188e0STrond Myklebust nfs_update_delegation_cred(struct nfs_delegation *delegation, 38957f188e0STrond Myklebust const struct cred *cred) 39057f188e0STrond Myklebust { 39157f188e0STrond Myklebust const struct cred *old; 39257f188e0STrond Myklebust 39357f188e0STrond Myklebust if (cred_fscmp(delegation->cred, cred) != 0) { 39457f188e0STrond Myklebust old = xchg(&delegation->cred, get_cred(cred)); 39557f188e0STrond Myklebust put_cred(old); 39657f188e0STrond Myklebust } 39757f188e0STrond Myklebust } 39857f188e0STrond Myklebust 39957f188e0STrond Myklebust static void 400cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation, 401cf6726e2STrond Myklebust const struct nfs_delegation *update) 402cf6726e2STrond Myklebust { 403cf6726e2STrond Myklebust if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 404cf6726e2STrond Myklebust delegation->stateid.seqid = update->stateid.seqid; 405cf6726e2STrond Myklebust smp_wmb(); 406cf6726e2STrond Myklebust delegation->type = update->type; 40757f188e0STrond Myklebust delegation->pagemod_limit = update->pagemod_limit; 40857f188e0STrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 40957f188e0STrond Myklebust delegation->change_attr = update->change_attr; 41057f188e0STrond Myklebust nfs_update_delegation_cred(delegation, update->cred); 41157f188e0STrond Myklebust /* smp_mb__before_atomic() is implicit due to xchg() */ 41257f188e0STrond Myklebust clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 413d2269ea1STrond Myklebust atomic_long_inc(&nfs_active_delegations); 414cf6726e2STrond Myklebust } 415cf6726e2STrond Myklebust } 41657f188e0STrond Myklebust } 417cf6726e2STrond Myklebust 418d3978bb3SChuck Lever /** 419d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 420d3978bb3SChuck Lever * @inode: inode to which delegation applies 421d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 42235156bffSTrond Myklebust * @type: delegation type 42335156bffSTrond Myklebust * @stateid: delegation stateid 42435156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 425d3978bb3SChuck Lever * 426d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 4271da177e4SLinus Torvalds */ 428a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, 42935156bffSTrond Myklebust fmode_t type, 43035156bffSTrond Myklebust const nfs4_stateid *stateid, 43135156bffSTrond Myklebust unsigned long pagemod_limit) 4321da177e4SLinus Torvalds { 433d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 434d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 4351da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 43617d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 43757bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 4381da177e4SLinus Torvalds int status = 0; 4391da177e4SLinus Torvalds 4409c00fd9aSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_KERNEL_ACCOUNT); 4411da177e4SLinus Torvalds if (delegation == NULL) 4421da177e4SLinus Torvalds return -ENOMEM; 44335156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 4448c75593cSTrond Myklebust refcount_set(&delegation->refcount, 1); 44535156bffSTrond Myklebust delegation->type = type; 44635156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 4471eb5d98fSJeff Layton delegation->change_attr = inode_peek_iversion_raw(inode); 448a52458b4SNeilBrown delegation->cred = get_cred(cred); 4491da177e4SLinus Torvalds delegation->inode = inode; 450b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 45134310430STrond Myklebust spin_lock_init(&delegation->lock); 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 45417d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 45517d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 456ee05f456STrond Myklebust if (old_delegation == NULL) 457ee05f456STrond Myklebust goto add_new; 458cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 459cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 460cf6726e2STrond Myklebust &delegation->stateid)) { 461e0f07896STrond Myklebust spin_lock(&old_delegation->lock); 462cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 463cf6726e2STrond Myklebust delegation); 464e0f07896STrond Myklebust spin_unlock(&old_delegation->lock); 46557bfa891STrond Myklebust goto out; 46657bfa891STrond Myklebust } 467ee05f456STrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) { 46857bfa891STrond Myklebust /* 46957bfa891STrond Myklebust * Deal with broken servers that hand out two 47057bfa891STrond Myklebust * delegations for the same file. 47117280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 47217280175STrond Myklebust * nothing else. 47357bfa891STrond Myklebust */ 47457bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 47557bfa891STrond Myklebust "a duplicate delegation!\n", 4763110ff80SHarvey Harrison __func__, clp->cl_hostname); 47717280175STrond Myklebust if (delegation->type == old_delegation->type || 47817280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 47957bfa891STrond Myklebust freeme = delegation; 48057bfa891STrond Myklebust delegation = NULL; 48157bfa891STrond Myklebust goto out; 48257bfa891STrond Myklebust } 483ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 484ade04647STrond Myklebust &old_delegation->flags)) 485ade04647STrond Myklebust goto out; 486ee05f456STrond Myklebust } 487ee05f456STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); 488d25be546STrond Myklebust if (freeme == NULL) 489d25be546STrond Myklebust goto out; 490ee05f456STrond Myklebust add_new: 491cc7f2daeSTrond Myklebust /* 492cc7f2daeSTrond Myklebust * If we didn't revalidate the change attribute before setting 493cc7f2daeSTrond Myklebust * the delegation, then pre-emptively ask for a full attribute 494cc7f2daeSTrond Myklebust * cache revalidation. 495cc7f2daeSTrond Myklebust */ 496cc7f2daeSTrond Myklebust spin_lock(&inode->i_lock); 497cc7f2daeSTrond Myklebust if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_CHANGE) 498cc7f2daeSTrond Myklebust nfs_set_cache_invalid(inode, 499cc7f2daeSTrond Myklebust NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME | 500cc7f2daeSTrond Myklebust NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE | 501cc7f2daeSTrond Myklebust NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK | 502cc7f2daeSTrond Myklebust NFS_INO_INVALID_OTHER | NFS_INO_INVALID_DATA | 503cc7f2daeSTrond Myklebust NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL | 504cc7f2daeSTrond Myklebust NFS_INO_INVALID_XATTR); 505cc7f2daeSTrond Myklebust spin_unlock(&inode->i_lock); 506cc7f2daeSTrond Myklebust 50738942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 5088383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 5091da177e4SLinus Torvalds delegation = NULL; 510412c77ceSTrond Myklebust 511d2269ea1STrond Myklebust atomic_long_inc(&nfs_active_delegations); 512d2269ea1STrond Myklebust 51335156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 51457bfa891STrond Myklebust out: 5151da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 516603c83daSTrond Myklebust if (delegation != NULL) 517d2269ea1STrond Myklebust __nfs_free_delegation(delegation); 518ee05f456STrond Myklebust if (freeme != NULL) { 51957bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 520ee05f456STrond Myklebust nfs_free_delegation(freeme); 521ee05f456STrond Myklebust } 5221da177e4SLinus Torvalds return status; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds /* 5261da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 5271da177e4SLinus Torvalds */ 528d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 5291da177e4SLinus Torvalds { 530d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 5316b4befc0STrond Myklebust unsigned int mode = O_WRONLY | O_RDWR; 532869f9dfaSTrond Myklebust int err = 0; 5331da177e4SLinus Torvalds 534d25be546STrond Myklebust if (delegation == NULL) 535d25be546STrond Myklebust return 0; 5366b4befc0STrond Myklebust 5376b4befc0STrond Myklebust if (!issync) 5386b4befc0STrond Myklebust mode |= O_NONBLOCK; 5396b4befc0STrond Myklebust /* Recall of any remaining application leases */ 5406b4befc0STrond Myklebust err = break_lease(inode, mode); 5416b4befc0STrond Myklebust 5426b4befc0STrond Myklebust while (err == 0) { 543869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 544869f9dfaSTrond Myklebust break; 54524311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 54624311f88STrond Myklebust delegation->type); 547d25be546STrond Myklebust if (!issync || err != -EAGAIN) 548d25be546STrond Myklebust break; 549d25be546STrond Myklebust /* 550d25be546STrond Myklebust * Guard against state recovery 551d25be546STrond Myklebust */ 552d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 5536b4befc0STrond Myklebust } 554d25be546STrond Myklebust 555d25be546STrond Myklebust if (err) { 556be200377STrond Myklebust nfs_abort_delegation_return(delegation, clp, err); 557d25be546STrond Myklebust goto out; 558d25be546STrond Myklebust } 5591da177e4SLinus Torvalds 560d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 561d18cc1fdSTrond Myklebust out: 5628c75593cSTrond Myklebust /* Refcount matched in nfs_start_delegation_return_locked() */ 5638c75593cSTrond Myklebust nfs_put_delegation(delegation); 564d18cc1fdSTrond Myklebust return err; 56590163027STrond Myklebust } 56690163027STrond Myklebust 567b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 568b757144fSTrond Myklebust { 569b757144fSTrond Myklebust bool ret = false; 570b757144fSTrond Myklebust 571b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 572b757144fSTrond Myklebust ret = true; 5730d104167STrond Myklebust else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { 574b757144fSTrond Myklebust struct inode *inode; 575b757144fSTrond Myklebust 576b757144fSTrond Myklebust spin_lock(&delegation->lock); 577b757144fSTrond Myklebust inode = delegation->inode; 578b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 579b757144fSTrond Myklebust ret = true; 580b757144fSTrond Myklebust spin_unlock(&delegation->lock); 581b757144fSTrond Myklebust } 5820d104167STrond Myklebust if (ret) 5830d104167STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 584af20b7b8STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || 585be200377STrond Myklebust test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || 586af20b7b8STrond Myklebust test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 587af20b7b8STrond Myklebust ret = false; 588af20b7b8STrond Myklebust 589b757144fSTrond Myklebust return ret; 590b757144fSTrond Myklebust } 591b757144fSTrond Myklebust 592af3b61bfSTrond Myklebust static int nfs_server_return_marked_delegations(struct nfs_server *server, 593af3b61bfSTrond Myklebust void __always_unused *data) 594af3b61bfSTrond Myklebust { 595af3b61bfSTrond Myklebust struct nfs_delegation *delegation; 596af3b61bfSTrond Myklebust struct nfs_delegation *prev; 597af3b61bfSTrond Myklebust struct inode *inode; 598af3b61bfSTrond Myklebust struct inode *place_holder = NULL; 599af3b61bfSTrond Myklebust struct nfs_delegation *place_holder_deleg = NULL; 600af3b61bfSTrond Myklebust int err = 0; 601af3b61bfSTrond Myklebust 602af3b61bfSTrond Myklebust restart: 603af3b61bfSTrond Myklebust /* 604af3b61bfSTrond Myklebust * To avoid quadratic looping we hold a reference 605af3b61bfSTrond Myklebust * to an inode place_holder. Each time we restart, we 606af3b61bfSTrond Myklebust * list delegation in the server from the delegations 607af3b61bfSTrond Myklebust * of that inode. 608af3b61bfSTrond Myklebust * prev is an RCU-protected pointer to a delegation which 609af3b61bfSTrond Myklebust * wasn't marked for return and might be a good choice for 610af3b61bfSTrond Myklebust * the next place_holder. 611af3b61bfSTrond Myklebust */ 612af3b61bfSTrond Myklebust prev = NULL; 613af3b61bfSTrond Myklebust delegation = NULL; 614af3b61bfSTrond Myklebust rcu_read_lock(); 615af3b61bfSTrond Myklebust if (place_holder) 616af3b61bfSTrond Myklebust delegation = rcu_dereference(NFS_I(place_holder)->delegation); 617af3b61bfSTrond Myklebust if (!delegation || delegation != place_holder_deleg) 618af3b61bfSTrond Myklebust delegation = list_entry_rcu(server->delegations.next, 619af3b61bfSTrond Myklebust struct nfs_delegation, super_list); 620af3b61bfSTrond Myklebust list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 621af3b61bfSTrond Myklebust struct inode *to_put = NULL; 622af3b61bfSTrond Myklebust 623af3b61bfSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags)) 624af3b61bfSTrond Myklebust continue; 625af3b61bfSTrond Myklebust if (!nfs_delegation_need_return(delegation)) { 626af3b61bfSTrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) 627af3b61bfSTrond Myklebust prev = delegation; 628af3b61bfSTrond Myklebust continue; 629af3b61bfSTrond Myklebust } 630af3b61bfSTrond Myklebust 631af3b61bfSTrond Myklebust if (prev) { 632af3b61bfSTrond Myklebust struct inode *tmp = nfs_delegation_grab_inode(prev); 633af3b61bfSTrond Myklebust if (tmp) { 634af3b61bfSTrond Myklebust to_put = place_holder; 635af3b61bfSTrond Myklebust place_holder = tmp; 636af3b61bfSTrond Myklebust place_holder_deleg = prev; 637af3b61bfSTrond Myklebust } 638af3b61bfSTrond Myklebust } 639af3b61bfSTrond Myklebust 640af3b61bfSTrond Myklebust inode = nfs_delegation_grab_inode(delegation); 641af3b61bfSTrond Myklebust if (inode == NULL) { 642af3b61bfSTrond Myklebust rcu_read_unlock(); 643af3b61bfSTrond Myklebust iput(to_put); 644af3b61bfSTrond Myklebust goto restart; 645af3b61bfSTrond Myklebust } 646af3b61bfSTrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 647af3b61bfSTrond Myklebust rcu_read_unlock(); 648af3b61bfSTrond Myklebust 649af3b61bfSTrond Myklebust iput(to_put); 650af3b61bfSTrond Myklebust 651af3b61bfSTrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 652af3b61bfSTrond Myklebust iput(inode); 653af3b61bfSTrond Myklebust cond_resched(); 654af3b61bfSTrond Myklebust if (!err) 655af3b61bfSTrond Myklebust goto restart; 656af3b61bfSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 657af3b61bfSTrond Myklebust goto out; 658af3b61bfSTrond Myklebust } 659af3b61bfSTrond Myklebust rcu_read_unlock(); 660af3b61bfSTrond Myklebust out: 661af3b61bfSTrond Myklebust iput(place_holder); 662af3b61bfSTrond Myklebust return err; 663af3b61bfSTrond Myklebust } 664af3b61bfSTrond Myklebust 665be200377STrond Myklebust static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) 666be200377STrond Myklebust { 667be200377STrond Myklebust struct nfs_delegation *d; 668be200377STrond Myklebust bool ret = false; 669be200377STrond Myklebust 670be200377STrond Myklebust list_for_each_entry_rcu (d, &server->delegations, super_list) { 671be200377STrond Myklebust if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) 672be200377STrond Myklebust continue; 673be200377STrond Myklebust nfs_mark_return_delegation(server, d); 674be200377STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); 675be200377STrond Myklebust ret = true; 676be200377STrond Myklebust } 677be200377STrond Myklebust return ret; 678be200377STrond Myklebust } 679be200377STrond Myklebust 680be200377STrond Myklebust static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp) 681be200377STrond Myklebust { 682be200377STrond Myklebust struct nfs_server *server; 683be200377STrond Myklebust bool ret = false; 684be200377STrond Myklebust 685be200377STrond Myklebust if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state)) 686be200377STrond Myklebust goto out; 687be200377STrond Myklebust rcu_read_lock(); 688be200377STrond Myklebust list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) { 689be200377STrond Myklebust if (nfs_server_clear_delayed_delegations(server)) 690be200377STrond Myklebust ret = true; 691be200377STrond Myklebust } 692be200377STrond Myklebust rcu_read_unlock(); 693be200377STrond Myklebust out: 694be200377STrond Myklebust return ret; 695be200377STrond Myklebust } 696be200377STrond Myklebust 697d3978bb3SChuck Lever /** 698d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 699d3978bb3SChuck Lever * @clp: nfs_client to process 700d3978bb3SChuck Lever * 701dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 702dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 703dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 704dc327ed4STrond Myklebust * 705d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 706515d8611STrond Myklebust */ 707d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 708515d8611STrond Myklebust { 709be200377STrond Myklebust int err = nfs_client_for_each_server( 710be200377STrond Myklebust clp, nfs_server_return_marked_delegations, NULL); 711be200377STrond Myklebust if (err) 712be200377STrond Myklebust return err; 713be200377STrond Myklebust /* If a return was delayed, sleep to prevent hard looping */ 714be200377STrond Myklebust if (nfs_client_clear_delayed_delegations(clp)) 715be200377STrond Myklebust ssleep(1); 716be200377STrond Myklebust return 0; 717515d8611STrond Myklebust } 718515d8611STrond Myklebust 719d3978bb3SChuck Lever /** 720b47e0e47STrond Myklebust * nfs_inode_evict_delegation - return delegation, don't reclaim opens 721d3978bb3SChuck Lever * @inode: inode to process 722d3978bb3SChuck Lever * 723d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 724b47e0e47STrond Myklebust * to be called from nfs4_clear_inode(). Guaranteed to always free 725b47e0e47STrond Myklebust * the delegation structure. 726e6f81075STrond Myklebust */ 727b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode) 728e6f81075STrond Myklebust { 729e6f81075STrond Myklebust struct nfs_delegation *delegation; 730e6f81075STrond Myklebust 731d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 732b47e0e47STrond Myklebust if (delegation != NULL) { 733f885ea64STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 734b47e0e47STrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 7355fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 736ee05f456STrond Myklebust nfs_free_delegation(delegation); 737e6f81075STrond Myklebust } 738b47e0e47STrond Myklebust } 739e6f81075STrond Myklebust 740d3978bb3SChuck Lever /** 7416453bcd0STrond Myklebust * nfs4_inode_return_delegation - synchronously return a delegation 742d3978bb3SChuck Lever * @inode: inode to process 743d3978bb3SChuck Lever * 744c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 745c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 746c57d1bc5STrond Myklebust * we should stop caching. 747c57d1bc5STrond Myklebust * 748d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 749d3978bb3SChuck Lever */ 75057ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 75190163027STrond Myklebust { 75290163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 75390163027STrond Myklebust struct nfs_delegation *delegation; 75490163027STrond Myklebust 755d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 7566e176d47STrond Myklebust if (delegation != NULL) { 7576b4befc0STrond Myklebust /* Synchronous recall of any application leases */ 7586b4befc0STrond Myklebust break_lease(inode, O_WRONLY | O_RDWR); 7596e176d47STrond Myklebust if (S_ISREG(inode->i_mode)) 7606b4befc0STrond Myklebust nfs_wb_all(inode); 7616b4befc0STrond Myklebust return nfs_end_delegation_return(inode, delegation, 1); 7626e176d47STrond Myklebust } 7636b4befc0STrond Myklebust return 0; 7641da177e4SLinus Torvalds } 7651da177e4SLinus Torvalds 766c01d3645STrond Myklebust /** 7676453bcd0STrond Myklebust * nfs4_inode_return_delegation_on_close - asynchronously return a delegation 768b7b7dac6STrond Myklebust * @inode: inode to process 769b7b7dac6STrond Myklebust * 770b7b7dac6STrond Myklebust * This routine is called on file close in order to determine if the 771b7b7dac6STrond Myklebust * inode delegation needs to be returned immediately. 772b7b7dac6STrond Myklebust */ 773b7b7dac6STrond Myklebust void nfs4_inode_return_delegation_on_close(struct inode *inode) 774b7b7dac6STrond Myklebust { 775b7b7dac6STrond Myklebust struct nfs_delegation *delegation; 776b7b7dac6STrond Myklebust struct nfs_delegation *ret = NULL; 777b7b7dac6STrond Myklebust 778b7b7dac6STrond Myklebust if (!inode) 779b7b7dac6STrond Myklebust return; 780b7b7dac6STrond Myklebust rcu_read_lock(); 781b7b7dac6STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 782b7b7dac6STrond Myklebust if (!delegation) 783b7b7dac6STrond Myklebust goto out; 78410717f45STrond Myklebust if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || 78510717f45STrond Myklebust atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) { 786b7b7dac6STrond Myklebust spin_lock(&delegation->lock); 787b7b7dac6STrond Myklebust if (delegation->inode && 788b7b7dac6STrond Myklebust list_empty(&NFS_I(inode)->open_files) && 789b7b7dac6STrond Myklebust !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 790b7b7dac6STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 7918c75593cSTrond Myklebust /* Refcount matched in nfs_end_delegation_return() */ 7928c75593cSTrond Myklebust ret = nfs_get_delegation(delegation); 793b7b7dac6STrond Myklebust } 794b7b7dac6STrond Myklebust spin_unlock(&delegation->lock); 795efeda80dSTrond Myklebust if (ret) 796efeda80dSTrond Myklebust nfs_clear_verifier_delegated(inode); 797b7b7dac6STrond Myklebust } 798b7b7dac6STrond Myklebust out: 799b7b7dac6STrond Myklebust rcu_read_unlock(); 800b7b7dac6STrond Myklebust nfs_end_delegation_return(inode, ret, 0); 801b7b7dac6STrond Myklebust } 802b7b7dac6STrond Myklebust 803b7b7dac6STrond Myklebust /** 804c01d3645STrond Myklebust * nfs4_inode_make_writeable 805c01d3645STrond Myklebust * @inode: pointer to inode 806c01d3645STrond Myklebust * 807c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 808c01d3645STrond Myklebust * 809c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 810c01d3645STrond Myklebust */ 811c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 812c01d3645STrond Myklebust { 8133887ce1aSTrond Myklebust struct nfs_delegation *delegation; 8143887ce1aSTrond Myklebust 8153887ce1aSTrond Myklebust rcu_read_lock(); 8163887ce1aSTrond Myklebust delegation = nfs4_get_valid_delegation(inode); 8173887ce1aSTrond Myklebust if (delegation == NULL || 8183887ce1aSTrond Myklebust (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && 8193887ce1aSTrond Myklebust (delegation->type & FMODE_WRITE))) { 8203887ce1aSTrond Myklebust rcu_read_unlock(); 821c01d3645STrond Myklebust return 0; 822c01d3645STrond Myklebust } 8233887ce1aSTrond Myklebust rcu_read_unlock(); 8243887ce1aSTrond Myklebust return nfs4_inode_return_delegation(inode); 8253887ce1aSTrond Myklebust } 826c01d3645STrond Myklebust 827b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 828b757144fSTrond Myklebust struct nfs_delegation *delegation) 829b757144fSTrond Myklebust { 830b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 831b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 832b757144fSTrond Myklebust } 833b757144fSTrond Myklebust 8345c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 8355c31e236STrond Myklebust { 8365c31e236STrond Myklebust struct nfs_delegation *delegation; 8375c31e236STrond Myklebust bool ret = false; 8385c31e236STrond Myklebust 8395c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 8405c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 8415c31e236STrond Myklebust ret = true; 8425c31e236STrond Myklebust } 8435c31e236STrond Myklebust return ret; 8445c31e236STrond Myklebust } 8455c31e236STrond Myklebust 846b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 847b02ba0b6STrond Myklebust { 848b02ba0b6STrond Myklebust struct nfs_server *server; 849b02ba0b6STrond Myklebust 850b02ba0b6STrond Myklebust rcu_read_lock(); 851b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 852b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 853b02ba0b6STrond Myklebust rcu_read_unlock(); 854b02ba0b6STrond Myklebust } 855b02ba0b6STrond Myklebust 856b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 857b02ba0b6STrond Myklebust { 858b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 859b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 860b02ba0b6STrond Myklebust } 861b02ba0b6STrond Myklebust 862b02ba0b6STrond Myklebust /** 863b02ba0b6STrond Myklebust * nfs_expire_all_delegations 864b02ba0b6STrond Myklebust * @clp: client to process 865b02ba0b6STrond Myklebust * 866b02ba0b6STrond Myklebust */ 867b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 868b02ba0b6STrond Myklebust { 869b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 870b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 871b02ba0b6STrond Myklebust } 872b02ba0b6STrond Myklebust 873d3978bb3SChuck Lever /** 8746453bcd0STrond Myklebust * nfs_server_return_all_delegations - return delegations for one superblock 875302fad7bSTrond Myklebust * @server: pointer to nfs_server to process 876d3978bb3SChuck Lever * 8771da177e4SLinus Torvalds */ 878eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 8791da177e4SLinus Torvalds { 880d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 8815c31e236STrond Myklebust bool need_wait; 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds if (clp == NULL) 8841da177e4SLinus Torvalds return; 885d3978bb3SChuck Lever 8868383e460STrond Myklebust rcu_read_lock(); 8875c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 8888383e460STrond Myklebust rcu_read_unlock(); 889d3978bb3SChuck Lever 8905c31e236STrond Myklebust if (need_wait) { 891d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 8925c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 8935c31e236STrond Myklebust } 894515d8611STrond Myklebust } 895515d8611STrond Myklebust 896826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 897d3978bb3SChuck Lever fmode_t flags) 898515d8611STrond Myklebust { 899515d8611STrond Myklebust struct nfs_delegation *delegation; 900515d8611STrond Myklebust 901d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 902c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 903c79571a5SAlexandros Batsakis continue; 904c79571a5SAlexandros Batsakis if (delegation->type & flags) 905826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 906707fb4b3STrond Myklebust } 907d3978bb3SChuck Lever } 908d3978bb3SChuck Lever 909826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 910d3978bb3SChuck Lever fmode_t flags) 911d3978bb3SChuck Lever { 912d3978bb3SChuck Lever struct nfs_server *server; 913d3978bb3SChuck Lever 914d3978bb3SChuck Lever rcu_read_lock(); 915d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 916826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 917515d8611STrond Myklebust rcu_read_unlock(); 9181da177e4SLinus Torvalds } 9191da177e4SLinus Torvalds 920ee05f456STrond Myklebust static void nfs_revoke_delegation(struct inode *inode, 92141020b67STrond Myklebust const nfs4_stateid *stateid) 922869f9dfaSTrond Myklebust { 923869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 9247f048831STrond Myklebust nfs4_stateid tmp; 92541020b67STrond Myklebust bool ret = false; 92641020b67STrond Myklebust 927869f9dfaSTrond Myklebust rcu_read_lock(); 928869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 92941020b67STrond Myklebust if (delegation == NULL) 93041020b67STrond Myklebust goto out; 9317f048831STrond Myklebust if (stateid == NULL) { 9327f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 9337f048831STrond Myklebust stateid = &tmp; 934f2d47b55STrond Myklebust } else { 935f2d47b55STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 93641020b67STrond Myklebust goto out; 937f2d47b55STrond Myklebust spin_lock(&delegation->lock); 938f2d47b55STrond Myklebust if (stateid->seqid) { 939f2d47b55STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { 940f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 941f2d47b55STrond Myklebust goto out; 942f2d47b55STrond Myklebust } 943f2d47b55STrond Myklebust delegation->stateid.seqid = stateid->seqid; 944f2d47b55STrond Myklebust } 945f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 946f2d47b55STrond Myklebust } 947d2269ea1STrond Myklebust nfs_mark_delegation_revoked(delegation); 94841020b67STrond Myklebust ret = true; 94941020b67STrond Myklebust out: 950869f9dfaSTrond Myklebust rcu_read_unlock(); 9517f048831STrond Myklebust if (ret) 9527f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 953869f9dfaSTrond Myklebust } 954869f9dfaSTrond Myklebust 95541020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 95641020b67STrond Myklebust const nfs4_stateid *stateid) 957a1d0b5eeSTrond Myklebust { 958ee05f456STrond Myklebust nfs_revoke_delegation(inode, stateid); 959a1d0b5eeSTrond Myklebust } 9609cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 961a1d0b5eeSTrond Myklebust 962d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode, 963d51f91d2STrond Myklebust const nfs4_stateid *stateid) 964d51f91d2STrond Myklebust { 965d51f91d2STrond Myklebust struct nfs_delegation *delegation; 966d51f91d2STrond Myklebust 967d51f91d2STrond Myklebust if (!inode) 968d51f91d2STrond Myklebust return; 969d51f91d2STrond Myklebust 970d51f91d2STrond Myklebust rcu_read_lock(); 971d51f91d2STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 972d51f91d2STrond Myklebust if (!delegation) 973d51f91d2STrond Myklebust goto out_rcu_unlock; 974d51f91d2STrond Myklebust 975d51f91d2STrond Myklebust spin_lock(&delegation->lock); 976d51f91d2STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 977d51f91d2STrond Myklebust goto out_spin_unlock; 978d51f91d2STrond Myklebust if (stateid->seqid) { 979d51f91d2STrond Myklebust /* If delegation->stateid is newer, dont mark as returned */ 980d51f91d2STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) 981d51f91d2STrond Myklebust goto out_clear_returning; 982d51f91d2STrond Myklebust if (delegation->stateid.seqid != stateid->seqid) 983d51f91d2STrond Myklebust delegation->stateid.seqid = stateid->seqid; 984d51f91d2STrond Myklebust } 985d51f91d2STrond Myklebust 986d2269ea1STrond Myklebust nfs_mark_delegation_revoked(delegation); 987d51f91d2STrond Myklebust 988d51f91d2STrond Myklebust out_clear_returning: 989d51f91d2STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 990d51f91d2STrond Myklebust out_spin_unlock: 991d51f91d2STrond Myklebust spin_unlock(&delegation->lock); 992d51f91d2STrond Myklebust out_rcu_unlock: 993d51f91d2STrond Myklebust rcu_read_unlock(); 994d51f91d2STrond Myklebust 995d51f91d2STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 996d51f91d2STrond Myklebust } 997d51f91d2STrond Myklebust 998d3978bb3SChuck Lever /** 999826e0013STrond Myklebust * nfs_expire_unused_delegation_types 1000d3978bb3SChuck Lever * @clp: client to process 1001d3978bb3SChuck Lever * @flags: delegation types to expire 1002d3978bb3SChuck Lever * 1003d3978bb3SChuck Lever */ 1004826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 1005c79571a5SAlexandros Batsakis { 1006826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 1007c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 1008c79571a5SAlexandros Batsakis } 1009c79571a5SAlexandros Batsakis 1010d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 1011b7391f44STrond Myklebust { 1012b7391f44STrond Myklebust struct nfs_delegation *delegation; 1013b7391f44STrond Myklebust 1014d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 1015b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 1016b7391f44STrond Myklebust continue; 1017b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 1018b7391f44STrond Myklebust } 1019b7391f44STrond Myklebust } 1020b7391f44STrond Myklebust 1021d3978bb3SChuck Lever /** 1022d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 1023d3978bb3SChuck Lever * @clp: nfs_client to process 1024d3978bb3SChuck Lever * 1025d3978bb3SChuck Lever */ 1026b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 1027b7391f44STrond Myklebust { 1028d3978bb3SChuck Lever struct nfs_server *server; 1029d3978bb3SChuck Lever 1030d3978bb3SChuck Lever rcu_read_lock(); 1031d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1032d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 1033d3978bb3SChuck Lever rcu_read_unlock(); 1034d3978bb3SChuck Lever 1035b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 1036b7391f44STrond Myklebust } 1037b7391f44STrond Myklebust 1038d3978bb3SChuck Lever /** 1039d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 1040d3978bb3SChuck Lever * @inode: inode to process 10418e663f0eSTrond Myklebust * @stateid: state ID information 1042d3978bb3SChuck Lever * 1043d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 10441da177e4SLinus Torvalds */ 1045d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 1046d3978bb3SChuck Lever const nfs4_stateid *stateid) 10471da177e4SLinus Torvalds { 1048ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1049ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 10506411bd4aSTrond Myklebust struct nfs_delegation *delegation; 10511da177e4SLinus Torvalds 10526411bd4aSTrond Myklebust rcu_read_lock(); 1053457a5042STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 1054755a48a7STrond Myklebust if (delegation == NULL) 1055755a48a7STrond Myklebust goto out_enoent; 10564816fdadSTrond Myklebust if (stateid != NULL && 10574816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 1058755a48a7STrond Myklebust goto out_enoent; 1059ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 10606411bd4aSTrond Myklebust rcu_read_unlock(); 1061d3978bb3SChuck Lever 10626b4befc0STrond Myklebust /* If there are any application leases or delegations, recall them */ 10636b4befc0STrond Myklebust break_lease(inode, O_WRONLY | O_RDWR | O_NONBLOCK); 10646b4befc0STrond Myklebust 10656411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 10666411bd4aSTrond Myklebust return 0; 1067755a48a7STrond Myklebust out_enoent: 1068755a48a7STrond Myklebust rcu_read_unlock(); 1069755a48a7STrond Myklebust return -ENOENT; 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds 1072d3978bb3SChuck Lever static struct inode * 1073d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 1074d3978bb3SChuck Lever const struct nfs_fh *fhandle) 10751da177e4SLinus Torvalds { 10761da177e4SLinus Torvalds struct nfs_delegation *delegation; 1077113aac6dSTrond Myklebust struct super_block *freeme = NULL; 1078113aac6dSTrond Myklebust struct inode *res = NULL; 1079d3978bb3SChuck Lever 1080d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 108186e89489STrond Myklebust spin_lock(&delegation->lock); 108286e89489STrond Myklebust if (delegation->inode != NULL && 1083457a5042STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 108486e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 1085113aac6dSTrond Myklebust if (nfs_sb_active(server->super)) { 1086113aac6dSTrond Myklebust freeme = server->super; 1087113aac6dSTrond Myklebust res = igrab(delegation->inode); 1088113aac6dSTrond Myklebust } 108986e89489STrond Myklebust spin_unlock(&delegation->lock); 109086e89489STrond Myklebust if (res != NULL) 1091d3978bb3SChuck Lever return res; 1092e39d8a18STrond Myklebust if (freeme) { 1093e39d8a18STrond Myklebust rcu_read_unlock(); 1094113aac6dSTrond Myklebust nfs_sb_deactive(freeme); 1095e39d8a18STrond Myklebust rcu_read_lock(); 1096e39d8a18STrond Myklebust } 10976c342655STrond Myklebust return ERR_PTR(-EAGAIN); 10986c342655STrond Myklebust } 10996c342655STrond Myklebust spin_unlock(&delegation->lock); 11006c342655STrond Myklebust } 11016c342655STrond Myklebust return ERR_PTR(-ENOENT); 1102d3978bb3SChuck Lever } 1103d3978bb3SChuck Lever 1104d3978bb3SChuck Lever /** 1105d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 1106d3978bb3SChuck Lever * @clp: client state handle 1107d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 1108d3978bb3SChuck Lever * 1109d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 1110d3978bb3SChuck Lever * cannot be found. 1111d3978bb3SChuck Lever */ 1112d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 1113d3978bb3SChuck Lever const struct nfs_fh *fhandle) 1114d3978bb3SChuck Lever { 1115d3978bb3SChuck Lever struct nfs_server *server; 11166c342655STrond Myklebust struct inode *res; 1117d3978bb3SChuck Lever 1118d3978bb3SChuck Lever rcu_read_lock(); 1119d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1120d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 1121d5681f59SAnna Schumaker if (res != ERR_PTR(-ENOENT)) { 1122d5681f59SAnna Schumaker rcu_read_unlock(); 11236c342655STrond Myklebust return res; 1124d3978bb3SChuck Lever } 1125d5681f59SAnna Schumaker } 11268383e460STrond Myklebust rcu_read_unlock(); 11276c342655STrond Myklebust return ERR_PTR(-ENOENT); 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds 1130d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 1131d3978bb3SChuck Lever { 1132d3978bb3SChuck Lever struct nfs_delegation *delegation; 1133d3978bb3SChuck Lever 113445870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 113545870d69STrond Myklebust /* 113645870d69STrond Myklebust * If the delegation may have been admin revoked, then we 113745870d69STrond Myklebust * cannot reclaim it. 113845870d69STrond Myklebust */ 113945870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 114045870d69STrond Myklebust continue; 1141d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 1142d3978bb3SChuck Lever } 114345870d69STrond Myklebust } 1144d3978bb3SChuck Lever 1145d3978bb3SChuck Lever /** 1146d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 1147d3978bb3SChuck Lever * @clp: nfs_client to process 1148d3978bb3SChuck Lever * 11491da177e4SLinus Torvalds */ 1150adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 11511da177e4SLinus Torvalds { 1152d3978bb3SChuck Lever struct nfs_server *server; 1153d3978bb3SChuck Lever 11548383e460STrond Myklebust rcu_read_lock(); 1155d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1156d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 11578383e460STrond Myklebust rcu_read_unlock(); 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds 11601bba38b2STrond Myklebust static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, 11611bba38b2STrond Myklebust void __always_unused *data) 11621da177e4SLinus Torvalds { 11638383e460STrond Myklebust struct nfs_delegation *delegation; 116486e89489STrond Myklebust struct inode *inode; 11658383e460STrond Myklebust restart: 11668383e460STrond Myklebust rcu_read_lock(); 11671bba38b2STrond Myklebust restart_locked: 11681bba38b2STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 11696f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 11706f9449beSTrond Myklebust &delegation->flags) || 11716f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 11726f9449beSTrond Myklebust &delegation->flags) || 11736f9449beSTrond Myklebust test_bit(NFS_DELEGATION_NEED_RECLAIM, 1174d3978bb3SChuck Lever &delegation->flags) == 0) 11751da177e4SLinus Torvalds continue; 11769f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 11771bba38b2STrond Myklebust if (inode == NULL) 11781bba38b2STrond Myklebust goto restart_locked; 1179b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 1180b04b22f4STrond Myklebust rcu_read_unlock(); 1181b04b22f4STrond Myklebust if (delegation != NULL) { 11828c75593cSTrond Myklebust if (nfs_detach_delegation(NFS_I(inode), delegation, 11838c75593cSTrond Myklebust server) != NULL) 1184905f8d16STrond Myklebust nfs_free_delegation(delegation); 11858c75593cSTrond Myklebust /* Match nfs_start_delegation_return_locked */ 11868c75593cSTrond Myklebust nfs_put_delegation(delegation); 1187b04b22f4STrond Myklebust } 118886e89489STrond Myklebust iput(inode); 11893ca951b6SNeilBrown cond_resched(); 11908383e460STrond Myklebust goto restart; 11911da177e4SLinus Torvalds } 11928383e460STrond Myklebust rcu_read_unlock(); 11931bba38b2STrond Myklebust return 0; 11941bba38b2STrond Myklebust } 11951bba38b2STrond Myklebust 11961bba38b2STrond Myklebust /** 11971bba38b2STrond Myklebust * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 11981bba38b2STrond Myklebust * @clp: nfs_client to process 11991bba38b2STrond Myklebust * 12001bba38b2STrond Myklebust */ 12011bba38b2STrond Myklebust void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 12021bba38b2STrond Myklebust { 12031bba38b2STrond Myklebust nfs_client_for_each_server(clp, nfs_server_reap_unclaimed_delegations, 12041bba38b2STrond Myklebust NULL); 12051da177e4SLinus Torvalds } 12063e4f6290STrond Myklebust 1207bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 1208bb3d1a3bSTrond Myklebust { 1209bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 1210bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 1211bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 1212bb3d1a3bSTrond Myklebust } 1213bb3d1a3bSTrond Myklebust 121445870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 121545870d69STrond Myklebust struct nfs_delegation *delegation) 121645870d69STrond Myklebust { 1217059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 1218059b43e9STrond Myklebust return; 121945870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 122045870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 122145870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 122245870d69STrond Myklebust } 122345870d69STrond Myklebust 1224bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 1225bb3d1a3bSTrond Myklebust struct inode *inode) 1226bb3d1a3bSTrond Myklebust { 1227bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 1228bb3d1a3bSTrond Myklebust 1229bb3d1a3bSTrond Myklebust rcu_read_lock(); 1230bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1231bb3d1a3bSTrond Myklebust if (delegation) 1232bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 1233bb3d1a3bSTrond Myklebust rcu_read_unlock(); 1234bb3d1a3bSTrond Myklebust 1235bb3d1a3bSTrond Myklebust } 1236bb3d1a3bSTrond Myklebust 123745870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 123845870d69STrond Myklebust { 123945870d69STrond Myklebust struct nfs_delegation *delegation; 124045870d69STrond Myklebust 124145870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 124245870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 124345870d69STrond Myklebust } 124445870d69STrond Myklebust 124545870d69STrond Myklebust /** 124645870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 124745870d69STrond Myklebust * @clp: nfs_client to process 124845870d69STrond Myklebust * 124945870d69STrond Myklebust * Iterates through all the delegations associated with this server and 125045870d69STrond Myklebust * marks them as needing to be checked for validity. 125145870d69STrond Myklebust */ 125245870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 125345870d69STrond Myklebust { 125445870d69STrond Myklebust struct nfs_server *server; 125545870d69STrond Myklebust 125645870d69STrond Myklebust rcu_read_lock(); 125745870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 125845870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 125945870d69STrond Myklebust rcu_read_unlock(); 126045870d69STrond Myklebust } 126145870d69STrond Myklebust 126245870d69STrond Myklebust /** 12638ca017c8SScott Mayhew * nfs_test_expired_all_delegations - test all delegations for a client 12648ca017c8SScott Mayhew * @clp: nfs_client to process 12658ca017c8SScott Mayhew * 12668ca017c8SScott Mayhew * Helper for handling "recallable state revoked" status from server. 12678ca017c8SScott Mayhew */ 12688ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp) 12698ca017c8SScott Mayhew { 12708ca017c8SScott Mayhew nfs_mark_test_expired_all_delegations(clp); 12718ca017c8SScott Mayhew nfs4_schedule_state_manager(clp); 12728ca017c8SScott Mayhew } 12738ca017c8SScott Mayhew 1274ad114089STrond Myklebust static void 1275ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode, 1276ad114089STrond Myklebust nfs4_stateid *stateid, 1277ad114089STrond Myklebust const struct cred *cred) 1278ad114089STrond Myklebust { 1279ad114089STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1280ad114089STrond Myklebust const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 1281ad114089STrond Myklebust int status; 1282ad114089STrond Myklebust 1283ad114089STrond Myklebust if (!cred) 1284ad114089STrond Myklebust return; 1285ad114089STrond Myklebust status = ops->test_and_free_expired(server, stateid, cred); 1286ad114089STrond Myklebust if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 1287ad114089STrond Myklebust nfs_remove_bad_delegation(inode, stateid); 1288ad114089STrond Myklebust } 1289ad114089STrond Myklebust 12907f156ef0STrond Myklebust static int nfs_server_reap_expired_delegations(struct nfs_server *server, 12917f156ef0STrond Myklebust void __always_unused *data) 12927f156ef0STrond Myklebust { 12937f156ef0STrond Myklebust struct nfs_delegation *delegation; 12947f156ef0STrond Myklebust struct inode *inode; 12957f156ef0STrond Myklebust const struct cred *cred; 12967f156ef0STrond Myklebust nfs4_stateid stateid; 12977f156ef0STrond Myklebust restart: 12987f156ef0STrond Myklebust rcu_read_lock(); 12997f156ef0STrond Myklebust restart_locked: 13007f156ef0STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 13017f156ef0STrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 13027f156ef0STrond Myklebust &delegation->flags) || 13037f156ef0STrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 13047f156ef0STrond Myklebust &delegation->flags) || 13057f156ef0STrond Myklebust test_bit(NFS_DELEGATION_TEST_EXPIRED, 13067f156ef0STrond Myklebust &delegation->flags) == 0) 13077f156ef0STrond Myklebust continue; 13087f156ef0STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 13097f156ef0STrond Myklebust if (inode == NULL) 13107f156ef0STrond Myklebust goto restart_locked; 1311fc51b1cfSTrond Myklebust spin_lock(&delegation->lock); 13127f156ef0STrond Myklebust cred = get_cred_rcu(delegation->cred); 13137f156ef0STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 1314fc51b1cfSTrond Myklebust spin_unlock(&delegation->lock); 13157f156ef0STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 13167f156ef0STrond Myklebust rcu_read_unlock(); 13177f156ef0STrond Myklebust nfs_delegation_test_free_expired(inode, &stateid, cred); 13187f156ef0STrond Myklebust put_cred(cred); 13197f156ef0STrond Myklebust if (!nfs4_server_rebooted(server->nfs_client)) { 13207f156ef0STrond Myklebust iput(inode); 13217f156ef0STrond Myklebust cond_resched(); 13227f156ef0STrond Myklebust goto restart; 13237f156ef0STrond Myklebust } 13247f156ef0STrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 13257f156ef0STrond Myklebust iput(inode); 13267f156ef0STrond Myklebust return -EAGAIN; 13277f156ef0STrond Myklebust } 13287f156ef0STrond Myklebust rcu_read_unlock(); 13297f156ef0STrond Myklebust return 0; 13307f156ef0STrond Myklebust } 13317f156ef0STrond Myklebust 13328ca017c8SScott Mayhew /** 133345870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 133445870d69STrond Myklebust * @clp: nfs_client to process 133545870d69STrond Myklebust * 133645870d69STrond Myklebust * Iterates through all the delegations associated with this server and 133745870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 133845870d69STrond Myklebust * expected to be called in cases where the server may have lost its 133945870d69STrond Myklebust * lease. 134045870d69STrond Myklebust */ 134145870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 134245870d69STrond Myklebust { 13437f156ef0STrond Myklebust nfs_client_for_each_server(clp, nfs_server_reap_expired_delegations, 13447f156ef0STrond Myklebust NULL); 134545870d69STrond Myklebust } 134645870d69STrond Myklebust 13476c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 13486c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 13496c2d8f8dSTrond Myklebust { 13506c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 13516c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 13526c2d8f8dSTrond Myklebust bool found = false; 13536c2d8f8dSTrond Myklebust 13546c2d8f8dSTrond Myklebust rcu_read_lock(); 13556c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 13566c2d8f8dSTrond Myklebust if (delegation && 135742c304c3STrond Myklebust nfs4_stateid_match_or_older(&delegation->stateid, stateid) && 135842c304c3STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 13596c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 13606c2d8f8dSTrond Myklebust found = true; 13616c2d8f8dSTrond Myklebust } 13626c2d8f8dSTrond Myklebust rcu_read_unlock(); 13636c2d8f8dSTrond Myklebust if (found) 13646c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 13656c2d8f8dSTrond Myklebust } 13666c2d8f8dSTrond Myklebust 1367d3978bb3SChuck Lever /** 1368d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1369d3978bb3SChuck Lever * @clp: client state handle 1370d3978bb3SChuck Lever * 1371d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1372d3978bb3SChuck Lever * to this nfs_client. 1373d3978bb3SChuck Lever */ 1374d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1375d3978bb3SChuck Lever { 1376d3978bb3SChuck Lever struct nfs_server *server; 1377d3978bb3SChuck Lever int ret = 0; 1378d3978bb3SChuck Lever 1379d3978bb3SChuck Lever rcu_read_lock(); 1380d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1381d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1382d3978bb3SChuck Lever ret = 1; 1383d3978bb3SChuck Lever break; 1384d3978bb3SChuck Lever } 1385d3978bb3SChuck Lever rcu_read_unlock(); 1386d3978bb3SChuck Lever return ret; 1387d3978bb3SChuck Lever } 1388d3978bb3SChuck Lever 1389d3978bb3SChuck Lever /** 139012f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 139112f275cdSTrond Myklebust * @dst: stateid to refresh 139212f275cdSTrond Myklebust * @inode: inode to check 139312f275cdSTrond Myklebust * 139412f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 139512f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 139612f275cdSTrond Myklebust */ 139712f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 139812f275cdSTrond Myklebust { 139912f275cdSTrond Myklebust struct nfs_delegation *delegation; 140012f275cdSTrond Myklebust bool ret = false; 140112f275cdSTrond Myklebust if (!inode) 140212f275cdSTrond Myklebust goto out; 140312f275cdSTrond Myklebust 140412f275cdSTrond Myklebust rcu_read_lock(); 140512f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 140612f275cdSTrond Myklebust if (delegation != NULL && 1407b5756208STrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid) && 1408246afc0aSTrond Myklebust nfs4_stateid_is_newer(&delegation->stateid, dst) && 1409b5756208STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 141012f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 141179cc5542STrond Myklebust ret = true; 141212f275cdSTrond Myklebust } 141312f275cdSTrond Myklebust rcu_read_unlock(); 141412f275cdSTrond Myklebust out: 141512f275cdSTrond Myklebust return ret; 141612f275cdSTrond Myklebust } 141712f275cdSTrond Myklebust 141812f275cdSTrond Myklebust /** 1419d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1420d3978bb3SChuck Lever * @inode: inode to check 14210032a7a7STrond Myklebust * @flags: delegation type requirement 1422abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1423abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1424d3978bb3SChuck Lever * 14250032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 14260032a7a7STrond Myklebust * otherwise "false" is returned. 1427d3978bb3SChuck Lever */ 1428abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1429a52458b4SNeilBrown nfs4_stateid *dst, const struct cred **cred) 14303e4f6290STrond Myklebust { 14313e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 14323e4f6290STrond Myklebust struct nfs_delegation *delegation; 1433fc51b1cfSTrond Myklebust bool ret = false; 14343e4f6290STrond Myklebust 14350032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 14368383e460STrond Myklebust rcu_read_lock(); 14378383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1438fc51b1cfSTrond Myklebust if (!delegation) 1439fc51b1cfSTrond Myklebust goto out; 1440fc51b1cfSTrond Myklebust spin_lock(&delegation->lock); 1441aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 14420032a7a7STrond Myklebust if (ret) { 1443f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 14440032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1445abf4e13cSTrond Myklebust if (cred) 1446a52458b4SNeilBrown *cred = get_cred(delegation->cred); 14473e4f6290STrond Myklebust } 1448fc51b1cfSTrond Myklebust spin_unlock(&delegation->lock); 1449fc51b1cfSTrond Myklebust out: 14508383e460STrond Myklebust rcu_read_unlock(); 14518383e460STrond Myklebust return ret; 14523e4f6290STrond Myklebust } 14535445b1fbSTrond Myklebust 14545445b1fbSTrond Myklebust /** 14555445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 14565445b1fbSTrond Myklebust * @inode: inode to check 14575445b1fbSTrond Myklebust * 14585445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 14595445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 14605445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 14615445b1fbSTrond Myklebust */ 14625445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 14635445b1fbSTrond Myklebust { 14645445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 14655445b1fbSTrond Myklebust struct nfs_delegation *delegation; 14665445b1fbSTrond Myklebust bool ret = true; 14675445b1fbSTrond Myklebust 14685445b1fbSTrond Myklebust rcu_read_lock(); 14695445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 14705445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 14715445b1fbSTrond Myklebust goto out; 1472a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 14735445b1fbSTrond Myklebust ret = false; 14745445b1fbSTrond Myklebust out: 14755445b1fbSTrond Myklebust rcu_read_unlock(); 14765445b1fbSTrond Myklebust return ret; 14775445b1fbSTrond Myklebust } 147810717f45STrond Myklebust 147910717f45STrond Myklebust module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644); 1480