Commit 0e65a32c authored by Olga Kornievskaia's avatar Olga Kornievskaia Committed by Olga Kornievskaia
Browse files

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: default avatarOlga Kornievskaia <kolga@netapp.com>
parent fefa1a81
Loading
Loading
Loading
Loading
+47 −21
Original line number Diff line number Diff line
@@ -153,22 +153,26 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
}

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 *dst,
			     nfs4_stateid *src_stateid)
			     nfs4_stateid *src_stateid,
			     bool *restart)
{
	struct nfs4_copy_state *copy, *tmp_copy;
	int status = NFS4_OK;
	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);
	if (!copy)
		return -ENOMEM;

	spin_lock(&server->nfs_client->cl_lock);
	list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
	spin_lock(&dst_server->nfs_client->cl_lock);
	list_for_each_entry(tmp_copy,
				&dst_server->nfs_client->pending_cb_stateids,
				copies) {
		if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
				NFS4_STATEID_SIZE))
@@ -178,7 +182,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
		break;
	}
	if (found_pending) {
		spin_unlock(&server->nfs_client->cl_lock);
		spin_unlock(&dst_server->nfs_client->cl_lock);
		kfree(copy);
		copy = tmp_copy;
		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);
	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, &dst_server->ss_copies);
	spin_unlock(&dst_server->nfs_client->cl_lock);

	list_add_tail(&copy->copies, &server->ss_copies);
	spin_unlock(&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);
	spin_lock(&server->nfs_client->cl_lock);
	spin_lock(&dst_server->nfs_client->cl_lock);
	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) {
		goto out_cancel;
	} else if (copy->flags) {
	} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
		status = -EAGAIN;
		*restart = true;
		goto out_cancel;
	}
out:
@@ -247,7 +264,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
				struct nfs42_copy_args *args,
				struct nfs42_copy_res *res,
				struct nl4_server *nss,
				nfs4_stateid *cnr_stateid)
				nfs4_stateid *cnr_stateid,
				bool *restart)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY],
@@ -255,7 +273,9 @@ static ssize_t _nfs42_proc_copy(struct file *src,
		.rpc_resp = res,
	};
	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_dst = args->dst_pos;
	size_t count = args->count;
@@ -291,13 +311,15 @@ static ssize_t _nfs42_proc_copy(struct file *src,
		if (!res->commit_res.verf)
			return -ENOMEM;
	}
	set_bit(NFS_CLNT_SRC_SSC_COPY_STATE,
		&src_lock->open_context->state->flags);
	set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
		&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);
	if (status == -ENOTSUPP)
		server->caps &= ~NFS_CAP_COPY;
		dst_server->caps &= ~NFS_CAP_COPY;
	if (status)
		goto out;

@@ -309,8 +331,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
	}

	if (!res->synchronous) {
		status = handle_async_copy(res, server, src, dst,
				&args->src_stateid);
		status = handle_async_copy(res, dst_server, src_server, src,
				dst, &args->src_stateid, restart);
		if (status)
			return status;
	}
@@ -358,6 +380,7 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
		.stateid	= &args.dst_stateid,
	};
	ssize_t err, err2;
	bool restart = false;

	src_lock = nfs_get_lock_context(nfs_file_open_context(src));
	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,
				dst, dst_lock,
				&args, &res,
				nss, cnr_stateid);
				nss, cnr_stateid, &restart);
		inode_unlock(file_inode(dst));

		if (err >= 0)
@@ -388,8 +411,11 @@ ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src,
			err = -EOPNOTSUPP;
			break;
		} else if (err == -EAGAIN) {
			if (!restart) {
				dst_exception.retry = 1;
				continue;
			}
			break;
		} else if (err == -NFS4ERR_OFFLOAD_NO_REQS && !args.sync) {
			args.sync = true;
			dst_exception.retry = 1;
+1 −0
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ enum {
	NFS_STATE_CHANGE_WAIT,		/* A state changing operation is outstanding */
#ifdef CONFIG_NFS_V4_2
	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 */
#endif /* CONFIG_NFS_V4_2 */
};
+3 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
		return -EOPNOTSUPP;
	if (file_inode(file_in) == file_inode(file_out))
		return -EOPNOTSUPP;
retry:
	if (!nfs42_files_from_same_server(file_in, file_out)) {
		cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res),
				GFP_NOFS);
@@ -164,6 +165,8 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
				nss, cnrs);
out:
	kfree(cn_resp);
	if (ret == -EAGAIN)
		goto retry;
	return ret;
}

+21 −5
Original line number Diff line number Diff line
@@ -1556,16 +1556,32 @@ static void nfs42_complete_copies(struct nfs4_state_owner *sp, struct nfs4_state
{
	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;

	spin_lock(&sp->so_server->nfs_client->cl_lock);
	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;
		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);
		break;
	}
	spin_unlock(&sp->so_server->nfs_client->cl_lock);
}
+3 −1
Original line number Diff line number Diff line
@@ -189,13 +189,15 @@ struct nfs_inode {

struct nfs4_copy_state {
	struct list_head	copies;
	struct list_head	src_copies;
	nfs4_stateid		stateid;
	struct completion	completion;
	uint64_t		count;
	struct nfs_writeverf	verf;
	int			error;
	int			flags;
	struct nfs4_state	*parent_state;
	struct nfs4_state	*parent_src_state;
	struct nfs4_state	*parent_dst_state;
};

/*