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; 10890163027STrond Myklebust if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0) 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) { 1421da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 1431da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 1441da177e4SLinus Torvalds delegation->type = res->delegation_type; 1451da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 14605c88babSTrond Myklebust oldcred = delegation->cred; 1471da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1488f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 1498f649c37STrond Myklebust &delegation->flags); 1501da177e4SLinus Torvalds NFS_I(inode)->delegation_state = delegation->type; 1518f649c37STrond Myklebust spin_unlock(&delegation->lock); 15205c88babSTrond Myklebust put_rpccred(oldcred); 1538f649c37STrond Myklebust rcu_read_unlock(); 1548f649c37STrond Myklebust } else { 1558f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 1568f649c37STrond Myklebust spin_unlock(&delegation->lock); 1578f649c37STrond Myklebust rcu_read_unlock(); 1588f649c37STrond Myklebust nfs_inode_set_delegation(inode, cred, res); 1598f649c37STrond Myklebust } 1608f649c37STrond Myklebust } else { 1618f649c37STrond Myklebust rcu_read_unlock(); 1628f649c37STrond Myklebust } 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 16557bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 16657bfa891STrond Myklebust { 16757bfa891STrond Myklebust int res = 0; 16857bfa891STrond Myklebust 16957bfa891STrond Myklebust res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 17057bfa891STrond Myklebust nfs_free_delegation(delegation); 17157bfa891STrond Myklebust return res; 17257bfa891STrond Myklebust } 17357bfa891STrond Myklebust 17486e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 17586e89489STrond Myklebust { 17686e89489STrond Myklebust struct inode *inode = NULL; 17786e89489STrond Myklebust 17886e89489STrond Myklebust spin_lock(&delegation->lock); 17986e89489STrond Myklebust if (delegation->inode != NULL) 18086e89489STrond Myklebust inode = igrab(delegation->inode); 18186e89489STrond Myklebust spin_unlock(&delegation->lock); 18286e89489STrond Myklebust return inode; 18386e89489STrond Myklebust } 18486e89489STrond Myklebust 185dda4b225SChuck Lever static struct nfs_delegation * 186dda4b225SChuck Lever nfs_detach_delegation_locked(struct nfs_inode *nfsi, 187d3978bb3SChuck Lever struct nfs_server *server) 18857bfa891STrond Myklebust { 18917d2c0a0SDavid Howells struct nfs_delegation *delegation = 19017d2c0a0SDavid Howells rcu_dereference_protected(nfsi->delegation, 191d3978bb3SChuck Lever lockdep_is_held(&server->nfs_client->cl_lock)); 19257bfa891STrond Myklebust 19357bfa891STrond Myklebust if (delegation == NULL) 19457bfa891STrond Myklebust goto nomatch; 195dda4b225SChuck Lever 19634310430STrond Myklebust spin_lock(&delegation->lock); 19757bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 19886e89489STrond Myklebust delegation->inode = NULL; 19957bfa891STrond Myklebust nfsi->delegation_state = 0; 20057bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 20134310430STrond Myklebust spin_unlock(&delegation->lock); 20257bfa891STrond Myklebust return delegation; 20357bfa891STrond Myklebust nomatch: 20457bfa891STrond Myklebust return NULL; 20557bfa891STrond Myklebust } 20657bfa891STrond Myklebust 207dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 208d3978bb3SChuck Lever struct nfs_server *server) 209dda4b225SChuck Lever { 210d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 211dda4b225SChuck Lever struct nfs_delegation *delegation; 212dda4b225SChuck Lever 213dda4b225SChuck Lever spin_lock(&clp->cl_lock); 214d3978bb3SChuck Lever delegation = nfs_detach_delegation_locked(nfsi, server); 215dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 216dda4b225SChuck Lever return delegation; 217dda4b225SChuck Lever } 218dda4b225SChuck Lever 219d3978bb3SChuck Lever /** 220d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 221d3978bb3SChuck Lever * @inode: inode to which delegation applies 222d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 223d3978bb3SChuck Lever * @res: new delegation state from server 224d3978bb3SChuck Lever * 225d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 2261da177e4SLinus Torvalds */ 2271da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 2281da177e4SLinus Torvalds { 229d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 230d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 2311da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 23217d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 23357bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 2341da177e4SLinus Torvalds int status = 0; 2351da177e4SLinus Torvalds 2368535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 2371da177e4SLinus Torvalds if (delegation == NULL) 2381da177e4SLinus Torvalds return -ENOMEM; 2391da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 2401da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 2411da177e4SLinus Torvalds delegation->type = res->delegation_type; 2421da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 243a9a4a87aSTrond Myklebust delegation->change_attr = inode->i_version; 2441da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 2451da177e4SLinus Torvalds delegation->inode = inode; 246b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 24734310430STrond Myklebust spin_lock_init(&delegation->lock); 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 25017d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 25117d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 25217d2c0a0SDavid Howells if (old_delegation != NULL) { 25317d2c0a0SDavid Howells if (memcmp(&delegation->stateid, &old_delegation->stateid, 25417d2c0a0SDavid Howells sizeof(old_delegation->stateid)) == 0 && 25517d2c0a0SDavid Howells delegation->type == old_delegation->type) { 25657bfa891STrond Myklebust goto out; 25757bfa891STrond Myklebust } 25857bfa891STrond Myklebust /* 25957bfa891STrond Myklebust * Deal with broken servers that hand out two 26057bfa891STrond Myklebust * delegations for the same file. 26157bfa891STrond Myklebust */ 26257bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 26357bfa891STrond Myklebust "a duplicate delegation!\n", 2643110ff80SHarvey Harrison __func__, clp->cl_hostname); 26517d2c0a0SDavid Howells if (delegation->type <= old_delegation->type) { 26657bfa891STrond Myklebust freeme = delegation; 26757bfa891STrond Myklebust delegation = NULL; 26857bfa891STrond Myklebust goto out; 26957bfa891STrond Myklebust } 270d3978bb3SChuck Lever freeme = nfs_detach_delegation_locked(nfsi, server); 27157bfa891STrond Myklebust } 272d3978bb3SChuck Lever list_add_rcu(&delegation->super_list, &server->delegations); 2731da177e4SLinus Torvalds nfsi->delegation_state = delegation->type; 2748383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 2751da177e4SLinus Torvalds delegation = NULL; 276412c77ceSTrond Myklebust 277412c77ceSTrond Myklebust /* Ensure we revalidate the attributes and page cache! */ 278412c77ceSTrond Myklebust spin_lock(&inode->i_lock); 279412c77ceSTrond Myklebust nfsi->cache_validity |= NFS_INO_REVAL_FORCED; 280412c77ceSTrond Myklebust spin_unlock(&inode->i_lock); 281412c77ceSTrond Myklebust 28257bfa891STrond Myklebust out: 2831da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 284603c83daSTrond Myklebust if (delegation != NULL) 285603c83daSTrond Myklebust nfs_free_delegation(delegation); 28657bfa891STrond Myklebust if (freeme != NULL) 28757bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 2881da177e4SLinus Torvalds return status; 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds /* 2921da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 2931da177e4SLinus Torvalds */ 294d18cc1fdSTrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 2951da177e4SLinus Torvalds { 2961da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 297d18cc1fdSTrond Myklebust int err; 2981da177e4SLinus Torvalds 2993f09df70STrond Myklebust /* 3003f09df70STrond Myklebust * Guard against new delegated open/lock/unlock calls and against 3013f09df70STrond Myklebust * state recovery 3023f09df70STrond Myklebust */ 3031da177e4SLinus Torvalds down_write(&nfsi->rwsem); 304d18cc1fdSTrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid); 3051da177e4SLinus Torvalds up_write(&nfsi->rwsem); 306d18cc1fdSTrond Myklebust if (err) 307d18cc1fdSTrond Myklebust goto out; 3081da177e4SLinus Torvalds 309d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 310d18cc1fdSTrond Myklebust out: 311d18cc1fdSTrond Myklebust return err; 31290163027STrond Myklebust } 31390163027STrond Myklebust 314d3978bb3SChuck Lever /** 315d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 316d3978bb3SChuck Lever * @clp: nfs_client to process 317d3978bb3SChuck Lever * 318d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 319515d8611STrond Myklebust */ 320d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 321515d8611STrond Myklebust { 322515d8611STrond Myklebust struct nfs_delegation *delegation; 323d3978bb3SChuck Lever struct nfs_server *server; 324515d8611STrond Myklebust struct inode *inode; 325d18cc1fdSTrond Myklebust int err = 0; 326515d8611STrond Myklebust 327515d8611STrond Myklebust restart: 328515d8611STrond Myklebust rcu_read_lock(); 329d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 330d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 331d3978bb3SChuck Lever super_list) { 332d3978bb3SChuck Lever if (!test_and_clear_bit(NFS_DELEGATION_RETURN, 333d3978bb3SChuck Lever &delegation->flags)) 334515d8611STrond Myklebust continue; 335515d8611STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 336515d8611STrond Myklebust if (inode == NULL) 337515d8611STrond Myklebust continue; 338d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 339d3978bb3SChuck Lever server); 340515d8611STrond Myklebust rcu_read_unlock(); 341d3978bb3SChuck Lever 342d18cc1fdSTrond Myklebust if (delegation != NULL) { 343d18cc1fdSTrond Myklebust filemap_flush(inode->i_mapping); 344d3978bb3SChuck Lever err = __nfs_inode_return_delegation(inode, 345d3978bb3SChuck Lever delegation, 0); 346d18cc1fdSTrond Myklebust } 347515d8611STrond Myklebust iput(inode); 348d18cc1fdSTrond Myklebust if (!err) 349515d8611STrond Myklebust goto restart; 350d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 351d18cc1fdSTrond Myklebust return err; 352515d8611STrond Myklebust } 353d3978bb3SChuck Lever } 354515d8611STrond Myklebust rcu_read_unlock(); 355d18cc1fdSTrond Myklebust return 0; 356515d8611STrond Myklebust } 357515d8611STrond Myklebust 358d3978bb3SChuck Lever /** 359d3978bb3SChuck Lever * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens 360d3978bb3SChuck Lever * @inode: inode to process 361d3978bb3SChuck Lever * 362d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 363d3978bb3SChuck Lever * to be called from nfs4_clear_inode(). 364e6f81075STrond Myklebust */ 365e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 366e6f81075STrond Myklebust { 367d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 368e6f81075STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 369e6f81075STrond Myklebust struct nfs_delegation *delegation; 370e6f81075STrond Myklebust 37117d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 372d3978bb3SChuck Lever delegation = nfs_detach_delegation(nfsi, server); 373e6f81075STrond Myklebust if (delegation != NULL) 374e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 0); 375e6f81075STrond Myklebust } 376e6f81075STrond Myklebust } 377e6f81075STrond Myklebust 378d3978bb3SChuck Lever /** 379d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 380d3978bb3SChuck Lever * @inode: inode to process 381d3978bb3SChuck Lever * 382d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 383d3978bb3SChuck Lever */ 38490163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode) 38590163027STrond Myklebust { 386d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 38790163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 38890163027STrond Myklebust struct nfs_delegation *delegation; 38990163027STrond Myklebust int err = 0; 39090163027STrond Myklebust 39117d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 392d3978bb3SChuck Lever delegation = nfs_detach_delegation(nfsi, server); 393d18cc1fdSTrond Myklebust if (delegation != NULL) { 3941b924e5fSTrond Myklebust nfs_wb_all(inode); 395d18cc1fdSTrond Myklebust err = __nfs_inode_return_delegation(inode, delegation, 1); 396d18cc1fdSTrond Myklebust } 39790163027STrond Myklebust } 39890163027STrond Myklebust return err; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 401ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 402ed1e6211STrond Myklebust struct nfs_delegation *delegation) 4036411bd4aSTrond Myklebust { 4046411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 405ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 4066411bd4aSTrond Myklebust } 4076411bd4aSTrond Myklebust 408d3978bb3SChuck Lever /** 409d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 410d3978bb3SChuck Lever * @sb: sb to process 411d3978bb3SChuck Lever * 4121da177e4SLinus Torvalds */ 413515d8611STrond Myklebust void nfs_super_return_all_delegations(struct super_block *sb) 4141da177e4SLinus Torvalds { 415d3978bb3SChuck Lever struct nfs_server *server = NFS_SB(sb); 416d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 4171da177e4SLinus Torvalds struct nfs_delegation *delegation; 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds if (clp == NULL) 4201da177e4SLinus Torvalds return; 421d3978bb3SChuck Lever 4228383e460STrond Myklebust rcu_read_lock(); 423d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 42486e89489STrond Myklebust spin_lock(&delegation->lock); 425515d8611STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 42686e89489STrond Myklebust spin_unlock(&delegation->lock); 4271da177e4SLinus Torvalds } 4288383e460STrond Myklebust rcu_read_unlock(); 429d3978bb3SChuck Lever 430d18cc1fdSTrond Myklebust if (nfs_client_return_marked_delegations(clp) != 0) 431d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 432515d8611STrond Myklebust } 433515d8611STrond Myklebust 434d3978bb3SChuck Lever static void nfs_mark_return_all_delegation_types(struct nfs_server *server, 435d3978bb3SChuck Lever fmode_t flags) 436515d8611STrond Myklebust { 437515d8611STrond Myklebust struct nfs_delegation *delegation; 438515d8611STrond Myklebust 439d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 440c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 441c79571a5SAlexandros Batsakis continue; 442c79571a5SAlexandros Batsakis if (delegation->type & flags) 443ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 444707fb4b3STrond Myklebust } 445d3978bb3SChuck Lever } 446d3978bb3SChuck Lever 447d3978bb3SChuck Lever static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, 448d3978bb3SChuck Lever fmode_t flags) 449d3978bb3SChuck Lever { 450d3978bb3SChuck Lever struct nfs_server *server; 451d3978bb3SChuck Lever 452d3978bb3SChuck Lever rcu_read_lock(); 453d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 454d3978bb3SChuck Lever nfs_mark_return_all_delegation_types(server, flags); 455515d8611STrond Myklebust rcu_read_unlock(); 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds 458c79571a5SAlexandros Batsakis static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 459c79571a5SAlexandros Batsakis { 460c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 461c79571a5SAlexandros Batsakis } 462c79571a5SAlexandros Batsakis 463b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 46458d9714aSTrond Myklebust { 465b0d3ded1STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 466b0d3ded1STrond Myklebust nfs4_schedule_state_manager(clp); 46758d9714aSTrond Myklebust } 46858d9714aSTrond Myklebust 469a1d0b5eeSTrond Myklebust void nfs_remove_bad_delegation(struct inode *inode) 470a1d0b5eeSTrond Myklebust { 471a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 472a1d0b5eeSTrond Myklebust 473a1d0b5eeSTrond Myklebust delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); 474a1d0b5eeSTrond Myklebust if (delegation) { 475a1d0b5eeSTrond Myklebust nfs_inode_find_state_and_recover(inode, &delegation->stateid); 476a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 477a1d0b5eeSTrond Myklebust } 478a1d0b5eeSTrond Myklebust } 479a1d0b5eeSTrond Myklebust 480d3978bb3SChuck Lever /** 481d3978bb3SChuck Lever * nfs_expire_all_delegation_types 482d3978bb3SChuck Lever * @clp: client to process 483d3978bb3SChuck Lever * @flags: delegation types to expire 484d3978bb3SChuck Lever * 485d3978bb3SChuck Lever */ 48631f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) 487c79571a5SAlexandros Batsakis { 488c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, flags); 489c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 490c79571a5SAlexandros Batsakis } 491c79571a5SAlexandros Batsakis 492d3978bb3SChuck Lever /** 493d3978bb3SChuck Lever * nfs_expire_all_delegations 494d3978bb3SChuck Lever * @clp: client to process 495d3978bb3SChuck Lever * 496d3978bb3SChuck Lever */ 497adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp) 49858d9714aSTrond Myklebust { 499c79571a5SAlexandros Batsakis nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 50058d9714aSTrond Myklebust } 50158d9714aSTrond Myklebust 502d3978bb3SChuck Lever /** 503d3978bb3SChuck Lever * nfs_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN 504d3978bb3SChuck Lever * @clp: client to process 505d3978bb3SChuck Lever * 5061da177e4SLinus Torvalds */ 507adfa6f98SDavid Howells void nfs_handle_cb_pathdown(struct nfs_client *clp) 5081da177e4SLinus Torvalds { 5091da177e4SLinus Torvalds if (clp == NULL) 5101da177e4SLinus Torvalds return; 511707fb4b3STrond Myklebust nfs_client_mark_return_all_delegations(clp); 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 514d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 515b7391f44STrond Myklebust { 516b7391f44STrond Myklebust struct nfs_delegation *delegation; 517b7391f44STrond Myklebust 518d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 519b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 520b7391f44STrond Myklebust continue; 521ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 522b7391f44STrond Myklebust } 523b7391f44STrond Myklebust } 524b7391f44STrond Myklebust 525d3978bb3SChuck Lever /** 526d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 527d3978bb3SChuck Lever * @clp: nfs_client to process 528d3978bb3SChuck Lever * 529d3978bb3SChuck Lever */ 530b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 531b7391f44STrond Myklebust { 532d3978bb3SChuck Lever struct nfs_server *server; 533d3978bb3SChuck Lever 534d3978bb3SChuck Lever rcu_read_lock(); 535d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 536d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 537d3978bb3SChuck Lever rcu_read_unlock(); 538d3978bb3SChuck Lever 539b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 540b7391f44STrond Myklebust } 541b7391f44STrond Myklebust 542d3978bb3SChuck Lever /** 543d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 544d3978bb3SChuck Lever * @inode: inode to process 5458e663f0eSTrond Myklebust * @stateid: state ID information 546d3978bb3SChuck Lever * 547d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 5481da177e4SLinus Torvalds */ 549d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 550d3978bb3SChuck Lever const nfs4_stateid *stateid) 5511da177e4SLinus Torvalds { 552ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 553ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 5546411bd4aSTrond Myklebust struct nfs_delegation *delegation; 5551da177e4SLinus Torvalds 5566411bd4aSTrond Myklebust rcu_read_lock(); 5576411bd4aSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 5582597641dSAlexandros Batsakis 55936281caaSTrond Myklebust if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { 5606411bd4aSTrond Myklebust rcu_read_unlock(); 5616411bd4aSTrond Myklebust return -ENOENT; 5626411bd4aSTrond Myklebust } 563ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 5646411bd4aSTrond Myklebust rcu_read_unlock(); 565d3978bb3SChuck Lever 5666411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 5676411bd4aSTrond Myklebust return 0; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds 570d3978bb3SChuck Lever static struct inode * 571d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 572d3978bb3SChuck Lever const struct nfs_fh *fhandle) 5731da177e4SLinus Torvalds { 5741da177e4SLinus Torvalds struct nfs_delegation *delegation; 5751da177e4SLinus Torvalds struct inode *res = NULL; 576d3978bb3SChuck Lever 577d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 57886e89489STrond Myklebust spin_lock(&delegation->lock); 57986e89489STrond Myklebust if (delegation->inode != NULL && 58086e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 5811da177e4SLinus Torvalds res = igrab(delegation->inode); 5821da177e4SLinus Torvalds } 58386e89489STrond Myklebust spin_unlock(&delegation->lock); 58486e89489STrond Myklebust if (res != NULL) 58586e89489STrond Myklebust break; 5861da177e4SLinus Torvalds } 587d3978bb3SChuck Lever return res; 588d3978bb3SChuck Lever } 589d3978bb3SChuck Lever 590d3978bb3SChuck Lever /** 591d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 592d3978bb3SChuck Lever * @clp: client state handle 593d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 594d3978bb3SChuck Lever * 595d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 596d3978bb3SChuck Lever * cannot be found. 597d3978bb3SChuck Lever */ 598d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 599d3978bb3SChuck Lever const struct nfs_fh *fhandle) 600d3978bb3SChuck Lever { 601d3978bb3SChuck Lever struct nfs_server *server; 602d3978bb3SChuck Lever struct inode *res = NULL; 603d3978bb3SChuck Lever 604d3978bb3SChuck Lever rcu_read_lock(); 605d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 606d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 607d3978bb3SChuck Lever if (res != NULL) 608d3978bb3SChuck Lever break; 609d3978bb3SChuck Lever } 6108383e460STrond Myklebust rcu_read_unlock(); 6111da177e4SLinus Torvalds return res; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 614d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 615d3978bb3SChuck Lever { 616d3978bb3SChuck Lever struct nfs_delegation *delegation; 617d3978bb3SChuck Lever 618d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) 619d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 620d3978bb3SChuck Lever } 621d3978bb3SChuck Lever 622d3978bb3SChuck Lever /** 623d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 624d3978bb3SChuck Lever * @clp: nfs_client to process 625d3978bb3SChuck Lever * 6261da177e4SLinus Torvalds */ 627adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 6281da177e4SLinus Torvalds { 629d3978bb3SChuck Lever struct nfs_server *server; 630d3978bb3SChuck Lever 6318383e460STrond Myklebust rcu_read_lock(); 632d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 633d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 6348383e460STrond Myklebust rcu_read_unlock(); 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 637d3978bb3SChuck Lever /** 638d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 639d3978bb3SChuck Lever * @clp: nfs_client to process 640d3978bb3SChuck Lever * 6411da177e4SLinus Torvalds */ 642adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 6431da177e4SLinus Torvalds { 6448383e460STrond Myklebust struct nfs_delegation *delegation; 645d3978bb3SChuck Lever struct nfs_server *server; 64686e89489STrond Myklebust struct inode *inode; 647d3978bb3SChuck Lever 6488383e460STrond Myklebust restart: 6498383e460STrond Myklebust rcu_read_lock(); 650d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 651d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 652d3978bb3SChuck Lever super_list) { 653d3978bb3SChuck Lever if (test_bit(NFS_DELEGATION_NEED_RECLAIM, 654d3978bb3SChuck Lever &delegation->flags) == 0) 6551da177e4SLinus Torvalds continue; 65686e89489STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 65786e89489STrond Myklebust if (inode == NULL) 65886e89489STrond Myklebust continue; 659d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 660d3978bb3SChuck Lever server); 6618383e460STrond Myklebust rcu_read_unlock(); 662d3978bb3SChuck Lever 6638383e460STrond Myklebust if (delegation != NULL) 664905f8d16STrond Myklebust nfs_free_delegation(delegation); 66586e89489STrond Myklebust iput(inode); 6668383e460STrond Myklebust goto restart; 6671da177e4SLinus Torvalds } 668d3978bb3SChuck Lever } 6698383e460STrond Myklebust rcu_read_unlock(); 6701da177e4SLinus Torvalds } 6713e4f6290STrond Myklebust 672d3978bb3SChuck Lever /** 673d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 674d3978bb3SChuck Lever * @clp: client state handle 675d3978bb3SChuck Lever * 676d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 677d3978bb3SChuck Lever * to this nfs_client. 678d3978bb3SChuck Lever */ 679d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 680d3978bb3SChuck Lever { 681d3978bb3SChuck Lever struct nfs_server *server; 682d3978bb3SChuck Lever int ret = 0; 683d3978bb3SChuck Lever 684d3978bb3SChuck Lever rcu_read_lock(); 685d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 686d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 687d3978bb3SChuck Lever ret = 1; 688d3978bb3SChuck Lever break; 689d3978bb3SChuck Lever } 690d3978bb3SChuck Lever rcu_read_unlock(); 691d3978bb3SChuck Lever return ret; 692d3978bb3SChuck Lever } 693d3978bb3SChuck Lever 694d3978bb3SChuck Lever /** 695d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 696d3978bb3SChuck Lever * @dst: stateid data structure to fill in 697d3978bb3SChuck Lever * @inode: inode to check 698d3978bb3SChuck Lever * 699d3978bb3SChuck Lever * Returns one and fills in "dst->data" * if inode had a delegation, 700d3978bb3SChuck Lever * otherwise zero is returned. 701d3978bb3SChuck Lever */ 7023e4f6290STrond Myklebust int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 7033e4f6290STrond Myklebust { 7043e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 7053e4f6290STrond Myklebust struct nfs_delegation *delegation; 7068383e460STrond Myklebust int ret = 0; 7073e4f6290STrond Myklebust 7088383e460STrond Myklebust rcu_read_lock(); 7098383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 7103e4f6290STrond Myklebust if (delegation != NULL) { 7113e4f6290STrond Myklebust memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); 7128383e460STrond Myklebust ret = 1; 7133e4f6290STrond Myklebust } 7148383e460STrond Myklebust rcu_read_unlock(); 7158383e460STrond Myklebust return ret; 7163e4f6290STrond Myklebust } 717