Skip to content

Commit

Permalink
NFSv4: Fix delegation return in cases where we have to retry
Browse files Browse the repository at this point in the history
[ Upstream commit be20037 ]

If we're unable to immediately recover all locks because the server is
unable to immediately service our reclaim calls, then we want to retry
after we've finished servicing all the other asynchronous delegation
returns on our queue.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Trond Myklebust authored and gregkh committed Jul 20, 2021
1 parent eeb8022 commit 9f397e9
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 15 deletions.
71 changes: 56 additions & 15 deletions fs/nfs/delegation.c
Expand Up @@ -75,6 +75,13 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
}

static void nfs_mark_return_delegation(struct nfs_server *server,
struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
}

static bool
nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
fmode_t flags)
Expand Down Expand Up @@ -293,6 +300,7 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
goto out;
spin_lock(&delegation->lock);
if (!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);
}
Expand All @@ -314,16 +322,17 @@ nfs_start_delegation_return(struct nfs_inode *nfsi)
return delegation;
}

static void
nfs_abort_delegation_return(struct nfs_delegation *delegation,
struct nfs_client *clp)
static void nfs_abort_delegation_return(struct nfs_delegation *delegation,
struct nfs_client *clp, int err)
{

spin_lock(&delegation->lock);
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
if (err == -EAGAIN) {
set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state);
}
spin_unlock(&delegation->lock);
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}

static struct nfs_delegation *
Expand Down Expand Up @@ -539,7 +548,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
} while (err == 0);

if (err) {
nfs_abort_delegation_return(delegation, clp);
nfs_abort_delegation_return(delegation, clp, err);
goto out;
}

Expand Down Expand Up @@ -568,6 +577,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
if (ret)
clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
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;

Expand Down Expand Up @@ -647,6 +657,38 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server,
return err;
}

static bool nfs_server_clear_delayed_delegations(struct nfs_server *server)
{
struct nfs_delegation *d;
bool ret = false;

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);
ret = true;
}
return ret;
}

static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp)
{
struct nfs_server *server;
bool ret = false;

if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state))
goto out;
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:
return ret;
}

/**
* nfs_client_return_marked_delegations - return previously marked delegations
* @clp: nfs_client to process
Expand All @@ -659,8 +701,14 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server,
*/
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{
return nfs_client_for_each_server(clp,
nfs_server_return_marked_delegations, NULL);
int err = nfs_client_for_each_server(
clp, nfs_server_return_marked_delegations, NULL);
if (err)
return err;
/* If a return was delayed, sleep to prevent hard looping */
if (nfs_client_clear_delayed_delegations(clp))
ssleep(1);
return 0;
}

/**
Expand Down Expand Up @@ -775,13 +823,6 @@ static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
}

static void nfs_mark_return_delegation(struct nfs_server *server,
struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
}

static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
{
struct nfs_delegation *delegation;
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/delegation.h
Expand Up @@ -36,6 +36,7 @@ enum {
NFS_DELEGATION_REVOKED,
NFS_DELEGATION_TEST_EXPIRED,
NFS_DELEGATION_INODE_FREEING,
NFS_DELEGATION_RETURN_DELAYED,
};

int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/nfs4_fs.h
Expand Up @@ -45,6 +45,7 @@ enum nfs4_client_state {
NFS4CLNT_RECALL_RUNNING,
NFS4CLNT_RECALL_ANY_LAYOUT_READ,
NFS4CLNT_RECALL_ANY_LAYOUT_RW,
NFS4CLNT_DELEGRETURN_DELAYED,
};

#define NFS4_RENEW_TIMEOUT 0x01
Expand Down

0 comments on commit 9f397e9

Please sign in to comment.