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); 2021deed572STrond Myklebust if (nfs4_is_valid_delegation(delegation, 0)) { 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 return res; 23357bfa891STrond Myklebust } 23457bfa891STrond Myklebust 23586e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 23686e89489STrond Myklebust { 23786e89489STrond Myklebust struct inode *inode = NULL; 23886e89489STrond Myklebust 23986e89489STrond Myklebust spin_lock(&delegation->lock); 24086e89489STrond Myklebust if (delegation->inode != NULL) 24186e89489STrond Myklebust inode = igrab(delegation->inode); 2426f9449beSTrond Myklebust if (!inode) 2436f9449beSTrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 24486e89489STrond Myklebust spin_unlock(&delegation->lock); 24586e89489STrond Myklebust return inode; 24686e89489STrond Myklebust } 24786e89489STrond Myklebust 248dda4b225SChuck Lever static struct nfs_delegation * 249d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 25057bfa891STrond Myklebust { 251d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 252d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 25357bfa891STrond Myklebust 25457bfa891STrond Myklebust if (delegation == NULL) 255d25be546STrond Myklebust goto out; 256d25be546STrond Myklebust spin_lock(&delegation->lock); 257d25be546STrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 258d25be546STrond Myklebust ret = delegation; 259d25be546STrond Myklebust spin_unlock(&delegation->lock); 260d25be546STrond Myklebust out: 261d25be546STrond Myklebust return ret; 262d25be546STrond Myklebust } 263d25be546STrond Myklebust 264d25be546STrond Myklebust static struct nfs_delegation * 265d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 266d25be546STrond Myklebust { 267d25be546STrond Myklebust struct nfs_delegation *delegation; 268d25be546STrond Myklebust 269d25be546STrond Myklebust rcu_read_lock(); 270d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 271d25be546STrond Myklebust rcu_read_unlock(); 272d25be546STrond Myklebust return delegation; 273d25be546STrond Myklebust } 274d25be546STrond Myklebust 275d25be546STrond Myklebust static void 276d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation, 277d25be546STrond Myklebust struct nfs_client *clp) 278d25be546STrond Myklebust { 279dda4b225SChuck Lever 28034310430STrond Myklebust spin_lock(&delegation->lock); 281d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 282d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 283d25be546STrond Myklebust spin_unlock(&delegation->lock); 284d25be546STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 285d25be546STrond Myklebust } 286d25be546STrond Myklebust 287d25be546STrond Myklebust static struct nfs_delegation * 288d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 289d25be546STrond Myklebust struct nfs_delegation *delegation, 290d25be546STrond Myklebust struct nfs_client *clp) 291d25be546STrond Myklebust { 292d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 293d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 294d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 295d25be546STrond Myklebust 296d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 297d25be546STrond Myklebust return NULL; 298d25be546STrond Myklebust 299d25be546STrond Myklebust spin_lock(&delegation->lock); 300f9e0cc9cSTrond Myklebust if (!delegation->inode) { 301f9e0cc9cSTrond Myklebust spin_unlock(&delegation->lock); 302f9e0cc9cSTrond Myklebust return NULL; 303f9e0cc9cSTrond Myklebust } 30457bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 30586e89489STrond Myklebust delegation->inode = NULL; 30657bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 30734310430STrond Myklebust spin_unlock(&delegation->lock); 30857bfa891STrond Myklebust return delegation; 30957bfa891STrond Myklebust } 31057bfa891STrond Myklebust 311dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 312d25be546STrond Myklebust struct nfs_delegation *delegation, 313d3978bb3SChuck Lever struct nfs_server *server) 314dda4b225SChuck Lever { 315d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 316dda4b225SChuck Lever 317dda4b225SChuck Lever spin_lock(&clp->cl_lock); 318d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 319dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 320dda4b225SChuck Lever return delegation; 321dda4b225SChuck Lever } 322dda4b225SChuck Lever 323d25be546STrond Myklebust static struct nfs_delegation * 324d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 325d25be546STrond Myklebust { 326d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 327d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 328d25be546STrond Myklebust struct nfs_delegation *delegation; 329d25be546STrond Myklebust 330ee05f456STrond Myklebust rcu_read_lock(); 331ee05f456STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 332ee05f456STrond Myklebust if (delegation != NULL) 333ee05f456STrond Myklebust delegation = nfs_detach_delegation(nfsi, delegation, server); 334ee05f456STrond Myklebust rcu_read_unlock(); 335ee05f456STrond Myklebust return delegation; 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)); 387ee05f456STrond Myklebust if (old_delegation == NULL) 388ee05f456STrond Myklebust goto add_new; 389cf6726e2STrond Myklebust /* Is this an update of the existing delegation? */ 390cf6726e2STrond Myklebust if (nfs4_stateid_match_other(&old_delegation->stateid, 391cf6726e2STrond Myklebust &delegation->stateid)) { 392e0f07896STrond Myklebust spin_lock(&old_delegation->lock); 393cf6726e2STrond Myklebust nfs_update_inplace_delegation(old_delegation, 394cf6726e2STrond Myklebust delegation); 395e0f07896STrond Myklebust spin_unlock(&old_delegation->lock); 39657bfa891STrond Myklebust goto out; 39757bfa891STrond Myklebust } 398ee05f456STrond Myklebust if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) { 39957bfa891STrond Myklebust /* 40057bfa891STrond Myklebust * Deal with broken servers that hand out two 40157bfa891STrond Myklebust * delegations for the same file. 40217280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 40317280175STrond Myklebust * nothing else. 40457bfa891STrond Myklebust */ 40557bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 40657bfa891STrond Myklebust "a duplicate delegation!\n", 4073110ff80SHarvey Harrison __func__, clp->cl_hostname); 40817280175STrond Myklebust if (delegation->type == old_delegation->type || 40917280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 41057bfa891STrond Myklebust freeme = delegation; 41157bfa891STrond Myklebust delegation = NULL; 41257bfa891STrond Myklebust goto out; 41357bfa891STrond Myklebust } 414ade04647STrond Myklebust if (test_and_set_bit(NFS_DELEGATION_RETURNING, 415ade04647STrond Myklebust &old_delegation->flags)) 416ade04647STrond Myklebust goto out; 417ee05f456STrond Myklebust } 418ee05f456STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); 419d25be546STrond Myklebust if (freeme == NULL) 420d25be546STrond Myklebust goto out; 421ee05f456STrond Myklebust add_new: 42238942ba2STrond Myklebust list_add_tail_rcu(&delegation->super_list, &server->delegations); 4238383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 4241da177e4SLinus Torvalds delegation = NULL; 425412c77ceSTrond Myklebust 42635156bffSTrond Myklebust trace_nfs4_set_delegation(inode, type); 427412c77ceSTrond Myklebust 42897c2c17aSTrond Myklebust spin_lock(&inode->i_lock); 42997c2c17aSTrond Myklebust if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 43097c2c17aSTrond Myklebust NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 43197c2c17aSTrond Myklebust spin_unlock(&inode->i_lock); 43257bfa891STrond Myklebust out: 4331da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 434603c83daSTrond Myklebust if (delegation != NULL) 435603c83daSTrond Myklebust nfs_free_delegation(delegation); 436ee05f456STrond Myklebust if (freeme != NULL) { 43757bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 438ee05f456STrond Myklebust nfs_free_delegation(freeme); 439ee05f456STrond Myklebust } 4401da177e4SLinus Torvalds return status; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds /* 4441da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 4451da177e4SLinus Torvalds */ 446d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 4471da177e4SLinus Torvalds { 448d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 449869f9dfaSTrond Myklebust int err = 0; 4501da177e4SLinus Torvalds 451d25be546STrond Myklebust if (delegation == NULL) 452d25be546STrond Myklebust return 0; 453d25be546STrond Myklebust do { 454869f9dfaSTrond Myklebust if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 455869f9dfaSTrond Myklebust break; 45624311f88STrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid, 45724311f88STrond Myklebust delegation->type); 458d25be546STrond Myklebust if (!issync || err != -EAGAIN) 459d25be546STrond Myklebust break; 460d25be546STrond Myklebust /* 461d25be546STrond Myklebust * Guard against state recovery 462d25be546STrond Myklebust */ 463d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 464d25be546STrond Myklebust } while (err == 0); 465d25be546STrond Myklebust 466d25be546STrond Myklebust if (err) { 467d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 468d25be546STrond Myklebust goto out; 469d25be546STrond Myklebust } 4701da177e4SLinus Torvalds 471d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 472d18cc1fdSTrond Myklebust out: 473d18cc1fdSTrond Myklebust return err; 47490163027STrond Myklebust } 47590163027STrond Myklebust 476b757144fSTrond Myklebust static bool nfs_delegation_need_return(struct nfs_delegation *delegation) 477b757144fSTrond Myklebust { 478b757144fSTrond Myklebust bool ret = false; 479b757144fSTrond Myklebust 480b757144fSTrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 481b757144fSTrond Myklebust ret = true; 4820d104167STrond Myklebust else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { 483b757144fSTrond Myklebust struct inode *inode; 484b757144fSTrond Myklebust 485b757144fSTrond Myklebust spin_lock(&delegation->lock); 486b757144fSTrond Myklebust inode = delegation->inode; 487b757144fSTrond Myklebust if (inode && list_empty(&NFS_I(inode)->open_files)) 488b757144fSTrond Myklebust ret = true; 489b757144fSTrond Myklebust spin_unlock(&delegation->lock); 490b757144fSTrond Myklebust } 4910d104167STrond Myklebust if (ret) 4920d104167STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 493af20b7b8STrond Myklebust if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || 494af20b7b8STrond Myklebust test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 495af20b7b8STrond Myklebust ret = false; 496af20b7b8STrond Myklebust 497b757144fSTrond Myklebust return ret; 498b757144fSTrond Myklebust } 499b757144fSTrond Myklebust 500d3978bb3SChuck Lever /** 501d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 502d3978bb3SChuck Lever * @clp: nfs_client to process 503d3978bb3SChuck Lever * 504dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 505dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 506dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 507dc327ed4STrond Myklebust * 508d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 509515d8611STrond Myklebust */ 510d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 511515d8611STrond Myklebust { 512515d8611STrond Myklebust struct nfs_delegation *delegation; 513e04bbf6bSNeilBrown struct nfs_delegation *prev; 514d3978bb3SChuck Lever struct nfs_server *server; 515515d8611STrond Myklebust struct inode *inode; 516e04bbf6bSNeilBrown struct inode *place_holder = NULL; 517e04bbf6bSNeilBrown struct nfs_delegation *place_holder_deleg = NULL; 518d18cc1fdSTrond Myklebust int err = 0; 519515d8611STrond Myklebust 520515d8611STrond Myklebust restart: 521e04bbf6bSNeilBrown /* 522e04bbf6bSNeilBrown * To avoid quadratic looping we hold a reference 523e04bbf6bSNeilBrown * to an inode place_holder. Each time we restart, we 524e04bbf6bSNeilBrown * list nfs_servers from the server of that inode, and 525e04bbf6bSNeilBrown * delegation in the server from the delegations of that 526e04bbf6bSNeilBrown * inode. 527e04bbf6bSNeilBrown * prev is an RCU-protected pointer to a delegation which 528e04bbf6bSNeilBrown * wasn't marked for return and might be a good choice for 529e04bbf6bSNeilBrown * the next place_holder. 530e04bbf6bSNeilBrown */ 531515d8611STrond Myklebust rcu_read_lock(); 532e04bbf6bSNeilBrown prev = NULL; 533e04bbf6bSNeilBrown if (place_holder) 534e04bbf6bSNeilBrown server = NFS_SERVER(place_holder); 535e04bbf6bSNeilBrown else 536e04bbf6bSNeilBrown server = list_entry_rcu(clp->cl_superblocks.next, 537e04bbf6bSNeilBrown struct nfs_server, client_link); 538e04bbf6bSNeilBrown list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) { 539e04bbf6bSNeilBrown delegation = NULL; 540e04bbf6bSNeilBrown if (place_holder && server == NFS_SERVER(place_holder)) 541e04bbf6bSNeilBrown delegation = rcu_dereference(NFS_I(place_holder)->delegation); 542e04bbf6bSNeilBrown if (!delegation || delegation != place_holder_deleg) 543e04bbf6bSNeilBrown delegation = list_entry_rcu(server->delegations.next, 544e04bbf6bSNeilBrown struct nfs_delegation, super_list); 545e04bbf6bSNeilBrown list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 546e04bbf6bSNeilBrown struct inode *to_put = NULL; 547e04bbf6bSNeilBrown 548e04bbf6bSNeilBrown if (!nfs_delegation_need_return(delegation)) { 549e04bbf6bSNeilBrown prev = delegation; 550515d8611STrond Myklebust continue; 551e04bbf6bSNeilBrown } 5529f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 553f3893491SNeilBrown break; /* continue in outer loop */ 554e04bbf6bSNeilBrown 555e04bbf6bSNeilBrown if (prev) { 556e04bbf6bSNeilBrown struct inode *tmp; 557e04bbf6bSNeilBrown 558e04bbf6bSNeilBrown tmp = nfs_delegation_grab_inode(prev); 559e04bbf6bSNeilBrown if (tmp) { 560e04bbf6bSNeilBrown to_put = place_holder; 561e04bbf6bSNeilBrown place_holder = tmp; 562e04bbf6bSNeilBrown place_holder_deleg = prev; 563e04bbf6bSNeilBrown } 564e04bbf6bSNeilBrown } 565e04bbf6bSNeilBrown 5669f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 5679f0f8e12STrond Myklebust if (inode == NULL) { 5689f0f8e12STrond Myklebust rcu_read_unlock(); 569e04bbf6bSNeilBrown if (to_put) 570e04bbf6bSNeilBrown iput(to_put); 5719f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5729f0f8e12STrond Myklebust goto restart; 5739f0f8e12STrond Myklebust } 574d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 575515d8611STrond Myklebust rcu_read_unlock(); 576d3978bb3SChuck Lever 577e04bbf6bSNeilBrown if (to_put) 578e04bbf6bSNeilBrown iput(to_put); 579e04bbf6bSNeilBrown 580d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 581515d8611STrond Myklebust iput(inode); 5829f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 5833ca951b6SNeilBrown cond_resched(); 584d18cc1fdSTrond Myklebust if (!err) 585515d8611STrond Myklebust goto restart; 586d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 587e04bbf6bSNeilBrown if (place_holder) 588e04bbf6bSNeilBrown iput(place_holder); 589d18cc1fdSTrond Myklebust return err; 590515d8611STrond Myklebust } 591d3978bb3SChuck Lever } 592515d8611STrond Myklebust rcu_read_unlock(); 593e04bbf6bSNeilBrown if (place_holder) 594e04bbf6bSNeilBrown iput(place_holder); 595d18cc1fdSTrond Myklebust return 0; 596515d8611STrond Myklebust } 597515d8611STrond Myklebust 598d3978bb3SChuck Lever /** 599b47e0e47STrond Myklebust * nfs_inode_evict_delegation - return delegation, don't reclaim opens 600d3978bb3SChuck Lever * @inode: inode to process 601d3978bb3SChuck Lever * 602d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 603b47e0e47STrond Myklebust * to be called from nfs4_clear_inode(). Guaranteed to always free 604b47e0e47STrond Myklebust * the delegation structure. 605e6f81075STrond Myklebust */ 606b47e0e47STrond Myklebust void nfs_inode_evict_delegation(struct inode *inode) 607e6f81075STrond Myklebust { 608e6f81075STrond Myklebust struct nfs_delegation *delegation; 609e6f81075STrond Myklebust 610d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 611b47e0e47STrond Myklebust if (delegation != NULL) { 612f885ea64STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 613b47e0e47STrond Myklebust set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 6145fcdfaccSTrond Myklebust nfs_do_return_delegation(inode, delegation, 1); 615ee05f456STrond Myklebust nfs_free_delegation(delegation); 616e6f81075STrond Myklebust } 617b47e0e47STrond Myklebust } 618e6f81075STrond Myklebust 619d3978bb3SChuck Lever /** 620d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 621d3978bb3SChuck Lever * @inode: inode to process 622d3978bb3SChuck Lever * 623c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 624c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 625c57d1bc5STrond Myklebust * we should stop caching. 626c57d1bc5STrond Myklebust * 627d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 628d3978bb3SChuck Lever */ 62957ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 63090163027STrond Myklebust { 63190163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 63290163027STrond Myklebust struct nfs_delegation *delegation; 63390163027STrond Myklebust int err = 0; 63490163027STrond Myklebust 635c57d1bc5STrond Myklebust nfs_wb_all(inode); 636d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 637d25be546STrond Myklebust if (delegation != NULL) 638d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 63990163027STrond Myklebust return err; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds 642c01d3645STrond Myklebust /** 643b7b7dac6STrond Myklebust * nfs_inode_return_delegation_on_close - asynchronously return a delegation 644b7b7dac6STrond Myklebust * @inode: inode to process 645b7b7dac6STrond Myklebust * 646b7b7dac6STrond Myklebust * This routine is called on file close in order to determine if the 647b7b7dac6STrond Myklebust * inode delegation needs to be returned immediately. 648b7b7dac6STrond Myklebust */ 649b7b7dac6STrond Myklebust void nfs4_inode_return_delegation_on_close(struct inode *inode) 650b7b7dac6STrond Myklebust { 651b7b7dac6STrond Myklebust struct nfs_delegation *delegation; 652b7b7dac6STrond Myklebust struct nfs_delegation *ret = NULL; 653b7b7dac6STrond Myklebust 654b7b7dac6STrond Myklebust if (!inode) 655b7b7dac6STrond Myklebust return; 656b7b7dac6STrond Myklebust rcu_read_lock(); 657b7b7dac6STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 658b7b7dac6STrond Myklebust if (!delegation) 659b7b7dac6STrond Myklebust goto out; 660b7b7dac6STrond Myklebust if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { 661b7b7dac6STrond Myklebust spin_lock(&delegation->lock); 662b7b7dac6STrond Myklebust if (delegation->inode && 663b7b7dac6STrond Myklebust list_empty(&NFS_I(inode)->open_files) && 664b7b7dac6STrond Myklebust !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 665b7b7dac6STrond Myklebust clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 666b7b7dac6STrond Myklebust ret = delegation; 667b7b7dac6STrond Myklebust } 668b7b7dac6STrond Myklebust spin_unlock(&delegation->lock); 669b7b7dac6STrond Myklebust } 670b7b7dac6STrond Myklebust out: 671b7b7dac6STrond Myklebust rcu_read_unlock(); 672b7b7dac6STrond Myklebust nfs_end_delegation_return(inode, ret, 0); 673b7b7dac6STrond Myklebust } 674b7b7dac6STrond Myklebust 675b7b7dac6STrond Myklebust /** 676c01d3645STrond Myklebust * nfs4_inode_make_writeable 677c01d3645STrond Myklebust * @inode: pointer to inode 678c01d3645STrond Myklebust * 679c01d3645STrond Myklebust * Make the inode writeable by returning the delegation if necessary 680c01d3645STrond Myklebust * 681c01d3645STrond Myklebust * Returns zero on success, or a negative errno value. 682c01d3645STrond Myklebust */ 683c01d3645STrond Myklebust int nfs4_inode_make_writeable(struct inode *inode) 684c01d3645STrond Myklebust { 6853887ce1aSTrond Myklebust struct nfs_delegation *delegation; 6863887ce1aSTrond Myklebust 6873887ce1aSTrond Myklebust rcu_read_lock(); 6883887ce1aSTrond Myklebust delegation = nfs4_get_valid_delegation(inode); 6893887ce1aSTrond Myklebust if (delegation == NULL || 6903887ce1aSTrond Myklebust (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && 6913887ce1aSTrond Myklebust (delegation->type & FMODE_WRITE))) { 6923887ce1aSTrond Myklebust rcu_read_unlock(); 693c01d3645STrond Myklebust return 0; 694c01d3645STrond Myklebust } 6953887ce1aSTrond Myklebust rcu_read_unlock(); 6963887ce1aSTrond Myklebust return nfs4_inode_return_delegation(inode); 6973887ce1aSTrond Myklebust } 698c01d3645STrond Myklebust 699b757144fSTrond Myklebust static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 700b757144fSTrond Myklebust struct nfs_delegation *delegation) 701b757144fSTrond Myklebust { 702b757144fSTrond Myklebust set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 703b757144fSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 704b757144fSTrond Myklebust } 705b757144fSTrond Myklebust 706ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 707ed1e6211STrond Myklebust struct nfs_delegation *delegation) 7086411bd4aSTrond Myklebust { 7096411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 710ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 7116411bd4aSTrond Myklebust } 7126411bd4aSTrond Myklebust 7135c31e236STrond Myklebust static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 7145c31e236STrond Myklebust { 7155c31e236STrond Myklebust struct nfs_delegation *delegation; 7165c31e236STrond Myklebust bool ret = false; 7175c31e236STrond Myklebust 7185c31e236STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 7195c31e236STrond Myklebust nfs_mark_return_delegation(server, delegation); 7205c31e236STrond Myklebust ret = true; 7215c31e236STrond Myklebust } 7225c31e236STrond Myklebust return ret; 7235c31e236STrond Myklebust } 7245c31e236STrond Myklebust 725b02ba0b6STrond Myklebust static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 726b02ba0b6STrond Myklebust { 727b02ba0b6STrond Myklebust struct nfs_server *server; 728b02ba0b6STrond Myklebust 729b02ba0b6STrond Myklebust rcu_read_lock(); 730b02ba0b6STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 731b02ba0b6STrond Myklebust nfs_server_mark_return_all_delegations(server); 732b02ba0b6STrond Myklebust rcu_read_unlock(); 733b02ba0b6STrond Myklebust } 734b02ba0b6STrond Myklebust 735b02ba0b6STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 736b02ba0b6STrond Myklebust { 737b02ba0b6STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 738b02ba0b6STrond Myklebust nfs4_schedule_state_manager(clp); 739b02ba0b6STrond Myklebust } 740b02ba0b6STrond Myklebust 741b02ba0b6STrond Myklebust /** 742b02ba0b6STrond Myklebust * nfs_expire_all_delegations 743b02ba0b6STrond Myklebust * @clp: client to process 744b02ba0b6STrond Myklebust * 745b02ba0b6STrond Myklebust */ 746b02ba0b6STrond Myklebust void nfs_expire_all_delegations(struct nfs_client *clp) 747b02ba0b6STrond Myklebust { 748b02ba0b6STrond Myklebust nfs_client_mark_return_all_delegations(clp); 749b02ba0b6STrond Myklebust nfs_delegation_run_state_manager(clp); 750b02ba0b6STrond Myklebust } 751b02ba0b6STrond Myklebust 752d3978bb3SChuck Lever /** 753d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 754302fad7bSTrond Myklebust * @server: pointer to nfs_server to process 755d3978bb3SChuck Lever * 7561da177e4SLinus Torvalds */ 757eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 7581da177e4SLinus Torvalds { 759d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 7605c31e236STrond Myklebust bool need_wait; 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds if (clp == NULL) 7631da177e4SLinus Torvalds return; 764d3978bb3SChuck Lever 7658383e460STrond Myklebust rcu_read_lock(); 7665c31e236STrond Myklebust need_wait = nfs_server_mark_return_all_delegations(server); 7678383e460STrond Myklebust rcu_read_unlock(); 768d3978bb3SChuck Lever 7695c31e236STrond Myklebust if (need_wait) { 770d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 7715c31e236STrond Myklebust nfs4_wait_clnt_recover(clp); 7725c31e236STrond Myklebust } 773515d8611STrond Myklebust } 774515d8611STrond Myklebust 775826e0013STrond Myklebust static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 776d3978bb3SChuck Lever fmode_t flags) 777515d8611STrond Myklebust { 778515d8611STrond Myklebust struct nfs_delegation *delegation; 779515d8611STrond Myklebust 780d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 781c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 782c79571a5SAlexandros Batsakis continue; 783c79571a5SAlexandros Batsakis if (delegation->type & flags) 784826e0013STrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 785707fb4b3STrond Myklebust } 786d3978bb3SChuck Lever } 787d3978bb3SChuck Lever 788826e0013STrond Myklebust static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 789d3978bb3SChuck Lever fmode_t flags) 790d3978bb3SChuck Lever { 791d3978bb3SChuck Lever struct nfs_server *server; 792d3978bb3SChuck Lever 793d3978bb3SChuck Lever rcu_read_lock(); 794d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 795826e0013STrond Myklebust nfs_mark_return_unused_delegation_types(server, flags); 796515d8611STrond Myklebust rcu_read_unlock(); 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 79941020b67STrond Myklebust static void nfs_mark_delegation_revoked(struct nfs_server *server, 80041020b67STrond Myklebust struct nfs_delegation *delegation) 80141020b67STrond Myklebust { 80241020b67STrond Myklebust set_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 803059b43e9STrond Myklebust delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 80441020b67STrond Myklebust } 80541020b67STrond Myklebust 806ee05f456STrond Myklebust static void nfs_revoke_delegation(struct inode *inode, 80741020b67STrond Myklebust const nfs4_stateid *stateid) 808869f9dfaSTrond Myklebust { 809869f9dfaSTrond Myklebust struct nfs_delegation *delegation; 8107f048831STrond Myklebust nfs4_stateid tmp; 81141020b67STrond Myklebust bool ret = false; 81241020b67STrond Myklebust 813869f9dfaSTrond Myklebust rcu_read_lock(); 814869f9dfaSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 81541020b67STrond Myklebust if (delegation == NULL) 81641020b67STrond Myklebust goto out; 8177f048831STrond Myklebust if (stateid == NULL) { 8187f048831STrond Myklebust nfs4_stateid_copy(&tmp, &delegation->stateid); 8197f048831STrond Myklebust stateid = &tmp; 820f2d47b55STrond Myklebust } else { 821f2d47b55STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 82241020b67STrond Myklebust goto out; 823f2d47b55STrond Myklebust spin_lock(&delegation->lock); 824f2d47b55STrond Myklebust if (stateid->seqid) { 825f2d47b55STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { 826f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 827f2d47b55STrond Myklebust goto out; 828f2d47b55STrond Myklebust } 829f2d47b55STrond Myklebust delegation->stateid.seqid = stateid->seqid; 830f2d47b55STrond Myklebust } 831f2d47b55STrond Myklebust spin_unlock(&delegation->lock); 832f2d47b55STrond Myklebust } 83341020b67STrond Myklebust nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation); 83441020b67STrond Myklebust ret = true; 83541020b67STrond Myklebust out: 836869f9dfaSTrond Myklebust rcu_read_unlock(); 8377f048831STrond Myklebust if (ret) 8387f048831STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 839869f9dfaSTrond Myklebust } 840869f9dfaSTrond Myklebust 84141020b67STrond Myklebust void nfs_remove_bad_delegation(struct inode *inode, 84241020b67STrond Myklebust const nfs4_stateid *stateid) 843a1d0b5eeSTrond Myklebust { 844ee05f456STrond Myklebust nfs_revoke_delegation(inode, stateid); 845a1d0b5eeSTrond Myklebust } 8469cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 847a1d0b5eeSTrond Myklebust 848d51f91d2STrond Myklebust void nfs_delegation_mark_returned(struct inode *inode, 849d51f91d2STrond Myklebust const nfs4_stateid *stateid) 850d51f91d2STrond Myklebust { 851d51f91d2STrond Myklebust struct nfs_delegation *delegation; 852d51f91d2STrond Myklebust 853d51f91d2STrond Myklebust if (!inode) 854d51f91d2STrond Myklebust return; 855d51f91d2STrond Myklebust 856d51f91d2STrond Myklebust rcu_read_lock(); 857d51f91d2STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 858d51f91d2STrond Myklebust if (!delegation) 859d51f91d2STrond Myklebust goto out_rcu_unlock; 860d51f91d2STrond Myklebust 861d51f91d2STrond Myklebust spin_lock(&delegation->lock); 862d51f91d2STrond Myklebust if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 863d51f91d2STrond Myklebust goto out_spin_unlock; 864d51f91d2STrond Myklebust if (stateid->seqid) { 865d51f91d2STrond Myklebust /* If delegation->stateid is newer, dont mark as returned */ 866d51f91d2STrond Myklebust if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) 867d51f91d2STrond Myklebust goto out_clear_returning; 868d51f91d2STrond Myklebust if (delegation->stateid.seqid != stateid->seqid) 869d51f91d2STrond Myklebust delegation->stateid.seqid = stateid->seqid; 870d51f91d2STrond Myklebust } 871d51f91d2STrond Myklebust 872ee05f456STrond Myklebust nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation); 873d51f91d2STrond Myklebust 874d51f91d2STrond Myklebust out_clear_returning: 875d51f91d2STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 876d51f91d2STrond Myklebust out_spin_unlock: 877d51f91d2STrond Myklebust spin_unlock(&delegation->lock); 878d51f91d2STrond Myklebust out_rcu_unlock: 879d51f91d2STrond Myklebust rcu_read_unlock(); 880d51f91d2STrond Myklebust 881d51f91d2STrond Myklebust nfs_inode_find_state_and_recover(inode, stateid); 882d51f91d2STrond Myklebust } 883d51f91d2STrond Myklebust 884d3978bb3SChuck Lever /** 885826e0013STrond Myklebust * nfs_expire_unused_delegation_types 886d3978bb3SChuck Lever * @clp: client to process 887d3978bb3SChuck Lever * @flags: delegation types to expire 888d3978bb3SChuck Lever * 889d3978bb3SChuck Lever */ 890826e0013STrond Myklebust void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 891c79571a5SAlexandros Batsakis { 892826e0013STrond Myklebust nfs_client_mark_return_unused_delegation_types(clp, flags); 893c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 894c79571a5SAlexandros Batsakis } 895c79571a5SAlexandros Batsakis 896d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 897b7391f44STrond Myklebust { 898b7391f44STrond Myklebust struct nfs_delegation *delegation; 899b7391f44STrond Myklebust 900d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 901b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 902b7391f44STrond Myklebust continue; 903b757144fSTrond Myklebust nfs_mark_return_if_closed_delegation(server, delegation); 904b7391f44STrond Myklebust } 905b7391f44STrond Myklebust } 906b7391f44STrond Myklebust 907d3978bb3SChuck Lever /** 908d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 909d3978bb3SChuck Lever * @clp: nfs_client to process 910d3978bb3SChuck Lever * 911d3978bb3SChuck Lever */ 912b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 913b7391f44STrond Myklebust { 914d3978bb3SChuck Lever struct nfs_server *server; 915d3978bb3SChuck Lever 916d3978bb3SChuck Lever rcu_read_lock(); 917d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 918d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 919d3978bb3SChuck Lever rcu_read_unlock(); 920d3978bb3SChuck Lever 921b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 922b7391f44STrond Myklebust } 923b7391f44STrond Myklebust 924d3978bb3SChuck Lever /** 925d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 926d3978bb3SChuck Lever * @inode: inode to process 9278e663f0eSTrond Myklebust * @stateid: state ID information 928d3978bb3SChuck Lever * 929d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 9301da177e4SLinus Torvalds */ 931d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 932d3978bb3SChuck Lever const nfs4_stateid *stateid) 9331da177e4SLinus Torvalds { 934ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 935ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 9366411bd4aSTrond Myklebust struct nfs_delegation *delegation; 9371da177e4SLinus Torvalds 9386411bd4aSTrond Myklebust rcu_read_lock(); 939457a5042STrond Myklebust delegation = nfs4_get_valid_delegation(inode); 940755a48a7STrond Myklebust if (delegation == NULL) 941755a48a7STrond Myklebust goto out_enoent; 9424816fdadSTrond Myklebust if (stateid != NULL && 9434816fdadSTrond Myklebust !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 944755a48a7STrond Myklebust goto out_enoent; 945ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 9466411bd4aSTrond Myklebust rcu_read_unlock(); 947d3978bb3SChuck Lever 9486411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 9496411bd4aSTrond Myklebust return 0; 950755a48a7STrond Myklebust out_enoent: 951755a48a7STrond Myklebust rcu_read_unlock(); 952755a48a7STrond Myklebust return -ENOENT; 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 955d3978bb3SChuck Lever static struct inode * 956d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 957d3978bb3SChuck Lever const struct nfs_fh *fhandle) 9581da177e4SLinus Torvalds { 9591da177e4SLinus Torvalds struct nfs_delegation *delegation; 960e39d8a18STrond Myklebust struct inode *freeme, *res = NULL; 961d3978bb3SChuck Lever 962d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 96386e89489STrond Myklebust spin_lock(&delegation->lock); 96486e89489STrond Myklebust if (delegation->inode != NULL && 965457a5042STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 96686e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 967e39d8a18STrond Myklebust freeme = igrab(delegation->inode); 968e39d8a18STrond Myklebust if (freeme && nfs_sb_active(freeme->i_sb)) 969e39d8a18STrond Myklebust res = freeme; 97086e89489STrond Myklebust spin_unlock(&delegation->lock); 97186e89489STrond Myklebust if (res != NULL) 972d3978bb3SChuck Lever return res; 973e39d8a18STrond Myklebust if (freeme) { 974e39d8a18STrond Myklebust rcu_read_unlock(); 975e39d8a18STrond Myklebust iput(freeme); 976e39d8a18STrond Myklebust rcu_read_lock(); 977e39d8a18STrond Myklebust } 9786c342655STrond Myklebust return ERR_PTR(-EAGAIN); 9796c342655STrond Myklebust } 9806c342655STrond Myklebust spin_unlock(&delegation->lock); 9816c342655STrond Myklebust } 9826c342655STrond Myklebust return ERR_PTR(-ENOENT); 983d3978bb3SChuck Lever } 984d3978bb3SChuck Lever 985d3978bb3SChuck Lever /** 986d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 987d3978bb3SChuck Lever * @clp: client state handle 988d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 989d3978bb3SChuck Lever * 990d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 991d3978bb3SChuck Lever * cannot be found. 992d3978bb3SChuck Lever */ 993d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 994d3978bb3SChuck Lever const struct nfs_fh *fhandle) 995d3978bb3SChuck Lever { 996d3978bb3SChuck Lever struct nfs_server *server; 9976c342655STrond Myklebust struct inode *res; 998d3978bb3SChuck Lever 999d3978bb3SChuck Lever rcu_read_lock(); 1000d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1001d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 1002d5681f59SAnna Schumaker if (res != ERR_PTR(-ENOENT)) { 1003d5681f59SAnna Schumaker rcu_read_unlock(); 10046c342655STrond Myklebust return res; 1005d3978bb3SChuck Lever } 1006d5681f59SAnna Schumaker } 10078383e460STrond Myklebust rcu_read_unlock(); 10086c342655STrond Myklebust return ERR_PTR(-ENOENT); 10091da177e4SLinus Torvalds } 10101da177e4SLinus Torvalds 1011d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 1012d3978bb3SChuck Lever { 1013d3978bb3SChuck Lever struct nfs_delegation *delegation; 1014d3978bb3SChuck Lever 101545870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 101645870d69STrond Myklebust /* 101745870d69STrond Myklebust * If the delegation may have been admin revoked, then we 101845870d69STrond Myklebust * cannot reclaim it. 101945870d69STrond Myklebust */ 102045870d69STrond Myklebust if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 102145870d69STrond Myklebust continue; 1022d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 1023d3978bb3SChuck Lever } 102445870d69STrond Myklebust } 1025d3978bb3SChuck Lever 1026d3978bb3SChuck Lever /** 1027d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 1028d3978bb3SChuck Lever * @clp: nfs_client to process 1029d3978bb3SChuck Lever * 10301da177e4SLinus Torvalds */ 1031adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 10321da177e4SLinus Torvalds { 1033d3978bb3SChuck Lever struct nfs_server *server; 1034d3978bb3SChuck Lever 10358383e460STrond Myklebust rcu_read_lock(); 1036d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1037d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 10388383e460STrond Myklebust rcu_read_unlock(); 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 1041d3978bb3SChuck Lever /** 1042d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 1043d3978bb3SChuck Lever * @clp: nfs_client to process 1044d3978bb3SChuck Lever * 10451da177e4SLinus Torvalds */ 1046adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 10471da177e4SLinus Torvalds { 10488383e460STrond Myklebust struct nfs_delegation *delegation; 1049d3978bb3SChuck Lever struct nfs_server *server; 105086e89489STrond Myklebust struct inode *inode; 1051d3978bb3SChuck Lever 10528383e460STrond Myklebust restart: 10538383e460STrond Myklebust rcu_read_lock(); 1054d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1055d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 1056d3978bb3SChuck Lever super_list) { 10576f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 10586f9449beSTrond Myklebust &delegation->flags) || 10596f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 10606f9449beSTrond Myklebust &delegation->flags) || 10616f9449beSTrond Myklebust test_bit(NFS_DELEGATION_NEED_RECLAIM, 1062d3978bb3SChuck Lever &delegation->flags) == 0) 10631da177e4SLinus Torvalds continue; 10649f0f8e12STrond Myklebust if (!nfs_sb_active(server->super)) 1065f3893491SNeilBrown break; /* continue in outer loop */ 10669f0f8e12STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 10679f0f8e12STrond Myklebust if (inode == NULL) { 10689f0f8e12STrond Myklebust rcu_read_unlock(); 10699f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 10709f0f8e12STrond Myklebust goto restart; 10719f0f8e12STrond Myklebust } 1072b04b22f4STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 1073b04b22f4STrond Myklebust rcu_read_unlock(); 1074b04b22f4STrond Myklebust if (delegation != NULL) { 1075d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 1076d25be546STrond Myklebust delegation, server); 10778383e460STrond Myklebust if (delegation != NULL) 1078905f8d16STrond Myklebust nfs_free_delegation(delegation); 1079b04b22f4STrond Myklebust } 108086e89489STrond Myklebust iput(inode); 10819f0f8e12STrond Myklebust nfs_sb_deactive(server->super); 10823ca951b6SNeilBrown cond_resched(); 10838383e460STrond Myklebust goto restart; 10841da177e4SLinus Torvalds } 1085d3978bb3SChuck Lever } 10868383e460STrond Myklebust rcu_read_unlock(); 10871da177e4SLinus Torvalds } 10883e4f6290STrond Myklebust 1089bb3d1a3bSTrond Myklebust static inline bool nfs4_server_rebooted(const struct nfs_client *clp) 1090bb3d1a3bSTrond Myklebust { 1091bb3d1a3bSTrond Myklebust return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 1092bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_LEASE_EXPIRED) | 1093bb3d1a3bSTrond Myklebust BIT(NFS4CLNT_SESSION_RESET))) != 0; 1094bb3d1a3bSTrond Myklebust } 1095bb3d1a3bSTrond Myklebust 109645870d69STrond Myklebust static void nfs_mark_test_expired_delegation(struct nfs_server *server, 109745870d69STrond Myklebust struct nfs_delegation *delegation) 109845870d69STrond Myklebust { 1099059b43e9STrond Myklebust if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 1100059b43e9STrond Myklebust return; 110145870d69STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 110245870d69STrond Myklebust set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 110345870d69STrond Myklebust set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 110445870d69STrond Myklebust } 110545870d69STrond Myklebust 1106bb3d1a3bSTrond Myklebust static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 1107bb3d1a3bSTrond Myklebust struct inode *inode) 1108bb3d1a3bSTrond Myklebust { 1109bb3d1a3bSTrond Myklebust struct nfs_delegation *delegation; 1110bb3d1a3bSTrond Myklebust 1111bb3d1a3bSTrond Myklebust rcu_read_lock(); 1112bb3d1a3bSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1113bb3d1a3bSTrond Myklebust if (delegation) 1114bb3d1a3bSTrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 1115bb3d1a3bSTrond Myklebust rcu_read_unlock(); 1116bb3d1a3bSTrond Myklebust 1117bb3d1a3bSTrond Myklebust } 1118bb3d1a3bSTrond Myklebust 111945870d69STrond Myklebust static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 112045870d69STrond Myklebust { 112145870d69STrond Myklebust struct nfs_delegation *delegation; 112245870d69STrond Myklebust 112345870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, super_list) 112445870d69STrond Myklebust nfs_mark_test_expired_delegation(server, delegation); 112545870d69STrond Myklebust } 112645870d69STrond Myklebust 112745870d69STrond Myklebust /** 112845870d69STrond Myklebust * nfs_mark_test_expired_all_delegations - mark all delegations for testing 112945870d69STrond Myklebust * @clp: nfs_client to process 113045870d69STrond Myklebust * 113145870d69STrond Myklebust * Iterates through all the delegations associated with this server and 113245870d69STrond Myklebust * marks them as needing to be checked for validity. 113345870d69STrond Myklebust */ 113445870d69STrond Myklebust void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 113545870d69STrond Myklebust { 113645870d69STrond Myklebust struct nfs_server *server; 113745870d69STrond Myklebust 113845870d69STrond Myklebust rcu_read_lock(); 113945870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 114045870d69STrond Myklebust nfs_delegation_mark_test_expired_server(server); 114145870d69STrond Myklebust rcu_read_unlock(); 114245870d69STrond Myklebust } 114345870d69STrond Myklebust 114445870d69STrond Myklebust /** 11458ca017c8SScott Mayhew * nfs_test_expired_all_delegations - test all delegations for a client 11468ca017c8SScott Mayhew * @clp: nfs_client to process 11478ca017c8SScott Mayhew * 11488ca017c8SScott Mayhew * Helper for handling "recallable state revoked" status from server. 11498ca017c8SScott Mayhew */ 11508ca017c8SScott Mayhew void nfs_test_expired_all_delegations(struct nfs_client *clp) 11518ca017c8SScott Mayhew { 11528ca017c8SScott Mayhew nfs_mark_test_expired_all_delegations(clp); 11538ca017c8SScott Mayhew nfs4_schedule_state_manager(clp); 11548ca017c8SScott Mayhew } 11558ca017c8SScott Mayhew 1156ad114089STrond Myklebust static void 1157ad114089STrond Myklebust nfs_delegation_test_free_expired(struct inode *inode, 1158ad114089STrond Myklebust nfs4_stateid *stateid, 1159ad114089STrond Myklebust const struct cred *cred) 1160ad114089STrond Myklebust { 1161ad114089STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 1162ad114089STrond Myklebust const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 1163ad114089STrond Myklebust int status; 1164ad114089STrond Myklebust 1165ad114089STrond Myklebust if (!cred) 1166ad114089STrond Myklebust return; 1167ad114089STrond Myklebust status = ops->test_and_free_expired(server, stateid, cred); 1168ad114089STrond Myklebust if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 1169ad114089STrond Myklebust nfs_remove_bad_delegation(inode, stateid); 1170ad114089STrond Myklebust } 1171ad114089STrond Myklebust 11728ca017c8SScott Mayhew /** 117345870d69STrond Myklebust * nfs_reap_expired_delegations - reap expired delegations 117445870d69STrond Myklebust * @clp: nfs_client to process 117545870d69STrond Myklebust * 117645870d69STrond Myklebust * Iterates through all the delegations associated with this server and 117745870d69STrond Myklebust * checks if they have may have been revoked. This function is usually 117845870d69STrond Myklebust * expected to be called in cases where the server may have lost its 117945870d69STrond Myklebust * lease. 118045870d69STrond Myklebust */ 118145870d69STrond Myklebust void nfs_reap_expired_delegations(struct nfs_client *clp) 118245870d69STrond Myklebust { 118345870d69STrond Myklebust struct nfs_delegation *delegation; 118445870d69STrond Myklebust struct nfs_server *server; 118545870d69STrond Myklebust struct inode *inode; 1186a52458b4SNeilBrown const struct cred *cred; 118745870d69STrond Myklebust nfs4_stateid stateid; 118845870d69STrond Myklebust 118945870d69STrond Myklebust restart: 119045870d69STrond Myklebust rcu_read_lock(); 119145870d69STrond Myklebust list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 119245870d69STrond Myklebust list_for_each_entry_rcu(delegation, &server->delegations, 119345870d69STrond Myklebust super_list) { 11946f9449beSTrond Myklebust if (test_bit(NFS_DELEGATION_INODE_FREEING, 11956f9449beSTrond Myklebust &delegation->flags) || 11966f9449beSTrond Myklebust test_bit(NFS_DELEGATION_RETURNING, 11976f9449beSTrond Myklebust &delegation->flags) || 11986f9449beSTrond Myklebust test_bit(NFS_DELEGATION_TEST_EXPIRED, 119945870d69STrond Myklebust &delegation->flags) == 0) 120045870d69STrond Myklebust continue; 120145870d69STrond Myklebust if (!nfs_sb_active(server->super)) 1202f3893491SNeilBrown break; /* continue in outer loop */ 120345870d69STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 120445870d69STrond Myklebust if (inode == NULL) { 120545870d69STrond Myklebust rcu_read_unlock(); 120645870d69STrond Myklebust nfs_sb_deactive(server->super); 120745870d69STrond Myklebust goto restart; 120845870d69STrond Myklebust } 1209a52458b4SNeilBrown cred = get_cred_rcu(delegation->cred); 121045870d69STrond Myklebust nfs4_stateid_copy(&stateid, &delegation->stateid); 121145870d69STrond Myklebust clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 121245870d69STrond Myklebust rcu_read_unlock(); 1213ad114089STrond Myklebust nfs_delegation_test_free_expired(inode, &stateid, cred); 1214a52458b4SNeilBrown put_cred(cred); 1215bb3d1a3bSTrond Myklebust if (nfs4_server_rebooted(clp)) { 1216bb3d1a3bSTrond Myklebust nfs_inode_mark_test_expired_delegation(server,inode); 1217bb3d1a3bSTrond Myklebust iput(inode); 1218bb3d1a3bSTrond Myklebust nfs_sb_deactive(server->super); 1219bb3d1a3bSTrond Myklebust return; 1220bb3d1a3bSTrond Myklebust } 122145870d69STrond Myklebust iput(inode); 122245870d69STrond Myklebust nfs_sb_deactive(server->super); 12233ca951b6SNeilBrown cond_resched(); 122445870d69STrond Myklebust goto restart; 122545870d69STrond Myklebust } 122645870d69STrond Myklebust } 122745870d69STrond Myklebust rcu_read_unlock(); 122845870d69STrond Myklebust } 122945870d69STrond Myklebust 12306c2d8f8dSTrond Myklebust void nfs_inode_find_delegation_state_and_recover(struct inode *inode, 12316c2d8f8dSTrond Myklebust const nfs4_stateid *stateid) 12326c2d8f8dSTrond Myklebust { 12336c2d8f8dSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 12346c2d8f8dSTrond Myklebust struct nfs_delegation *delegation; 12356c2d8f8dSTrond Myklebust bool found = false; 12366c2d8f8dSTrond Myklebust 12376c2d8f8dSTrond Myklebust rcu_read_lock(); 12386c2d8f8dSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 12396c2d8f8dSTrond Myklebust if (delegation && 124042c304c3STrond Myklebust nfs4_stateid_match_or_older(&delegation->stateid, stateid) && 124142c304c3STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 12426c2d8f8dSTrond Myklebust nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 12436c2d8f8dSTrond Myklebust found = true; 12446c2d8f8dSTrond Myklebust } 12456c2d8f8dSTrond Myklebust rcu_read_unlock(); 12466c2d8f8dSTrond Myklebust if (found) 12476c2d8f8dSTrond Myklebust nfs4_schedule_state_manager(clp); 12486c2d8f8dSTrond Myklebust } 12496c2d8f8dSTrond Myklebust 1250d3978bb3SChuck Lever /** 1251d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 1252d3978bb3SChuck Lever * @clp: client state handle 1253d3978bb3SChuck Lever * 1254d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 1255d3978bb3SChuck Lever * to this nfs_client. 1256d3978bb3SChuck Lever */ 1257d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 1258d3978bb3SChuck Lever { 1259d3978bb3SChuck Lever struct nfs_server *server; 1260d3978bb3SChuck Lever int ret = 0; 1261d3978bb3SChuck Lever 1262d3978bb3SChuck Lever rcu_read_lock(); 1263d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 1264d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 1265d3978bb3SChuck Lever ret = 1; 1266d3978bb3SChuck Lever break; 1267d3978bb3SChuck Lever } 1268d3978bb3SChuck Lever rcu_read_unlock(); 1269d3978bb3SChuck Lever return ret; 1270d3978bb3SChuck Lever } 1271d3978bb3SChuck Lever 1272d3978bb3SChuck Lever /** 127312f275cdSTrond Myklebust * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 127412f275cdSTrond Myklebust * @dst: stateid to refresh 127512f275cdSTrond Myklebust * @inode: inode to check 127612f275cdSTrond Myklebust * 127712f275cdSTrond Myklebust * Returns "true" and updates "dst->seqid" * if inode had a delegation 127812f275cdSTrond Myklebust * that matches our delegation stateid. Otherwise "false" is returned. 127912f275cdSTrond Myklebust */ 128012f275cdSTrond Myklebust bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 128112f275cdSTrond Myklebust { 128212f275cdSTrond Myklebust struct nfs_delegation *delegation; 128312f275cdSTrond Myklebust bool ret = false; 128412f275cdSTrond Myklebust if (!inode) 128512f275cdSTrond Myklebust goto out; 128612f275cdSTrond Myklebust 128712f275cdSTrond Myklebust rcu_read_lock(); 128812f275cdSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 128912f275cdSTrond Myklebust if (delegation != NULL && 1290b5756208STrond Myklebust nfs4_stateid_match_other(dst, &delegation->stateid) && 1291246afc0aSTrond Myklebust nfs4_stateid_is_newer(&delegation->stateid, dst) && 1292b5756208STrond Myklebust !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 129312f275cdSTrond Myklebust dst->seqid = delegation->stateid.seqid; 129479cc5542STrond Myklebust ret = true; 129512f275cdSTrond Myklebust } 129612f275cdSTrond Myklebust rcu_read_unlock(); 129712f275cdSTrond Myklebust out: 129812f275cdSTrond Myklebust return ret; 129912f275cdSTrond Myklebust } 130012f275cdSTrond Myklebust 130112f275cdSTrond Myklebust /** 1302d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 1303d3978bb3SChuck Lever * @inode: inode to check 13040032a7a7STrond Myklebust * @flags: delegation type requirement 1305abf4e13cSTrond Myklebust * @dst: stateid data structure to fill in 1306abf4e13cSTrond Myklebust * @cred: optional argument to retrieve credential 1307d3978bb3SChuck Lever * 13080032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 13090032a7a7STrond Myklebust * otherwise "false" is returned. 1310d3978bb3SChuck Lever */ 1311abf4e13cSTrond Myklebust bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 1312a52458b4SNeilBrown nfs4_stateid *dst, const struct cred **cred) 13133e4f6290STrond Myklebust { 13143e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 13153e4f6290STrond Myklebust struct nfs_delegation *delegation; 13160032a7a7STrond Myklebust bool ret; 13173e4f6290STrond Myklebust 13180032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 13198383e460STrond Myklebust rcu_read_lock(); 13208383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 1321aa05c87fSTrond Myklebust ret = nfs4_is_valid_delegation(delegation, flags); 13220032a7a7STrond Myklebust if (ret) { 1323f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 13240032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 1325abf4e13cSTrond Myklebust if (cred) 1326a52458b4SNeilBrown *cred = get_cred(delegation->cred); 13273e4f6290STrond Myklebust } 13288383e460STrond Myklebust rcu_read_unlock(); 13298383e460STrond Myklebust return ret; 13303e4f6290STrond Myklebust } 13315445b1fbSTrond Myklebust 13325445b1fbSTrond Myklebust /** 13335445b1fbSTrond Myklebust * nfs4_delegation_flush_on_close - Check if we must flush file on close 13345445b1fbSTrond Myklebust * @inode: inode to check 13355445b1fbSTrond Myklebust * 13365445b1fbSTrond Myklebust * This function checks the number of outstanding writes to the file 13375445b1fbSTrond Myklebust * against the delegation 'space_limit' field to see if 13385445b1fbSTrond Myklebust * the spec requires us to flush the file on close. 13395445b1fbSTrond Myklebust */ 13405445b1fbSTrond Myklebust bool nfs4_delegation_flush_on_close(const struct inode *inode) 13415445b1fbSTrond Myklebust { 13425445b1fbSTrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 13435445b1fbSTrond Myklebust struct nfs_delegation *delegation; 13445445b1fbSTrond Myklebust bool ret = true; 13455445b1fbSTrond Myklebust 13465445b1fbSTrond Myklebust rcu_read_lock(); 13475445b1fbSTrond Myklebust delegation = rcu_dereference(nfsi->delegation); 13485445b1fbSTrond Myklebust if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 13495445b1fbSTrond Myklebust goto out; 1350a6b6d5b8STrond Myklebust if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 13515445b1fbSTrond Myklebust ret = false; 13525445b1fbSTrond Myklebust out: 13535445b1fbSTrond Myklebust rcu_read_unlock(); 13545445b1fbSTrond Myklebust return ret; 13555445b1fbSTrond Myklebust } 1356