diff options
Diffstat (limited to 'fs/nfs/delegation.c')
| -rw-r--r-- | fs/nfs/delegation.c | 590 |
1 files changed, 292 insertions, 298 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 8a3857a49d84..94103f8d3f21 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -52,33 +52,33 @@ static void __nfs_free_delegation(struct nfs_delegation *delegation) static void nfs_mark_delegation_revoked(struct nfs_server *server, struct nfs_delegation *delegation) { - if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { - delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; - atomic_long_dec(&server->nr_active_delegations); - if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) - nfs_clear_verifier_delegated(delegation->inode); + bool put_ref = false; + + if (test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) + return; + + delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; + atomic_long_dec(&server->nr_active_delegations); + if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + nfs_clear_verifier_delegated(delegation->inode); + + spin_lock(&server->delegations_lock); + if (!list_empty(&delegation->entry)) { + list_del_init(&delegation->entry); + put_ref = true; } -} + spin_unlock(&server->delegations_lock); -static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation) -{ - refcount_inc(&delegation->refcount); - return delegation; + if (put_ref) + nfs_put_delegation(delegation); } -static void nfs_put_delegation(struct nfs_delegation *delegation) +void nfs_put_delegation(struct nfs_delegation *delegation) { if (refcount_dec_and_test(&delegation->refcount)) __nfs_free_delegation(delegation); } -static void nfs_free_delegation(struct nfs_server *server, - struct nfs_delegation *delegation) -{ - nfs_mark_delegation_revoked(server, delegation); - nfs_put_delegation(delegation); -} - /** * nfs_mark_delegation_referenced - set delegation's REFERENCED flag * @delegation: delegation to process @@ -92,8 +92,12 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) static void nfs_mark_return_delegation(struct nfs_server *server, struct nfs_delegation *delegation) { - set_bit(NFS_DELEGATION_RETURN, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) + refcount_inc(&delegation->refcount); + list_move_tail(&delegation->entry, &server->delegations_return); + spin_unlock(&server->delegations_lock); + set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); } @@ -111,10 +115,14 @@ struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) { struct nfs_delegation *delegation; + rcu_read_lock(); delegation = rcu_dereference(NFS_I(inode)->delegation); - if (nfs4_is_valid_delegation(delegation, 0)) - return delegation; - return NULL; + if (!nfs4_is_valid_delegation(delegation, 0) || + !refcount_inc_not_zero(&delegation->refcount)) + delegation = NULL; + rcu_read_unlock(); + + return delegation; } static int nfs4_do_check_delegation(struct inode *inode, fmode_t type, @@ -308,78 +316,51 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation spin_lock(&delegation->lock); if (delegation->inode != NULL) inode = igrab(delegation->inode); - if (!inode) - set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); spin_unlock(&delegation->lock); return inode; } static struct nfs_delegation * -nfs_start_delegation_return_locked(struct nfs_inode *nfsi) -{ - struct nfs_delegation *ret = NULL; - struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); - - if (delegation == NULL) - goto out; - spin_lock(&delegation->lock); - if (delegation->inode && - !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - /* Refcount matched in nfs_end_delegation_return() */ - ret = nfs_get_delegation(delegation); - } - spin_unlock(&delegation->lock); - if (ret) - nfs_clear_verifier_delegated(&nfsi->vfs_inode); -out: - return ret; -} - -static struct nfs_delegation * nfs_start_delegation_return(struct nfs_inode *nfsi) { struct nfs_delegation *delegation; + bool return_now = false; rcu_read_lock(); - delegation = nfs_start_delegation_return_locked(nfsi); + delegation = rcu_dereference(nfsi->delegation); + if (!delegation || !refcount_inc_not_zero(&delegation->refcount)) { + rcu_read_unlock(); + return NULL; + } rcu_read_unlock(); - return delegation; -} -static void nfs_abort_delegation_return(struct nfs_delegation *delegation, - struct nfs_server *server, int err) -{ spin_lock(&delegation->lock); - clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); - if (err == -EAGAIN) { - set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); - set_bit(NFS4SERV_DELEGRETURN_DELAYED, - &server->delegation_flags); - set_bit(NFS4CLNT_DELEGRETURN_DELAYED, - &server->nfs_client->cl_state); - } + if (delegation->inode && + !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + return_now = true; spin_unlock(&delegation->lock); + + if (!return_now) { + nfs_put_delegation(delegation); + return NULL; + } + nfs_clear_verifier_delegated(&nfsi->vfs_inode); + return delegation; } -static struct nfs_delegation * -nfs_detach_delegation_locked(struct nfs_inode *nfsi, +static bool +nfs_detach_delegations_locked(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_client *clp) { - struct nfs_delegation *deleg_cur = - rcu_dereference_protected(nfsi->delegation, - lockdep_is_held(&clp->cl_lock)); + lockdep_assert_held(&clp->cl_lock); trace_nfs4_detach_delegation(&nfsi->vfs_inode, delegation->type); - if (deleg_cur == NULL || delegation != deleg_cur) - return NULL; - spin_lock(&delegation->lock); if (!delegation->inode) { spin_unlock(&delegation->lock); - return NULL; + return false; } hlist_del_init_rcu(&delegation->hash); list_del_rcu(&delegation->super_list); @@ -387,34 +368,24 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi, rcu_assign_pointer(nfsi->delegation, NULL); spin_unlock(&delegation->lock); clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags); - return delegation; + return true; } -static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, +static bool nfs_detach_delegation(struct nfs_inode *nfsi, struct nfs_delegation *delegation, struct nfs_server *server) { struct nfs_client *clp = server->nfs_client; + struct nfs_delegation *deleg_cur; + bool ret = false; spin_lock(&clp->cl_lock); - delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); + deleg_cur = rcu_dereference_protected(nfsi->delegation, + lockdep_is_held(&clp->cl_lock)); + if (delegation == deleg_cur) + ret = nfs_detach_delegations_locked(nfsi, delegation, clp); spin_unlock(&clp->cl_lock); - return delegation; -} - -static struct nfs_delegation * -nfs_inode_detach_delegation(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_delegation *delegation; - - rcu_read_lock(); - delegation = rcu_dereference(nfsi->delegation); - if (delegation != NULL) - delegation = nfs_detach_delegation(nfsi, delegation, server); - rcu_read_unlock(); - return delegation; + return ret; } static void @@ -482,6 +453,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, delegation->cred = get_cred(cred); delegation->inode = inode; delegation->flags = 1<<NFS_DELEGATION_REFERENCED; + INIT_LIST_HEAD(&delegation->entry); switch (deleg_type) { case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG: case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG: @@ -524,9 +496,9 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, &old_delegation->flags)) goto out; } - freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); - if (freeme == NULL) + if (!nfs_detach_delegations_locked(nfsi, old_delegation, clp)) goto out; + freeme = old_delegation; add_new: /* * If we didn't revalidate the change attribute before setting @@ -564,23 +536,24 @@ out: __nfs_free_delegation(delegation); if (freeme != NULL) { nfs_do_return_delegation(inode, freeme, 0); - nfs_free_delegation(server, freeme); + nfs_mark_delegation_revoked(server, freeme); + nfs_put_delegation(freeme); } return status; } /* - * Basic procedure for returning a delegation to the server + * Basic procedure for returning a delegation to the server. + * If @issync is set, wait until state recovery has finished. Otherwise + * return -EAGAIN to the caller if we need more time. */ -static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) +static int nfs_end_delegation_return(struct inode *inode, + struct nfs_delegation *delegation, bool issync) { struct nfs_server *server = NFS_SERVER(inode); unsigned int mode = O_WRONLY | O_RDWR; int err = 0; - if (delegation == NULL) - return 0; - /* Directory delegations don't require any state recovery */ if (!S_ISREG(inode->i_mode)) goto out_return; @@ -595,133 +568,154 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation break; err = nfs_delegation_claim_opens(inode, &delegation->stateid, delegation->type); - if (!issync || err != -EAGAIN) + if (!err) break; + if (err != -EAGAIN) + goto abort; + if (!issync) + goto delay; + /* * Guard against state recovery */ err = nfs4_wait_clnt_recover(server->nfs_client); } - if (err) { - nfs_abort_delegation_return(delegation, server, err); - goto out; - } - out_return: - err = nfs_do_return_delegation(inode, delegation, issync); -out: - /* Refcount matched in nfs_start_delegation_return_locked() */ - nfs_put_delegation(delegation); + return nfs_do_return_delegation(inode, delegation, issync); +delay: + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) + refcount_inc(&delegation->refcount); + list_move_tail(&delegation->entry, &server->delegations_return); + spin_unlock(&server->delegations_lock); + set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &server->nfs_client->cl_state); +abort: + clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); return err; } -static bool nfs_delegation_need_return(struct nfs_delegation *delegation) +static int nfs_return_one_delegation(struct nfs_server *server) { - bool ret = false; + struct nfs_delegation *delegation; + struct inode *inode; + int err = 0; - trace_nfs_delegation_need_return(delegation); + spin_lock(&server->delegations_lock); + delegation = list_first_entry_or_null(&server->delegations_return, + struct nfs_delegation, entry); + if (!delegation) { + spin_unlock(&server->delegations_lock); + return 0; /* no more delegations */ + } + list_del_init(&delegation->entry); + spin_unlock(&server->delegations_lock); - if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) - ret = true; - if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || - test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || - test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) - ret = false; + spin_lock(&delegation->lock); + inode = delegation->inode; + if (!inode || !igrab(inode)) { + spin_unlock(&delegation->lock); + goto out_put_delegation; + } + if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) || + test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { + spin_unlock(&delegation->lock); + goto out_put_inode; + } + spin_unlock(&delegation->lock); - return ret; + nfs_clear_verifier_delegated(inode); + + err = nfs_end_delegation_return(inode, delegation, false); + +out_put_inode: + iput(inode); +out_put_delegation: + nfs_put_delegation(delegation); + if (err) + return err; + return 1; /* keep going */ } static int nfs_server_return_marked_delegations(struct nfs_server *server, void __always_unused *data) { - struct nfs_delegation *delegation; - struct nfs_delegation *prev; - struct inode *inode; - struct inode *place_holder = NULL; - struct nfs_delegation *place_holder_deleg = NULL; - int err = 0; + int err; - if (!test_and_clear_bit(NFS4SERV_DELEGRETURN, - &server->delegation_flags)) - return 0; -restart: - /* - * To avoid quadratic looping we hold a reference - * to an inode place_holder. Each time we restart, we - * list delegation in the server from the delegations - * of that inode. - * prev is an RCU-protected pointer to a delegation which - * wasn't marked for return and might be a good choice for - * the next place_holder. - */ - prev = NULL; - delegation = NULL; - rcu_read_lock(); - if (place_holder) - delegation = rcu_dereference(NFS_I(place_holder)->delegation); - if (!delegation || delegation != place_holder_deleg) - delegation = list_entry_rcu(server->delegations.next, - struct nfs_delegation, super_list); - list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { - struct inode *to_put = NULL; - - if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags)) - continue; - if (!nfs_delegation_need_return(delegation)) { - if (nfs4_is_valid_delegation(delegation, 0)) - prev = delegation; - continue; - } - inode = nfs_delegation_grab_inode(delegation); - if (inode == NULL) - continue; + while ((err = nfs_return_one_delegation(server)) > 0) + cond_resched(); + return err; +} - if (prev) { - struct inode *tmp = nfs_delegation_grab_inode(prev); - if (tmp) { - to_put = place_holder; - place_holder = tmp; - place_holder_deleg = prev; - } - } +static inline bool nfs_delegations_over_limit(struct nfs_server *server) +{ + return !list_empty_careful(&server->delegations_lru) && + atomic_long_read(&server->nr_active_delegations) > + nfs_delegation_watermark; +} - delegation = nfs_start_delegation_return_locked(NFS_I(inode)); - rcu_read_unlock(); +static void nfs_delegations_return_from_lru(struct nfs_server *server) +{ + struct nfs_delegation *d, *n; + unsigned int pass = 0; + bool moved = false; + +retry: + spin_lock(&server->delegations_lock); + list_for_each_entry_safe(d, n, &server->delegations_lru, entry) { + if (!nfs_delegations_over_limit(server)) + break; + if (pass == 0 && test_bit(NFS_DELEGATION_REFERENCED, &d->flags)) + continue; + list_move_tail(&d->entry, &server->delegations_return); + moved = true; + } + spin_unlock(&server->delegations_lock); - iput(to_put); + /* + * If we are still over the limit, try to reclaim referenced delegations + * as well. + */ + if (pass == 0 && nfs_delegations_over_limit(server)) { + pass++; + goto retry; + } - err = nfs_end_delegation_return(inode, delegation, 0); - iput(inode); - cond_resched(); - if (!err) - goto restart; - set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); + if (moved) { set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); - goto out; + nfs4_schedule_state_manager(server->nfs_client); } - rcu_read_unlock(); -out: - iput(place_holder); - return err; +} + +static void nfs_delegation_add_lru(struct nfs_server *server, + struct nfs_delegation *delegation) +{ + spin_lock(&server->delegations_lock); + if (list_empty(&delegation->entry)) { + list_add_tail(&delegation->entry, &server->delegations_lru); + refcount_inc(&delegation->refcount); + } + spin_unlock(&server->delegations_lock); + + if (nfs_delegations_over_limit(server)) + nfs_delegations_return_from_lru(server); } static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) { - struct nfs_delegation *d; bool ret = false; - if (!test_and_clear_bit(NFS4SERV_DELEGRETURN_DELAYED, - &server->delegation_flags)) - goto out; - list_for_each_entry_rcu (d, &server->delegations, super_list) { - if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) - continue; - nfs_mark_return_delegation(server, d); - clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); + if (list_empty_careful(&server->delegations_delayed)) + return false; + + spin_lock(&server->delegations_lock); + if (!list_empty(&server->delegations_delayed)) { + list_splice_tail_init(&server->delegations_delayed, + &server->delegations_return); ret = true; } -out: + spin_unlock(&server->delegations_lock); + return ret; } @@ -731,14 +725,17 @@ static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp) bool ret = false; if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state)) - goto out; + return false; + rcu_read_lock(); list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) { if (nfs_server_clear_delayed_delegations(server)) ret = true; } rcu_read_unlock(); -out: + + if (ret) + set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); return ret; } @@ -774,15 +771,23 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp) */ void nfs_inode_evict_delegation(struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_server *server = NFS_SERVER(inode); struct nfs_delegation *delegation; - delegation = nfs_inode_detach_delegation(inode); - if (delegation != NULL) { - set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); - set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); - nfs_do_return_delegation(inode, delegation, 1); - nfs_free_delegation(NFS_SERVER(inode), delegation); - } + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); + if (delegation && !nfs_detach_delegation(nfsi, delegation, server)) + delegation = NULL; + rcu_read_unlock(); + + if (!delegation) + return; + + set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); + nfs_do_return_delegation(inode, delegation, 1); + nfs_mark_delegation_revoked(server, delegation); + nfs_put_delegation(delegation); } /** @@ -795,20 +800,21 @@ void nfs_inode_evict_delegation(struct inode *inode) * * Returns zero on success, or a negative errno value. */ -int nfs4_inode_return_delegation(struct inode *inode) +void nfs4_inode_return_delegation(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; delegation = nfs_start_delegation_return(nfsi); - if (delegation != NULL) { - /* Synchronous recall of any application leases */ - break_lease(inode, O_WRONLY | O_RDWR); - if (S_ISREG(inode->i_mode)) - nfs_wb_all(inode); - return nfs_end_delegation_return(inode, delegation, 1); - } - return 0; + if (!delegation) + return; + + /* Synchronous recall of any application leases */ + break_lease(inode, O_WRONLY | O_RDWR); + if (S_ISREG(inode->i_mode)) + nfs_wb_all(inode); + nfs_end_delegation_return(inode, delegation, true); + nfs_put_delegation(delegation); } /** @@ -822,30 +828,30 @@ int nfs4_inode_return_delegation(struct inode *inode) void nfs4_inode_set_return_delegation_on_close(struct inode *inode) { struct nfs_delegation *delegation; - struct nfs_delegation *ret = NULL; + bool return_now = false; if (!inode) return; - rcu_read_lock(); + delegation = nfs4_get_valid_delegation(inode); if (!delegation) - goto out; + return; + spin_lock(&delegation->lock); if (!delegation->inode) goto out_unlock; if (list_empty(&NFS_I(inode)->open_files) && - !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { - /* Refcount matched in nfs_end_delegation_return() */ - ret = nfs_get_delegation(delegation); - } else + !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + return_now = true; + else set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); out_unlock: spin_unlock(&delegation->lock); - if (ret) + if (return_now) { nfs_clear_verifier_delegated(inode); -out: - rcu_read_unlock(); - nfs_end_delegation_return(inode, ret, 0); + nfs_end_delegation_return(inode, delegation, false); + } + nfs_put_delegation(delegation); } /** @@ -857,33 +863,32 @@ out: */ void nfs4_inode_return_delegation_on_close(struct inode *inode) { + struct nfs_server *server = NFS_SERVER(inode); struct nfs_delegation *delegation; - struct nfs_delegation *ret = NULL; + bool return_now = false; - if (!inode) - return; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); if (!delegation) - goto out; - if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || - atomic_long_read(&NFS_SERVER(inode)->nr_active_delegations) >= - nfs_delegation_watermark) { + return; + + if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { spin_lock(&delegation->lock); if (delegation->inode && list_empty(&NFS_I(inode)->open_files) && !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); - /* Refcount matched in nfs_end_delegation_return() */ - ret = nfs_get_delegation(delegation); + return_now = true; } spin_unlock(&delegation->lock); - if (ret) - nfs_clear_verifier_delegated(inode); } -out: - rcu_read_unlock(); - nfs_end_delegation_return(inode, ret, 0); + + if (return_now) { + nfs_clear_verifier_delegated(inode); + nfs_end_delegation_return(inode, delegation, false); + } else { + nfs_delegation_add_lru(server, delegation); + } + nfs_put_delegation(delegation); } /** @@ -891,23 +896,19 @@ out: * @inode: pointer to inode * * Make the inode writeable by returning the delegation if necessary - * - * Returns zero on success, or a negative errno value. */ -int nfs4_inode_make_writeable(struct inode *inode) +void nfs4_inode_make_writeable(struct inode *inode) { struct nfs_delegation *delegation; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL || - (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && - (delegation->type & FMODE_WRITE))) { - rcu_read_unlock(); - return 0; - } - rcu_read_unlock(); - return nfs4_inode_return_delegation(inode); + if (!delegation) + return; + + if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) || + !(delegation->type & FMODE_WRITE)) + nfs4_inode_return_delegation(inode); + nfs_put_delegation(delegation); } static void @@ -916,7 +917,7 @@ nfs_mark_return_if_closed_delegation(struct nfs_server *server, { struct inode *inode; - if (test_bit(NFS_DELEGATION_RETURN, &delegation->flags) || + if (!list_empty_careful(&server->delegations_return) || test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) return; spin_lock(&delegation->lock); @@ -943,16 +944,6 @@ static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) return ret; } -static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) -{ - struct nfs_server *server; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_server_mark_return_all_delegations(server); - rcu_read_unlock(); -} - static void nfs_delegation_run_state_manager(struct nfs_client *clp) { if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) @@ -966,7 +957,13 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp) */ void nfs_expire_all_delegations(struct nfs_client *clp) { - nfs_client_mark_return_all_delegations(clp); + struct nfs_server *server; + + rcu_read_lock(); + list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) + nfs_server_mark_return_all_delegations(server); + rcu_read_unlock(); + nfs_delegation_run_state_manager(clp); } @@ -1006,8 +1003,7 @@ static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, } } -static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, - fmode_t flags) +void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) { struct nfs_server *server; @@ -1015,6 +1011,8 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) nfs_mark_return_unused_delegation_types(server, flags); rcu_read_unlock(); + + nfs_delegation_run_state_manager(clp); } static void nfs_revoke_delegation(struct inode *inode, @@ -1111,27 +1109,21 @@ void nfs_remove_bad_delegation(struct inode *inode, } EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); -/** - * nfs_expire_unused_delegation_types - * @clp: client to process - * @flags: delegation types to expire - * - */ -void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) -{ - nfs_client_mark_return_unused_delegation_types(clp, flags); - nfs_delegation_run_state_manager(clp); -} - -static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) +static bool nfs_mark_return_unreferenced_delegations(struct nfs_server *server) { - struct nfs_delegation *delegation; + struct nfs_delegation *d, *n; + bool marked = false; - list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) + spin_lock(&server->delegations_lock); + list_for_each_entry_safe(d, n, &server->delegations_lru, entry) { + if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &d->flags)) continue; - nfs_mark_return_if_closed_delegation(server, delegation); + list_move_tail(&d->entry, &server->delegations_return); + marked = true; } + spin_unlock(&server->delegations_lock); + + return marked; } /** @@ -1142,13 +1134,17 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) void nfs_expire_unreferenced_delegations(struct nfs_client *clp) { struct nfs_server *server; + bool marked = false; rcu_read_lock(); list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - nfs_mark_return_unreferenced_delegations(server); + marked |= nfs_mark_return_unreferenced_delegations(server); rcu_read_unlock(); - nfs_delegation_run_state_manager(clp); + if (marked) { + set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); + nfs4_schedule_state_manager(clp); + } } /** @@ -1165,24 +1161,24 @@ int nfs_async_inode_return_delegation(struct inode *inode, struct nfs_client *clp = server->nfs_client; struct nfs_delegation *delegation; - rcu_read_lock(); delegation = nfs4_get_valid_delegation(inode); - if (delegation == NULL) - goto out_enoent; + if (!delegation) + return -ENOENT; + if (stateid != NULL && - !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) - goto out_enoent; + !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) { + nfs_put_delegation(delegation); + return -ENOENT; + } + nfs_mark_return_delegation(server, delegation); - rcu_read_unlock(); + nfs_put_delegation(delegation); /* If there are any application leases or delegations, recall them */ break_lease(inode, O_WRONLY | O_RDWR | O_NONBLOCK); nfs_delegation_run_state_manager(clp); return 0; -out_enoent: - rcu_read_unlock(); - return -ENOENT; } static struct inode * @@ -1282,9 +1278,7 @@ static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, restart: rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_INODE_FREEING, - &delegation->flags) || - test_bit(NFS_DELEGATION_RETURNING, + if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) @@ -1292,13 +1286,15 @@ restart: inode = nfs_delegation_grab_inode(delegation); if (inode == NULL) continue; - delegation = nfs_start_delegation_return_locked(NFS_I(inode)); + delegation = nfs_start_delegation_return(NFS_I(inode)); rcu_read_unlock(); if (delegation != NULL) { if (nfs_detach_delegation(NFS_I(inode), delegation, - server) != NULL) - nfs_free_delegation(server, delegation); - /* Match nfs_start_delegation_return_locked */ + server)) { + nfs_mark_delegation_revoked(server, delegation); + nfs_put_delegation(delegation); + } + /* Match nfs_start_delegation_return */ nfs_put_delegation(delegation); } iput(inode); @@ -1419,9 +1415,7 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server, restart: rcu_read_lock(); list_for_each_entry_rcu(delegation, &server->delegations, super_list) { - if (test_bit(NFS_DELEGATION_INODE_FREEING, - &delegation->flags) || - test_bit(NFS_DELEGATION_RETURNING, + if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags) == 0 || @@ -1504,7 +1498,7 @@ int nfs_delegations_present(struct nfs_client *clp) rcu_read_lock(); list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) - if (!list_empty(&server->delegations)) { + if (atomic_long_read(&server->nr_active_delegations) > 0) { ret = 1; break; } |
