NFS: handle source server reboot

When the source server reboots after a server-to-server copy was
issued, we need to retry the copy from COPY_NOTIFY. We need to
detect that the source server rebooted and there is a copy waiting
on a destination server and wake it up.

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
This commit is contained in:
Olga Kornievskaia 2019-06-14 14:38:40 -04:00 committed by Olga Kornievskaia
parent fefa1a812a
commit 0e65a32c8a
5 changed files with 75 additions and 27 deletions

View File

@ -153,22 +153,26 @@ out_unlock:
} }
static int handle_async_copy(struct nfs42_copy_res *res, static int handle_async_copy(struct nfs42_copy_res *res,
struct nfs_server *server, struct nfs_server *dst_server,
struct nfs_server *src_server,
struct file *src, struct file *src,
struct file *dst, struct file *dst,
nfs4_stateid *src_stateid) nfs4_stateid *src_stateid,
bool *restart)
{ {
struct nfs4_copy_state *copy, *tmp_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 *dst_ctx = nfs_file_open_context(dst);
struct nfs_open_context *src_ctx = nfs_file_open_context(src);
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS); copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy) if (!copy)
return -ENOMEM; return -ENOMEM;
spin_lock(&server->nfs_client->cl_lock); spin_lock(&dst_server->nfs_client->cl_lock);
list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids, list_for_each_entry(tmp_copy,
&dst_server->nfs_client->pending_cb_stateids,
copies) { copies) {
if (memcmp(&res->write_res.stateid, &tmp_copy->stateid, if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
NFS4_STATEID_SIZE)) NFS4_STATEID_SIZE))
@ -178,7 +182,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
break; break;
} }
if (found_pending) { if (found_pending) {
spin_unlock(&server->nfs_client->cl_lock); spin_unlock(&dst_server->nfs_client->cl_lock);
kfree(copy); kfree(copy);
copy = tmp_copy; copy = tmp_copy;
goto out; goto out;
@ -186,19 +190,32 @@ static int handle_async_copy(struct nfs42_copy_res *res,
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_dst_state = dst_ctx->state;
copy->parent_src_state = src_ctx->state;
list_add_tail(&copy->copies, &server->ss_copies); list_add_tail(&copy->copies, &dst_server->ss_copies);
spin_unlock(&server->nfs_client->cl_lock); spin_unlock(&dst_server->nfs_client->cl_lock);
if (dst_server != src_server) {
spin_lock(&src_server->nfs_client->cl_lock);
list_add_tail(&copy->src_copies, &src_server->ss_copies);
spin_unlock(&src_server->nfs_client->cl_lock);
}
status = wait_for_completion_interruptible(&copy->completion); status = wait_for_completion_interruptible(&copy->completion);
spin_lock(&server->nfs_client->cl_lock); spin_lock(&dst_server->nfs_client->cl_lock);
list_del_init(&copy->copies); list_del_init(&copy->copies);
spin_unlock(&server->nfs_client->cl_lock); spin_unlock(&dst_server->nfs_client->cl_lock);
if (dst_server != src_server) {
spin_lock(&src_server->nfs_client->cl_lock);
list_del_init(&copy->src_copies);
spin_unlock(&src_server->nfs_client->cl_lock);
}
if (status == -ERESTARTSYS) { if (status == -ERESTARTSYS) {
goto out_cancel; goto out_cancel;
} else if (copy->flags) { } else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
status = -EAGAIN; status = -EAGAIN;
*restart = true;
goto out_cancel; goto out_cancel;
} }
out: out:
@ -247,7 +264,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
struct nfs42_copy_args *args, struct nfs42_copy_args *args,
struct nfs42_copy_res *res, struct nfs42_copy_res *res,
struct nl4_server *nss, struct nl4_server *nss,
nfs4_stateid *cnr_stateid) nfs4_stateid *cnr_stateid,
bool *restart)
{ {
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
@ -255,7 +273,9 @@ static ssize_t _nfs42_proc_copy(struct file *src,
.rpc_resp = res, .rpc_resp = res,
}; };
struct inode *dst_inode = file_inode(dst); struct inode *dst_inode = file_inode(dst);
struct nfs_server *server = NFS_SERVER(dst_inode); struct inode *src_inode = file_inode(src);
struct nfs_server *dst_server = NFS_SERVER(dst_inode);
struct nfs_server *src_server = NFS_SERVER(src_inode);
loff_t pos_src = args->src_pos; loff_t pos_src = args->src_pos;
loff_t pos_dst = args->dst_pos; loff_t pos_dst = args->dst_pos;
size_t count = args->count; size_t count = args->count;
@ -291,13 +311,15 @@ static ssize_t _nfs42_proc_copy(struct file *src,
if (!res->commit_res.verf) if (!res->commit_res.verf)
return -ENOMEM; return -ENOMEM;
} }
set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
&src_lock->open_context->state->flags);
set_bit(NFS_CLNT_DST_SSC_COPY_STATE, set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
&dst_lock->open_context->state->flags); &dst_lock->open_context->state->flags);
status = nfs4_call_sync(server->client, server, &msg, status = nfs4_call_sync(dst_server->client, dst_server, &msg,
&args->seq_args, &res->seq_res, 0); &args->seq_args, &res->seq_res, 0);
if (status == -ENOTSUPP) if (status == -ENOTSUPP)
server->caps &= ~NFS_CAP_COPY; dst_server->caps &= ~NFS_CAP_COPY;
if (status) if (status)
goto out; goto out;
@ -309,8 +331,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
} }
if (!res->synchronous) { if (!res->synchronous) {
status = handle_async_copy(res, server, src, dst, status = handle_async_copy(res, dst_server, src_server, src,
&args->src_stateid); dst, &args->src_stateid, restart);
if (status) if (status)
return status; return status;
} }
@ -358,6 +380,7 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
.stateid = &args.dst_stateid, .stateid = &args.dst_stateid,
}; };
ssize_t err, err2; ssize_t err, err2;
bool restart = false;
src_lock = nfs_get_lock_context(nfs_file_open_context(src)); src_lock = nfs_get_lock_context(nfs_file_open_context(src));
if (IS_ERR(src_lock)) if (IS_ERR(src_lock))
@ -378,7 +401,7 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
err = _nfs42_proc_copy(src, src_lock, err = _nfs42_proc_copy(src, src_lock,
dst, dst_lock, dst, dst_lock,
&args, &res, &args, &res,
nss, cnr_stateid); nss, cnr_stateid, &restart);
inode_unlock(file_inode(dst)); inode_unlock(file_inode(dst));
if (err >= 0) if (err >= 0)
@ -388,8 +411,11 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
break; break;
} else if (err == -EAGAIN) { } else if (err == -EAGAIN) {
if (!restart) {
dst_exception.retry = 1; dst_exception.retry = 1;
continue; continue;
}
break;
} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) { } else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
args.sync = true; args.sync = true;
dst_exception.retry = 1; dst_exception.retry = 1;

