Commit 17c2f540 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-4.20-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client bugfixes from Trond Myklebust:

 - Fix a NFSv4 state manager deadlock when returning a delegation

 - NFSv4.2 copy do not allocate memory under the lock

 - flexfiles: Use the correct stateid for IO in the tightly coupled case

* tag 'nfs-for-4.20-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  flexfiles: use per-mirror specified stateid for IO
  NFSv4.2 copy do not allocate memory under the lock
  NFSv4: Fix a NFSv4 state manager deadlock
parents 4e962ff6 bb21ce0a
...@@ -686,20 +686,24 @@ __be32 nfs4_callback_offload(void *data, void *dummy, ...@@ -686,20 +686,24 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
{ {
struct cb_offloadargs *args = data; struct cb_offloadargs *args = data;
struct nfs_server *server; struct nfs_server *server;
struct nfs4_copy_state *copy; struct nfs4_copy_state *copy, *tmp_copy;
bool found = false; bool found = false;
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy)
return htonl(NFS4ERR_SERVERFAULT);
spin_lock(&cps->clp->cl_lock); spin_lock(&cps->clp->cl_lock);
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(server, &cps->clp->cl_superblocks, list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
client_link) { client_link) {
list_for_each_entry(copy, &server->ss_copies, copies) { list_for_each_entry(tmp_copy, &server->ss_copies, copies) {
if (memcmp(args->coa_stateid.other, if (memcmp(args->coa_stateid.other,
copy->stateid.other, tmp_copy->stateid.other,
sizeof(args->coa_stateid.other))) sizeof(args->coa_stateid.other)))
continue; continue;
nfs4_copy_cb_args(copy, args); nfs4_copy_cb_args(tmp_copy, args);
complete(&copy->completion); complete(&tmp_copy->completion);
found = true; found = true;
goto out; goto out;
} }
...@@ -707,15 +711,11 @@ __be32 nfs4_callback_offload(void *data, void *dummy, ...@@ -707,15 +711,11 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
out: out:
rcu_read_unlock(); rcu_read_unlock();
if (!found) { if (!found) {
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy) {
spin_unlock(&cps->clp->cl_lock);
return htonl(NFS4ERR_SERVERFAULT);
}
memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE); memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
nfs4_copy_cb_args(copy, args); nfs4_copy_cb_args(copy, args);
list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids); list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
} } else
kfree(copy);
spin_unlock(&cps->clp->cl_lock); spin_unlock(&cps->clp->cl_lock);
return 0; return 0;
......
...@@ -1361,12 +1361,7 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data) ...@@ -1361,12 +1361,7 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data)
task)) task))
return; return;
if (ff_layout_read_prepare_common(task, hdr)) ff_layout_read_prepare_common(task, hdr);
return;
if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
hdr->args.lock_context, FMODE_READ) == -EIO)
rpc_exit(task, -EIO); /* lost lock, terminate I/O */
} }
static void ff_layout_read_call_done(struct rpc_task *task, void *data) static void ff_layout_read_call_done(struct rpc_task *task, void *data)
...@@ -1542,12 +1537,7 @@ static void ff_layout_write_prepare_v4(struct rpc_task *task, void *data) ...@@ -1542,12 +1537,7 @@ static void ff_layout_write_prepare_v4(struct rpc_task *task, void *data)
task)) task))
return; return;
if (ff_layout_write_prepare_common(task, hdr)) ff_layout_write_prepare_common(task, hdr);
return;
if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
hdr->args.lock_context, FMODE_WRITE) == -EIO)
rpc_exit(task, -EIO); /* lost lock, terminate I/O */
} }
static void ff_layout_write_call_done(struct rpc_task *task, void *data) static void ff_layout_write_call_done(struct rpc_task *task, void *data)
...@@ -1742,6 +1732,10 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr) ...@@ -1742,6 +1732,10 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
fh = nfs4_ff_layout_select_ds_fh(lseg, idx); fh = nfs4_ff_layout_select_ds_fh(lseg, idx);
if (fh) if (fh)
hdr->args.fh = fh; hdr->args.fh = fh;
if (!nfs4_ff_layout_select_ds_stateid(lseg, idx, &hdr->args.stateid))
goto out_failed;
/* /*
* Note that if we ever decide to split across DSes, * Note that if we ever decide to split across DSes,
* then we may need to handle dense-like offsets. * then we may need to handle dense-like offsets.
...@@ -1804,6 +1798,9 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync) ...@@ -1804,6 +1798,9 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
if (fh) if (fh)
hdr->args.fh = fh; hdr->args.fh = fh;
if (!nfs4_ff_layout_select_ds_stateid(lseg, idx, &hdr->args.stateid))
goto out_failed;
/* /*
* Note that if we ever decide to split across DSes, * Note that if we ever decide to split across DSes,
* then we may need to handle dense-like offsets. * then we may need to handle dense-like offsets.
......
...@@ -215,6 +215,10 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo, ...@@ -215,6 +215,10 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
unsigned int maxnum); unsigned int maxnum);
struct nfs_fh * struct nfs_fh *
nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx); nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx);
int
nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg,
u32 mirror_idx,
nfs4_stateid *stateid);
struct nfs4_pnfs_ds * struct nfs4_pnfs_ds *
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
......
...@@ -370,6 +370,25 @@ nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx) ...@@ -370,6 +370,25 @@ nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx)
return fh; return fh;
} }
int
nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg,
u32 mirror_idx,
nfs4_stateid *stateid)
{
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, mirror_idx);
if (!ff_layout_mirror_valid(lseg, mirror, false)) {
pr_err_ratelimited("NFS: %s: No data server for mirror offset index %d\n",
__func__, mirror_idx);
goto out;
}
nfs4_stateid_copy(stateid, &mirror->stateid);
return 1;
out:
return 0;
}
/** /**
* nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call * nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call
* @lseg: the layout segment we're operating on * @lseg: the layout segment we're operating on
......
...@@ -137,31 +137,32 @@ static int handle_async_copy(struct nfs42_copy_res *res, ...@@ -137,31 +137,32 @@ static int handle_async_copy(struct nfs42_copy_res *res,
struct file *dst, struct file *dst,
nfs4_stateid *src_stateid) nfs4_stateid *src_stateid)
{ {
struct nfs4_copy_state *copy; struct nfs4_copy_state *copy, *tmp_copy;
int status = NFS4_OK; int status = NFS4_OK;
bool found_pending = false; bool found_pending = false;
struct nfs_open_context *ctx = nfs_file_open_context(dst); struct nfs_open_context *ctx = nfs_file_open_context(dst);
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy)
return -ENOMEM;
spin_lock(&server->nfs_client->cl_lock); spin_lock(&server->nfs_client->cl_lock);
list_for_each_entry(copy, &server->nfs_client->pending_cb_stateids, list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
copies) { copies) {
if (memcmp(&res->write_res.stateid, &copy->stateid, if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
NFS4_STATEID_SIZE)) NFS4_STATEID_SIZE))
continue; continue;
found_pending = true; found_pending = true;
list_del(&copy->copies); list_del(&tmp_copy->copies);
break; break;
} }
if (found_pending) { if (found_pending) {
spin_unlock(&server->nfs_client->cl_lock); spin_unlock(&server->nfs_client->cl_lock);
kfree(copy);
copy = tmp_copy;
goto out; goto out;
} }
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy) {
spin_unlock(&server->nfs_client->cl_lock);
return -ENOMEM;
}
memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE); memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
init_completion(&copy->completion); init_completion(&copy->completion);
copy->parent_state = ctx->state; copy->parent_state = ctx->state;
......
...@@ -41,6 +41,8 @@ enum nfs4_client_state { ...@@ -41,6 +41,8 @@ enum nfs4_client_state {
NFS4CLNT_MOVED, NFS4CLNT_MOVED,
NFS4CLNT_LEASE_MOVED, NFS4CLNT_LEASE_MOVED,
NFS4CLNT_DELEGATION_EXPIRED, NFS4CLNT_DELEGATION_EXPIRED,
NFS4CLNT_RUN_MANAGER,
NFS4CLNT_DELEGRETURN_RUNNING,
}; };
#define NFS4_RENEW_TIMEOUT 0x01 #define NFS4_RENEW_TIMEOUT 0x01
......
...@@ -1210,6 +1210,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp) ...@@ -1210,6 +1210,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
struct task_struct *task; struct task_struct *task;
char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1]; char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return; return;
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
...@@ -2503,6 +2504,7 @@ static void nfs4_state_manager(struct nfs_client *clp) ...@@ -2503,6 +2504,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
/* Ensure exclusive access to NFSv4 state */ /* Ensure exclusive access to NFSv4 state */
do { do {
clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) { if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
section = "purge state"; section = "purge state";
status = nfs4_purge_lease(clp); status = nfs4_purge_lease(clp);
...@@ -2593,14 +2595,18 @@ static void nfs4_state_manager(struct nfs_client *clp) ...@@ -2593,14 +2595,18 @@ static void nfs4_state_manager(struct nfs_client *clp)
} }
nfs4_end_drain_session(clp); nfs4_end_drain_session(clp);
nfs4_clear_state_manager_bit(clp);
if (!test_and_set_bit(NFS4CLNT_DELEGRETURN_RUNNING, &clp->cl_state)) {
if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
nfs_client_return_marked_delegations(clp); nfs_client_return_marked_delegations(clp);
continue; set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
}
clear_bit(NFS4CLNT_DELEGRETURN_RUNNING, &clp->cl_state);
} }
nfs4_clear_state_manager_bit(clp);
/* Did we race with an attempt to give us more work? */ /* Did we race with an attempt to give us more work? */
if (clp->cl_state == 0) if (!test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
return; return;
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment