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); 301f9e0cc9cSTrond Myklebust if (!delegation->inode) { 302f9e0cc9cSTrond Myklebust spin_unlock(&delegation->lock); 303f9e0cc9cSTrond Myklebust return NULL; 304f9e0cc9cSTrond Myklebust } 305d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 30657bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 30786e89489STrond Myklebust delegation->inode = NULL; 30857bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 30934310430STrond Myklebust spin_unlock(&delegation->lock); 31057bfa891STrond Myklebust return delegation; 31157bfa891STrond Myklebust } 31257bfa891STrond Myklebust 313dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 314d25be546STrond Myklebust struct nfs_delegation *delegation, 315d3978bb3SChuck Lever struct nfs_server *server) 316dda4b225SChuck Lever { 317d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 318dda4b225SChuck Lever 319dda4b225SChuck Lever spin_lock(&clp->cl_lock); 320d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 321dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 322dda4b225SChuck Lever return delegation; 323dda4b225SChuck Lever } 324dda4b225SChuck Lever 325d25be546STrond Myklebust static struct nfs_delegation * 326d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 327d25be546STrond Myklebust { 328d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 329d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 330d25be546STrond Myklebust struct nfs_delegation *delegation; 331d25be546STrond Myklebust 332d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 333d25be546STrond Myklebust if (delegation == NULL) 334d25be546STrond Myklebust return NULL; 335d25be546STrond Myklebust return nfs_detach_delegation(nfsi, delegation, server); 336d25be546STrond Myklebust } 337d25be546STrond Myklebust 338cf6726e2STrond Myklebust static void 339cf6726e2STrond Myklebust nfs_update_inplace_delegation(struct nfs_delegation *delegation, 340cf6726e2STrond Myklebust const struct nfs_delegation *update) 341cf6726e2STrond Myklebust { 342cf6726e2STrond Myklebust if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 343cf6726e2STrond Myklebust delegation->stateid.seqid = update->stateid.seqid; 344cf6726e2STrond Myklebust smp_wmb(); 345cf6726e2STrond Myklebust delegation->type = update->type; 346ae084a32STrond Myklebust clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 347cf6726e2STrond Myklebust } 348cf6726e2STrond Myklebust } 349cf6726e2STrond Myklebust 350d3978bb3SChuck Lever /** 351d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 352d3978bb3SChuck Lever * @inode: inode to which delegation applies 353d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 35435156bffSTrond Myklebust * @type: delegation type 35535156bffSTrond Myklebust * @stateid: delegation stateid 35635156bffSTrond Myklebust * @pagemod_limit: write delegation "space_limit" 357d3978bb3SChuck Lever * 358d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 3591da177e4SLinus Torvalds */ 360a52458b4SNeilBrown int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, 36135156bffSTrond Myklebust fmode_t type, 36235156bffSTrond Myklebust const nfs4_stateid *stateid, 36335156bffSTrond Myklebust unsigned long pagemod_limit) 3641da177e4SLinus Torvalds { 365d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 366d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 3671da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 36817d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 36957bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 3701da177e4SLinus Torvalds int status = 0; 3711da177e4SLinus Torvalds 3728535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 3731da177e4SLinus Torvalds if (delegation == NULL) 3741da177e4SLinus Torvalds return -ENOMEM; 37535156bffSTrond Myklebust nfs4_stateid_copy(&delegation->stateid, stateid); 37635156bffSTrond Myklebust delegation->type = type; 37735156bffSTrond Myklebust delegation->pagemod_limit = pagemod_limit; 3781eb5d98fSJeff Layton delegation->change_attr = inode_peek_iversion_raw(inode); 379a52458b4SNeilBrown delegation->cred = get_cred(cred); 3801da177e4SLinus Torvalds delegation->inode = inode; 381b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 38234310430STrond Myklebust spin_lock_init(&delegation->lock); 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 38517d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 38617d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 38717d2c0a0SDavid Howells if (old_delegation != NULL) { 388cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 389cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 390cf6726e2STrond Myklebust &delegation->stateid)) { 391e0f07896STrond Myklebust spin_lock(&old_delegation->lock); 392cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 393cf6726e2STrond Myklebust delegation); 394e0f07896STrond Myklebust spin_unlock(&old_delegation->lock); 39557bfa891STrond Myklebust goto out; 39657bfa891STrond Myklebust } 39757bfa891STrond Myklebust /* 39857bfa891STrond Myklebust * Deal with broken servers that hand out two 39957bfa891STrond Myklebust * delegations for the same file. 40017280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 40117280175STrond Myklebust * nothing else. 40257bfa891STrond Myklebust */ 40357bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 40457bfa891STrond Myklebust "a duplicate delegation!\n", 4053110ff80SHarvey Harrison __func__, clp->cl_hostname); 40617280175STrond Myklebust if (delegation->type == old_delegation->type || 40717280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 40857bfa891STrond Myklebust freeme = delegation; 40957bfa891STrond Myklebust delegation = NULL; 41057bfa891STrond Myklebust goto out; 41157bfa891STrond Myklebust } 412ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 413ade04647STrond Myklebust &old_delegation->flags)) 414ade04647STrond Myklebust goto out; 415d25be546STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, 416d25be546STrond Myklebust old_delegation, clp); 417d25be546STrond Myklebust if (freeme == NULL) 418d25be546STrond Myklebust goto out; 41957bfa891STrond Myklebust } 42038942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 4218383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 4221da177e4SLinus Torvalds delegation = NULL; 423412c77ceSTrond Myklebust 42435156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 425412c77ceSTrond Myklebust 42697c2c17aSTrond Myklebust spin_lock(&inode->i_lock); 42797c2c17aSTrond Myklebust if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 42897c2c17aSTrond Myklebust NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 42997c2c17aSTrond Myklebust spin_unlock(&inode->i_lock); 43057bfa891STrond Myklebust out: 4311da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 432603c83daSTrond Myklebust if (delegation != NULL) 433603c83daSTrond Myklebust nfs_free_delegation(delegation); 43457bfa891STrond Myklebust if (freeme != NULL) 43557bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 4361da177e4SLinus Torvalds return status; 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds /* 4401da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 4411da177e4SLinus Torvalds */ 442d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 4431da177e4SLinus Torvalds { 444d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 4451da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 446869f9dfaSTrond Myklebust int err = 0; 4471da177e4SLinus Torvalds 448d25be546STrond Myklebust if (delegation == NULL) 449d25be546STrond Myklebust return 0; 450d25be546STrond Myklebust do { 451869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 452869f9dfaSTrond Myklebust break; 45324311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 45424311f88STrond Myklebust delegation->type); 455d25be546STrond Myklebust if (!issync || err != -EAGAIN) 456d25be546STrond Myklebust break; 457d25be546STrond Myklebust /* 458d25be546STrond Myklebust * Guard against state recovery 459d25be546STrond Myklebust */ 460d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 461d25be546STrond Myklebust } while (err == 0); 462d25be546STrond Myklebust 463d25be546STrond Myklebust if (err) { 464d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 465d25be546STrond Myklebust goto out; 466d25be546STrond Myklebust } 467d25be546STrond Myklebust if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode))) 468d18cc1fdSTrond Myklebust goto out; 4691da177e4SLinus Torvalds 470d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 471d18cc1fdSTrond Myklebust out: 472d18cc1fdSTrond Myklebust return err; 47390163027STrond Myklebust } 47490163027STrond Myklebust 475b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 476b757144fSTrond Myklebust { 477b757144fSTrond Myklebust bool ret = false; 478b757144fSTrond Myklebust 479ec3ca4e5STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 480ec3ca4e5STrond Myklebust goto out; 481b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 482b757144fSTrond Myklebust ret = true; 483b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { 484b757144fSTrond Myklebust struct inode *inode; 485b757144fSTrond Myklebust 486b757144fSTrond Myklebust spin_lock(&delegation->lock); 487b757144fSTrond Myklebust inode = delegation->inode; 488b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 489b757144fSTrond Myklebust ret = true; 490b757144fSTrond Myklebust spin_unlock(&delegation->lock); 491b757144fSTrond Myklebust } 492ec3ca4e5STrond Myklebust out: 493b757144fSTrond Myklebust return ret; 494b757144fSTrond Myklebust } 495b757144fSTrond Myklebust 496d3978bb3SChuck Lever /** 497d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 498d3978bb3SChuck Lever * @clp: nfs_client to process 499d3978bb3SChuck Lever * 500dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 501dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 502dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 503dc327ed4STrond Myklebust * 504d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 505515d8611STrond Myklebust */ 506d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 507515d8611STrond Myklebust { 508515d8611STrond Myklebust struct nfs_delegation *delegation; 509e04bbf6bSNeilBrown struct nfs_delegation *prev; 510d3978bb3SChuck Lever struct nfs_server *server; 511515d8611STrond Myklebust struct inode *inode; 512e04bbf6bSNeilBrown struct inode *place_holder = NULL; 513e04bbf6bSNeilBrown struct nfs_delegation *place_holder_deleg = NULL; 514d18cc1fdSTrond Myklebust int err = 0; 515515d8611STrond Myklebust 516515d8611STrond Myklebust restart: 517e04bbf6bSNeilBrown /* 518e04bbf6bSNeilBrown * To avoid quadratic looping we hold a reference 519e04bbf6bSNeilBrown * to an inode place_holder. Each time we restart, we 520e04bbf6bSNeilBrown * list nfs_servers from the server of that inode, and 521e04bbf6bSNeilBrown * delegation in the server from the delegations of that 522e04bbf6bSNeilBrown * inode. 523e04bbf6bSNeilBrown * prev is an RCU-protected pointer to a delegation which 524e04bbf6bSNeilBrown * wasn't marked for return and might be a good choice for 525e04bbf6bSNeilBrown * the next place_holder. 526e04bbf6bSNeilBrown */ 527515d8611STrond Myklebust rcu_read_lock(); 528e04bbf6bSNeilBrown prev = NULL; 529e04bbf6bSNeilBrown if (place_holder) 530e04bbf6bSNeilBrown server = NFS_SERVER(place_holder); 531e04bbf6bSNeilBrown else 532e04bbf6bSNeilBrown server = list_entry_rcu(clp->cl_superblocks.next, 533e04bbf6bSNeilBrown struct nfs_server, client_link); 534e04bbf6bSNeilBrown list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { 535e04bbf6bSNeilBrown delegation = NULL; 536e04bbf6bSNeilBrown if (place_holder && server == NFS_SERVER(place_holder)) 537e04bbf6bSNeilBrown delegation = rcu_dereference(NFS_I(place_holder)->delegation); 538e04bbf6bSNeilBrown if (!delegation || delegation != place_holder_deleg) 539e04bbf6bSNeilBrown delegation = list_entry_rcu(server->delegations.next, 540e04bbf6bSNeilBrown struct nfs_delegation, super_list); 541e04bbf6bSNeilBrown list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 542e04bbf6bSNeilBrown struct inode *to_put = NULL; 543e04bbf6bSNeilBrown 544e04bbf6bSNeilBrown if (!nfs_delegation_need_return(delegation)) { 545e04bbf6bSNeilBrown prev = delegation; 546515d8611STrond Myklebust continue; 547e04bbf6bSNeilBrown } 5489f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 549f3893491SNeilBrown break; /* continue in outer loop */ 550e04bbf6bSNeilBrown 551e04bbf6bSNeilBrown if (prev) { 552e04bbf6bSNeilBrown struct inode *tmp; 553e04bbf6bSNeilBrown 554e04bbf6bSNeilBrown tmp = nfs_delegation_grab_inode(prev); 555e04bbf6bSNeilBrown if (tmp) { 556e04bbf6bSNeilBrown to_put = place_holder; 557e04bbf6bSNeilBrown place_holder = tmp; 558e04bbf6bSNeilBrown place_holder_deleg = prev; 559e04bbf6bSNeilBrown } 560e04bbf6bSNeilBrown } 561e04bbf6bSNeilBrown 5629f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 5639f0f8e12STrond Myklebust if (inode == NULL) { 5649f0f8e12STrond Myklebust rcu_read_unlock(); 565e04bbf6bSNeilBrown if (to_put) 566e04bbf6bSNeilBrown iput(to_put); 5679f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5689f0f8e12STrond Myklebust goto restart; 5699f0f8e12STrond Myklebust } 570d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 571515d8611STrond Myklebust rcu_read_unlock(); 572d3978bb3SChuck Lever 573e04bbf6bSNeilBrown if (to_put) 574e04bbf6bSNeilBrown iput(to_put); 575e04bbf6bSNeilBrown 576d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 577515d8611STrond Myklebust iput(inode); 5789f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5793ca951b6SNeilBrown cond_resched(); 580d18cc1fdSTrond Myklebust if (!err) 581515d8611STrond Myklebust goto restart; 582d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 583e04bbf6bSNeilBrown if (place_holder) 584e04bbf6bSNeilBrown iput(place_holder); 585d18cc1fdSTrond Myklebust return err; 586515d8611STrond Myklebust } 587d3978bb3SChuck Lever } 588515d8611STrond Myklebust rcu_read_unlock(); 589e04bbf6bSNeilBrown if (place_holder) 590e04bbf6bSNeilBrown iput(place_holder); 591d18cc1fdSTrond Myklebust return 0; 592515d8611STrond Myklebust } 593515d8611STrond Myklebust 594d3978bb3SChuck Lever /** 595b47e0e47STrond Myklebust * nfs_inode_evict_delegation - return delegation, don't reclaim opens 596d3978bb3SChuck Lever * @inode: inode to process 597d3978bb3SChuck Lever * 598d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 599b47e0e47STrond Myklebust * to be called from nfs4_clear_inode(). Guaranteed to always free 600b47e0e47STrond Myklebust * the delegation structure. 601e6f81075STrond Myklebust */ 602b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode) 603e6f81075STrond Myklebust { 604e6f81075STrond Myklebust struct nfs_delegation *delegation; 605e6f81075STrond Myklebust 606d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 607b47e0e47STrond Myklebust if (delegation != NULL) { 608b47e0e47STrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 6095fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 610e6f81075STrond Myklebust } 611b47e0e47STrond Myklebust } 612e6f81075STrond Myklebust 613d3978bb3SChuck Lever /** 614d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 615d3978bb3SChuck Lever * @inode: inode to process 616d3978bb3SChuck Lever * 617c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 618c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 619c57d1bc5STrond Myklebust * we should stop caching. 620c57d1bc5STrond Myklebust * 621d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 622d3978bb3SChuck Lever */ 62357ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 62490163027STrond Myklebust { 62590163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 62690163027STrond Myklebust struct nfs_delegation *delegation; 62790163027STrond Myklebust int err = 0; 62890163027STrond Myklebust 629c57d1bc5STrond Myklebust nfs_wb_all(inode); 630d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 631d25be546STrond Myklebust if (delegation != NULL) 632d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 63390163027STrond Myklebust return err; 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 636c01d3645STrond Myklebust /** 637c01d3645STrond Myklebust * nfs4_inode_make_writeable 638c01d3645STrond Myklebust * @inode: pointer to inode 639c01d3645STrond Myklebust * 640c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 641c01d3645STrond Myklebust * 642c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 643c01d3645STrond Myklebust */ 644c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 645c01d3645STrond Myklebust { 646c01d3645STrond Myklebust if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || 647c01d3645STrond Myklebust !nfs4_check_delegation(inode, FMODE_WRITE)) 648c01d3645STrond Myklebust return nfs4_inode_return_delegation(inode); 649c01d3645STrond Myklebust return 0; 650c01d3645STrond Myklebust } 651c01d3645STrond Myklebust 652b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 653b757144fSTrond Myklebust struct nfs_delegation *delegation) 654b757144fSTrond Myklebust { 655b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 656b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 657b757144fSTrond Myklebust } 658b757144fSTrond Myklebust 659ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 660ed1e6211STrond Myklebust struct nfs_delegation *delegation) 6616411bd4aSTrond Myklebust { 6626411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 663ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 6646411bd4aSTrond Myklebust } 6656411bd4aSTrond Myklebust 6665c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 6675c31e236STrond Myklebust { 6685c31e236STrond Myklebust struct nfs_delegation *delegation; 6695c31e236STrond Myklebust bool ret = false; 6705c31e236STrond Myklebust 6715c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 6725c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 6735c31e236STrond Myklebust ret = true; 6745c31e236STrond Myklebust } 6755c31e236STrond Myklebust return ret; 6765c31e236STrond Myklebust } 6775c31e236STrond Myklebust 678b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 679b02ba0b6STrond Myklebust { 680b02ba0b6STrond Myklebust struct nfs_server *server; 681b02ba0b6STrond Myklebust 682b02ba0b6STrond Myklebust rcu_read_lock(); 683b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 684b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 685b02ba0b6STrond Myklebust rcu_read_unlock(); 686b02ba0b6STrond Myklebust } 687b02ba0b6STrond Myklebust 688b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 689b02ba0b6STrond Myklebust { 690b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 691b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 692b02ba0b6STrond Myklebust } 693b02ba0b6STrond Myklebust 694b02ba0b6STrond Myklebust /** 695b02ba0b6STrond Myklebust * nfs_expire_all_delegations 696b02ba0b6STrond Myklebust * @clp: client to process 697b02ba0b6STrond Myklebust * 698b02ba0b6STrond Myklebust */ 699b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 700b02ba0b6STrond Myklebust { 701b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 702b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 703b02ba0b6STrond Myklebust } 704b02ba0b6STrond Myklebust 705d3978bb3SChuck Lever /** 706d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 707302fad7bSTrond Myklebust * @server: pointer to nfs_server to process 708d3978bb3SChuck Lever * 7091da177e4SLinus Torvalds */ 710eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 7111da177e4SLinus Torvalds { 712d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 7135c31e236STrond Myklebust bool need_wait; 7141da177e4SLinus Torvalds 7151da177e4SLinus Torvalds if (clp == NULL) 7161da177e4SLinus Torvalds return; 717d3978bb3SChuck Lever 7188383e460STrond Myklebust rcu_read_lock(); 7195c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 7208383e460STrond Myklebust rcu_read_unlock(); 721d3978bb3SChuck Lever 7225c31e236STrond Myklebust if (need_wait) { 723d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 7245c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 7255c31e236STrond Myklebust } 726515d8611STrond Myklebust } 727515d8611STrond Myklebust 728826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 729d3978bb3SChuck Lever fmode_t flags) 730515d8611STrond Myklebust { 731515d8611STrond Myklebust struct nfs_delegation *delegation; 732515d8611STrond Myklebust 733d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 734c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 735c79571a5SAlexandros Batsakis continue; 736c79571a5SAlexandros Batsakis if (delegation->type & flags) 737826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 738707fb4b3STrond Myklebust } 739d3978bb3SChuck Lever } 740d3978bb3SChuck Lever 741826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 742d3978bb3SChuck Lever fmode_t flags) 743d3978bb3SChuck Lever { 744d3978bb3SChuck Lever struct nfs_server *server; 745d3978bb3SChuck Lever 746d3978bb3SChuck Lever rcu_read_lock(); 747d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 748826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 749515d8611STrond Myklebust rcu_read_unlock(); 7501da177e4SLinus Torvalds } 7511da177e4SLinus Torvalds 75241020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server, 75341020b67STrond Myklebust struct nfs_delegation *delegation) 75441020b67STrond Myklebust { 75541020b67STrond Myklebust set_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 756059b43e9STrond Myklebust delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 75741020b67STrond Myklebust nfs_mark_return_delegation(server, delegation); 75841020b67STrond Myklebust } 75941020b67STrond Myklebust 76041020b67STrond Myklebust static bool nfs_revoke_delegation(struct inode *inode, 76141020b67STrond Myklebust const nfs4_stateid *stateid) 762869f9dfaSTrond Myklebust { 763869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 7647f048831STrond Myklebust nfs4_stateid tmp; 76541020b67STrond Myklebust bool ret = false; 76641020b67STrond Myklebust 767869f9dfaSTrond Myklebust rcu_read_lock(); 768869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 76941020b67STrond Myklebust if (delegation == NULL) 77041020b67STrond Myklebust goto out; 7717f048831STrond Myklebust if (stateid == NULL) { 7727f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 7737f048831STrond Myklebust stateid = &tmp; 774f2d47b55STrond Myklebust } else { 775f2d47b55STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 77641020b67STrond Myklebust goto out; 777f2d47b55STrond Myklebust spin_lock(&delegation->lock); 778f2d47b55STrond Myklebust if (stateid->seqid) { 779f2d47b55STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { 780f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 781f2d47b55STrond Myklebust goto out; 782f2d47b55STrond Myklebust } 783f2d47b55STrond Myklebust delegation->stateid.seqid = stateid->seqid; 784f2d47b55STrond Myklebust } 785f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 786f2d47b55STrond Myklebust } 78741020b67STrond Myklebust nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation); 78841020b67STrond Myklebust ret = true; 78941020b67STrond Myklebust out: 790869f9dfaSTrond Myklebust rcu_read_unlock(); 7917f048831STrond Myklebust if (ret) 7927f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 79341020b67STrond Myklebust return ret; 794869f9dfaSTrond Myklebust } 795869f9dfaSTrond Myklebust 79641020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 79741020b67STrond Myklebust const nfs4_stateid *stateid) 798a1d0b5eeSTrond Myklebust { 799a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 800a1d0b5eeSTrond Myklebust 80141020b67STrond Myklebust if (!nfs_revoke_delegation(inode, stateid)) 80241020b67STrond Myklebust return; 803d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 8047f048831STrond Myklebust if (delegation) 805a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 806a1d0b5eeSTrond Myklebust } 8079cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 808a1d0b5eeSTrond Myklebust 809d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode, 810d51f91d2STrond Myklebust const nfs4_stateid *stateid) 811d51f91d2STrond Myklebust { 812d51f91d2STrond Myklebust struct nfs_delegation *delegation; 813d51f91d2STrond Myklebust 814d51f91d2STrond Myklebust if (!inode) 815d51f91d2STrond Myklebust return; 816d51f91d2STrond Myklebust 817d51f91d2STrond Myklebust rcu_read_lock(); 818d51f91d2STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 819d51f91d2STrond Myklebust if (!delegation) 820d51f91d2STrond Myklebust goto out_rcu_unlock; 821d51f91d2STrond Myklebust 822d51f91d2STrond Myklebust spin_lock(&delegation->lock); 823d51f91d2STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 824d51f91d2STrond Myklebust goto out_spin_unlock; 825d51f91d2STrond Myklebust if (stateid->seqid) { 826d51f91d2STrond Myklebust /* If delegation->stateid is newer, dont mark as returned */ 827d51f91d2STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) 828d51f91d2STrond Myklebust goto out_clear_returning; 829d51f91d2STrond Myklebust if (delegation->stateid.seqid != stateid->seqid) 830d51f91d2STrond Myklebust delegation->stateid.seqid = stateid->seqid; 831d51f91d2STrond Myklebust } 832d51f91d2STrond Myklebust 833d51f91d2STrond Myklebust set_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 834d51f91d2STrond Myklebust 835d51f91d2STrond Myklebust out_clear_returning: 836d51f91d2STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 837d51f91d2STrond Myklebust out_spin_unlock: 838d51f91d2STrond Myklebust spin_unlock(&delegation->lock); 839d51f91d2STrond Myklebust out_rcu_unlock: 840d51f91d2STrond Myklebust rcu_read_unlock(); 841d51f91d2STrond Myklebust 842d51f91d2STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 843d51f91d2STrond Myklebust } 844d51f91d2STrond Myklebust 845d3978bb3SChuck Lever /** 846826e0013STrond Myklebust * nfs_expire_unused_delegation_types 847d3978bb3SChuck Lever * @clp: client to process 848d3978bb3SChuck Lever * @flags: delegation types to expire 849d3978bb3SChuck Lever * 850d3978bb3SChuck Lever */ 851826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 852c79571a5SAlexandros Batsakis { 853826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 854c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 855c79571a5SAlexandros Batsakis } 856c79571a5SAlexandros Batsakis 857d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 858b7391f44STrond Myklebust { 859b7391f44STrond Myklebust struct nfs_delegation *delegation; 860b7391f44STrond Myklebust 861d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 862b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 863b7391f44STrond Myklebust continue; 864b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 865b7391f44STrond Myklebust } 866b7391f44STrond Myklebust } 867b7391f44STrond Myklebust 868d3978bb3SChuck Lever /** 869d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 870d3978bb3SChuck Lever * @clp: nfs_client to process 871d3978bb3SChuck Lever * 872d3978bb3SChuck Lever */ 873b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 874b7391f44STrond Myklebust { 875d3978bb3SChuck Lever struct nfs_server *server; 876d3978bb3SChuck Lever 877d3978bb3SChuck Lever rcu_read_lock(); 878d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 879d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 880d3978bb3SChuck Lever rcu_read_unlock(); 881d3978bb3SChuck Lever 882b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 883b7391f44STrond Myklebust } 884b7391f44STrond Myklebust 885d3978bb3SChuck Lever /** 886d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 887d3978bb3SChuck Lever * @inode: inode to process 8888e663f0eSTrond Myklebust * @stateid: state ID information 889d3978bb3SChuck Lever * 890d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 8911da177e4SLinus Torvalds */ 892d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 893d3978bb3SChuck Lever const nfs4_stateid *stateid) 8941da177e4SLinus Torvalds { 895ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 896ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 8976411bd4aSTrond Myklebust struct nfs_delegation *delegation; 8981da177e4SLinus Torvalds 8996411bd4aSTrond Myklebust rcu_read_lock(); 900457a5042STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 901755a48a7STrond Myklebust if (delegation == NULL) 902755a48a7STrond Myklebust goto out_enoent; 9034816fdadSTrond Myklebust if (stateid != NULL && 9044816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 905755a48a7STrond Myklebust goto out_enoent; 906ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 9076411bd4aSTrond Myklebust rcu_read_unlock(); 908d3978bb3SChuck Lever 9096411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 9106411bd4aSTrond Myklebust return 0; 911755a48a7STrond Myklebust out_enoent: 912755a48a7STrond Myklebust rcu_read_unlock(); 913755a48a7STrond Myklebust return -ENOENT; 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds 916d3978bb3SChuck Lever static struct inode * 917d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 918d3978bb3SChuck Lever const struct nfs_fh *fhandle) 9191da177e4SLinus Torvalds { 9201da177e4SLinus Torvalds struct nfs_delegation *delegation; 921e39d8a18STrond Myklebust struct inode *freeme, *res = NULL; 922d3978bb3SChuck Lever 923d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 92486e89489STrond Myklebust spin_lock(&delegation->lock); 92586e89489STrond Myklebust if (delegation->inode != NULL && 926457a5042STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 92786e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 928e39d8a18STrond Myklebust freeme = igrab(delegation->inode); 929e39d8a18STrond Myklebust if (freeme && nfs_sb_active(freeme->i_sb)) 930e39d8a18STrond Myklebust res = freeme; 93186e89489STrond Myklebust spin_unlock(&delegation->lock); 93286e89489STrond Myklebust if (res != NULL) 933d3978bb3SChuck Lever return res; 934e39d8a18STrond Myklebust if (freeme) { 935e39d8a18STrond Myklebust rcu_read_unlock(); 936e39d8a18STrond Myklebust iput(freeme); 937e39d8a18STrond Myklebust rcu_read_lock(); 938e39d8a18STrond Myklebust } 9396c342655STrond Myklebust return ERR_PTR(-EAGAIN); 9406c342655STrond Myklebust } 9416c342655STrond Myklebust spin_unlock(&delegation->lock); 9426c342655STrond Myklebust } 9436c342655STrond Myklebust return ERR_PTR(-ENOENT); 944d3978bb3SChuck Lever } 945d3978bb3SChuck Lever 946d3978bb3SChuck Lever /** 947d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 948d3978bb3SChuck Lever * @clp: client state handle 949d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 950d3978bb3SChuck Lever * 951d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 952d3978bb3SChuck Lever * cannot be found. 953d3978bb3SChuck Lever */ 954d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 955d3978bb3SChuck Lever const struct nfs_fh *fhandle) 956d3978bb3SChuck Lever { 957d3978bb3SChuck Lever struct nfs_server *server; 9586c342655STrond Myklebust struct inode *res; 959d3978bb3SChuck Lever 960d3978bb3SChuck Lever rcu_read_lock(); 961d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 962d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 963d5681f59SAnna Schumaker if (res != ERR_PTR(-ENOENT)) { 964d5681f59SAnna Schumaker rcu_read_unlock(); 9656c342655STrond Myklebust return res; 966d3978bb3SChuck Lever } 967d5681f59SAnna Schumaker } 9688383e460STrond Myklebust rcu_read_unlock(); 9696c342655STrond Myklebust return ERR_PTR(-ENOENT); 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds 972d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 973d3978bb3SChuck Lever { 974d3978bb3SChuck Lever struct nfs_delegation *delegation; 975d3978bb3SChuck Lever 97645870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 97745870d69STrond Myklebust /* 97845870d69STrond Myklebust * If the delegation may have been admin revoked, then we 97945870d69STrond Myklebust * cannot reclaim it. 98045870d69STrond Myklebust */ 98145870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 98245870d69STrond Myklebust continue; 983d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 984d3978bb3SChuck Lever } 98545870d69STrond Myklebust } 986d3978bb3SChuck Lever 987d3978bb3SChuck Lever /** 988d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 989d3978bb3SChuck Lever * @clp: nfs_client to process 990d3978bb3SChuck Lever * 9911da177e4SLinus Torvalds */ 992adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 9931da177e4SLinus Torvalds { 994d3978bb3SChuck Lever struct nfs_server *server; 995d3978bb3SChuck Lever 9968383e460STrond Myklebust rcu_read_lock(); 997d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 998d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 9998383e460STrond Myklebust rcu_read_unlock(); 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 1002d3978bb3SChuck Lever /** 1003d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 1004d3978bb3SChuck Lever * @clp: nfs_client to process 1005d3978bb3SChuck Lever * 10061da177e4SLinus Torvalds */ 1007adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 10081da177e4SLinus Torvalds { 10098383e460STrond Myklebust struct nfs_delegation *delegation; 1010d3978bb3SChuck Lever struct nfs_server *server; 101186e89489STrond Myklebust struct inode *inode; 1012d3978bb3SChuck Lever 10138383e460STrond Myklebust restart: 10148383e460STrond Myklebust rcu_read_lock(); 1015d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1016d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 1017d3978bb3SChuck Lever super_list) { 10186f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 10196f9449beSTrond Myklebust &delegation->flags) || 10206f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 10216f9449beSTrond Myklebust &delegation->flags) || 10226f9449beSTrond Myklebust test_bit(NFS_DELEGATION_NEED_RECLAIM, 1023d3978bb3SChuck Lever &delegation->flags) == 0) 10241da177e4SLinus Torvalds continue; 10259f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 1026f3893491SNeilBrown break; /* continue in outer loop */ 10279f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 10289f0f8e12STrond Myklebust if (inode == NULL) { 10299f0f8e12STrond Myklebust rcu_read_unlock(); 10309f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 10319f0f8e12STrond Myklebust goto restart; 10329f0f8e12STrond Myklebust } 1033b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 1034b04b22f4STrond Myklebust rcu_read_unlock(); 1035b04b22f4STrond Myklebust if (delegation != NULL) { 1036d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 1037d25be546STrond Myklebust delegation, server); 10388383e460STrond Myklebust if (delegation != NULL) 1039905f8d16STrond Myklebust nfs_free_delegation(delegation); 1040b04b22f4STrond Myklebust } 104186e89489STrond Myklebust iput(inode); 10429f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 10433ca951b6SNeilBrown cond_resched(); 10448383e460STrond Myklebust goto restart; 10451da177e4SLinus Torvalds } 1046d3978bb3SChuck Lever } 10478383e460STrond Myklebust rcu_read_unlock(); 10481da177e4SLinus Torvalds } 10493e4f6290STrond Myklebust 1050bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 1051bb3d1a3bSTrond Myklebust { 1052bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 1053bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 1054bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 1055bb3d1a3bSTrond Myklebust } 1056bb3d1a3bSTrond Myklebust 105745870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 105845870d69STrond Myklebust struct nfs_delegation *delegation) 105945870d69STrond Myklebust { 1060059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 1061059b43e9STrond Myklebust return; 106245870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 106345870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 106445870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 106545870d69STrond Myklebust } 106645870d69STrond Myklebust 1067bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 1068bb3d1a3bSTrond Myklebust struct inode *inode) 1069bb3d1a3bSTrond Myklebust { 1070bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 1071bb3d1a3bSTrond Myklebust 1072bb3d1a3bSTrond Myklebust rcu_read_lock(); 1073bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1074bb3d1a3bSTrond Myklebust if (delegation) 1075bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 1076bb3d1a3bSTrond Myklebust rcu_read_unlock(); 1077bb3d1a3bSTrond Myklebust 1078bb3d1a3bSTrond Myklebust } 1079bb3d1a3bSTrond Myklebust 108045870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 108145870d69STrond Myklebust { 108245870d69STrond Myklebust struct nfs_delegation *delegation; 108345870d69STrond Myklebust 108445870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 108545870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 108645870d69STrond Myklebust } 108745870d69STrond Myklebust 108845870d69STrond Myklebust /** 108945870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 109045870d69STrond Myklebust * @clp: nfs_client to process 109145870d69STrond Myklebust * 109245870d69STrond Myklebust * Iterates through all the delegations associated with this server and 109345870d69STrond Myklebust * marks them as needing to be checked for validity. 109445870d69STrond Myklebust */ 109545870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 109645870d69STrond Myklebust { 109745870d69STrond Myklebust struct nfs_server *server; 109845870d69STrond Myklebust 109945870d69STrond Myklebust rcu_read_lock(); 110045870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 110145870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 110245870d69STrond Myklebust rcu_read_unlock(); 110345870d69STrond Myklebust } 110445870d69STrond Myklebust 110545870d69STrond Myklebust /** 11068ca017c8SScott Mayhew * nfs_test_expired_all_delegations - test all delegations for a client 11078ca017c8SScott Mayhew * @clp: nfs_client to process 11088ca017c8SScott Mayhew * 11098ca017c8SScott Mayhew * Helper for handling "recallable state revoked" status from server. 11108ca017c8SScott Mayhew */ 11118ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp) 11128ca017c8SScott Mayhew { 11138ca017c8SScott Mayhew nfs_mark_test_expired_all_delegations(clp); 11148ca017c8SScott Mayhew nfs4_schedule_state_manager(clp); 11158ca017c8SScott Mayhew } 11168ca017c8SScott Mayhew 1117ad114089STrond Myklebust static void 1118ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode, 1119ad114089STrond Myklebust nfs4_stateid *stateid, 1120ad114089STrond Myklebust const struct cred *cred) 1121ad114089STrond Myklebust { 1122ad114089STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1123ad114089STrond Myklebust const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 1124ad114089STrond Myklebust int status; 1125ad114089STrond Myklebust 1126ad114089STrond Myklebust if (!cred) 1127ad114089STrond Myklebust return; 1128ad114089STrond Myklebust status = ops->test_and_free_expired(server, stateid, cred); 1129ad114089STrond Myklebust if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 1130ad114089STrond Myklebust nfs_remove_bad_delegation(inode, stateid); 1131ad114089STrond Myklebust } 1132ad114089STrond Myklebust 11338ca017c8SScott Mayhew /** 113445870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 113545870d69STrond Myklebust * @clp: nfs_client to process 113645870d69STrond Myklebust * 113745870d69STrond Myklebust * Iterates through all the delegations associated with this server and 113845870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 113945870d69STrond Myklebust * expected to be called in cases where the server may have lost its 114045870d69STrond Myklebust * lease. 114145870d69STrond Myklebust */ 114245870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 114345870d69STrond Myklebust { 114445870d69STrond Myklebust struct nfs_delegation *delegation; 114545870d69STrond Myklebust struct nfs_server *server; 114645870d69STrond Myklebust struct inode *inode; 1147a52458b4SNeilBrown const struct cred *cred; 114845870d69STrond Myklebust nfs4_stateid stateid; 114945870d69STrond Myklebust 115045870d69STrond Myklebust restart: 115145870d69STrond Myklebust rcu_read_lock(); 115245870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 115345870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, 115445870d69STrond Myklebust super_list) { 11556f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 11566f9449beSTrond Myklebust &delegation->flags) || 11576f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 11586f9449beSTrond Myklebust &delegation->flags) || 11596f9449beSTrond Myklebust test_bit(NFS_DELEGATION_TEST_EXPIRED, 116045870d69STrond Myklebust &delegation->flags) == 0) 116145870d69STrond Myklebust continue; 116245870d69STrond Myklebust if (!nfs_sb_active(server->super)) 1163f3893491SNeilBrown break; /* continue in outer loop */ 116445870d69STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 116545870d69STrond Myklebust if (inode == NULL) { 116645870d69STrond Myklebust rcu_read_unlock(); 116745870d69STrond Myklebust nfs_sb_deactive(server->super); 116845870d69STrond Myklebust goto restart; 116945870d69STrond Myklebust } 1170a52458b4SNeilBrown cred = get_cred_rcu(delegation->cred); 117145870d69STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 117245870d69STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 117345870d69STrond Myklebust rcu_read_unlock(); 1174ad114089STrond Myklebust nfs_delegation_test_free_expired(inode, &stateid, cred); 1175a52458b4SNeilBrown put_cred(cred); 1176bb3d1a3bSTrond Myklebust if (nfs4_server_rebooted(clp)) { 1177bb3d1a3bSTrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 1178bb3d1a3bSTrond Myklebust iput(inode); 1179bb3d1a3bSTrond Myklebust nfs_sb_deactive(server->super); 1180bb3d1a3bSTrond Myklebust return; 1181bb3d1a3bSTrond Myklebust } 118245870d69STrond Myklebust iput(inode); 118345870d69STrond Myklebust nfs_sb_deactive(server->super); 11843ca951b6SNeilBrown cond_resched(); 118545870d69STrond Myklebust goto restart; 118645870d69STrond Myklebust } 118745870d69STrond Myklebust } 118845870d69STrond Myklebust rcu_read_unlock(); 118945870d69STrond Myklebust } 119045870d69STrond Myklebust 11916c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 11926c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 11936c2d8f8dSTrond Myklebust { 11946c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 11956c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 11966c2d8f8dSTrond Myklebust bool found = false; 11976c2d8f8dSTrond Myklebust 11986c2d8f8dSTrond Myklebust rcu_read_lock(); 11996c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 12006c2d8f8dSTrond Myklebust if (delegation && 12016c2d8f8dSTrond Myklebust nfs4_stateid_match_other(&delegation->stateid, stateid)) { 12026c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 12036c2d8f8dSTrond Myklebust found = true; 12046c2d8f8dSTrond Myklebust } 12056c2d8f8dSTrond Myklebust rcu_read_unlock(); 12066c2d8f8dSTrond Myklebust if (found) 12076c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 12086c2d8f8dSTrond Myklebust } 12096c2d8f8dSTrond Myklebust 1210d3978bb3SChuck Lever /** 1211d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1212d3978bb3SChuck Lever * @clp: client state handle 1213d3978bb3SChuck Lever * 1214d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1215d3978bb3SChuck Lever * to this nfs_client. 1216d3978bb3SChuck Lever */ 1217d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1218d3978bb3SChuck Lever { 1219d3978bb3SChuck Lever struct nfs_server *server; 1220d3978bb3SChuck Lever int ret = 0; 1221d3978bb3SChuck Lever 1222d3978bb3SChuck Lever rcu_read_lock(); 1223d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1224d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1225d3978bb3SChuck Lever ret = 1; 1226d3978bb3SChuck Lever break; 1227d3978bb3SChuck Lever } 1228d3978bb3SChuck Lever rcu_read_unlock(); 1229d3978bb3SChuck Lever return ret; 1230d3978bb3SChuck Lever } 1231d3978bb3SChuck Lever 1232d3978bb3SChuck Lever /** 123312f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 123412f275cdSTrond Myklebust * @dst: stateid to refresh 123512f275cdSTrond Myklebust * @inode: inode to check 123612f275cdSTrond Myklebust * 123712f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 123812f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 123912f275cdSTrond Myklebust */ 124012f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 124112f275cdSTrond Myklebust { 124212f275cdSTrond Myklebust struct nfs_delegation *delegation; 124312f275cdSTrond Myklebust bool ret = false; 124412f275cdSTrond Myklebust if (!inode) 124512f275cdSTrond Myklebust goto out; 124612f275cdSTrond Myklebust 124712f275cdSTrond Myklebust rcu_read_lock(); 124812f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 124912f275cdSTrond Myklebust if (delegation != NULL && 1250b5756208STrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid) && 1251b5756208STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 125212f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 125379cc5542STrond Myklebust ret = true; 125412f275cdSTrond Myklebust } 125512f275cdSTrond Myklebust rcu_read_unlock(); 125612f275cdSTrond Myklebust out: 125712f275cdSTrond Myklebust return ret; 125812f275cdSTrond Myklebust } 125912f275cdSTrond Myklebust 126012f275cdSTrond Myklebust /** 1261d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1262d3978bb3SChuck Lever * @inode: inode to check 12630032a7a7STrond Myklebust * @flags: delegation type requirement 1264abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1265abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1266d3978bb3SChuck Lever * 12670032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 12680032a7a7STrond Myklebust * otherwise "false" is returned. 1269d3978bb3SChuck Lever */ 1270abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1271a52458b4SNeilBrown nfs4_stateid *dst, const struct cred **cred) 12723e4f6290STrond Myklebust { 12733e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 12743e4f6290STrond Myklebust struct nfs_delegation *delegation; 12750032a7a7STrond Myklebust bool ret; 12763e4f6290STrond Myklebust 12770032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 12788383e460STrond Myklebust rcu_read_lock(); 12798383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1280aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 12810032a7a7STrond Myklebust if (ret) { 1282f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 12830032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1284abf4e13cSTrond Myklebust if (cred) 1285a52458b4SNeilBrown *cred = get_cred(delegation->cred); 12863e4f6290STrond Myklebust } 12878383e460STrond Myklebust rcu_read_unlock(); 12888383e460STrond Myklebust return ret; 12893e4f6290STrond Myklebust } 12905445b1fbSTrond Myklebust 12915445b1fbSTrond Myklebust /** 12925445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 12935445b1fbSTrond Myklebust * @inode: inode to check 12945445b1fbSTrond Myklebust * 12955445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 12965445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 12975445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 12985445b1fbSTrond Myklebust */ 12995445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 13005445b1fbSTrond Myklebust { 13015445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 13025445b1fbSTrond Myklebust struct nfs_delegation *delegation; 13035445b1fbSTrond Myklebust bool ret = true; 13045445b1fbSTrond Myklebust 13055445b1fbSTrond Myklebust rcu_read_lock(); 13065445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 13075445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 13085445b1fbSTrond Myklebust goto out; 1309a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 13105445b1fbSTrond Myklebust ret = false; 13115445b1fbSTrond Myklebust out: 13125445b1fbSTrond Myklebust rcu_read_unlock(); 13135445b1fbSTrond Myklebust return ret; 13145445b1fbSTrond Myklebust } 1315