11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/nfs/delegation.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2004 Trond Myklebust 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * NFS file delegation management 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds #include <linux/completion.h> 1058d9714aSTrond Myklebust #include <linux/kthread.h> 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/sched.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 141da177e4SLinus Torvalds #include <linux/spinlock.h> 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds #include <linux/nfs4.h> 171da177e4SLinus Torvalds #include <linux/nfs_fs.h> 181da177e4SLinus Torvalds #include <linux/nfs_xdr.h> 191da177e4SLinus Torvalds 204ce79717STrond Myklebust #include "nfs4_fs.h" 211da177e4SLinus Torvalds #include "delegation.h" 2224c8dbbbSDavid Howells #include "internal.h" 231da177e4SLinus Torvalds 24905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 25905f8d16STrond Myklebust { 26e00b8a24STrond Myklebust if (delegation->cred) { 27e00b8a24STrond Myklebust put_rpccred(delegation->cred); 28e00b8a24STrond Myklebust delegation->cred = NULL; 29e00b8a24STrond Myklebust } 3026f04ddeSLai Jiangshan kfree_rcu(delegation, rcu); 318383e460STrond Myklebust } 328383e460STrond Myklebust 33d3978bb3SChuck Lever /** 34d3978bb3SChuck Lever * nfs_mark_delegation_referenced - set delegation's REFERENCED flag 35d3978bb3SChuck Lever * @delegation: delegation to process 36d3978bb3SChuck Lever * 37d3978bb3SChuck Lever */ 38b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 39b7391f44STrond Myklebust { 40b7391f44STrond Myklebust set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 41b7391f44STrond Myklebust } 42b7391f44STrond Myklebust 43d3978bb3SChuck Lever /** 44d3978bb3SChuck Lever * nfs_have_delegation - check if inode has a delegation 45d3978bb3SChuck Lever * @inode: inode to check 46d3978bb3SChuck Lever * @flags: delegation types to check for 47d3978bb3SChuck Lever * 48d3978bb3SChuck Lever * Returns one if inode has the indicated delegation, otherwise zero. 49d3978bb3SChuck Lever */ 50bd7bf9d5STrond Myklebust int nfs_have_delegation(struct inode *inode, fmode_t flags) 51b7391f44STrond Myklebust { 52b7391f44STrond Myklebust struct nfs_delegation *delegation; 53b7391f44STrond Myklebust int ret = 0; 54b7391f44STrond Myklebust 55b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 56b7391f44STrond Myklebust rcu_read_lock(); 57b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 58b7391f44STrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags) { 59b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 60b7391f44STrond Myklebust ret = 1; 61b7391f44STrond Myklebust } 62b7391f44STrond Myklebust rcu_read_unlock(); 63b7391f44STrond Myklebust return ret; 64b7391f44STrond Myklebust } 65b7391f44STrond Myklebust 66888e694cSTrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) 67888e694cSTrond Myklebust { 68888e694cSTrond Myklebust struct inode *inode = state->inode; 69888e694cSTrond Myklebust struct file_lock *fl; 70d5122201STrond Myklebust int status = 0; 71888e694cSTrond Myklebust 723f09df70STrond Myklebust if (inode->i_flock == NULL) 733f09df70STrond Myklebust goto out; 743f09df70STrond Myklebust 75b89f4321SArnd Bergmann /* Protect inode->i_flock using the file locks lock */ 76b89f4321SArnd Bergmann lock_flocks(); 7790dc7d27SHarvey Harrison for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { 78888e694cSTrond Myklebust if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) 79888e694cSTrond Myklebust continue; 80cd3758e3STrond Myklebust if (nfs_file_open_context(fl->fl_file) != ctx) 81888e694cSTrond Myklebust continue; 82b89f4321SArnd Bergmann unlock_flocks(); 83888e694cSTrond Myklebust status = nfs4_lock_delegation_recall(state, fl); 84d5122201STrond Myklebust if (status < 0) 853f09df70STrond Myklebust goto out; 86b89f4321SArnd Bergmann lock_flocks(); 87888e694cSTrond Myklebust } 88b89f4321SArnd Bergmann unlock_flocks(); 893f09df70STrond Myklebust out: 90888e694cSTrond Myklebust return status; 91888e694cSTrond Myklebust } 92888e694cSTrond Myklebust 93d18cc1fdSTrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 961da177e4SLinus Torvalds struct nfs_open_context *ctx; 971da177e4SLinus Torvalds struct nfs4_state *state; 98888e694cSTrond Myklebust int err; 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds again: 1011da177e4SLinus Torvalds spin_lock(&inode->i_lock); 1021da177e4SLinus Torvalds list_for_each_entry(ctx, &nfsi->open_files, list) { 1031da177e4SLinus Torvalds state = ctx->state; 1041da177e4SLinus Torvalds if (state == NULL) 1051da177e4SLinus Torvalds continue; 1061da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1071da177e4SLinus Torvalds continue; 108f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 10990163027STrond Myklebust continue; 1101da177e4SLinus Torvalds get_nfs_open_context(ctx); 1111da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 11213437e12STrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 113888e694cSTrond Myklebust if (err >= 0) 114888e694cSTrond Myklebust err = nfs_delegation_claim_locks(ctx, state); 1151da177e4SLinus Torvalds put_nfs_open_context(ctx); 116888e694cSTrond Myklebust if (err != 0) 117d18cc1fdSTrond Myklebust return err; 1181da177e4SLinus Torvalds goto again; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 121d18cc1fdSTrond Myklebust return 0; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 124d3978bb3SChuck Lever /** 125d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 126d3978bb3SChuck Lever * @inode: inode to process 127d3978bb3SChuck Lever * @cred: credential to use for request 128d3978bb3SChuck Lever * @res: new delegation state from server 129d3978bb3SChuck Lever * 1301da177e4SLinus Torvalds */ 131d3978bb3SChuck Lever void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, 132d3978bb3SChuck Lever struct nfs_openres *res) 1331da177e4SLinus Torvalds { 1348f649c37STrond Myklebust struct nfs_delegation *delegation; 1358f649c37STrond Myklebust struct rpc_cred *oldcred = NULL; 1361da177e4SLinus Torvalds 1378f649c37STrond Myklebust rcu_read_lock(); 1388f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1398f649c37STrond Myklebust if (delegation != NULL) { 1408f649c37STrond Myklebust spin_lock(&delegation->lock); 1418f649c37STrond Myklebust if (delegation->inode != NULL) { 142f597c537STrond Myklebust nfs4_stateid_copy(&delegation->stateid, &res->delegation); 1431da177e4SLinus Torvalds delegation->type = res->delegation_type; 1441da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 14505c88babSTrond Myklebust oldcred = delegation->cred; 1461da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1478f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 1488f649c37STrond Myklebust &delegation->flags); 1491da177e4SLinus Torvalds NFS_I(inode)->delegation_state = delegation->type; 1508f649c37STrond Myklebust spin_unlock(&delegation->lock); 15105c88babSTrond Myklebust put_rpccred(oldcred); 1528f649c37STrond Myklebust rcu_read_unlock(); 1538f649c37STrond Myklebust } else { 1548f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 1558f649c37STrond Myklebust spin_unlock(&delegation->lock); 1568f649c37STrond Myklebust rcu_read_unlock(); 1578f649c37STrond Myklebust nfs_inode_set_delegation(inode, cred, res); 1588f649c37STrond Myklebust } 1598f649c37STrond Myklebust } else { 1608f649c37STrond Myklebust rcu_read_unlock(); 1618f649c37STrond Myklebust } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 16457bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 16557bfa891STrond Myklebust { 16657bfa891STrond Myklebust int res = 0; 16757bfa891STrond Myklebust 16857bfa891STrond Myklebust res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 16957bfa891STrond Myklebust nfs_free_delegation(delegation); 17057bfa891STrond Myklebust return res; 17157bfa891STrond Myklebust } 17257bfa891STrond Myklebust 17386e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 17486e89489STrond Myklebust { 17586e89489STrond Myklebust struct inode *inode = NULL; 17686e89489STrond Myklebust 17786e89489STrond Myklebust spin_lock(&delegation->lock); 17886e89489STrond Myklebust if (delegation->inode != NULL) 17986e89489STrond Myklebust inode = igrab(delegation->inode); 18086e89489STrond Myklebust spin_unlock(&delegation->lock); 18186e89489STrond Myklebust return inode; 18286e89489STrond Myklebust } 18386e89489STrond Myklebust 184dda4b225SChuck Lever static struct nfs_delegation * 185dda4b225SChuck Lever nfs_detach_delegation_locked(struct nfs_inode *nfsi, 186d3978bb3SChuck Lever struct nfs_server *server) 18757bfa891STrond Myklebust { 18817d2c0a0SDavid Howells struct nfs_delegation *delegation = 18917d2c0a0SDavid Howells rcu_dereference_protected(nfsi->delegation, 190d3978bb3SChuck Lever lockdep_is_held(&server->nfs_client->cl_lock)); 19157bfa891STrond Myklebust 19257bfa891STrond Myklebust if (delegation == NULL) 19357bfa891STrond Myklebust goto nomatch; 194dda4b225SChuck Lever 19534310430STrond Myklebust spin_lock(&delegation->lock); 19657bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 19786e89489STrond Myklebust delegation->inode = NULL; 19857bfa891STrond Myklebust nfsi->delegation_state = 0; 19957bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 20034310430STrond Myklebust spin_unlock(&delegation->lock); 20157bfa891STrond Myklebust return delegation; 20257bfa891STrond Myklebust nomatch: 20357bfa891STrond Myklebust return NULL; 20457bfa891STrond Myklebust } 20557bfa891STrond Myklebust 206dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 207d3978bb3SChuck Lever struct nfs_server *server) 208dda4b225SChuck Lever { 209d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 210dda4b225SChuck Lever struct nfs_delegation *delegation; 211dda4b225SChuck Lever 212dda4b225SChuck Lever spin_lock(&clp->cl_lock); 213d3978bb3SChuck Lever delegation = nfs_detach_delegation_locked(nfsi, server); 214dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 215dda4b225SChuck Lever return delegation; 216dda4b225SChuck Lever } 217dda4b225SChuck Lever 218d3978bb3SChuck Lever /** 219d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 220d3978bb3SChuck Lever * @inode: inode to which delegation applies 221d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 222d3978bb3SChuck Lever * @res: new delegation state from server 223d3978bb3SChuck Lever * 224d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 2251da177e4SLinus Torvalds */ 2261da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 2271da177e4SLinus Torvalds { 228d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 229d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 2301da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 23117d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 23257bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 2331da177e4SLinus Torvalds int status = 0; 2341da177e4SLinus Torvalds 2358535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 2361da177e4SLinus Torvalds if (delegation == NULL) 2371da177e4SLinus Torvalds return -ENOMEM; 238f597c537STrond Myklebust nfs4_stateid_copy(&delegation->stateid, &res->delegation); 2391da177e4SLinus Torvalds delegation->type = res->delegation_type; 2401da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 241a9a4a87aSTrond Myklebust delegation->change_attr = inode->i_version; 2421da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 2431da177e4SLinus Torvalds delegation->inode = inode; 244b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 24534310430STrond Myklebust spin_lock_init(&delegation->lock); 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 24817d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 24917d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 25017d2c0a0SDavid Howells if (old_delegation != NULL) { 251f597c537STrond Myklebust if (nfs4_stateid_match(&delegation->stateid, 252f597c537STrond Myklebust &old_delegation->stateid) && 25317d2c0a0SDavid Howells delegation->type == old_delegation->type) { 25457bfa891STrond Myklebust goto out; 25557bfa891STrond Myklebust } 25657bfa891STrond Myklebust /* 25757bfa891STrond Myklebust * Deal with broken servers that hand out two 25857bfa891STrond Myklebust * delegations for the same file. 25917280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 26017280175STrond Myklebust * nothing else. 26157bfa891STrond Myklebust */ 26257bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 26357bfa891STrond Myklebust "a duplicate delegation!\n", 2643110ff80SHarvey Harrison __func__, clp->cl_hostname); 26517280175STrond Myklebust if (delegation->type == old_delegation->type || 26617280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 26757bfa891STrond Myklebust freeme = delegation; 26857bfa891STrond Myklebust delegation = NULL; 26957bfa891STrond Myklebust goto out; 27057bfa891STrond Myklebust } 271d3978bb3SChuck Lever freeme = nfs_detach_delegation_locked(nfsi, server); 27257bfa891STrond Myklebust } 273d3978bb3SChuck Lever list_add_rcu(&delegation->super_list, &server->delegations); 2741da177e4SLinus Torvalds nfsi->delegation_state = delegation->type; 2758383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 2761da177e4SLinus Torvalds delegation = NULL; 277412c77ceSTrond Myklebust 278412c77ceSTrond Myklebust /* Ensure we revalidate the attributes and page cache! */ 279412c77ceSTrond Myklebust spin_lock(&inode->i_lock); 280412c77ceSTrond Myklebust nfsi->cache_validity |= NFS_INO_REVAL_FORCED; 281412c77ceSTrond Myklebust spin_unlock(&inode->i_lock); 282412c77ceSTrond Myklebust 28357bfa891STrond Myklebust out: 2841da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 285603c83daSTrond Myklebust if (delegation != NULL) 286603c83daSTrond Myklebust nfs_free_delegation(delegation); 28757bfa891STrond Myklebust if (freeme != NULL) 28857bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 2891da177e4SLinus Torvalds return status; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds /* 2931da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 2941da177e4SLinus Torvalds */ 295d18cc1fdSTrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 298d18cc1fdSTrond Myklebust int err; 2991da177e4SLinus Torvalds 3003f09df70STrond Myklebust /* 3013f09df70STrond Myklebust * Guard against new delegated open/lock/unlock calls and against 3023f09df70STrond Myklebust * state recovery 3033f09df70STrond Myklebust */ 3041da177e4SLinus Torvalds down_write(&nfsi->rwsem); 305d18cc1fdSTrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid); 3061da177e4SLinus Torvalds up_write(&nfsi->rwsem); 307d18cc1fdSTrond Myklebust if (err) 308d18cc1fdSTrond Myklebust goto out; 3091da177e4SLinus Torvalds 310d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 311d18cc1fdSTrond Myklebust out: 312d18cc1fdSTrond Myklebust return err; 31390163027STrond Myklebust } 31490163027STrond Myklebust 315d3978bb3SChuck Lever /** 316d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 317d3978bb3SChuck Lever * @clp: nfs_client to process 318d3978bb3SChuck Lever * 319d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 320515d8611STrond Myklebust */ 321d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 322515d8611STrond Myklebust { 323515d8611STrond Myklebust struct nfs_delegation *delegation; 324d3978bb3SChuck Lever struct nfs_server *server; 325515d8611STrond Myklebust struct inode *inode; 326d18cc1fdSTrond Myklebust int err = 0; 327515d8611STrond Myklebust 328515d8611STrond Myklebust restart: 329515d8611STrond Myklebust rcu_read_lock(); 330d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 331d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 332d3978bb3SChuck Lever super_list) { 333d3978bb3SChuck Lever if (!test_and_clear_bit(NFS_DELEGATION_RETURN, 334d3978bb3SChuck Lever &delegation->flags)) 335515d8611STrond Myklebust continue; 336515d8611STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 337515d8611STrond Myklebust if (inode == NULL) 338515d8611STrond Myklebust continue; 339d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 340d3978bb3SChuck Lever server); 341515d8611STrond Myklebust rcu_read_unlock(); 342d3978bb3SChuck Lever 343d18cc1fdSTrond Myklebust if (delegation != NULL) { 344d18cc1fdSTrond Myklebust filemap_flush(inode->i_mapping); 345d3978bb3SChuck Lever err = __nfs_inode_return_delegation(inode, 346d3978bb3SChuck Lever delegation, 0); 347d18cc1fdSTrond Myklebust } 348515d8611STrond Myklebust iput(inode); 349d18cc1fdSTrond Myklebust if (!err) 350515d8611STrond Myklebust goto restart; 351d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 352d18cc1fdSTrond Myklebust return err; 353515d8611STrond Myklebust } 354d3978bb3SChuck Lever } 355515d8611STrond Myklebust rcu_read_unlock(); 356d18cc1fdSTrond Myklebust return 0; 357515d8611STrond Myklebust } 358515d8611STrond Myklebust 359d3978bb3SChuck Lever /** 360d3978bb3SChuck Lever * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens 361d3978bb3SChuck Lever * @inode: inode to process 362d3978bb3SChuck Lever * 363d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 364d3978bb3SChuck Lever * to be called from nfs4_clear_inode(). 365e6f81075STrond Myklebust */ 366e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 367e6f81075STrond Myklebust { 368d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 369e6f81075STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 370e6f81075STrond Myklebust struct nfs_delegation *delegation; 371e6f81075STrond Myklebust 37217d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 373d3978bb3SChuck Lever delegation = nfs_detach_delegation(nfsi, server); 374e6f81075STrond Myklebust if (delegation != NULL) 375e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 0); 376e6f81075STrond Myklebust } 377e6f81075STrond Myklebust } 378e6f81075STrond Myklebust 379d3978bb3SChuck Lever /** 380d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 381d3978bb3SChuck Lever * @inode: inode to process 382d3978bb3SChuck Lever * 383d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 384d3978bb3SChuck Lever */ 38590163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode) 38690163027STrond Myklebust { 387d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 38890163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 38990163027STrond Myklebust struct nfs_delegation *delegation; 39090163027STrond Myklebust int err = 0; 39190163027STrond Myklebust 39217d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 393d3978bb3SChuck Lever delegation = nfs_detach_delegation(nfsi, server); 394d18cc1fdSTrond Myklebust if (delegation != NULL) { 3951b924e5fSTrond Myklebust nfs_wb_all(inode); 396d18cc1fdSTrond Myklebust err = __nfs_inode_return_delegation(inode, delegation, 1); 397d18cc1fdSTrond Myklebust } 39890163027STrond Myklebust } 39990163027STrond Myklebust return err; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 402ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 403ed1e6211STrond Myklebust struct nfs_delegation *delegation) 4046411bd4aSTrond Myklebust { 4056411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 406ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 4076411bd4aSTrond Myklebust } 4086411bd4aSTrond Myklebust 409d3978bb3SChuck Lever /** 410d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 411d3978bb3SChuck Lever * @sb: sb to process 412d3978bb3SChuck Lever * 4131da177e4SLinus Torvalds */ 414515d8611STrond Myklebust void nfs_super_return_all_delegations(struct super_block *sb) 4151da177e4SLinus Torvalds { 416d3978bb3SChuck Lever struct nfs_server *server = NFS_SB(sb); 417d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 4181da177e4SLinus Torvalds struct nfs_delegation *delegation; 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds if (clp == NULL) 4211da177e4SLinus Torvalds return; 422d3978bb3SChuck Lever 4238383e460STrond Myklebust rcu_read_lock(); 424d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 42586e89489STrond Myklebust spin_lock(&delegation->lock); 426515d8611STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 42786e89489STrond Myklebust spin_unlock(&delegation->lock); 4281da177e4SLinus Torvalds } 4298383e460STrond Myklebust rcu_read_unlock(); 430d3978bb3SChuck Lever 431d18cc1fdSTrond Myklebust if (nfs_client_return_marked_delegations(clp) != 0) 432d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 433515d8611STrond Myklebust } 434515d8611STrond Myklebust 435d3978bb3SChuck Lever static void nfs_mark_return_all_delegation_types(struct nfs_server *server, 436d3978bb3SChuck Lever fmode_t flags) 437515d8611STrond Myklebust { 438515d8611STrond Myklebust struct nfs_delegation *delegation; 439515d8611STrond Myklebust 440d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 441c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 442c79571a5SAlexandros Batsakis continue; 443c79571a5SAlexandros Batsakis if (delegation->type & flags) 444ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 445707fb4b3STrond Myklebust } 446d3978bb3SChuck Lever } 447d3978bb3SChuck Lever 448d3978bb3SChuck Lever static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, 449d3978bb3SChuck Lever fmode_t flags) 450d3978bb3SChuck Lever { 451d3978bb3SChuck Lever struct nfs_server *server; 452d3978bb3SChuck Lever 453d3978bb3SChuck Lever rcu_read_lock(); 454d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 455d3978bb3SChuck Lever nfs_mark_return_all_delegation_types(server, flags); 456515d8611STrond Myklebust rcu_read_unlock(); 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds 459b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 46058d9714aSTrond Myklebust { 461b0d3ded1STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 462b0d3ded1STrond Myklebust nfs4_schedule_state_manager(clp); 46358d9714aSTrond Myklebust } 46458d9714aSTrond Myklebust 465a1d0b5eeSTrond Myklebust void nfs_remove_bad_delegation(struct inode *inode) 466a1d0b5eeSTrond Myklebust { 467a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 468a1d0b5eeSTrond Myklebust 469a1d0b5eeSTrond Myklebust delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); 470a1d0b5eeSTrond Myklebust if (delegation) { 471a1d0b5eeSTrond Myklebust nfs_inode_find_state_and_recover(inode, &delegation->stateid); 472a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 473a1d0b5eeSTrond Myklebust } 474a1d0b5eeSTrond Myklebust } 4759cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 476a1d0b5eeSTrond Myklebust 477d3978bb3SChuck Lever /** 478d3978bb3SChuck Lever * nfs_expire_all_delegation_types 479d3978bb3SChuck Lever * @clp: client to process 480d3978bb3SChuck Lever * @flags: delegation types to expire 481d3978bb3SChuck Lever * 482d3978bb3SChuck Lever */ 48331f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) 484c79571a5SAlexandros Batsakis { 485c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, flags); 486c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 487c79571a5SAlexandros Batsakis } 488c79571a5SAlexandros Batsakis 489d3978bb3SChuck Lever /** 490d3978bb3SChuck Lever * nfs_expire_all_delegations 491d3978bb3SChuck Lever * @clp: client to process 492d3978bb3SChuck Lever * 493d3978bb3SChuck Lever */ 494adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp) 49558d9714aSTrond Myklebust { 496c79571a5SAlexandros Batsakis nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 49758d9714aSTrond Myklebust } 49858d9714aSTrond Myklebust 499d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 500b7391f44STrond Myklebust { 501b7391f44STrond Myklebust struct nfs_delegation *delegation; 502b7391f44STrond Myklebust 503d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 504b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 505b7391f44STrond Myklebust continue; 506ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 507b7391f44STrond Myklebust } 508b7391f44STrond Myklebust } 509b7391f44STrond Myklebust 510d3978bb3SChuck Lever /** 511d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 512d3978bb3SChuck Lever * @clp: nfs_client to process 513d3978bb3SChuck Lever * 514d3978bb3SChuck Lever */ 515b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 516b7391f44STrond Myklebust { 517d3978bb3SChuck Lever struct nfs_server *server; 518d3978bb3SChuck Lever 519d3978bb3SChuck Lever rcu_read_lock(); 520d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 521d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 522d3978bb3SChuck Lever rcu_read_unlock(); 523d3978bb3SChuck Lever 524b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 525b7391f44STrond Myklebust } 526b7391f44STrond Myklebust 527d3978bb3SChuck Lever /** 528d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 529d3978bb3SChuck Lever * @inode: inode to process 5308e663f0eSTrond Myklebust * @stateid: state ID information 531d3978bb3SChuck Lever * 532d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 5331da177e4SLinus Torvalds */ 534d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 535d3978bb3SChuck Lever const nfs4_stateid *stateid) 5361da177e4SLinus Torvalds { 537ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 538ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 5396411bd4aSTrond Myklebust struct nfs_delegation *delegation; 5401da177e4SLinus Torvalds 5416411bd4aSTrond Myklebust rcu_read_lock(); 5426411bd4aSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 5432597641dSAlexandros Batsakis 54436281caaSTrond Myklebust if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { 5456411bd4aSTrond Myklebust rcu_read_unlock(); 5466411bd4aSTrond Myklebust return -ENOENT; 5476411bd4aSTrond Myklebust } 548ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 5496411bd4aSTrond Myklebust rcu_read_unlock(); 550d3978bb3SChuck Lever 5516411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 5526411bd4aSTrond Myklebust return 0; 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds 555d3978bb3SChuck Lever static struct inode * 556d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 557d3978bb3SChuck Lever const struct nfs_fh *fhandle) 5581da177e4SLinus Torvalds { 5591da177e4SLinus Torvalds struct nfs_delegation *delegation; 5601da177e4SLinus Torvalds struct inode *res = NULL; 561d3978bb3SChuck Lever 562d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 56386e89489STrond Myklebust spin_lock(&delegation->lock); 56486e89489STrond Myklebust if (delegation->inode != NULL && 56586e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 5661da177e4SLinus Torvalds res = igrab(delegation->inode); 5671da177e4SLinus Torvalds } 56886e89489STrond Myklebust spin_unlock(&delegation->lock); 56986e89489STrond Myklebust if (res != NULL) 57086e89489STrond Myklebust break; 5711da177e4SLinus Torvalds } 572d3978bb3SChuck Lever return res; 573d3978bb3SChuck Lever } 574d3978bb3SChuck Lever 575d3978bb3SChuck Lever /** 576d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 577d3978bb3SChuck Lever * @clp: client state handle 578d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 579d3978bb3SChuck Lever * 580d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 581d3978bb3SChuck Lever * cannot be found. 582d3978bb3SChuck Lever */ 583d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 584d3978bb3SChuck Lever const struct nfs_fh *fhandle) 585d3978bb3SChuck Lever { 586d3978bb3SChuck Lever struct nfs_server *server; 587d3978bb3SChuck Lever struct inode *res = NULL; 588d3978bb3SChuck Lever 589d3978bb3SChuck Lever rcu_read_lock(); 590d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 591d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 592d3978bb3SChuck Lever if (res != NULL) 593d3978bb3SChuck Lever break; 594d3978bb3SChuck Lever } 5958383e460STrond Myklebust rcu_read_unlock(); 5961da177e4SLinus Torvalds return res; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 599d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 600d3978bb3SChuck Lever { 601d3978bb3SChuck Lever struct nfs_delegation *delegation; 602d3978bb3SChuck Lever 603d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) 604d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 605d3978bb3SChuck Lever } 606d3978bb3SChuck Lever 607d3978bb3SChuck Lever /** 608d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 609d3978bb3SChuck Lever * @clp: nfs_client to process 610d3978bb3SChuck Lever * 6111da177e4SLinus Torvalds */ 612adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 6131da177e4SLinus Torvalds { 614d3978bb3SChuck Lever struct nfs_server *server; 615d3978bb3SChuck Lever 6168383e460STrond Myklebust rcu_read_lock(); 617d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 618d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 6198383e460STrond Myklebust rcu_read_unlock(); 6201da177e4SLinus Torvalds } 6211da177e4SLinus Torvalds 622d3978bb3SChuck Lever /** 623d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 624d3978bb3SChuck Lever * @clp: nfs_client to process 625d3978bb3SChuck Lever * 6261da177e4SLinus Torvalds */ 627adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 6281da177e4SLinus Torvalds { 6298383e460STrond Myklebust struct nfs_delegation *delegation; 630d3978bb3SChuck Lever struct nfs_server *server; 63186e89489STrond Myklebust struct inode *inode; 632d3978bb3SChuck Lever 6338383e460STrond Myklebust restart: 6348383e460STrond Myklebust rcu_read_lock(); 635d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 636d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 637d3978bb3SChuck Lever super_list) { 638d3978bb3SChuck Lever if (test_bit(NFS_DELEGATION_NEED_RECLAIM, 639d3978bb3SChuck Lever &delegation->flags) == 0) 6401da177e4SLinus Torvalds continue; 64186e89489STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 64286e89489STrond Myklebust if (inode == NULL) 64386e89489STrond Myklebust continue; 644d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 645d3978bb3SChuck Lever server); 6468383e460STrond Myklebust rcu_read_unlock(); 647d3978bb3SChuck Lever 6488383e460STrond Myklebust if (delegation != NULL) 649905f8d16STrond Myklebust nfs_free_delegation(delegation); 65086e89489STrond Myklebust iput(inode); 6518383e460STrond Myklebust goto restart; 6521da177e4SLinus Torvalds } 653d3978bb3SChuck Lever } 6548383e460STrond Myklebust rcu_read_unlock(); 6551da177e4SLinus Torvalds } 6563e4f6290STrond Myklebust 657d3978bb3SChuck Lever /** 658d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 659d3978bb3SChuck Lever * @clp: client state handle 660d3978bb3SChuck Lever * 661d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 662d3978bb3SChuck Lever * to this nfs_client. 663d3978bb3SChuck Lever */ 664d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 665d3978bb3SChuck Lever { 666d3978bb3SChuck Lever struct nfs_server *server; 667d3978bb3SChuck Lever int ret = 0; 668d3978bb3SChuck Lever 669d3978bb3SChuck Lever rcu_read_lock(); 670d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 671d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 672d3978bb3SChuck Lever ret = 1; 673d3978bb3SChuck Lever break; 674d3978bb3SChuck Lever } 675d3978bb3SChuck Lever rcu_read_unlock(); 676d3978bb3SChuck Lever return ret; 677d3978bb3SChuck Lever } 678d3978bb3SChuck Lever 679d3978bb3SChuck Lever /** 680d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 681d3978bb3SChuck Lever * @dst: stateid data structure to fill in 682d3978bb3SChuck Lever * @inode: inode to check 6830032a7a7STrond Myklebust * @flags: delegation type requirement 684d3978bb3SChuck Lever * 6850032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 6860032a7a7STrond Myklebust * otherwise "false" is returned. 687d3978bb3SChuck Lever */ 6880032a7a7STrond Myklebust bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, 6890032a7a7STrond Myklebust fmode_t flags) 6903e4f6290STrond Myklebust { 6913e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 6923e4f6290STrond Myklebust struct nfs_delegation *delegation; 6930032a7a7STrond Myklebust bool ret; 6943e4f6290STrond Myklebust 6950032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 6968383e460STrond Myklebust rcu_read_lock(); 6978383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 6980032a7a7STrond Myklebust ret = (delegation != NULL && (delegation->type & flags) == flags); 6990032a7a7STrond Myklebust if (ret) { 700f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 7010032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 7023e4f6290STrond Myklebust } 7038383e460STrond Myklebust rcu_read_unlock(); 7048383e460STrond Myklebust return ret; 7053e4f6290STrond Myklebust } 706