View File

@ -168,6 +168,7 @@ enum {
NFS_STATE_CHANGE_WAIT, /* A state changing operation is outstanding */ NFS_STATE_CHANGE_WAIT, /* A state changing operation is outstanding */
#ifdef CONFIG_NFS_V4_2 #ifdef CONFIG_NFS_V4_2
NFS_CLNT_DST_SSC_COPY_STATE, /* dst server open state on client*/ NFS_CLNT_DST_SSC_COPY_STATE, /* dst server open state on client*/
NFS_CLNT_SRC_SSC_COPY_STATE, /* src server open state on client*/
NFS_SRV_SSC_COPY_STATE, /* ssc state on the dst server */ NFS_SRV_SSC_COPY_STATE, /* ssc state on the dst server */
#endif /* CONFIG_NFS_V4_2 */ #endif /* CONFIG_NFS_V4_2 */
}; };

View File

@ -146,6 +146,7 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (file_inode(file_in) == file_inode(file_out)) if (file_inode(file_in) == file_inode(file_out))
return -EOPNOTSUPP; return -EOPNOTSUPP;
retry:
if (!nfs42_files_from_same_server(file_in, file_out)) { if (!nfs42_files_from_same_server(file_in, file_out)) {
cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res), cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res),
GFP_NOFS); GFP_NOFS);
@ -164,6 +165,8 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
nss, cnrs); nss, cnrs);
out: out:
kfree(cn_resp); kfree(cn_resp);
if (ret == -EAGAIN)
goto retry;
return ret; return ret;
} }

View File

@ -1556,16 +1556,32 @@ static void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state
{ {
struct nfs4_copy_state *copy; struct nfs4_copy_state *copy;
if (!test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags)) if (!test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
!test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags))
return; return;
spin_lock(&sp->so_server->nfs_client->cl_lock); spin_lock(&sp->so_server->nfs_client->cl_lock);
list_for_each_entry(copy, &sp->so_server->ss_copies, copies) { list_for_each_entry(copy, &sp->so_server->ss_copies, copies) {
if (!nfs4_stateid_match_other(&state->stateid, &copy->parent_state->stateid)) if ((test_bit(NFS_CLNT_DST_SSC_COPY_STATE, &state->flags) &&
!nfs4_stateid_match_other(&state->stateid,
&copy->parent_dst_state->stateid)))
continue; continue;
copy->flags = 1; copy->flags = 1;
if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
&state->flags)) {
clear_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags);
complete(&copy->completion);
}
}
list_for_each_entry(copy, &sp->so_server->ss_copies, src_copies) {
if ((test_bit(NFS_CLNT_SRC_SSC_COPY_STATE, &state->flags) &&
!nfs4_stateid_match_other(&state->stateid,
&copy->parent_src_state->stateid)))
continue;
copy->flags = 1;
if (test_and_clear_bit(NFS_CLNT_DST_SSC_COPY_STATE,
&state->flags))
complete(&copy->completion); complete(&copy->completion);
break;
} }
spin_unlock(&sp->so_server->nfs_client->cl_lock); spin_unlock(&sp->so_server->nfs_client->cl_lock);
} }

View File

@ -189,13 +189,15 @@ struct nfs_inode {
struct nfs4_copy_state { struct nfs4_copy_state {
struct list_head copies; struct list_head copies;
struct list_head src_copies;
nfs4_stateid stateid; nfs4_stateid stateid;
struct completion completion; struct completion completion;
uint64_t count; uint64_t count;
struct nfs_writeverf verf; struct nfs_writeverf verf;
int error; int error;
int flags; int flags;
struct nfs4_state *parent_state; struct nfs4_state *parent_src_state;
struct nfs4_state *parent_dst_state;
}; };
/* /*