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 28905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 29905f8d16STrond Myklebust { 30a52458b4SNeilBrown put_cred(delegation->cred); 31e00b8a24STrond Myklebust delegation->cred = NULL; 3226f04ddeSLai Jiangshan kfree_rcu(delegation, rcu); 338383e460STrond Myklebust } 348383e460STrond Myklebust 35d3978bb3SChuck Lever /** 36d3978bb3SChuck Lever * nfs_mark_delegation_referenced - set delegation's REFERENCED flag 37d3978bb3SChuck Lever * @delegation: delegation to process 38d3978bb3SChuck Lever * 39d3978bb3SChuck Lever */ 40b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 41b7391f44STrond Myklebust { 42b7391f44STrond Myklebust set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 43b7391f44STrond Myklebust } 44b7391f44STrond Myklebust 45aa05c87fSTrond Myklebust static bool 46aa05c87fSTrond Myklebust nfs4_is_valid_delegation(const struct nfs_delegation *delegation, 47aa05c87fSTrond Myklebust fmode_t flags) 48aa05c87fSTrond Myklebust { 49aa05c87fSTrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags && 50aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 51aa05c87fSTrond Myklebust !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 52aa05c87fSTrond Myklebust return true; 53aa05c87fSTrond Myklebust return false; 54aa05c87fSTrond Myklebust } 55aa05c87fSTrond Myklebust 56be3df3ddSTrond Myklebust struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) 57be3df3ddSTrond Myklebust { 58be3df3ddSTrond Myklebust struct nfs_delegation *delegation; 59be3df3ddSTrond Myklebust 60be3df3ddSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 61be3df3ddSTrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) 62be3df3ddSTrond Myklebust return delegation; 63be3df3ddSTrond Myklebust return NULL; 64be3df3ddSTrond Myklebust } 65be3df3ddSTrond Myklebust 6615bb3afeSPeng Tao static int 6715bb3afeSPeng Tao nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark) 68b7391f44STrond Myklebust { 69b7391f44STrond Myklebust struct nfs_delegation *delegation; 70b7391f44STrond Myklebust int ret = 0; 71b7391f44STrond Myklebust 72b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 73b7391f44STrond Myklebust rcu_read_lock(); 74b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 75aa05c87fSTrond Myklebust if (nfs4_is_valid_delegation(delegation, flags)) { 7615bb3afeSPeng Tao if (mark) 77b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 78b7391f44STrond Myklebust ret = 1; 79b7391f44STrond Myklebust } 80b7391f44STrond Myklebust rcu_read_unlock(); 81b7391f44STrond Myklebust return ret; 82b7391f44STrond Myklebust } 8315bb3afeSPeng Tao /** 8415bb3afeSPeng Tao * nfs_have_delegation - check if inode has a delegation, mark it 8515bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if there is one. 8615bb3afeSPeng Tao * @inode: inode to check 8715bb3afeSPeng Tao * @flags: delegation types to check for 8815bb3afeSPeng Tao * 8915bb3afeSPeng Tao * Returns one if inode has the indicated delegation, otherwise zero. 9015bb3afeSPeng Tao */ 9115bb3afeSPeng Tao int nfs4_have_delegation(struct inode *inode, fmode_t flags) 9215bb3afeSPeng Tao { 9315bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, true); 9415bb3afeSPeng Tao } 9515bb3afeSPeng Tao 9615bb3afeSPeng Tao /* 9715bb3afeSPeng Tao * nfs4_check_delegation - check if inode has a delegation, do not mark 9815bb3afeSPeng Tao * NFS_DELEGATION_REFERENCED if it has one. 9915bb3afeSPeng Tao */ 10015bb3afeSPeng Tao int nfs4_check_delegation(struct inode *inode, fmode_t flags) 10115bb3afeSPeng Tao { 10215bb3afeSPeng Tao return nfs4_do_check_delegation(inode, flags, false); 10315bb3afeSPeng Tao } 104b7391f44STrond Myklebust 10544f411c3SOlga Kornievskaia static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) 106888e694cSTrond Myklebust { 107888e694cSTrond Myklebust struct inode *inode = state->inode; 108888e694cSTrond Myklebust struct file_lock *fl; 109bd61e0a9SJeff Layton struct file_lock_context *flctx = inode->i_flctx; 110bd61e0a9SJeff Layton struct list_head *list; 111d5122201STrond Myklebust int status = 0; 112888e694cSTrond Myklebust 113bd61e0a9SJeff Layton if (flctx == NULL) 11465b62a29STrond Myklebust goto out; 115314d7cc0SJeff Layton 116bd61e0a9SJeff Layton list = &flctx->flc_posix; 1176109c850SJeff Layton spin_lock(&flctx->flc_lock); 118bd61e0a9SJeff Layton restart: 119bd61e0a9SJeff Layton list_for_each_entry(fl, list, fl_list) { 12044f411c3SOlga Kornievskaia if (nfs_file_open_context(fl->fl_file)->state != state) 121888e694cSTrond Myklebust continue; 1226109c850SJeff Layton spin_unlock(&flctx->flc_lock); 123db4f2e63STrond Myklebust status = nfs4_lock_delegation_recall(fl, state, stateid); 124d5122201STrond Myklebust if (status < 0) 1253f09df70STrond Myklebust goto out; 1266109c850SJeff Layton spin_lock(&flctx->flc_lock); 127888e694cSTrond Myklebust } 128bd61e0a9SJeff Layton if (list == &flctx->flc_posix) { 129bd61e0a9SJeff Layton list = &flctx->flc_flock; 130bd61e0a9SJeff Layton goto restart; 1315263e31eSJeff Layton } 1326109c850SJeff Layton spin_unlock(&flctx->flc_lock); 1333f09df70STrond Myklebust out: 134888e694cSTrond Myklebust return status; 135888e694cSTrond Myklebust } 136888e694cSTrond Myklebust 13724311f88STrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, 13824311f88STrond Myklebust const nfs4_stateid *stateid, fmode_t type) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 1411da177e4SLinus Torvalds struct nfs_open_context *ctx; 142d25be546STrond Myklebust struct nfs4_state_owner *sp; 1431da177e4SLinus Torvalds struct nfs4_state *state; 144d25be546STrond Myklebust unsigned int seq; 145888e694cSTrond Myklebust int err; 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds again: 1480de43976STrond Myklebust rcu_read_lock(); 1490de43976STrond Myklebust list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 1501da177e4SLinus Torvalds state = ctx->state; 1511da177e4SLinus Torvalds if (state == NULL) 1521da177e4SLinus Torvalds continue; 1531da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1541da177e4SLinus Torvalds continue; 155f8ebf7a8STrond Myklebust if (!nfs4_valid_open_stateid(state)) 156f8ebf7a8STrond Myklebust continue; 157f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 15890163027STrond Myklebust continue; 1590de43976STrond Myklebust if (!get_nfs_open_context(ctx)) 1600de43976STrond Myklebust continue; 1610de43976STrond Myklebust rcu_read_unlock(); 162d25be546STrond Myklebust sp = state->owner; 16365b62a29STrond Myklebust /* Block nfs4_proc_unlck */ 16465b62a29STrond Myklebust mutex_lock(&sp->so_delegreturn_mutex); 165d25be546STrond Myklebust seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 1665eb8d18cSTrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 167d25be546STrond Myklebust if (!err) 16844f411c3SOlga Kornievskaia err = nfs_delegation_claim_locks(state, stateid); 169d25be546STrond Myklebust if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 170d25be546STrond Myklebust err = -EAGAIN; 17165b62a29STrond Myklebust mutex_unlock(&sp->so_delegreturn_mutex); 1721da177e4SLinus Torvalds put_nfs_open_context(ctx); 173888e694cSTrond Myklebust if (err != 0) 174d18cc1fdSTrond Myklebust return err; 1751da177e4SLinus Torvalds goto again; 1761da177e4SLinus Torvalds } 1770de43976STrond Myklebust rcu_read_unlock(); 178d18cc1fdSTrond Myklebust return 0; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 181d3978bb3SChuck Lever /** 182d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 183d3978bb3SChuck Lever * @inode: inode to process 184d3978bb3SChuck Lever * @cred: credential to use for request 18535156bffSTrond Myklebust * @type: delegation type 18635156bffSTrond Myklebust * @stateid: delegation stateid 18735156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 188d3978bb3SChuck Lever * 1891da177e4SLinus Torvalds */ 190a52458b4SNeilBrown void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, 19135156bffSTrond Myklebust fmode_t type, 19235156bffSTrond Myklebust const nfs4_stateid *stateid, 19335156bffSTrond Myklebust unsigned long pagemod_limit) 1941da177e4SLinus Torvalds { 1958f649c37STrond Myklebust struct nfs_delegation *delegation; 196a52458b4SNeilBrown const struct cred *oldcred = NULL; 1971da177e4SLinus Torvalds 1988f649c37STrond Myklebust rcu_read_lock(); 1998f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 2008f649c37STrond Myklebust if (delegation != NULL) { 2018f649c37STrond Myklebust spin_lock(&delegation->lock); 2028f649c37STrond Myklebust if (delegation->inode != NULL) { 20335156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 20435156bffSTrond Myklebust delegation->type = type; 20535156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 20605c88babSTrond Myklebust oldcred = delegation->cred; 207a52458b4SNeilBrown delegation->cred = get_cred(cred); 2088f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 2098f649c37STrond Myklebust &delegation->flags); 2108f649c37STrond Myklebust spin_unlock(&delegation->lock); 2118f649c37STrond Myklebust rcu_read_unlock(); 212a52458b4SNeilBrown put_cred(oldcred); 21335156bffSTrond Myklebust trace_nfs4_reclaim_delegation(inode, type); 214b1a318deSTrond Myklebust return; 215b1a318deSTrond Myklebust } 2168f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 2178f649c37STrond Myklebust spin_unlock(&delegation->lock); 218b1a318deSTrond Myklebust } 2198f649c37STrond Myklebust rcu_read_unlock(); 22035156bffSTrond Myklebust nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit); 2218f649c37STrond Myklebust } 2221da177e4SLinus Torvalds 22357bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 22457bfa891STrond Myklebust { 22557bfa891STrond Myklebust int res = 0; 22657bfa891STrond Myklebust 227869f9dfaSTrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 228869f9dfaSTrond Myklebust res = nfs4_proc_delegreturn(inode, 229869f9dfaSTrond Myklebust delegation->cred, 230869f9dfaSTrond Myklebust &delegation->stateid, 231869f9dfaSTrond Myklebust issync); 23257bfa891STrond Myklebust nfs_free_delegation(delegation); 23357bfa891STrond Myklebust return res; 23457bfa891STrond Myklebust } 23557bfa891STrond Myklebust 23686e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 23786e89489STrond Myklebust { 23886e89489STrond Myklebust struct inode *inode = NULL; 23986e89489STrond Myklebust 24086e89489STrond Myklebust spin_lock(&delegation->lock); 24186e89489STrond Myklebust if (delegation->inode != NULL) 24286e89489STrond Myklebust inode = igrab(delegation->inode); 2436f9449beSTrond Myklebust if (!inode) 2446f9449beSTrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 24586e89489STrond Myklebust spin_unlock(&delegation->lock); 24686e89489STrond Myklebust return inode; 24786e89489STrond Myklebust } 24886e89489STrond Myklebust 249dda4b225SChuck Lever static struct nfs_delegation * 250d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 25157bfa891STrond Myklebust { 252d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 253d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 25457bfa891STrond Myklebust 25557bfa891STrond Myklebust if (delegation == NULL) 256d25be546STrond Myklebust goto out; 257d25be546STrond Myklebust spin_lock(&delegation->lock); 258d25be546STrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 259d25be546STrond Myklebust ret = delegation; 260d25be546STrond Myklebust spin_unlock(&delegation->lock); 261d25be546STrond Myklebust out: 262d25be546STrond Myklebust return ret; 263d25be546STrond Myklebust } 264d25be546STrond Myklebust 265d25be546STrond Myklebust static struct nfs_delegation * 266d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 267d25be546STrond Myklebust { 268d25be546STrond Myklebust struct nfs_delegation *delegation; 269d25be546STrond Myklebust 270d25be546STrond Myklebust rcu_read_lock(); 271d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 272d25be546STrond Myklebust rcu_read_unlock(); 273d25be546STrond Myklebust return delegation; 274d25be546STrond Myklebust } 275d25be546STrond Myklebust 276d25be546STrond Myklebust static void 277d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation, 278d25be546STrond Myklebust struct nfs_client *clp) 279d25be546STrond Myklebust { 280dda4b225SChuck Lever 28134310430STrond Myklebust spin_lock(&delegation->lock); 282d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 283d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 284d25be546STrond Myklebust spin_unlock(&delegation->lock); 285d25be546STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 286d25be546STrond Myklebust } 287d25be546STrond Myklebust 288d25be546STrond Myklebust static struct nfs_delegation * 289d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 290d25be546STrond Myklebust struct nfs_delegation *delegation, 291d25be546STrond Myklebust struct nfs_client *clp) 292d25be546STrond Myklebust { 293d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 294d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 295d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 296d25be546STrond Myklebust 297d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 298d25be546STrond Myklebust return NULL; 299d25be546STrond Myklebust 300d25be546STrond Myklebust spin_lock(&delegation->lock); 301d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 30257bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 30386e89489STrond Myklebust delegation->inode = NULL; 30457bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 30534310430STrond Myklebust spin_unlock(&delegation->lock); 30657bfa891STrond Myklebust return delegation; 30757bfa891STrond Myklebust } 30857bfa891STrond Myklebust 309dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 310d25be546STrond Myklebust struct nfs_delegation *delegation, 311d3978bb3SChuck Lever struct nfs_server *server) 312dda4b225SChuck Lever { 313d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 314dda4b225SChuck Lever 315dda4b225SChuck Lever spin_lock(&clp->cl_lock); 316d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 317dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 318dda4b225SChuck Lever return delegation; 319dda4b225SChuck Lever } 320dda4b225SChuck Lever 321d25be546STrond Myklebust static struct nfs_delegation * 322d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 323d25be546STrond Myklebust { 324d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 325d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 326d25be546STrond Myklebust struct nfs_delegation *delegation; 327d25be546STrond Myklebust 328d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 329d25be546STrond Myklebust if (delegation == NULL) 330d25be546STrond Myklebust return NULL; 331d25be546STrond Myklebust return nfs_detach_delegation(nfsi, delegation, server); 332d25be546STrond Myklebust } 333d25be546STrond Myklebust 334cf6726e2STrond Myklebust static void 335cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation, 336cf6726e2STrond Myklebust const struct nfs_delegation *update) 337cf6726e2STrond Myklebust { 338cf6726e2STrond Myklebust if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 339cf6726e2STrond Myklebust delegation->stateid.seqid = update->stateid.seqid; 340cf6726e2STrond Myklebust smp_wmb(); 341cf6726e2STrond Myklebust delegation->type = update->type; 342cf6726e2STrond Myklebust } 343cf6726e2STrond Myklebust } 344cf6726e2STrond Myklebust 345d3978bb3SChuck Lever /** 346d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 347d3978bb3SChuck Lever * @inode: inode to which delegation applies 348d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 34935156bffSTrond Myklebust * @type: delegation type 35035156bffSTrond Myklebust * @stateid: delegation stateid 35135156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 352d3978bb3SChuck Lever * 353d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 3541da177e4SLinus Torvalds */ 355a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, 35635156bffSTrond Myklebust fmode_t type, 35735156bffSTrond Myklebust const nfs4_stateid *stateid, 35835156bffSTrond Myklebust unsigned long pagemod_limit) 3591da177e4SLinus Torvalds { 360d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 361d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 3621da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 36317d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 36457bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 3651da177e4SLinus Torvalds int status = 0; 3661da177e4SLinus Torvalds 3678535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 3681da177e4SLinus Torvalds if (delegation == NULL) 3691da177e4SLinus Torvalds return -ENOMEM; 37035156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 37135156bffSTrond Myklebust delegation->type = type; 37235156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 3731eb5d98fSJeff Layton delegation->change_attr = inode_peek_iversion_raw(inode); 374a52458b4SNeilBrown delegation->cred = get_cred(cred); 3751da177e4SLinus Torvalds delegation->inode = inode; 376b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 37734310430STrond Myklebust spin_lock_init(&delegation->lock); 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 38017d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 38117d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 38217d2c0a0SDavid Howells if (old_delegation != NULL) { 383cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 384cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 385cf6726e2STrond Myklebust &delegation->stateid)) { 386cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 387cf6726e2STrond Myklebust delegation); 38857bfa891STrond Myklebust goto out; 38957bfa891STrond Myklebust } 39057bfa891STrond Myklebust /* 39157bfa891STrond Myklebust * Deal with broken servers that hand out two 39257bfa891STrond Myklebust * delegations for the same file. 39317280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 39417280175STrond Myklebust * nothing else. 39557bfa891STrond Myklebust */ 39657bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 39757bfa891STrond Myklebust "a duplicate delegation!\n", 3983110ff80SHarvey Harrison __func__, clp->cl_hostname); 39917280175STrond Myklebust if (delegation->type == old_delegation->type || 40017280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 40157bfa891STrond Myklebust freeme = delegation; 40257bfa891STrond Myklebust delegation = NULL; 40357bfa891STrond Myklebust goto out; 40457bfa891STrond Myklebust } 405ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 406ade04647STrond Myklebust &old_delegation->flags)) 407ade04647STrond Myklebust goto out; 408d25be546STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, 409d25be546STrond Myklebust old_delegation, clp); 410d25be546STrond Myklebust if (freeme == NULL) 411d25be546STrond Myklebust goto out; 41257bfa891STrond Myklebust } 41338942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 4148383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 4151da177e4SLinus Torvalds delegation = NULL; 416412c77ceSTrond Myklebust 41735156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 418412c77ceSTrond Myklebust 41997c2c17aSTrond Myklebust spin_lock(&inode->i_lock); 42097c2c17aSTrond Myklebust if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 42197c2c17aSTrond Myklebust NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 42297c2c17aSTrond Myklebust spin_unlock(&inode->i_lock); 42357bfa891STrond Myklebust out: 4241da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 425603c83daSTrond Myklebust if (delegation != NULL) 426603c83daSTrond Myklebust nfs_free_delegation(delegation); 42757bfa891STrond Myklebust if (freeme != NULL) 42857bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 4291da177e4SLinus Torvalds return status; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds /* 4331da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 4341da177e4SLinus Torvalds */ 435d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 4361da177e4SLinus Torvalds { 437d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 4381da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 439869f9dfaSTrond Myklebust int err = 0; 4401da177e4SLinus Torvalds 441d25be546STrond Myklebust if (delegation == NULL) 442d25be546STrond Myklebust return 0; 443d25be546STrond Myklebust do { 444869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 445869f9dfaSTrond Myklebust break; 44624311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 44724311f88STrond Myklebust delegation->type); 448d25be546STrond Myklebust if (!issync || err != -EAGAIN) 449d25be546STrond Myklebust break; 450d25be546STrond Myklebust /* 451d25be546STrond Myklebust * Guard against state recovery 452d25be546STrond Myklebust */ 453d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 454d25be546STrond Myklebust } while (err == 0); 455d25be546STrond Myklebust 456d25be546STrond Myklebust if (err) { 457d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 458d25be546STrond Myklebust goto out; 459d25be546STrond Myklebust } 460d25be546STrond Myklebust if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode))) 461d18cc1fdSTrond Myklebust goto out; 4621da177e4SLinus Torvalds 463d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 464d18cc1fdSTrond Myklebust out: 465d18cc1fdSTrond Myklebust return err; 46690163027STrond Myklebust } 46790163027STrond Myklebust 468b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 469b757144fSTrond Myklebust { 470b757144fSTrond Myklebust bool ret = false; 471b757144fSTrond Myklebust 472ec3ca4e5STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 473ec3ca4e5STrond Myklebust goto out; 474b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 475b757144fSTrond Myklebust ret = true; 476b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { 477b757144fSTrond Myklebust struct inode *inode; 478b757144fSTrond Myklebust 479b757144fSTrond Myklebust spin_lock(&delegation->lock); 480b757144fSTrond Myklebust inode = delegation->inode; 481b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 482b757144fSTrond Myklebust ret = true; 483b757144fSTrond Myklebust spin_unlock(&delegation->lock); 484b757144fSTrond Myklebust } 485ec3ca4e5STrond Myklebust out: 486b757144fSTrond Myklebust return ret; 487b757144fSTrond Myklebust } 488b757144fSTrond Myklebust 489d3978bb3SChuck Lever /** 490d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 491d3978bb3SChuck Lever * @clp: nfs_client to process 492d3978bb3SChuck Lever * 493dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 494dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 495dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 496dc327ed4STrond Myklebust * 497d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 498515d8611STrond Myklebust */ 499d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 500515d8611STrond Myklebust { 501515d8611STrond Myklebust struct nfs_delegation *delegation; 502e04bbf6bSNeilBrown struct nfs_delegation *prev; 503d3978bb3SChuck Lever struct nfs_server *server; 504515d8611STrond Myklebust struct inode *inode; 505e04bbf6bSNeilBrown struct inode *place_holder = NULL; 506e04bbf6bSNeilBrown struct nfs_delegation *place_holder_deleg = NULL; 507d18cc1fdSTrond Myklebust int err = 0; 508515d8611STrond Myklebust 509515d8611STrond Myklebust restart: 510e04bbf6bSNeilBrown /* 511e04bbf6bSNeilBrown * To avoid quadratic looping we hold a reference 512e04bbf6bSNeilBrown * to an inode place_holder. Each time we restart, we 513e04bbf6bSNeilBrown * list nfs_servers from the server of that inode, and 514e04bbf6bSNeilBrown * delegation in the server from the delegations of that 515e04bbf6bSNeilBrown * inode. 516e04bbf6bSNeilBrown * prev is an RCU-protected pointer to a delegation which 517e04bbf6bSNeilBrown * wasn't marked for return and might be a good choice for 518e04bbf6bSNeilBrown * the next place_holder. 519e04bbf6bSNeilBrown */ 520515d8611STrond Myklebust rcu_read_lock(); 521e04bbf6bSNeilBrown prev = NULL; 522e04bbf6bSNeilBrown if (place_holder) 523e04bbf6bSNeilBrown server = NFS_SERVER(place_holder); 524e04bbf6bSNeilBrown else 525e04bbf6bSNeilBrown server = list_entry_rcu(clp->cl_superblocks.next, 526e04bbf6bSNeilBrown struct nfs_server, client_link); 527e04bbf6bSNeilBrown list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { 528e04bbf6bSNeilBrown delegation = NULL; 529e04bbf6bSNeilBrown if (place_holder && server == NFS_SERVER(place_holder)) 530e04bbf6bSNeilBrown delegation = rcu_dereference(NFS_I(place_holder)->delegation); 531e04bbf6bSNeilBrown if (!delegation || delegation != place_holder_deleg) 532e04bbf6bSNeilBrown delegation = list_entry_rcu(server->delegations.next, 533e04bbf6bSNeilBrown struct nfs_delegation, super_list); 534e04bbf6bSNeilBrown list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 535e04bbf6bSNeilBrown struct inode *to_put = NULL; 536e04bbf6bSNeilBrown 537e04bbf6bSNeilBrown if (!nfs_delegation_need_return(delegation)) { 538e04bbf6bSNeilBrown prev = delegation; 539515d8611STrond Myklebust continue; 540e04bbf6bSNeilBrown } 5419f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 542f3893491SNeilBrown break; /* continue in outer loop */ 543e04bbf6bSNeilBrown 544e04bbf6bSNeilBrown if (prev) { 545e04bbf6bSNeilBrown struct inode *tmp; 546e04bbf6bSNeilBrown 547e04bbf6bSNeilBrown tmp = nfs_delegation_grab_inode(prev); 548e04bbf6bSNeilBrown if (tmp) { 549e04bbf6bSNeilBrown to_put = place_holder; 550e04bbf6bSNeilBrown place_holder = tmp; 551e04bbf6bSNeilBrown place_holder_deleg = prev; 552e04bbf6bSNeilBrown } 553e04bbf6bSNeilBrown } 554e04bbf6bSNeilBrown 5559f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 5569f0f8e12STrond Myklebust if (inode == NULL) { 5579f0f8e12STrond Myklebust rcu_read_unlock(); 558e04bbf6bSNeilBrown if (to_put) 559e04bbf6bSNeilBrown iput(to_put); 5609f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5619f0f8e12STrond Myklebust goto restart; 5629f0f8e12STrond Myklebust } 563d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 564515d8611STrond Myklebust rcu_read_unlock(); 565d3978bb3SChuck Lever 566e04bbf6bSNeilBrown if (to_put) 567e04bbf6bSNeilBrown iput(to_put); 568e04bbf6bSNeilBrown 569d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 570515d8611STrond Myklebust iput(inode); 5719f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5723ca951b6SNeilBrown cond_resched(); 573d18cc1fdSTrond Myklebust if (!err) 574515d8611STrond Myklebust goto restart; 575d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 576e04bbf6bSNeilBrown if (place_holder) 577e04bbf6bSNeilBrown iput(place_holder); 578d18cc1fdSTrond Myklebust return err; 579515d8611STrond Myklebust } 580d3978bb3SChuck Lever } 581515d8611STrond Myklebust rcu_read_unlock(); 582e04bbf6bSNeilBrown if (place_holder) 583e04bbf6bSNeilBrown iput(place_holder); 584d18cc1fdSTrond Myklebust return 0; 585515d8611STrond Myklebust } 586515d8611STrond Myklebust 587d3978bb3SChuck Lever /** 588d3978bb3SChuck Lever * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens 589d3978bb3SChuck Lever * @inode: inode to process 590d3978bb3SChuck Lever * 591d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 592d3978bb3SChuck Lever * to be called from nfs4_clear_inode(). 593e6f81075STrond Myklebust */ 594e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 595e6f81075STrond Myklebust { 596e6f81075STrond Myklebust struct nfs_delegation *delegation; 597e6f81075STrond Myklebust 598d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 599e6f81075STrond Myklebust if (delegation != NULL) 6005fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 601e6f81075STrond Myklebust } 602e6f81075STrond Myklebust 603d3978bb3SChuck Lever /** 604d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 605d3978bb3SChuck Lever * @inode: inode to process 606d3978bb3SChuck Lever * 607c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 608c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 609c57d1bc5STrond Myklebust * we should stop caching. 610c57d1bc5STrond Myklebust * 611d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 612d3978bb3SChuck Lever */ 61357ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 61490163027STrond Myklebust { 61590163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 61690163027STrond Myklebust struct nfs_delegation *delegation; 61790163027STrond Myklebust int err = 0; 61890163027STrond Myklebust 619c57d1bc5STrond Myklebust nfs_wb_all(inode); 620d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 621d25be546STrond Myklebust if (delegation != NULL) 622d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 62390163027STrond Myklebust return err; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 626c01d3645STrond Myklebust /** 627c01d3645STrond Myklebust * nfs4_inode_make_writeable 628c01d3645STrond Myklebust * @inode: pointer to inode 629c01d3645STrond Myklebust * 630c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 631c01d3645STrond Myklebust * 632c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 633c01d3645STrond Myklebust */ 634c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 635c01d3645STrond Myklebust { 636c01d3645STrond Myklebust if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || 637c01d3645STrond Myklebust !nfs4_check_delegation(inode, FMODE_WRITE)) 638c01d3645STrond Myklebust return nfs4_inode_return_delegation(inode); 639c01d3645STrond Myklebust return 0; 640c01d3645STrond Myklebust } 641c01d3645STrond Myklebust 642b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 643b757144fSTrond Myklebust struct nfs_delegation *delegation) 644b757144fSTrond Myklebust { 645b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 646b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 647b757144fSTrond Myklebust } 648b757144fSTrond Myklebust 649ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 650ed1e6211STrond Myklebust struct nfs_delegation *delegation) 6516411bd4aSTrond Myklebust { 6526411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 653ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 6546411bd4aSTrond Myklebust } 6556411bd4aSTrond Myklebust 6565c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 6575c31e236STrond Myklebust { 6585c31e236STrond Myklebust struct nfs_delegation *delegation; 6595c31e236STrond Myklebust bool ret = false; 6605c31e236STrond Myklebust 6615c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 6625c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 6635c31e236STrond Myklebust ret = true; 6645c31e236STrond Myklebust } 6655c31e236STrond Myklebust return ret; 6665c31e236STrond Myklebust } 6675c31e236STrond Myklebust 668b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 669b02ba0b6STrond Myklebust { 670b02ba0b6STrond Myklebust struct nfs_server *server; 671b02ba0b6STrond Myklebust 672b02ba0b6STrond Myklebust rcu_read_lock(); 673b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 674b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 675b02ba0b6STrond Myklebust rcu_read_unlock(); 676b02ba0b6STrond Myklebust } 677b02ba0b6STrond Myklebust 678b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 679b02ba0b6STrond Myklebust { 680b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 681b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 682b02ba0b6STrond Myklebust } 683b02ba0b6STrond Myklebust 684b02ba0b6STrond Myklebust /** 685b02ba0b6STrond Myklebust * nfs_expire_all_delegations 686b02ba0b6STrond Myklebust * @clp: client to process 687b02ba0b6STrond Myklebust * 688b02ba0b6STrond Myklebust */ 689b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 690b02ba0b6STrond Myklebust { 691b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 692b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 693b02ba0b6STrond Myklebust } 694b02ba0b6STrond Myklebust 695d3978bb3SChuck Lever /** 696d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 697302fad7bSTrond Myklebust * @server: pointer to nfs_server to process 698d3978bb3SChuck Lever * 6991da177e4SLinus Torvalds */ 700eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 7011da177e4SLinus Torvalds { 702d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 7035c31e236STrond Myklebust bool need_wait; 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds if (clp == NULL) 7061da177e4SLinus Torvalds return; 707d3978bb3SChuck Lever 7088383e460STrond Myklebust rcu_read_lock(); 7095c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 7108383e460STrond Myklebust rcu_read_unlock(); 711d3978bb3SChuck Lever 7125c31e236STrond Myklebust if (need_wait) { 713d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 7145c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 7155c31e236STrond Myklebust } 716515d8611STrond Myklebust } 717515d8611STrond Myklebust 718826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 719d3978bb3SChuck Lever fmode_t flags) 720515d8611STrond Myklebust { 721515d8611STrond Myklebust struct nfs_delegation *delegation; 722515d8611STrond Myklebust 723d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 724c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 725c79571a5SAlexandros Batsakis continue; 726c79571a5SAlexandros Batsakis if (delegation->type & flags) 727826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 728707fb4b3STrond Myklebust } 729d3978bb3SChuck Lever } 730d3978bb3SChuck Lever 731826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 732d3978bb3SChuck Lever fmode_t flags) 733d3978bb3SChuck Lever { 734d3978bb3SChuck Lever struct nfs_server *server; 735d3978bb3SChuck Lever 736d3978bb3SChuck Lever rcu_read_lock(); 737d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 738826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 739515d8611STrond Myklebust rcu_read_unlock(); 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds 74241020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server, 74341020b67STrond Myklebust struct nfs_delegation *delegation) 74441020b67STrond Myklebust { 74541020b67STrond Myklebust set_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 746059b43e9STrond Myklebust delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 74741020b67STrond Myklebust nfs_mark_return_delegation(server, delegation); 74841020b67STrond Myklebust } 74941020b67STrond Myklebust 75041020b67STrond Myklebust static bool nfs_revoke_delegation(struct inode *inode, 75141020b67STrond Myklebust const nfs4_stateid *stateid) 752869f9dfaSTrond Myklebust { 753869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 7547f048831STrond Myklebust nfs4_stateid tmp; 75541020b67STrond Myklebust bool ret = false; 75641020b67STrond Myklebust 757869f9dfaSTrond Myklebust rcu_read_lock(); 758869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 75941020b67STrond Myklebust if (delegation == NULL) 76041020b67STrond Myklebust goto out; 7617f048831STrond Myklebust if (stateid == NULL) { 7627f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 7637f048831STrond Myklebust stateid = &tmp; 7647f048831STrond Myklebust } else if (!nfs4_stateid_match(stateid, &delegation->stateid)) 76541020b67STrond Myklebust goto out; 76641020b67STrond Myklebust nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation); 76741020b67STrond Myklebust ret = true; 76841020b67STrond Myklebust out: 769869f9dfaSTrond Myklebust rcu_read_unlock(); 7707f048831STrond Myklebust if (ret) 7717f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 77241020b67STrond Myklebust return ret; 773869f9dfaSTrond Myklebust } 774869f9dfaSTrond Myklebust 77541020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 77641020b67STrond Myklebust const nfs4_stateid *stateid) 777a1d0b5eeSTrond Myklebust { 778a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 779a1d0b5eeSTrond Myklebust 78041020b67STrond Myklebust if (!nfs_revoke_delegation(inode, stateid)) 78141020b67STrond Myklebust return; 782d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 7837f048831STrond Myklebust if (delegation) 784a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 785a1d0b5eeSTrond Myklebust } 7869cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 787a1d0b5eeSTrond Myklebust 788d3978bb3SChuck Lever /** 789826e0013STrond Myklebust * nfs_expire_unused_delegation_types 790d3978bb3SChuck Lever * @clp: client to process 791d3978bb3SChuck Lever * @flags: delegation types to expire 792d3978bb3SChuck Lever * 793d3978bb3SChuck Lever */ 794826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 795c79571a5SAlexandros Batsakis { 796826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 797c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 798c79571a5SAlexandros Batsakis } 799c79571a5SAlexandros Batsakis 800d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 801b7391f44STrond Myklebust { 802b7391f44STrond Myklebust struct nfs_delegation *delegation; 803b7391f44STrond Myklebust 804d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 805b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 806b7391f44STrond Myklebust continue; 807b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 808b7391f44STrond Myklebust } 809b7391f44STrond Myklebust } 810b7391f44STrond Myklebust 811d3978bb3SChuck Lever /** 812d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 813d3978bb3SChuck Lever * @clp: nfs_client to process 814d3978bb3SChuck Lever * 815d3978bb3SChuck Lever */ 816b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 817b7391f44STrond Myklebust { 818d3978bb3SChuck Lever struct nfs_server *server; 819d3978bb3SChuck Lever 820d3978bb3SChuck Lever rcu_read_lock(); 821d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 822d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 823d3978bb3SChuck Lever rcu_read_unlock(); 824d3978bb3SChuck Lever 825b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 826b7391f44STrond Myklebust } 827b7391f44STrond Myklebust 828d3978bb3SChuck Lever /** 829d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 830d3978bb3SChuck Lever * @inode: inode to process 8318e663f0eSTrond Myklebust * @stateid: state ID information 832d3978bb3SChuck Lever * 833d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 8341da177e4SLinus Torvalds */ 835d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 836d3978bb3SChuck Lever const nfs4_stateid *stateid) 8371da177e4SLinus Torvalds { 838ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 839ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 8406411bd4aSTrond Myklebust struct nfs_delegation *delegation; 8411da177e4SLinus Torvalds 8426411bd4aSTrond Myklebust rcu_read_lock(); 843457a5042STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 844755a48a7STrond Myklebust if (delegation == NULL) 845755a48a7STrond Myklebust goto out_enoent; 8464816fdadSTrond Myklebust if (stateid != NULL && 8474816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 848755a48a7STrond Myklebust goto out_enoent; 849ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 8506411bd4aSTrond Myklebust rcu_read_unlock(); 851d3978bb3SChuck Lever 8526411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 8536411bd4aSTrond Myklebust return 0; 854755a48a7STrond Myklebust out_enoent: 855755a48a7STrond Myklebust rcu_read_unlock(); 856755a48a7STrond Myklebust return -ENOENT; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds 859d3978bb3SChuck Lever static struct inode * 860d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 861d3978bb3SChuck Lever const struct nfs_fh *fhandle) 8621da177e4SLinus Torvalds { 8631da177e4SLinus Torvalds struct nfs_delegation *delegation; 864e39d8a18STrond Myklebust struct inode *freeme, *res = NULL; 865d3978bb3SChuck Lever 866d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 86786e89489STrond Myklebust spin_lock(&delegation->lock); 86886e89489STrond Myklebust if (delegation->inode != NULL && 869457a5042STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 87086e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 871e39d8a18STrond Myklebust freeme = igrab(delegation->inode); 872e39d8a18STrond Myklebust if (freeme && nfs_sb_active(freeme->i_sb)) 873e39d8a18STrond Myklebust res = freeme; 87486e89489STrond Myklebust spin_unlock(&delegation->lock); 87586e89489STrond Myklebust if (res != NULL) 876d3978bb3SChuck Lever return res; 877e39d8a18STrond Myklebust if (freeme) { 878e39d8a18STrond Myklebust rcu_read_unlock(); 879e39d8a18STrond Myklebust iput(freeme); 880e39d8a18STrond Myklebust rcu_read_lock(); 881e39d8a18STrond Myklebust } 8826c342655STrond Myklebust return ERR_PTR(-EAGAIN); 8836c342655STrond Myklebust } 8846c342655STrond Myklebust spin_unlock(&delegation->lock); 8856c342655STrond Myklebust } 8866c342655STrond Myklebust return ERR_PTR(-ENOENT); 887d3978bb3SChuck Lever } 888d3978bb3SChuck Lever 889d3978bb3SChuck Lever /** 890d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 891d3978bb3SChuck Lever * @clp: client state handle 892d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 893d3978bb3SChuck Lever * 894d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 895d3978bb3SChuck Lever * cannot be found. 896d3978bb3SChuck Lever */ 897d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 898d3978bb3SChuck Lever const struct nfs_fh *fhandle) 899d3978bb3SChuck Lever { 900d3978bb3SChuck Lever struct nfs_server *server; 9016c342655STrond Myklebust struct inode *res; 902d3978bb3SChuck Lever 903d3978bb3SChuck Lever rcu_read_lock(); 904d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 905d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 906d5681f59SAnna Schumaker if (res != ERR_PTR(-ENOENT)) { 907d5681f59SAnna Schumaker rcu_read_unlock(); 9086c342655STrond Myklebust return res; 909d3978bb3SChuck Lever } 910d5681f59SAnna Schumaker } 9118383e460STrond Myklebust rcu_read_unlock(); 9126c342655STrond Myklebust return ERR_PTR(-ENOENT); 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds 915d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 916d3978bb3SChuck Lever { 917d3978bb3SChuck Lever struct nfs_delegation *delegation; 918d3978bb3SChuck Lever 91945870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 92045870d69STrond Myklebust /* 92145870d69STrond Myklebust * If the delegation may have been admin revoked, then we 92245870d69STrond Myklebust * cannot reclaim it. 92345870d69STrond Myklebust */ 92445870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 92545870d69STrond Myklebust continue; 926d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 927d3978bb3SChuck Lever } 92845870d69STrond Myklebust } 929d3978bb3SChuck Lever 930d3978bb3SChuck Lever /** 931d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 932d3978bb3SChuck Lever * @clp: nfs_client to process 933d3978bb3SChuck Lever * 9341da177e4SLinus Torvalds */ 935adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 9361da177e4SLinus Torvalds { 937d3978bb3SChuck Lever struct nfs_server *server; 938d3978bb3SChuck Lever 9398383e460STrond Myklebust rcu_read_lock(); 940d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 941d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 9428383e460STrond Myklebust rcu_read_unlock(); 9431da177e4SLinus Torvalds } 9441da177e4SLinus Torvalds 945d3978bb3SChuck Lever /** 946d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 947d3978bb3SChuck Lever * @clp: nfs_client to process 948d3978bb3SChuck Lever * 9491da177e4SLinus Torvalds */ 950adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 9511da177e4SLinus Torvalds { 9528383e460STrond Myklebust struct nfs_delegation *delegation; 953d3978bb3SChuck Lever struct nfs_server *server; 95486e89489STrond Myklebust struct inode *inode; 955d3978bb3SChuck Lever 9568383e460STrond Myklebust restart: 9578383e460STrond Myklebust rcu_read_lock(); 958d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 959d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 960d3978bb3SChuck Lever super_list) { 9616f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 9626f9449beSTrond Myklebust &delegation->flags) || 9636f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 9646f9449beSTrond Myklebust &delegation->flags) || 9656f9449beSTrond Myklebust test_bit(NFS_DELEGATION_NEED_RECLAIM, 966d3978bb3SChuck Lever &delegation->flags) == 0) 9671da177e4SLinus Torvalds continue; 9689f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 969f3893491SNeilBrown break; /* continue in outer loop */ 9709f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 9719f0f8e12STrond Myklebust if (inode == NULL) { 9729f0f8e12STrond Myklebust rcu_read_unlock(); 9739f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 9749f0f8e12STrond Myklebust goto restart; 9759f0f8e12STrond Myklebust } 976b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 977b04b22f4STrond Myklebust rcu_read_unlock(); 978b04b22f4STrond Myklebust if (delegation != NULL) { 979d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 980d25be546STrond Myklebust delegation, server); 9818383e460STrond Myklebust if (delegation != NULL) 982905f8d16STrond Myklebust nfs_free_delegation(delegation); 983b04b22f4STrond Myklebust } 98486e89489STrond Myklebust iput(inode); 9859f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 9863ca951b6SNeilBrown cond_resched(); 9878383e460STrond Myklebust goto restart; 9881da177e4SLinus Torvalds } 989d3978bb3SChuck Lever } 9908383e460STrond Myklebust rcu_read_unlock(); 9911da177e4SLinus Torvalds } 9923e4f6290STrond Myklebust 993bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 994bb3d1a3bSTrond Myklebust { 995bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 996bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 997bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 998bb3d1a3bSTrond Myklebust } 999bb3d1a3bSTrond Myklebust 100045870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 100145870d69STrond Myklebust struct nfs_delegation *delegation) 100245870d69STrond Myklebust { 1003059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 1004059b43e9STrond Myklebust return; 100545870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 100645870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 100745870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 100845870d69STrond Myklebust } 100945870d69STrond Myklebust 1010bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 1011bb3d1a3bSTrond Myklebust struct inode *inode) 1012bb3d1a3bSTrond Myklebust { 1013bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 1014bb3d1a3bSTrond Myklebust 1015bb3d1a3bSTrond Myklebust rcu_read_lock(); 1016bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1017bb3d1a3bSTrond Myklebust if (delegation) 1018bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 1019bb3d1a3bSTrond Myklebust rcu_read_unlock(); 1020bb3d1a3bSTrond Myklebust 1021bb3d1a3bSTrond Myklebust } 1022bb3d1a3bSTrond Myklebust 102345870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 102445870d69STrond Myklebust { 102545870d69STrond Myklebust struct nfs_delegation *delegation; 102645870d69STrond Myklebust 102745870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 102845870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 102945870d69STrond Myklebust } 103045870d69STrond Myklebust 103145870d69STrond Myklebust /** 103245870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 103345870d69STrond Myklebust * @clp: nfs_client to process 103445870d69STrond Myklebust * 103545870d69STrond Myklebust * Iterates through all the delegations associated with this server and 103645870d69STrond Myklebust * marks them as needing to be checked for validity. 103745870d69STrond Myklebust */ 103845870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 103945870d69STrond Myklebust { 104045870d69STrond Myklebust struct nfs_server *server; 104145870d69STrond Myklebust 104245870d69STrond Myklebust rcu_read_lock(); 104345870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 104445870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 104545870d69STrond Myklebust rcu_read_unlock(); 104645870d69STrond Myklebust } 104745870d69STrond Myklebust 104845870d69STrond Myklebust /** 10498ca017c8SScott Mayhew * nfs_test_expired_all_delegations - test all delegations for a client 10508ca017c8SScott Mayhew * @clp: nfs_client to process 10518ca017c8SScott Mayhew * 10528ca017c8SScott Mayhew * Helper for handling "recallable state revoked" status from server. 10538ca017c8SScott Mayhew */ 10548ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp) 10558ca017c8SScott Mayhew { 10568ca017c8SScott Mayhew nfs_mark_test_expired_all_delegations(clp); 10578ca017c8SScott Mayhew nfs4_schedule_state_manager(clp); 10588ca017c8SScott Mayhew } 10598ca017c8SScott Mayhew 1060ad114089STrond Myklebust static void 1061ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode, 1062ad114089STrond Myklebust nfs4_stateid *stateid, 1063ad114089STrond Myklebust const struct cred *cred) 1064ad114089STrond Myklebust { 1065ad114089STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1066ad114089STrond Myklebust const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 1067ad114089STrond Myklebust int status; 1068ad114089STrond Myklebust 1069ad114089STrond Myklebust if (!cred) 1070ad114089STrond Myklebust return; 1071ad114089STrond Myklebust status = ops->test_and_free_expired(server, stateid, cred); 1072ad114089STrond Myklebust if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 1073ad114089STrond Myklebust nfs_remove_bad_delegation(inode, stateid); 1074ad114089STrond Myklebust } 1075ad114089STrond Myklebust 10768ca017c8SScott Mayhew /** 107745870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 107845870d69STrond Myklebust * @clp: nfs_client to process 107945870d69STrond Myklebust * 108045870d69STrond Myklebust * Iterates through all the delegations associated with this server and 108145870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 108245870d69STrond Myklebust * expected to be called in cases where the server may have lost its 108345870d69STrond Myklebust * lease. 108445870d69STrond Myklebust */ 108545870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 108645870d69STrond Myklebust { 108745870d69STrond Myklebust struct nfs_delegation *delegation; 108845870d69STrond Myklebust struct nfs_server *server; 108945870d69STrond Myklebust struct inode *inode; 1090a52458b4SNeilBrown const struct cred *cred; 109145870d69STrond Myklebust nfs4_stateid stateid; 109245870d69STrond Myklebust 109345870d69STrond Myklebust restart: 109445870d69STrond Myklebust rcu_read_lock(); 109545870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 109645870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, 109745870d69STrond Myklebust super_list) { 10986f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 10996f9449beSTrond Myklebust &delegation->flags) || 11006f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 11016f9449beSTrond Myklebust &delegation->flags) || 11026f9449beSTrond Myklebust test_bit(NFS_DELEGATION_TEST_EXPIRED, 110345870d69STrond Myklebust &delegation->flags) == 0) 110445870d69STrond Myklebust continue; 110545870d69STrond Myklebust if (!nfs_sb_active(server->super)) 1106f3893491SNeilBrown break; /* continue in outer loop */ 110745870d69STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 110845870d69STrond Myklebust if (inode == NULL) { 110945870d69STrond Myklebust rcu_read_unlock(); 111045870d69STrond Myklebust nfs_sb_deactive(server->super); 111145870d69STrond Myklebust goto restart; 111245870d69STrond Myklebust } 1113a52458b4SNeilBrown cred = get_cred_rcu(delegation->cred); 111445870d69STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 111545870d69STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 111645870d69STrond Myklebust rcu_read_unlock(); 1117ad114089STrond Myklebust nfs_delegation_test_free_expired(inode, &stateid, cred); 1118a52458b4SNeilBrown put_cred(cred); 1119bb3d1a3bSTrond Myklebust if (nfs4_server_rebooted(clp)) { 1120bb3d1a3bSTrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 1121bb3d1a3bSTrond Myklebust iput(inode); 1122bb3d1a3bSTrond Myklebust nfs_sb_deactive(server->super); 1123bb3d1a3bSTrond Myklebust return; 1124bb3d1a3bSTrond Myklebust } 112545870d69STrond Myklebust iput(inode); 112645870d69STrond Myklebust nfs_sb_deactive(server->super); 11273ca951b6SNeilBrown cond_resched(); 112845870d69STrond Myklebust goto restart; 112945870d69STrond Myklebust } 113045870d69STrond Myklebust } 113145870d69STrond Myklebust rcu_read_unlock(); 113245870d69STrond Myklebust } 113345870d69STrond Myklebust 11346c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 11356c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 11366c2d8f8dSTrond Myklebust { 11376c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 11386c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 11396c2d8f8dSTrond Myklebust bool found = false; 11406c2d8f8dSTrond Myklebust 11416c2d8f8dSTrond Myklebust rcu_read_lock(); 11426c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 11436c2d8f8dSTrond Myklebust if (delegation && 11446c2d8f8dSTrond Myklebust nfs4_stateid_match_other(&delegation->stateid, stateid)) { 11456c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 11466c2d8f8dSTrond Myklebust found = true; 11476c2d8f8dSTrond Myklebust } 11486c2d8f8dSTrond Myklebust rcu_read_unlock(); 11496c2d8f8dSTrond Myklebust if (found) 11506c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 11516c2d8f8dSTrond Myklebust } 11526c2d8f8dSTrond Myklebust 1153d3978bb3SChuck Lever /** 1154d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1155d3978bb3SChuck Lever * @clp: client state handle 1156d3978bb3SChuck Lever * 1157d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1158d3978bb3SChuck Lever * to this nfs_client. 1159d3978bb3SChuck Lever */ 1160d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1161d3978bb3SChuck Lever { 1162d3978bb3SChuck Lever struct nfs_server *server; 1163d3978bb3SChuck Lever int ret = 0; 1164d3978bb3SChuck Lever 1165d3978bb3SChuck Lever rcu_read_lock(); 1166d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1167d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1168d3978bb3SChuck Lever ret = 1; 1169d3978bb3SChuck Lever break; 1170d3978bb3SChuck Lever } 1171d3978bb3SChuck Lever rcu_read_unlock(); 1172d3978bb3SChuck Lever return ret; 1173d3978bb3SChuck Lever } 1174d3978bb3SChuck Lever 1175d3978bb3SChuck Lever /** 117612f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 117712f275cdSTrond Myklebust * @dst: stateid to refresh 117812f275cdSTrond Myklebust * @inode: inode to check 117912f275cdSTrond Myklebust * 118012f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 118112f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 118212f275cdSTrond Myklebust */ 118312f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 118412f275cdSTrond Myklebust { 118512f275cdSTrond Myklebust struct nfs_delegation *delegation; 118612f275cdSTrond Myklebust bool ret = false; 118712f275cdSTrond Myklebust if (!inode) 118812f275cdSTrond Myklebust goto out; 118912f275cdSTrond Myklebust 119012f275cdSTrond Myklebust rcu_read_lock(); 119112f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 119212f275cdSTrond Myklebust if (delegation != NULL && 119312f275cdSTrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid)) { 119412f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 119579cc5542STrond Myklebust ret = true; 119612f275cdSTrond Myklebust } 119712f275cdSTrond Myklebust rcu_read_unlock(); 119812f275cdSTrond Myklebust out: 119912f275cdSTrond Myklebust return ret; 120012f275cdSTrond Myklebust } 120112f275cdSTrond Myklebust 120212f275cdSTrond Myklebust /** 1203d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1204d3978bb3SChuck Lever * @inode: inode to check 12050032a7a7STrond Myklebust * @flags: delegation type requirement 1206abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1207abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1208d3978bb3SChuck Lever * 12090032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 12100032a7a7STrond Myklebust * otherwise "false" is returned. 1211d3978bb3SChuck Lever */ 1212abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1213a52458b4SNeilBrown nfs4_stateid *dst, const struct cred **cred) 12143e4f6290STrond Myklebust { 12153e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 12163e4f6290STrond Myklebust struct nfs_delegation *delegation; 12170032a7a7STrond Myklebust bool ret; 12183e4f6290STrond Myklebust 12190032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 12208383e460STrond Myklebust rcu_read_lock(); 12218383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1222aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 12230032a7a7STrond Myklebust if (ret) { 1224f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 12250032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1226abf4e13cSTrond Myklebust if (cred) 1227a52458b4SNeilBrown *cred = get_cred(delegation->cred); 12283e4f6290STrond Myklebust } 12298383e460STrond Myklebust rcu_read_unlock(); 12308383e460STrond Myklebust return ret; 12313e4f6290STrond Myklebust } 12325445b1fbSTrond Myklebust 12335445b1fbSTrond Myklebust /** 12345445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 12355445b1fbSTrond Myklebust * @inode: inode to check 12365445b1fbSTrond Myklebust * 12375445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 12385445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 12395445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 12405445b1fbSTrond Myklebust */ 12415445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 12425445b1fbSTrond Myklebust { 12435445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 12445445b1fbSTrond Myklebust struct nfs_delegation *delegation; 12455445b1fbSTrond Myklebust bool ret = true; 12465445b1fbSTrond Myklebust 12475445b1fbSTrond Myklebust rcu_read_lock(); 12485445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 12495445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 12505445b1fbSTrond Myklebust goto out; 1251a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 12525445b1fbSTrond Myklebust ret = false; 12535445b1fbSTrond Myklebust out: 12545445b1fbSTrond Myklebust rcu_read_unlock(); 12555445b1fbSTrond Myklebust return ret; 12565445b1fbSTrond Myklebust } 1257