Commit 911d137a authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'nfsd-5.5' of git://linux-nfs.org/~bfields/linux

Pull nfsd updates from Bruce Fields:
 "This is a relatively quiet cycle for nfsd, mainly various bugfixes.

  Possibly most interesting is Trond's fixes for some callback races
  that were due to my incomplete understanding of rpc client shutdown.
  Unfortunately at the last minute I've started noticing a new
  intermittent failure to send callbacks. As the logic seems basically
  correct, I'm leaving Trond's patches in for now, and hope to find a
  fix in the next week so I don't have to revert those patches"

* tag 'nfsd-5.5' of git://linux-nfs.org/~bfields/linux: (24 commits)
  nfsd: depend on CRYPTO_MD5 for legacy client tracking
  NFSD fixing possible null pointer derefering in copy offload
  nfsd: check for EBUSY from vfs_rmdir/vfs_unink.
  nfsd: Ensure CLONE persists data and metadata changes to the target file
  SUNRPC: Fix backchannel latency metrics
  nfsd: restore NFSv3 ACL support
  nfsd: v4 support requires CRYPTO_SHA256
  nfsd: Fix cld_net->cn_tfm initialization
  lockd: remove __KERNEL__ ifdefs
  sunrpc: remove __KERNEL__ ifdefs
  race in exportfs_decode_fh()
  nfsd: Drop LIST_HEAD where the variable it declares is never used.
  nfsd: document callback_wq serialization of callback code
  nfsd: mark cb path down on unknown errors
  nfsd: Fix races between nfsd4_cb_release() and nfsd4_shutdown_callback()
  nfsd: minor 4.1 callback cleanup
  SUNRPC: Fix svcauth_gss_proxy_init()
  SUNRPC: Trace gssproxy upcall results
  sunrpc: fix crash when cache_head become valid before update
  nfsd: remove private bin2hex implementation
  ...
parents fb9bf40c 38a2204f
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -73,7 +73,8 @@ config NFSD_V4
	select NFSD_V3
	select FS_POSIX_ACL
	select SUNRPC_GSS
	select CRYPTO
	select CRYPTO_MD5
	select CRYPTO_SHA256
	select GRACE_PERIOD
	help
	  This option enables support in your system's NFS server for
+0 −2
Original line number Diff line number Diff line
@@ -685,8 +685,6 @@ nfsd_file_cache_purge(struct net *net)
void
nfsd_file_cache_shutdown(void)
{
	LIST_HEAD(dispose);

	set_bit(NFSD_FILE_SHUTDOWN, &nfsd_file_lru_flags);

	lease_unregister_notifier(&nfsd_file_lease_notifier);
+85 −19
Original line number Diff line number Diff line
@@ -826,6 +826,31 @@ static int max_cb_time(struct net *net)
	return max(nn->nfsd4_lease/10, (time_t)1) * HZ;
}

static struct workqueue_struct *callback_wq;

static bool nfsd4_queue_cb(struct nfsd4_callback *cb)
{
	return queue_work(callback_wq, &cb->cb_work);
}

static void nfsd41_cb_inflight_begin(struct nfs4_client *clp)
{
	atomic_inc(&clp->cl_cb_inflight);
}

static void nfsd41_cb_inflight_end(struct nfs4_client *clp)
{

	if (atomic_dec_and_test(&clp->cl_cb_inflight))
		wake_up_var(&clp->cl_cb_inflight);
}

static void nfsd41_cb_inflight_wait_complete(struct nfs4_client *clp)
{
	wait_var_event(&clp->cl_cb_inflight,
			!atomic_read(&clp->cl_cb_inflight));
}

static const struct cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
{
	if (clp->cl_minorversion == 0) {
@@ -937,14 +962,21 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
		clp->cl_cb_state = NFSD4_CB_UP;
}

static void nfsd4_cb_probe_release(void *calldata)
{
	struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);

	nfsd41_cb_inflight_end(clp);

}

static const struct rpc_call_ops nfsd4_cb_probe_ops = {
	/* XXX: release method to ensure we set the cb channel down if
	 * necessary on early failure? */
	.rpc_call_done = nfsd4_cb_probe_done,
	.rpc_release = nfsd4_cb_probe_release,
};

static struct workqueue_struct *callback_wq;

/*
 * Poke the callback thread to process any updates to the callback
 * parameters, and send a null probe.
@@ -975,9 +1007,12 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
 * If the slot is available, then mark it busy.  Otherwise, set the
 * thread for sleeping on the callback RPC wait queue.
 */
static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
{
	if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
	struct nfs4_client *clp = cb->cb_clp;

	if (!cb->cb_holds_slot &&
	    test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
		rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
		/* Race breaker */
		if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
@@ -986,9 +1021,31 @@ static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
		}
		rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
	}
	cb->cb_holds_slot = true;
	return true;
}

static void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
{
	struct nfs4_client *clp = cb->cb_clp;

	if (cb->cb_holds_slot) {
		cb->cb_holds_slot = false;
		clear_bit(0, &clp->cl_cb_slot_busy);
		rpc_wake_up_next(&clp->cl_cb_waitq);
	}
}

static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
{
	struct nfs4_client *clp = cb->cb_clp;

	nfsd41_cb_release_slot(cb);
	if (cb->cb_ops && cb->cb_ops->release)
		cb->cb_ops->release(cb);
	nfsd41_cb_inflight_end(clp);
}

/*
 * TODO: cb_sequence should support referring call lists, cachethis, multiple
 * slots, and mark callback channel down on communication errors.
@@ -1005,11 +1062,8 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
	 */
	cb->cb_seq_status = 1;
	cb->cb_status = 0;
	if (minorversion) {
		if (!cb->cb_holds_slot && !nfsd41_cb_get_slot(clp, task))
	if (minorversion && !nfsd41_cb_get_slot(cb, task))
		return;
		cb->cb_holds_slot = true;
	}
	rpc_call_start(task);
}

