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> 14405f5571SAlexey Dobriyan #include <linux/smp_lock.h> 151da177e4SLinus Torvalds #include <linux/spinlock.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/nfs4.h> 181da177e4SLinus Torvalds #include <linux/nfs_fs.h> 191da177e4SLinus Torvalds #include <linux/nfs_xdr.h> 201da177e4SLinus Torvalds 214ce79717STrond Myklebust #include "nfs4_fs.h" 221da177e4SLinus Torvalds #include "delegation.h" 2324c8dbbbSDavid Howells #include "internal.h" 241da177e4SLinus Torvalds 25905f8d16STrond Myklebust static void nfs_do_free_delegation(struct nfs_delegation *delegation) 261da177e4SLinus Torvalds { 2717d2c0a0SDavid Howells if (delegation->cred) 2817d2c0a0SDavid Howells put_rpccred(delegation->cred); 291da177e4SLinus Torvalds kfree(delegation); 301da177e4SLinus Torvalds } 311da177e4SLinus Torvalds 328383e460STrond Myklebust static void nfs_free_delegation_callback(struct rcu_head *head) 338383e460STrond Myklebust { 348383e460STrond Myklebust struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu); 358383e460STrond Myklebust 36905f8d16STrond Myklebust nfs_do_free_delegation(delegation); 37905f8d16STrond Myklebust } 38905f8d16STrond Myklebust 39905f8d16STrond Myklebust static void nfs_free_delegation(struct nfs_delegation *delegation) 40905f8d16STrond Myklebust { 41905f8d16STrond Myklebust call_rcu(&delegation->rcu, nfs_free_delegation_callback); 428383e460STrond Myklebust } 438383e460STrond Myklebust 44b7391f44STrond Myklebust void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 45b7391f44STrond Myklebust { 46b7391f44STrond Myklebust set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 47b7391f44STrond Myklebust } 48b7391f44STrond Myklebust 49bd7bf9d5STrond Myklebust int nfs_have_delegation(struct inode *inode, fmode_t flags) 50b7391f44STrond Myklebust { 51b7391f44STrond Myklebust struct nfs_delegation *delegation; 52b7391f44STrond Myklebust int ret = 0; 53b7391f44STrond Myklebust 54b7391f44STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 55b7391f44STrond Myklebust rcu_read_lock(); 56b7391f44STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 57b7391f44STrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags) { 58b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 59b7391f44STrond Myklebust ret = 1; 60b7391f44STrond Myklebust } 61b7391f44STrond Myklebust rcu_read_unlock(); 62b7391f44STrond Myklebust return ret; 63b7391f44STrond Myklebust } 64b7391f44STrond Myklebust 65888e694cSTrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) 66888e694cSTrond Myklebust { 67888e694cSTrond Myklebust struct inode *inode = state->inode; 68888e694cSTrond Myklebust struct file_lock *fl; 69d5122201STrond Myklebust int status = 0; 70888e694cSTrond Myklebust 713f09df70STrond Myklebust if (inode->i_flock == NULL) 723f09df70STrond Myklebust goto out; 733f09df70STrond Myklebust 743f09df70STrond Myklebust /* Protect inode->i_flock using the BKL */ 753f09df70STrond Myklebust lock_kernel(); 7690dc7d27SHarvey Harrison for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { 77888e694cSTrond Myklebust if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) 78888e694cSTrond Myklebust continue; 79cd3758e3STrond Myklebust if (nfs_file_open_context(fl->fl_file) != ctx) 80888e694cSTrond Myklebust continue; 813f09df70STrond Myklebust unlock_kernel(); 82888e694cSTrond Myklebust status = nfs4_lock_delegation_recall(state, fl); 83d5122201STrond Myklebust if (status < 0) 843f09df70STrond Myklebust goto out; 853f09df70STrond Myklebust lock_kernel(); 86888e694cSTrond Myklebust } 873f09df70STrond Myklebust unlock_kernel(); 883f09df70STrond Myklebust out: 89888e694cSTrond Myklebust return status; 90888e694cSTrond Myklebust } 91888e694cSTrond Myklebust 92d18cc1fdSTrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) 931da177e4SLinus Torvalds { 941da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 951da177e4SLinus Torvalds struct nfs_open_context *ctx; 961da177e4SLinus Torvalds struct nfs4_state *state; 97888e694cSTrond Myklebust int err; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds again: 1001da177e4SLinus Torvalds spin_lock(&inode->i_lock); 1011da177e4SLinus Torvalds list_for_each_entry(ctx, &nfsi->open_files, list) { 1021da177e4SLinus Torvalds state = ctx->state; 1031da177e4SLinus Torvalds if (state == NULL) 1041da177e4SLinus Torvalds continue; 1051da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1061da177e4SLinus Torvalds continue; 10790163027STrond Myklebust if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0) 10890163027STrond Myklebust continue; 1091da177e4SLinus Torvalds get_nfs_open_context(ctx); 1101da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 11113437e12STrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 112888e694cSTrond Myklebust if (err >= 0) 113888e694cSTrond Myklebust err = nfs_delegation_claim_locks(ctx, state); 1141da177e4SLinus Torvalds put_nfs_open_context(ctx); 115888e694cSTrond Myklebust if (err != 0) 116d18cc1fdSTrond Myklebust return err; 1171da177e4SLinus Torvalds goto again; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 120d18cc1fdSTrond Myklebust return 0; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds /* 1241da177e4SLinus Torvalds * Set up a delegation on an inode 1251da177e4SLinus Torvalds */ 1261da177e4SLinus Torvalds void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 1271da177e4SLinus Torvalds { 1288f649c37STrond Myklebust struct nfs_delegation *delegation; 1298f649c37STrond Myklebust struct rpc_cred *oldcred = NULL; 1301da177e4SLinus Torvalds 1318f649c37STrond Myklebust rcu_read_lock(); 1328f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1338f649c37STrond Myklebust if (delegation != NULL) { 1348f649c37STrond Myklebust spin_lock(&delegation->lock); 1358f649c37STrond Myklebust if (delegation->inode != NULL) { 1361da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 1371da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 1381da177e4SLinus Torvalds delegation->type = res->delegation_type; 1391da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 14005c88babSTrond Myklebust oldcred = delegation->cred; 1411da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1428f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 1438f649c37STrond Myklebust &delegation->flags); 1441da177e4SLinus Torvalds NFS_I(inode)->delegation_state = delegation->type; 1458f649c37STrond Myklebust spin_unlock(&delegation->lock); 14605c88babSTrond Myklebust put_rpccred(oldcred); 1478f649c37STrond Myklebust rcu_read_unlock(); 1488f649c37STrond Myklebust } else { 1498f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 1508f649c37STrond Myklebust spin_unlock(&delegation->lock); 1518f649c37STrond Myklebust rcu_read_unlock(); 1528f649c37STrond Myklebust nfs_inode_set_delegation(inode, cred, res); 1538f649c37STrond Myklebust } 1548f649c37STrond Myklebust } else { 1558f649c37STrond Myklebust rcu_read_unlock(); 1568f649c37STrond Myklebust } 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 15957bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 16057bfa891STrond Myklebust { 16157bfa891STrond Myklebust int res = 0; 16257bfa891STrond Myklebust 16357bfa891STrond Myklebust res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 16457bfa891STrond Myklebust nfs_free_delegation(delegation); 16557bfa891STrond Myklebust return res; 16657bfa891STrond Myklebust } 16757bfa891STrond Myklebust 16886e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 16986e89489STrond Myklebust { 17086e89489STrond Myklebust struct inode *inode = NULL; 17186e89489STrond Myklebust 17286e89489STrond Myklebust spin_lock(&delegation->lock); 17386e89489STrond Myklebust if (delegation->inode != NULL) 17486e89489STrond Myklebust inode = igrab(delegation->inode); 17586e89489STrond Myklebust spin_unlock(&delegation->lock); 17686e89489STrond Myklebust return inode; 17786e89489STrond Myklebust } 17886e89489STrond Myklebust 17917d2c0a0SDavid Howells static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, 18017d2c0a0SDavid Howells const nfs4_stateid *stateid, 18117d2c0a0SDavid Howells struct nfs_client *clp) 18257bfa891STrond Myklebust { 18317d2c0a0SDavid Howells struct nfs_delegation *delegation = 18417d2c0a0SDavid Howells rcu_dereference_protected(nfsi->delegation, 18517d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 18657bfa891STrond Myklebust 18757bfa891STrond Myklebust if (delegation == NULL) 18857bfa891STrond Myklebust goto nomatch; 18934310430STrond Myklebust spin_lock(&delegation->lock); 19057bfa891STrond Myklebust if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, 19157bfa891STrond Myklebust sizeof(delegation->stateid.data)) != 0) 19234310430STrond Myklebust goto nomatch_unlock; 19357bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 19486e89489STrond Myklebust delegation->inode = NULL; 19557bfa891STrond Myklebust nfsi->delegation_state = 0; 19657bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 19734310430STrond Myklebust spin_unlock(&delegation->lock); 19857bfa891STrond Myklebust return delegation; 19934310430STrond Myklebust nomatch_unlock: 20034310430STrond Myklebust spin_unlock(&delegation->lock); 20157bfa891STrond Myklebust nomatch: 20257bfa891STrond Myklebust return NULL; 20357bfa891STrond Myklebust } 20457bfa891STrond Myklebust 2051da177e4SLinus Torvalds /* 2061da177e4SLinus Torvalds * Set up a delegation on an inode 2071da177e4SLinus Torvalds */ 2081da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 2091da177e4SLinus Torvalds { 2107539bbabSDavid Howells struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 2111da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 21217d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 21357bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 2141da177e4SLinus Torvalds int status = 0; 2151da177e4SLinus Torvalds 2168535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 2171da177e4SLinus Torvalds if (delegation == NULL) 2181da177e4SLinus Torvalds return -ENOMEM; 2191da177e4SLinus Torvalds memcpy(delegation->stateid.data, res->delegation.data, 2201da177e4SLinus Torvalds sizeof(delegation->stateid.data)); 2211da177e4SLinus Torvalds delegation->type = res->delegation_type; 2221da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 223beb2a5ecSTrond Myklebust delegation->change_attr = nfsi->change_attr; 2241da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 2251da177e4SLinus Torvalds delegation->inode = inode; 226b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 22734310430STrond Myklebust spin_lock_init(&delegation->lock); 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 23017d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 23117d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 23217d2c0a0SDavid Howells if (old_delegation != NULL) { 23317d2c0a0SDavid Howells if (memcmp(&delegation->stateid, &old_delegation->stateid, 23417d2c0a0SDavid Howells sizeof(old_delegation->stateid)) == 0 && 23517d2c0a0SDavid Howells delegation->type == old_delegation->type) { 23657bfa891STrond Myklebust goto out; 23757bfa891STrond Myklebust } 23857bfa891STrond Myklebust /* 23957bfa891STrond Myklebust * Deal with broken servers that hand out two 24057bfa891STrond Myklebust * delegations for the same file. 24157bfa891STrond Myklebust */ 24257bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 24357bfa891STrond Myklebust "a duplicate delegation!\n", 2443110ff80SHarvey Harrison __func__, clp->cl_hostname); 24517d2c0a0SDavid Howells if (delegation->type <= old_delegation->type) { 24657bfa891STrond Myklebust freeme = delegation; 24757bfa891STrond Myklebust delegation = NULL; 24857bfa891STrond Myklebust goto out; 24957bfa891STrond Myklebust } 25017d2c0a0SDavid Howells freeme = nfs_detach_delegation_locked(nfsi, NULL, clp); 25157bfa891STrond Myklebust } 2528383e460STrond Myklebust list_add_rcu(&delegation->super_list, &clp->cl_delegations); 2531da177e4SLinus Torvalds nfsi->delegation_state = delegation->type; 2548383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 2551da177e4SLinus Torvalds delegation = NULL; 256412c77ceSTrond Myklebust 257412c77ceSTrond Myklebust /* Ensure we revalidate the attributes and page cache! */ 258412c77ceSTrond Myklebust spin_lock(&inode->i_lock); 259412c77ceSTrond Myklebust nfsi->cache_validity |= NFS_INO_REVAL_FORCED; 260412c77ceSTrond Myklebust spin_unlock(&inode->i_lock); 261412c77ceSTrond Myklebust 26257bfa891STrond Myklebust out: 2631da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 264603c83daSTrond Myklebust if (delegation != NULL) 265603c83daSTrond Myklebust nfs_free_delegation(delegation); 26657bfa891STrond Myklebust if (freeme != NULL) 26757bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 2681da177e4SLinus Torvalds return status; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* Sync all data to disk upon delegation return */ 2721da177e4SLinus Torvalds static void nfs_msync_inode(struct inode *inode) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds filemap_fdatawrite(inode->i_mapping); 2751da177e4SLinus Torvalds nfs_wb_all(inode); 2761da177e4SLinus Torvalds filemap_fdatawait(inode->i_mapping); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds /* 2801da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 2811da177e4SLinus Torvalds */ 282d18cc1fdSTrond Myklebust static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 2831da177e4SLinus Torvalds { 2841da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 285d18cc1fdSTrond Myklebust int err; 2861da177e4SLinus Torvalds 2873f09df70STrond Myklebust /* 2883f09df70STrond Myklebust * Guard against new delegated open/lock/unlock calls and against 2893f09df70STrond Myklebust * state recovery 2903f09df70STrond Myklebust */ 2911da177e4SLinus Torvalds down_write(&nfsi->rwsem); 292d18cc1fdSTrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid); 2931da177e4SLinus Torvalds up_write(&nfsi->rwsem); 294d18cc1fdSTrond Myklebust if (err) 295d18cc1fdSTrond Myklebust goto out; 2961da177e4SLinus Torvalds 297d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 298d18cc1fdSTrond Myklebust out: 299d18cc1fdSTrond Myklebust return err; 30090163027STrond Myklebust } 30190163027STrond Myklebust 302e6f81075STrond Myklebust /* 303515d8611STrond Myklebust * Return all delegations that have been marked for return 304515d8611STrond Myklebust */ 305d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 306515d8611STrond Myklebust { 307515d8611STrond Myklebust struct nfs_delegation *delegation; 308515d8611STrond Myklebust struct inode *inode; 309d18cc1fdSTrond Myklebust int err = 0; 310515d8611STrond Myklebust 311515d8611STrond Myklebust restart: 312515d8611STrond Myklebust rcu_read_lock(); 313515d8611STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 314515d8611STrond Myklebust if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 315515d8611STrond Myklebust continue; 316515d8611STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 317515d8611STrond Myklebust if (inode == NULL) 318515d8611STrond Myklebust continue; 319515d8611STrond Myklebust spin_lock(&clp->cl_lock); 32017d2c0a0SDavid Howells delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); 321515d8611STrond Myklebust spin_unlock(&clp->cl_lock); 322515d8611STrond Myklebust rcu_read_unlock(); 323d18cc1fdSTrond Myklebust if (delegation != NULL) { 324d18cc1fdSTrond Myklebust filemap_flush(inode->i_mapping); 325d18cc1fdSTrond Myklebust err = __nfs_inode_return_delegation(inode, delegation, 0); 326d18cc1fdSTrond Myklebust } 327515d8611STrond Myklebust iput(inode); 328d18cc1fdSTrond Myklebust if (!err) 329515d8611STrond Myklebust goto restart; 330d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 331d18cc1fdSTrond Myklebust return err; 332515d8611STrond Myklebust } 333515d8611STrond Myklebust rcu_read_unlock(); 334d18cc1fdSTrond Myklebust return 0; 335515d8611STrond Myklebust } 336515d8611STrond Myklebust 337515d8611STrond Myklebust /* 338e6f81075STrond Myklebust * This function returns the delegation without reclaiming opens 339e6f81075STrond Myklebust * or protecting against delegation reclaims. 340e6f81075STrond Myklebust * It is therefore really only safe to be called from 341e6f81075STrond Myklebust * nfs4_clear_inode() 342e6f81075STrond Myklebust */ 343e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 344e6f81075STrond Myklebust { 345e6f81075STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 346e6f81075STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 347e6f81075STrond Myklebust struct nfs_delegation *delegation; 348e6f81075STrond Myklebust 34917d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 350e6f81075STrond Myklebust spin_lock(&clp->cl_lock); 35117d2c0a0SDavid Howells delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); 352e6f81075STrond Myklebust spin_unlock(&clp->cl_lock); 353e6f81075STrond Myklebust if (delegation != NULL) 354e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 0); 355e6f81075STrond Myklebust } 356e6f81075STrond Myklebust } 357e6f81075STrond Myklebust 35890163027STrond Myklebust int nfs_inode_return_delegation(struct inode *inode) 35990163027STrond Myklebust { 36090163027STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 36190163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 36290163027STrond Myklebust struct nfs_delegation *delegation; 36390163027STrond Myklebust int err = 0; 36490163027STrond Myklebust 36517d2c0a0SDavid Howells if (rcu_access_pointer(nfsi->delegation) != NULL) { 36690163027STrond Myklebust spin_lock(&clp->cl_lock); 36717d2c0a0SDavid Howells delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); 36890163027STrond Myklebust spin_unlock(&clp->cl_lock); 369d18cc1fdSTrond Myklebust if (delegation != NULL) { 370d18cc1fdSTrond Myklebust nfs_msync_inode(inode); 371d18cc1fdSTrond Myklebust err = __nfs_inode_return_delegation(inode, delegation, 1); 372d18cc1fdSTrond Myklebust } 37390163027STrond Myklebust } 37490163027STrond Myklebust return err; 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 3776411bd4aSTrond Myklebust static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation) 3786411bd4aSTrond Myklebust { 3796411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 3806411bd4aSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 3816411bd4aSTrond Myklebust } 3826411bd4aSTrond Myklebust 3831da177e4SLinus Torvalds /* 3841da177e4SLinus Torvalds * Return all delegations associated to a super block 3851da177e4SLinus Torvalds */ 386515d8611STrond Myklebust void nfs_super_return_all_delegations(struct super_block *sb) 3871da177e4SLinus Torvalds { 3887539bbabSDavid Howells struct nfs_client *clp = NFS_SB(sb)->nfs_client; 3891da177e4SLinus Torvalds struct nfs_delegation *delegation; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds if (clp == NULL) 3921da177e4SLinus Torvalds return; 3938383e460STrond Myklebust rcu_read_lock(); 3948383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 39586e89489STrond Myklebust spin_lock(&delegation->lock); 39686e89489STrond Myklebust if (delegation->inode != NULL && delegation->inode->i_sb == sb) 397515d8611STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 39886e89489STrond Myklebust spin_unlock(&delegation->lock); 3991da177e4SLinus Torvalds } 4008383e460STrond Myklebust rcu_read_unlock(); 401d18cc1fdSTrond Myklebust if (nfs_client_return_marked_delegations(clp) != 0) 402d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 403515d8611STrond Myklebust } 404515d8611STrond Myklebust 405c79571a5SAlexandros Batsakis static 406c79571a5SAlexandros Batsakis void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags) 407515d8611STrond Myklebust { 408515d8611STrond Myklebust struct nfs_delegation *delegation; 409515d8611STrond Myklebust 410515d8611STrond Myklebust rcu_read_lock(); 411707fb4b3STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 412c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 413c79571a5SAlexandros Batsakis continue; 414c79571a5SAlexandros Batsakis if (delegation->type & flags) 415b4a6f496SAlexandros Batsakis nfs_mark_return_delegation(clp, delegation); 416707fb4b3STrond Myklebust } 417515d8611STrond Myklebust rcu_read_unlock(); 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 420c79571a5SAlexandros Batsakis static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 421c79571a5SAlexandros Batsakis { 422c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 423c79571a5SAlexandros Batsakis } 424c79571a5SAlexandros Batsakis 425b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 42658d9714aSTrond Myklebust { 427b0d3ded1STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 428b0d3ded1STrond Myklebust nfs4_schedule_state_manager(clp); 42958d9714aSTrond Myklebust } 43058d9714aSTrond Myklebust 43131f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) 432c79571a5SAlexandros Batsakis { 433c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, flags); 434c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 435c79571a5SAlexandros Batsakis } 436c79571a5SAlexandros Batsakis 437adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp) 43858d9714aSTrond Myklebust { 439c79571a5SAlexandros Batsakis nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 44058d9714aSTrond Myklebust } 44158d9714aSTrond Myklebust 4421da177e4SLinus Torvalds /* 4431da177e4SLinus Torvalds * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. 4441da177e4SLinus Torvalds */ 445adfa6f98SDavid Howells void nfs_handle_cb_pathdown(struct nfs_client *clp) 4461da177e4SLinus Torvalds { 4471da177e4SLinus Torvalds if (clp == NULL) 4481da177e4SLinus Torvalds return; 449707fb4b3STrond Myklebust nfs_client_mark_return_all_delegations(clp); 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds 452b7391f44STrond Myklebust static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp) 453b7391f44STrond Myklebust { 454b7391f44STrond Myklebust struct nfs_delegation *delegation; 455b7391f44STrond Myklebust 456b7391f44STrond Myklebust rcu_read_lock(); 457b7391f44STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 458b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 459b7391f44STrond Myklebust continue; 460b4a6f496SAlexandros Batsakis nfs_mark_return_delegation(clp, delegation); 461b7391f44STrond Myklebust } 462b7391f44STrond Myklebust rcu_read_unlock(); 463b7391f44STrond Myklebust } 464b7391f44STrond Myklebust 465b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 466b7391f44STrond Myklebust { 467b7391f44STrond Myklebust nfs_client_mark_return_unreferenced_delegations(clp); 468b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 469b7391f44STrond Myklebust } 470b7391f44STrond Myklebust 4711da177e4SLinus Torvalds /* 4721da177e4SLinus Torvalds * Asynchronous delegation recall! 4731da177e4SLinus Torvalds */ 4742597641dSAlexandros Batsakis int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid, 4752597641dSAlexandros Batsakis int (*validate_stateid)(struct nfs_delegation *delegation, 4762597641dSAlexandros Batsakis const nfs4_stateid *stateid)) 4771da177e4SLinus Torvalds { 4786411bd4aSTrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 4796411bd4aSTrond Myklebust struct nfs_delegation *delegation; 4801da177e4SLinus Torvalds 4816411bd4aSTrond Myklebust rcu_read_lock(); 4826411bd4aSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 4832597641dSAlexandros Batsakis 4842597641dSAlexandros Batsakis if (!validate_stateid(delegation, stateid)) { 4856411bd4aSTrond Myklebust rcu_read_unlock(); 4866411bd4aSTrond Myklebust return -ENOENT; 4876411bd4aSTrond Myklebust } 4882597641dSAlexandros Batsakis 4896411bd4aSTrond Myklebust nfs_mark_return_delegation(clp, delegation); 4906411bd4aSTrond Myklebust rcu_read_unlock(); 4916411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 4926411bd4aSTrond Myklebust return 0; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds /* 4961da177e4SLinus Torvalds * Retrieve the inode associated with a delegation 4971da177e4SLinus Torvalds */ 498adfa6f98SDavid Howells struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle) 4991da177e4SLinus Torvalds { 5001da177e4SLinus Torvalds struct nfs_delegation *delegation; 5011da177e4SLinus Torvalds struct inode *res = NULL; 5028383e460STrond Myklebust rcu_read_lock(); 5038383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 50486e89489STrond Myklebust spin_lock(&delegation->lock); 50586e89489STrond Myklebust if (delegation->inode != NULL && 50686e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 5071da177e4SLinus Torvalds res = igrab(delegation->inode); 5081da177e4SLinus Torvalds } 50986e89489STrond Myklebust spin_unlock(&delegation->lock); 51086e89489STrond Myklebust if (res != NULL) 51186e89489STrond Myklebust break; 5121da177e4SLinus Torvalds } 5138383e460STrond Myklebust rcu_read_unlock(); 5141da177e4SLinus Torvalds return res; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds /* 5181da177e4SLinus Torvalds * Mark all delegations as needing to be reclaimed 5191da177e4SLinus Torvalds */ 520adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 5211da177e4SLinus Torvalds { 5221da177e4SLinus Torvalds struct nfs_delegation *delegation; 5238383e460STrond Myklebust rcu_read_lock(); 5248383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) 52515c831bfSTrond Myklebust set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 5268383e460STrond Myklebust rcu_read_unlock(); 5271da177e4SLinus Torvalds } 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds /* 5301da177e4SLinus Torvalds * Reap all unclaimed delegations after reboot recovery is done 5311da177e4SLinus Torvalds */ 532adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 5331da177e4SLinus Torvalds { 5348383e460STrond Myklebust struct nfs_delegation *delegation; 53586e89489STrond Myklebust struct inode *inode; 5368383e460STrond Myklebust restart: 5378383e460STrond Myklebust rcu_read_lock(); 5388383e460STrond Myklebust list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { 53915c831bfSTrond Myklebust if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) 5401da177e4SLinus Torvalds continue; 54186e89489STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 54286e89489STrond Myklebust if (inode == NULL) 54386e89489STrond Myklebust continue; 5448383e460STrond Myklebust spin_lock(&clp->cl_lock); 54517d2c0a0SDavid Howells delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); 5461da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 5478383e460STrond Myklebust rcu_read_unlock(); 5488383e460STrond Myklebust if (delegation != NULL) 549905f8d16STrond Myklebust nfs_free_delegation(delegation); 55086e89489STrond Myklebust iput(inode); 5518383e460STrond Myklebust goto restart; 5521da177e4SLinus Torvalds } 5538383e460STrond Myklebust rcu_read_unlock(); 5541da177e4SLinus Torvalds } 5553e4f6290STrond Myklebust 5563e4f6290STrond Myklebust int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 5573e4f6290STrond Myklebust { 5583e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 5593e4f6290STrond Myklebust struct nfs_delegation *delegation; 5608383e460STrond Myklebust int ret = 0; 5613e4f6290STrond Myklebust 5628383e460STrond Myklebust rcu_read_lock(); 5638383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 5643e4f6290STrond Myklebust if (delegation != NULL) { 5653e4f6290STrond Myklebust memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); 5668383e460STrond Myklebust ret = 1; 5673e4f6290STrond Myklebust } 5688383e460STrond Myklebust rcu_read_unlock(); 5698383e460STrond Myklebust return ret; 5703e4f6290STrond Myklebust } 571