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 */ 50011e2a7fSBryan Schumaker int nfs4_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); 58d25be546STrond Myklebust if (delegation != NULL && (delegation->type & flags) == flags && 59d25be546STrond Myklebust !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 60b7391f44STrond Myklebust nfs_mark_delegation_referenced(delegation); 61b7391f44STrond Myklebust ret = 1; 62b7391f44STrond Myklebust } 63b7391f44STrond Myklebust rcu_read_unlock(); 64b7391f44STrond Myklebust return ret; 65b7391f44STrond Myklebust } 66b7391f44STrond Myklebust 67db4f2e63STrond Myklebust static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) 68888e694cSTrond Myklebust { 69888e694cSTrond Myklebust struct inode *inode = state->inode; 70888e694cSTrond Myklebust struct file_lock *fl; 71d5122201STrond Myklebust int status = 0; 72888e694cSTrond Myklebust 733f09df70STrond Myklebust if (inode->i_flock == NULL) 7465b62a29STrond Myklebust return 0; 753f09df70STrond Myklebust 7665b62a29STrond Myklebust if (inode->i_flock == NULL) 7765b62a29STrond Myklebust goto out; 78b89f4321SArnd Bergmann /* Protect inode->i_flock using the file locks lock */ 79b89f4321SArnd Bergmann lock_flocks(); 8090dc7d27SHarvey Harrison for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { 81888e694cSTrond Myklebust if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) 82888e694cSTrond Myklebust continue; 83cd3758e3STrond Myklebust if (nfs_file_open_context(fl->fl_file) != ctx) 84888e694cSTrond Myklebust continue; 85b89f4321SArnd Bergmann unlock_flocks(); 86db4f2e63STrond Myklebust status = nfs4_lock_delegation_recall(fl, state, stateid); 87d5122201STrond Myklebust if (status < 0) 883f09df70STrond Myklebust goto out; 89b89f4321SArnd Bergmann lock_flocks(); 90888e694cSTrond Myklebust } 91b89f4321SArnd Bergmann unlock_flocks(); 923f09df70STrond Myklebust out: 93888e694cSTrond Myklebust return status; 94888e694cSTrond Myklebust } 95888e694cSTrond Myklebust 96d18cc1fdSTrond Myklebust static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 991da177e4SLinus Torvalds struct nfs_open_context *ctx; 100d25be546STrond Myklebust struct nfs4_state_owner *sp; 1011da177e4SLinus Torvalds struct nfs4_state *state; 102d25be546STrond Myklebust unsigned int seq; 103888e694cSTrond Myklebust int err; 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds again: 1061da177e4SLinus Torvalds spin_lock(&inode->i_lock); 1071da177e4SLinus Torvalds list_for_each_entry(ctx, &nfsi->open_files, list) { 1081da177e4SLinus Torvalds state = ctx->state; 1091da177e4SLinus Torvalds if (state == NULL) 1101da177e4SLinus Torvalds continue; 1111da177e4SLinus Torvalds if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1121da177e4SLinus Torvalds continue; 113f597c537STrond Myklebust if (!nfs4_stateid_match(&state->stateid, stateid)) 11490163027STrond Myklebust continue; 1151da177e4SLinus Torvalds get_nfs_open_context(ctx); 1161da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 117d25be546STrond Myklebust sp = state->owner; 11865b62a29STrond Myklebust /* Block nfs4_proc_unlck */ 11965b62a29STrond Myklebust mutex_lock(&sp->so_delegreturn_mutex); 120d25be546STrond Myklebust seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 12113437e12STrond Myklebust err = nfs4_open_delegation_recall(ctx, state, stateid); 122d25be546STrond Myklebust if (!err) 123db4f2e63STrond Myklebust err = nfs_delegation_claim_locks(ctx, state, stateid); 124d25be546STrond Myklebust if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 125d25be546STrond Myklebust err = -EAGAIN; 12665b62a29STrond Myklebust mutex_unlock(&sp->so_delegreturn_mutex); 1271da177e4SLinus Torvalds put_nfs_open_context(ctx); 128888e694cSTrond Myklebust if (err != 0) 129d18cc1fdSTrond Myklebust return err; 1301da177e4SLinus Torvalds goto again; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds spin_unlock(&inode->i_lock); 133d18cc1fdSTrond Myklebust return 0; 1341da177e4SLinus Torvalds } 1351da177e4SLinus Torvalds 136d3978bb3SChuck Lever /** 137d3978bb3SChuck Lever * nfs_inode_reclaim_delegation - process a delegation reclaim request 138d3978bb3SChuck Lever * @inode: inode to process 139d3978bb3SChuck Lever * @cred: credential to use for request 140d3978bb3SChuck Lever * @res: new delegation state from server 141d3978bb3SChuck Lever * 1421da177e4SLinus Torvalds */ 143d3978bb3SChuck Lever void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, 144d3978bb3SChuck Lever struct nfs_openres *res) 1451da177e4SLinus Torvalds { 1468f649c37STrond Myklebust struct nfs_delegation *delegation; 1478f649c37STrond Myklebust struct rpc_cred *oldcred = NULL; 1481da177e4SLinus Torvalds 1498f649c37STrond Myklebust rcu_read_lock(); 1508f649c37STrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 1518f649c37STrond Myklebust if (delegation != NULL) { 1528f649c37STrond Myklebust spin_lock(&delegation->lock); 1538f649c37STrond Myklebust if (delegation->inode != NULL) { 154f597c537STrond Myklebust nfs4_stateid_copy(&delegation->stateid, &res->delegation); 1551da177e4SLinus Torvalds delegation->type = res->delegation_type; 1561da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 15705c88babSTrond Myklebust oldcred = delegation->cred; 1581da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 1598f649c37STrond Myklebust clear_bit(NFS_DELEGATION_NEED_RECLAIM, 1608f649c37STrond Myklebust &delegation->flags); 1611da177e4SLinus Torvalds NFS_I(inode)->delegation_state = delegation->type; 1628f649c37STrond Myklebust spin_unlock(&delegation->lock); 16305c88babSTrond Myklebust put_rpccred(oldcred); 1648f649c37STrond Myklebust rcu_read_unlock(); 1658f649c37STrond Myklebust } else { 1668f649c37STrond Myklebust /* We appear to have raced with a delegation return. */ 1678f649c37STrond Myklebust spin_unlock(&delegation->lock); 1688f649c37STrond Myklebust rcu_read_unlock(); 1698f649c37STrond Myklebust nfs_inode_set_delegation(inode, cred, res); 1708f649c37STrond Myklebust } 1718f649c37STrond Myklebust } else { 1728f649c37STrond Myklebust rcu_read_unlock(); 1738f649c37STrond Myklebust } 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 17657bfa891STrond Myklebust static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 17757bfa891STrond Myklebust { 17857bfa891STrond Myklebust int res = 0; 17957bfa891STrond Myklebust 18057bfa891STrond Myklebust res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 18157bfa891STrond Myklebust nfs_free_delegation(delegation); 18257bfa891STrond Myklebust return res; 18357bfa891STrond Myklebust } 18457bfa891STrond Myklebust 18586e89489STrond Myklebust static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 18686e89489STrond Myklebust { 18786e89489STrond Myklebust struct inode *inode = NULL; 18886e89489STrond Myklebust 18986e89489STrond Myklebust spin_lock(&delegation->lock); 19086e89489STrond Myklebust if (delegation->inode != NULL) 19186e89489STrond Myklebust inode = igrab(delegation->inode); 19286e89489STrond Myklebust spin_unlock(&delegation->lock); 19386e89489STrond Myklebust return inode; 19486e89489STrond Myklebust } 19586e89489STrond Myklebust 196dda4b225SChuck Lever static struct nfs_delegation * 197d25be546STrond Myklebust nfs_start_delegation_return_locked(struct nfs_inode *nfsi) 19857bfa891STrond Myklebust { 199d25be546STrond Myklebust struct nfs_delegation *ret = NULL; 200d25be546STrond Myklebust struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 20157bfa891STrond Myklebust 20257bfa891STrond Myklebust if (delegation == NULL) 203d25be546STrond Myklebust goto out; 204d25be546STrond Myklebust spin_lock(&delegation->lock); 205d25be546STrond Myklebust if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 206d25be546STrond Myklebust ret = delegation; 207d25be546STrond Myklebust spin_unlock(&delegation->lock); 208d25be546STrond Myklebust out: 209d25be546STrond Myklebust return ret; 210d25be546STrond Myklebust } 211d25be546STrond Myklebust 212d25be546STrond Myklebust static struct nfs_delegation * 213d25be546STrond Myklebust nfs_start_delegation_return(struct nfs_inode *nfsi) 214d25be546STrond Myklebust { 215d25be546STrond Myklebust struct nfs_delegation *delegation; 216d25be546STrond Myklebust 217d25be546STrond Myklebust rcu_read_lock(); 218d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(nfsi); 219d25be546STrond Myklebust rcu_read_unlock(); 220d25be546STrond Myklebust return delegation; 221d25be546STrond Myklebust } 222d25be546STrond Myklebust 223d25be546STrond Myklebust static void 224d25be546STrond Myklebust nfs_abort_delegation_return(struct nfs_delegation *delegation, 225d25be546STrond Myklebust struct nfs_client *clp) 226d25be546STrond Myklebust { 227dda4b225SChuck Lever 22834310430STrond Myklebust spin_lock(&delegation->lock); 229d25be546STrond Myklebust clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 230d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 231d25be546STrond Myklebust spin_unlock(&delegation->lock); 232d25be546STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 233d25be546STrond Myklebust } 234d25be546STrond Myklebust 235d25be546STrond Myklebust static struct nfs_delegation * 236d25be546STrond Myklebust nfs_detach_delegation_locked(struct nfs_inode *nfsi, 237d25be546STrond Myklebust struct nfs_delegation *delegation, 238d25be546STrond Myklebust struct nfs_client *clp) 239d25be546STrond Myklebust { 240d25be546STrond Myklebust struct nfs_delegation *deleg_cur = 241d25be546STrond Myklebust rcu_dereference_protected(nfsi->delegation, 242d25be546STrond Myklebust lockdep_is_held(&clp->cl_lock)); 243d25be546STrond Myklebust 244d25be546STrond Myklebust if (deleg_cur == NULL || delegation != deleg_cur) 245d25be546STrond Myklebust return NULL; 246d25be546STrond Myklebust 247d25be546STrond Myklebust spin_lock(&delegation->lock); 248d25be546STrond Myklebust set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 24957bfa891STrond Myklebust list_del_rcu(&delegation->super_list); 25086e89489STrond Myklebust delegation->inode = NULL; 25157bfa891STrond Myklebust nfsi->delegation_state = 0; 25257bfa891STrond Myklebust rcu_assign_pointer(nfsi->delegation, NULL); 25334310430STrond Myklebust spin_unlock(&delegation->lock); 25457bfa891STrond Myklebust return delegation; 25557bfa891STrond Myklebust } 25657bfa891STrond Myklebust 257dda4b225SChuck Lever static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 258d25be546STrond Myklebust struct nfs_delegation *delegation, 259d3978bb3SChuck Lever struct nfs_server *server) 260dda4b225SChuck Lever { 261d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 262dda4b225SChuck Lever 263dda4b225SChuck Lever spin_lock(&clp->cl_lock); 264d25be546STrond Myklebust delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 265dda4b225SChuck Lever spin_unlock(&clp->cl_lock); 266dda4b225SChuck Lever return delegation; 267dda4b225SChuck Lever } 268dda4b225SChuck Lever 269d25be546STrond Myklebust static struct nfs_delegation * 270d25be546STrond Myklebust nfs_inode_detach_delegation(struct inode *inode) 271d25be546STrond Myklebust { 272d25be546STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 273d25be546STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 274d25be546STrond Myklebust struct nfs_delegation *delegation; 275d25be546STrond Myklebust 276d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 277d25be546STrond Myklebust if (delegation == NULL) 278d25be546STrond Myklebust return NULL; 279d25be546STrond Myklebust return nfs_detach_delegation(nfsi, delegation, server); 280d25be546STrond Myklebust } 281d25be546STrond Myklebust 282d3978bb3SChuck Lever /** 283d3978bb3SChuck Lever * nfs_inode_set_delegation - set up a delegation on an inode 284d3978bb3SChuck Lever * @inode: inode to which delegation applies 285d3978bb3SChuck Lever * @cred: cred to use for subsequent delegation processing 286d3978bb3SChuck Lever * @res: new delegation state from server 287d3978bb3SChuck Lever * 288d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 2891da177e4SLinus Torvalds */ 2901da177e4SLinus Torvalds int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 2911da177e4SLinus Torvalds { 292d3978bb3SChuck Lever struct nfs_server *server = NFS_SERVER(inode); 293d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 2941da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 29517d2c0a0SDavid Howells struct nfs_delegation *delegation, *old_delegation; 29657bfa891STrond Myklebust struct nfs_delegation *freeme = NULL; 2971da177e4SLinus Torvalds int status = 0; 2981da177e4SLinus Torvalds 2998535b2beSTrond Myklebust delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 3001da177e4SLinus Torvalds if (delegation == NULL) 3011da177e4SLinus Torvalds return -ENOMEM; 302f597c537STrond Myklebust nfs4_stateid_copy(&delegation->stateid, &res->delegation); 3031da177e4SLinus Torvalds delegation->type = res->delegation_type; 3041da177e4SLinus Torvalds delegation->maxsize = res->maxsize; 305a9a4a87aSTrond Myklebust delegation->change_attr = inode->i_version; 3061da177e4SLinus Torvalds delegation->cred = get_rpccred(cred); 3071da177e4SLinus Torvalds delegation->inode = inode; 308b7391f44STrond Myklebust delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 30934310430STrond Myklebust spin_lock_init(&delegation->lock); 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds spin_lock(&clp->cl_lock); 31217d2c0a0SDavid Howells old_delegation = rcu_dereference_protected(nfsi->delegation, 31317d2c0a0SDavid Howells lockdep_is_held(&clp->cl_lock)); 31417d2c0a0SDavid Howells if (old_delegation != NULL) { 315f597c537STrond Myklebust if (nfs4_stateid_match(&delegation->stateid, 316f597c537STrond Myklebust &old_delegation->stateid) && 31717d2c0a0SDavid Howells delegation->type == old_delegation->type) { 31857bfa891STrond Myklebust goto out; 31957bfa891STrond Myklebust } 32057bfa891STrond Myklebust /* 32157bfa891STrond Myklebust * Deal with broken servers that hand out two 32257bfa891STrond Myklebust * delegations for the same file. 32317280175STrond Myklebust * Allow for upgrades to a WRITE delegation, but 32417280175STrond Myklebust * nothing else. 32557bfa891STrond Myklebust */ 32657bfa891STrond Myklebust dfprintk(FILE, "%s: server %s handed out " 32757bfa891STrond Myklebust "a duplicate delegation!\n", 3283110ff80SHarvey Harrison __func__, clp->cl_hostname); 32917280175STrond Myklebust if (delegation->type == old_delegation->type || 33017280175STrond Myklebust !(delegation->type & FMODE_WRITE)) { 33157bfa891STrond Myklebust freeme = delegation; 33257bfa891STrond Myklebust delegation = NULL; 33357bfa891STrond Myklebust goto out; 33457bfa891STrond Myklebust } 335d25be546STrond Myklebust freeme = nfs_detach_delegation_locked(nfsi, 336d25be546STrond Myklebust old_delegation, clp); 337d25be546STrond Myklebust if (freeme == NULL) 338d25be546STrond Myklebust goto out; 33957bfa891STrond Myklebust } 340d3978bb3SChuck Lever list_add_rcu(&delegation->super_list, &server->delegations); 3411da177e4SLinus Torvalds nfsi->delegation_state = delegation->type; 3428383e460STrond Myklebust rcu_assign_pointer(nfsi->delegation, delegation); 3431da177e4SLinus Torvalds delegation = NULL; 344412c77ceSTrond Myklebust 345412c77ceSTrond Myklebust /* Ensure we revalidate the attributes and page cache! */ 346412c77ceSTrond Myklebust spin_lock(&inode->i_lock); 347412c77ceSTrond Myklebust nfsi->cache_validity |= NFS_INO_REVAL_FORCED; 348412c77ceSTrond Myklebust spin_unlock(&inode->i_lock); 349412c77ceSTrond Myklebust 35057bfa891STrond Myklebust out: 3511da177e4SLinus Torvalds spin_unlock(&clp->cl_lock); 352603c83daSTrond Myklebust if (delegation != NULL) 353603c83daSTrond Myklebust nfs_free_delegation(delegation); 35457bfa891STrond Myklebust if (freeme != NULL) 35557bfa891STrond Myklebust nfs_do_return_delegation(inode, freeme, 0); 3561da177e4SLinus Torvalds return status; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds /* 3601da177e4SLinus Torvalds * Basic procedure for returning a delegation to the server 3611da177e4SLinus Torvalds */ 362d25be546STrond Myklebust static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 3631da177e4SLinus Torvalds { 364d25be546STrond Myklebust struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 3651da177e4SLinus Torvalds struct nfs_inode *nfsi = NFS_I(inode); 366d18cc1fdSTrond Myklebust int err; 3671da177e4SLinus Torvalds 368d25be546STrond Myklebust if (delegation == NULL) 369d25be546STrond Myklebust return 0; 370d25be546STrond Myklebust do { 371d18cc1fdSTrond Myklebust err = nfs_delegation_claim_opens(inode, &delegation->stateid); 372d25be546STrond Myklebust if (!issync || err != -EAGAIN) 373d25be546STrond Myklebust break; 374d25be546STrond Myklebust /* 375d25be546STrond Myklebust * Guard against state recovery 376d25be546STrond Myklebust */ 377d25be546STrond Myklebust err = nfs4_wait_clnt_recover(clp); 378d25be546STrond Myklebust } while (err == 0); 379d25be546STrond Myklebust 380d25be546STrond Myklebust if (err) { 381d25be546STrond Myklebust nfs_abort_delegation_return(delegation, clp); 382d25be546STrond Myklebust goto out; 383d25be546STrond Myklebust } 384d25be546STrond Myklebust if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode))) 385d18cc1fdSTrond Myklebust goto out; 3861da177e4SLinus Torvalds 387d18cc1fdSTrond Myklebust err = nfs_do_return_delegation(inode, delegation, issync); 388d18cc1fdSTrond Myklebust out: 389d18cc1fdSTrond Myklebust return err; 39090163027STrond Myklebust } 39190163027STrond Myklebust 392d3978bb3SChuck Lever /** 393d3978bb3SChuck Lever * nfs_client_return_marked_delegations - return previously marked delegations 394d3978bb3SChuck Lever * @clp: nfs_client to process 395d3978bb3SChuck Lever * 396dc327ed4STrond Myklebust * Note that this function is designed to be called by the state 397dc327ed4STrond Myklebust * manager thread. For this reason, it cannot flush the dirty data, 398dc327ed4STrond Myklebust * since that could deadlock in case of a state recovery error. 399dc327ed4STrond Myklebust * 400d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 401515d8611STrond Myklebust */ 402d18cc1fdSTrond Myklebust int nfs_client_return_marked_delegations(struct nfs_client *clp) 403515d8611STrond Myklebust { 404515d8611STrond Myklebust struct nfs_delegation *delegation; 405d3978bb3SChuck Lever struct nfs_server *server; 406515d8611STrond Myklebust struct inode *inode; 407d18cc1fdSTrond Myklebust int err = 0; 408515d8611STrond Myklebust 409515d8611STrond Myklebust restart: 410515d8611STrond Myklebust rcu_read_lock(); 411d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 412d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 413d3978bb3SChuck Lever super_list) { 414d3978bb3SChuck Lever if (!test_and_clear_bit(NFS_DELEGATION_RETURN, 415d3978bb3SChuck Lever &delegation->flags)) 416515d8611STrond Myklebust continue; 417515d8611STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 418515d8611STrond Myklebust if (inode == NULL) 419515d8611STrond Myklebust continue; 420d25be546STrond Myklebust delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 421515d8611STrond Myklebust rcu_read_unlock(); 422d3978bb3SChuck Lever 423d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 0); 424515d8611STrond Myklebust iput(inode); 425d18cc1fdSTrond Myklebust if (!err) 426515d8611STrond Myklebust goto restart; 427d18cc1fdSTrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); 428d18cc1fdSTrond Myklebust return err; 429515d8611STrond Myklebust } 430d3978bb3SChuck Lever } 431515d8611STrond Myklebust rcu_read_unlock(); 432d18cc1fdSTrond Myklebust return 0; 433515d8611STrond Myklebust } 434515d8611STrond Myklebust 435d3978bb3SChuck Lever /** 436d3978bb3SChuck Lever * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens 437d3978bb3SChuck Lever * @inode: inode to process 438d3978bb3SChuck Lever * 439d3978bb3SChuck Lever * Does not protect against delegation reclaims, therefore really only safe 440d3978bb3SChuck Lever * to be called from nfs4_clear_inode(). 441e6f81075STrond Myklebust */ 442e6f81075STrond Myklebust void nfs_inode_return_delegation_noreclaim(struct inode *inode) 443e6f81075STrond Myklebust { 444e6f81075STrond Myklebust struct nfs_delegation *delegation; 445e6f81075STrond Myklebust 446d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 447e6f81075STrond Myklebust if (delegation != NULL) 448e6f81075STrond Myklebust nfs_do_return_delegation(inode, delegation, 0); 449e6f81075STrond Myklebust } 450e6f81075STrond Myklebust 451d3978bb3SChuck Lever /** 452d3978bb3SChuck Lever * nfs_inode_return_delegation - synchronously return a delegation 453d3978bb3SChuck Lever * @inode: inode to process 454d3978bb3SChuck Lever * 455c57d1bc5STrond Myklebust * This routine will always flush any dirty data to disk on the 456c57d1bc5STrond Myklebust * assumption that if we need to return the delegation, then 457c57d1bc5STrond Myklebust * we should stop caching. 458c57d1bc5STrond Myklebust * 459d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 460d3978bb3SChuck Lever */ 46157ec14c5SBryan Schumaker int nfs4_inode_return_delegation(struct inode *inode) 46290163027STrond Myklebust { 46390163027STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 46490163027STrond Myklebust struct nfs_delegation *delegation; 46590163027STrond Myklebust int err = 0; 46690163027STrond Myklebust 467c57d1bc5STrond Myklebust nfs_wb_all(inode); 468d25be546STrond Myklebust delegation = nfs_start_delegation_return(nfsi); 469d25be546STrond Myklebust if (delegation != NULL) 470d25be546STrond Myklebust err = nfs_end_delegation_return(inode, delegation, 1); 47190163027STrond Myklebust return err; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds 474ed1e6211STrond Myklebust static void nfs_mark_return_delegation(struct nfs_server *server, 475ed1e6211STrond Myklebust struct nfs_delegation *delegation) 4766411bd4aSTrond Myklebust { 4776411bd4aSTrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 478ed1e6211STrond Myklebust set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 4796411bd4aSTrond Myklebust } 4806411bd4aSTrond Myklebust 481d3978bb3SChuck Lever /** 482d3978bb3SChuck Lever * nfs_super_return_all_delegations - return delegations for one superblock 483d3978bb3SChuck Lever * @sb: sb to process 484d3978bb3SChuck Lever * 4851da177e4SLinus Torvalds */ 486eeebf916SBryan Schumaker void nfs_server_return_all_delegations(struct nfs_server *server) 4871da177e4SLinus Torvalds { 488d3978bb3SChuck Lever struct nfs_client *clp = server->nfs_client; 4891da177e4SLinus Torvalds struct nfs_delegation *delegation; 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds if (clp == NULL) 4921da177e4SLinus Torvalds return; 493d3978bb3SChuck Lever 4948383e460STrond Myklebust rcu_read_lock(); 495d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 49686e89489STrond Myklebust spin_lock(&delegation->lock); 497515d8611STrond Myklebust set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 49886e89489STrond Myklebust spin_unlock(&delegation->lock); 4991da177e4SLinus Torvalds } 5008383e460STrond Myklebust rcu_read_unlock(); 501d3978bb3SChuck Lever 502d18cc1fdSTrond Myklebust if (nfs_client_return_marked_delegations(clp) != 0) 503d18cc1fdSTrond Myklebust nfs4_schedule_state_manager(clp); 504515d8611STrond Myklebust } 505515d8611STrond Myklebust 506d3978bb3SChuck Lever static void nfs_mark_return_all_delegation_types(struct nfs_server *server, 507d3978bb3SChuck Lever fmode_t flags) 508515d8611STrond Myklebust { 509515d8611STrond Myklebust struct nfs_delegation *delegation; 510515d8611STrond Myklebust 511d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 512c79571a5SAlexandros Batsakis if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 513c79571a5SAlexandros Batsakis continue; 514c79571a5SAlexandros Batsakis if (delegation->type & flags) 515ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 516707fb4b3STrond Myklebust } 517d3978bb3SChuck Lever } 518d3978bb3SChuck Lever 519d3978bb3SChuck Lever static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, 520d3978bb3SChuck Lever fmode_t flags) 521d3978bb3SChuck Lever { 522d3978bb3SChuck Lever struct nfs_server *server; 523d3978bb3SChuck Lever 524d3978bb3SChuck Lever rcu_read_lock(); 525d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 526d3978bb3SChuck Lever nfs_mark_return_all_delegation_types(server, flags); 527515d8611STrond Myklebust rcu_read_unlock(); 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds 530b0d3ded1STrond Myklebust static void nfs_delegation_run_state_manager(struct nfs_client *clp) 53158d9714aSTrond Myklebust { 532b0d3ded1STrond Myklebust if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 533b0d3ded1STrond Myklebust nfs4_schedule_state_manager(clp); 53458d9714aSTrond Myklebust } 53558d9714aSTrond Myklebust 536a1d0b5eeSTrond Myklebust void nfs_remove_bad_delegation(struct inode *inode) 537a1d0b5eeSTrond Myklebust { 538a1d0b5eeSTrond Myklebust struct nfs_delegation *delegation; 539a1d0b5eeSTrond Myklebust 540d25be546STrond Myklebust delegation = nfs_inode_detach_delegation(inode); 541a1d0b5eeSTrond Myklebust if (delegation) { 542a1d0b5eeSTrond Myklebust nfs_inode_find_state_and_recover(inode, &delegation->stateid); 543a1d0b5eeSTrond Myklebust nfs_free_delegation(delegation); 544a1d0b5eeSTrond Myklebust } 545a1d0b5eeSTrond Myklebust } 5469cb81968SAndy Adamson EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 547a1d0b5eeSTrond Myklebust 548d3978bb3SChuck Lever /** 549d3978bb3SChuck Lever * nfs_expire_all_delegation_types 550d3978bb3SChuck Lever * @clp: client to process 551d3978bb3SChuck Lever * @flags: delegation types to expire 552d3978bb3SChuck Lever * 553d3978bb3SChuck Lever */ 55431f09607SAlexandros Batsakis void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) 555c79571a5SAlexandros Batsakis { 556c79571a5SAlexandros Batsakis nfs_client_mark_return_all_delegation_types(clp, flags); 557c79571a5SAlexandros Batsakis nfs_delegation_run_state_manager(clp); 558c79571a5SAlexandros Batsakis } 559c79571a5SAlexandros Batsakis 560d3978bb3SChuck Lever /** 561d3978bb3SChuck Lever * nfs_expire_all_delegations 562d3978bb3SChuck Lever * @clp: client to process 563d3978bb3SChuck Lever * 564d3978bb3SChuck Lever */ 565adfa6f98SDavid Howells void nfs_expire_all_delegations(struct nfs_client *clp) 56658d9714aSTrond Myklebust { 567c79571a5SAlexandros Batsakis nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); 56858d9714aSTrond Myklebust } 56958d9714aSTrond Myklebust 570d3978bb3SChuck Lever static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 571b7391f44STrond Myklebust { 572b7391f44STrond Myklebust struct nfs_delegation *delegation; 573b7391f44STrond Myklebust 574d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 575b7391f44STrond Myklebust if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 576b7391f44STrond Myklebust continue; 577ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 578b7391f44STrond Myklebust } 579b7391f44STrond Myklebust } 580b7391f44STrond Myklebust 581d3978bb3SChuck Lever /** 582d3978bb3SChuck Lever * nfs_expire_unreferenced_delegations - Eliminate unused delegations 583d3978bb3SChuck Lever * @clp: nfs_client to process 584d3978bb3SChuck Lever * 585d3978bb3SChuck Lever */ 586b7391f44STrond Myklebust void nfs_expire_unreferenced_delegations(struct nfs_client *clp) 587b7391f44STrond Myklebust { 588d3978bb3SChuck Lever struct nfs_server *server; 589d3978bb3SChuck Lever 590d3978bb3SChuck Lever rcu_read_lock(); 591d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 592d3978bb3SChuck Lever nfs_mark_return_unreferenced_delegations(server); 593d3978bb3SChuck Lever rcu_read_unlock(); 594d3978bb3SChuck Lever 595b7391f44STrond Myklebust nfs_delegation_run_state_manager(clp); 596b7391f44STrond Myklebust } 597b7391f44STrond Myklebust 598d3978bb3SChuck Lever /** 599d3978bb3SChuck Lever * nfs_async_inode_return_delegation - asynchronously return a delegation 600d3978bb3SChuck Lever * @inode: inode to process 6018e663f0eSTrond Myklebust * @stateid: state ID information 602d3978bb3SChuck Lever * 603d3978bb3SChuck Lever * Returns zero on success, or a negative errno value. 6041da177e4SLinus Torvalds */ 605d3978bb3SChuck Lever int nfs_async_inode_return_delegation(struct inode *inode, 606d3978bb3SChuck Lever const nfs4_stateid *stateid) 6071da177e4SLinus Torvalds { 608ed1e6211STrond Myklebust struct nfs_server *server = NFS_SERVER(inode); 609ed1e6211STrond Myklebust struct nfs_client *clp = server->nfs_client; 6106411bd4aSTrond Myklebust struct nfs_delegation *delegation; 6111da177e4SLinus Torvalds 612dc327ed4STrond Myklebust filemap_flush(inode->i_mapping); 613dc327ed4STrond Myklebust 6146411bd4aSTrond Myklebust rcu_read_lock(); 6156411bd4aSTrond Myklebust delegation = rcu_dereference(NFS_I(inode)->delegation); 6162597641dSAlexandros Batsakis 61736281caaSTrond Myklebust if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { 6186411bd4aSTrond Myklebust rcu_read_unlock(); 6196411bd4aSTrond Myklebust return -ENOENT; 6206411bd4aSTrond Myklebust } 621ed1e6211STrond Myklebust nfs_mark_return_delegation(server, delegation); 6226411bd4aSTrond Myklebust rcu_read_unlock(); 623d3978bb3SChuck Lever 6246411bd4aSTrond Myklebust nfs_delegation_run_state_manager(clp); 6256411bd4aSTrond Myklebust return 0; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 628d3978bb3SChuck Lever static struct inode * 629d3978bb3SChuck Lever nfs_delegation_find_inode_server(struct nfs_server *server, 630d3978bb3SChuck Lever const struct nfs_fh *fhandle) 6311da177e4SLinus Torvalds { 6321da177e4SLinus Torvalds struct nfs_delegation *delegation; 6331da177e4SLinus Torvalds struct inode *res = NULL; 634d3978bb3SChuck Lever 635d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 63686e89489STrond Myklebust spin_lock(&delegation->lock); 63786e89489STrond Myklebust if (delegation->inode != NULL && 63886e89489STrond Myklebust nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 6391da177e4SLinus Torvalds res = igrab(delegation->inode); 6401da177e4SLinus Torvalds } 64186e89489STrond Myklebust spin_unlock(&delegation->lock); 64286e89489STrond Myklebust if (res != NULL) 64386e89489STrond Myklebust break; 6441da177e4SLinus Torvalds } 645d3978bb3SChuck Lever return res; 646d3978bb3SChuck Lever } 647d3978bb3SChuck Lever 648d3978bb3SChuck Lever /** 649d3978bb3SChuck Lever * nfs_delegation_find_inode - retrieve the inode associated with a delegation 650d3978bb3SChuck Lever * @clp: client state handle 651d3978bb3SChuck Lever * @fhandle: filehandle from a delegation recall 652d3978bb3SChuck Lever * 653d3978bb3SChuck Lever * Returns pointer to inode matching "fhandle," or NULL if a matching inode 654d3978bb3SChuck Lever * cannot be found. 655d3978bb3SChuck Lever */ 656d3978bb3SChuck Lever struct inode *nfs_delegation_find_inode(struct nfs_client *clp, 657d3978bb3SChuck Lever const struct nfs_fh *fhandle) 658d3978bb3SChuck Lever { 659d3978bb3SChuck Lever struct nfs_server *server; 660d3978bb3SChuck Lever struct inode *res = NULL; 661d3978bb3SChuck Lever 662d3978bb3SChuck Lever rcu_read_lock(); 663d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 664d3978bb3SChuck Lever res = nfs_delegation_find_inode_server(server, fhandle); 665d3978bb3SChuck Lever if (res != NULL) 666d3978bb3SChuck Lever break; 667d3978bb3SChuck Lever } 6688383e460STrond Myklebust rcu_read_unlock(); 6691da177e4SLinus Torvalds return res; 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds 672d3978bb3SChuck Lever static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 673d3978bb3SChuck Lever { 674d3978bb3SChuck Lever struct nfs_delegation *delegation; 675d3978bb3SChuck Lever 676d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, super_list) 677d3978bb3SChuck Lever set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 678d3978bb3SChuck Lever } 679d3978bb3SChuck Lever 680d3978bb3SChuck Lever /** 681d3978bb3SChuck Lever * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 682d3978bb3SChuck Lever * @clp: nfs_client to process 683d3978bb3SChuck Lever * 6841da177e4SLinus Torvalds */ 685adfa6f98SDavid Howells void nfs_delegation_mark_reclaim(struct nfs_client *clp) 6861da177e4SLinus Torvalds { 687d3978bb3SChuck Lever struct nfs_server *server; 688d3978bb3SChuck Lever 6898383e460STrond Myklebust rcu_read_lock(); 690d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 691d3978bb3SChuck Lever nfs_delegation_mark_reclaim_server(server); 6928383e460STrond Myklebust rcu_read_unlock(); 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds 695d3978bb3SChuck Lever /** 696d3978bb3SChuck Lever * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 697d3978bb3SChuck Lever * @clp: nfs_client to process 698d3978bb3SChuck Lever * 6991da177e4SLinus Torvalds */ 700adfa6f98SDavid Howells void nfs_delegation_reap_unclaimed(struct nfs_client *clp) 7011da177e4SLinus Torvalds { 7028383e460STrond Myklebust struct nfs_delegation *delegation; 703d3978bb3SChuck Lever struct nfs_server *server; 70486e89489STrond Myklebust struct inode *inode; 705d3978bb3SChuck Lever 7068383e460STrond Myklebust restart: 7078383e460STrond Myklebust rcu_read_lock(); 708d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 709d3978bb3SChuck Lever list_for_each_entry_rcu(delegation, &server->delegations, 710d3978bb3SChuck Lever super_list) { 711d3978bb3SChuck Lever if (test_bit(NFS_DELEGATION_NEED_RECLAIM, 712d3978bb3SChuck Lever &delegation->flags) == 0) 7131da177e4SLinus Torvalds continue; 71486e89489STrond Myklebust inode = nfs_delegation_grab_inode(delegation); 71586e89489STrond Myklebust if (inode == NULL) 71686e89489STrond Myklebust continue; 717d3978bb3SChuck Lever delegation = nfs_detach_delegation(NFS_I(inode), 718d25be546STrond Myklebust delegation, server); 7198383e460STrond Myklebust rcu_read_unlock(); 720d3978bb3SChuck Lever 7218383e460STrond Myklebust if (delegation != NULL) 722905f8d16STrond Myklebust nfs_free_delegation(delegation); 72386e89489STrond Myklebust iput(inode); 7248383e460STrond Myklebust goto restart; 7251da177e4SLinus Torvalds } 726d3978bb3SChuck Lever } 7278383e460STrond Myklebust rcu_read_unlock(); 7281da177e4SLinus Torvalds } 7293e4f6290STrond Myklebust 730d3978bb3SChuck Lever /** 731d3978bb3SChuck Lever * nfs_delegations_present - check for existence of delegations 732d3978bb3SChuck Lever * @clp: client state handle 733d3978bb3SChuck Lever * 734d3978bb3SChuck Lever * Returns one if there are any nfs_delegation structures attached 735d3978bb3SChuck Lever * to this nfs_client. 736d3978bb3SChuck Lever */ 737d3978bb3SChuck Lever int nfs_delegations_present(struct nfs_client *clp) 738d3978bb3SChuck Lever { 739d3978bb3SChuck Lever struct nfs_server *server; 740d3978bb3SChuck Lever int ret = 0; 741d3978bb3SChuck Lever 742d3978bb3SChuck Lever rcu_read_lock(); 743d3978bb3SChuck Lever list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 744d3978bb3SChuck Lever if (!list_empty(&server->delegations)) { 745d3978bb3SChuck Lever ret = 1; 746d3978bb3SChuck Lever break; 747d3978bb3SChuck Lever } 748d3978bb3SChuck Lever rcu_read_unlock(); 749d3978bb3SChuck Lever return ret; 750d3978bb3SChuck Lever } 751d3978bb3SChuck Lever 752d3978bb3SChuck Lever /** 753d3978bb3SChuck Lever * nfs4_copy_delegation_stateid - Copy inode's state ID information 754d3978bb3SChuck Lever * @dst: stateid data structure to fill in 755d3978bb3SChuck Lever * @inode: inode to check 7560032a7a7STrond Myklebust * @flags: delegation type requirement 757d3978bb3SChuck Lever * 7580032a7a7STrond Myklebust * Returns "true" and fills in "dst->data" * if inode had a delegation, 7590032a7a7STrond Myklebust * otherwise "false" is returned. 760d3978bb3SChuck Lever */ 7610032a7a7STrond Myklebust bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, 7620032a7a7STrond Myklebust fmode_t flags) 7633e4f6290STrond Myklebust { 7643e4f6290STrond Myklebust struct nfs_inode *nfsi = NFS_I(inode); 7653e4f6290STrond Myklebust struct nfs_delegation *delegation; 7660032a7a7STrond Myklebust bool ret; 7673e4f6290STrond Myklebust 7680032a7a7STrond Myklebust flags &= FMODE_READ|FMODE_WRITE; 7698383e460STrond Myklebust rcu_read_lock(); 7708383e460STrond Myklebust delegation = rcu_dereference(nfsi->delegation); 7710032a7a7STrond Myklebust ret = (delegation != NULL && (delegation->type & flags) == flags); 7720032a7a7STrond Myklebust if (ret) { 773f597c537STrond Myklebust nfs4_stateid_copy(dst, &delegation->stateid); 7740032a7a7STrond Myklebust nfs_mark_delegation_referenced(delegation); 7753e4f6290STrond Myklebust } 7768383e460STrond Myklebust rcu_read_unlock(); 7778383e460STrond Myklebust return ret; 7783e4f6290STrond Myklebust } 779