@@ -1072,13 +1126,12 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
		}
		break;
	default:
		nfsd4_mark_cb_fault(cb->cb_clp, cb->cb_seq_status);
		dprintk("%s: unprocessed error %d\n", __func__,
			cb->cb_seq_status);
	}

	cb->cb_holds_slot = false;
	clear_bit(0, &clp->cl_cb_slot_busy);
	rpc_wake_up_next(&clp->cl_cb_waitq);
	nfsd41_cb_release_slot(cb);
	dprintk("%s: freed slot, new seqid=%d\n", __func__,
		clp->cl_cb_session->se_cb_seq_nr);

@@ -1091,8 +1144,10 @@ retry_nowait:
		ret = false;
	goto out;
need_restart:
	if (!test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) {
		task->tk_status = 0;
		cb->cb_need_restart = true;
	}
	return false;
}

@@ -1134,9 +1189,9 @@ static void nfsd4_cb_release(void *calldata)
	struct nfsd4_callback *cb = calldata;

	if (cb->cb_need_restart)
		nfsd4_run_cb(cb);
		nfsd4_queue_cb(cb);
	else
		cb->cb_ops->release(cb);
		nfsd41_destroy_cb(cb);

}

@@ -1170,6 +1225,7 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp)
	 */
	nfsd4_run_cb(&clp->cl_cb_null);
	flush_workqueue(callback_wq);
	nfsd41_cb_inflight_wait_complete(clp);
}

/* requires cl_lock: */
@@ -1187,6 +1243,12 @@ static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
	return NULL;
}

/*
 * Note there isn't a lot of locking in this code; instead we depend on
 * the fact that it is run from the callback_wq, which won't run two
 * work items at once.  So, for example, callback_wq handles all access
 * of cl_cb_client and all calls to rpc_create or rpc_shutdown_client.
 */
static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
{
	struct nfs4_cb_conn conn;
@@ -1255,8 +1317,7 @@ nfsd4_run_cb_work(struct work_struct *work)
	clnt = clp->cl_cb_client;
	if (!clnt) {
		/* Callback channel broken, or client killed; give up: */
		if (cb->cb_ops && cb->cb_ops->release)
			cb->cb_ops->release(cb);
		nfsd41_destroy_cb(cb);
		return;
	}

@@ -1265,6 +1326,7 @@ nfsd4_run_cb_work(struct work_struct *work)
	 */
	if (!cb->cb_ops && clp->cl_minorversion) {
		clp->cl_cb_state = NFSD4_CB_UP;
		nfsd41_destroy_cb(cb);
		return;
	}

@@ -1290,5 +1352,9 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,

void nfsd4_run_cb(struct nfsd4_callback *cb)
{
	queue_work(callback_wq, &cb->cb_work);
	struct nfs4_client *clp = cb->cb_clp;

	nfsd41_cb_inflight_begin(clp);
	if (!nfsd4_queue_cb(cb))
		nfsd41_cb_inflight_end(clp);
}
+4 −2
Original line number Diff line number Diff line
@@ -1077,7 +1077,8 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		goto out;

	status = nfsd4_clone_file_range(src->nf_file, clone->cl_src_pos,
			dst->nf_file, clone->cl_dst_pos, clone->cl_count);
			dst->nf_file, clone->cl_dst_pos, clone->cl_count,
			EX_ISSYNC(cstate->current_fh.fh_export));

	nfsd_file_put(dst);
	nfsd_file_put(src);
@@ -1297,6 +1298,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
out:
	return status;
out_err:
	if (async_copy)
		cleanup_async_copy(async_copy);
	goto out;
}
+10 −13
Original line number Diff line number Diff line
@@ -1578,6 +1578,7 @@ nfsd4_cld_tracking_init(struct net *net)
	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
	bool running;
	int retries = 10;
	struct crypto_shash *tfm;

	status = nfs4_cld_state_init(net);
	if (status)
@@ -1586,11 +1587,6 @@ nfsd4_cld_tracking_init(struct net *net)
	status = __nfsd4_init_cld_pipe(net);
	if (status)
		goto err_shutdown;
	nn->cld_net->cn_tfm = crypto_alloc_shash("sha256", 0, 0);
	if (IS_ERR(nn->cld_net->cn_tfm)) {
		status = PTR_ERR(nn->cld_net->cn_tfm);
		goto err_remove;
	}

	/*
	 * rpc pipe upcalls take 30 seconds to time out, so we don't want to
@@ -1607,6 +1603,12 @@ nfsd4_cld_tracking_init(struct net *net)
		status = -ETIMEDOUT;
		goto err_remove;
	}
	tfm = crypto_alloc_shash("sha256", 0, 0);
	if (IS_ERR(tfm)) {
		status = PTR_ERR(tfm);
		goto err_remove;
	}
	nn->cld_net->cn_tfm = tfm;

	status = nfsd4_cld_get_version(nn);
	if (status == -EOPNOTSUPP)
@@ -1850,19 +1852,14 @@ nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
static char *
bin_to_hex_dup(const unsigned char *src, int srclen)
{
	int i;
	char *buf, *hex;
	char *buf;

	/* +1 for terminating NULL */
	buf = kmalloc((srclen * 2) + 1, GFP_KERNEL);
	buf = kzalloc((srclen * 2) + 1, GFP_KERNEL);
	if (!buf)
		return buf;

	hex = buf;
	for (i = 0; i < srclen; i++) {
		sprintf(hex, "%2.2x", *src++);
		hex += 2;
	}
	bin2hex(buf, src, srclen);
	return buf;
}

Loading