Commit 94d471a4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client updates from Trond Myklebust:
 "Bugfixes:

   - Three fixes for looping in the NFSv4 state manager delegation code

   - Fix for the NFSv4 state XDR code (Neil Brown)

   - Fix a leaked reference in nfs_lock_and_join_requests()

   - Fix a use-after-free in the delegation return code

  Features:

   - Implement the NFSv4.2 copy offload OFFLOAD_STATUS operation to
     allow monitoring of an in-progress copy

   - Add a mount option to force NFSv3/NFSv4 to use READDIRPLUS in a
     getdents() call

   - SUNRPC now allows some basic management of an existing RPC client's
     connections using sysfs

   - Improvements to the automated teardown of a NFS client when the
     container it was initiated from gets killed

   - Improvements to prevent tasks from getting stuck in a killable wait
     state after calling exit_signals()"

* tag 'nfs-for-6.15-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (29 commits)
  nfs: Add missing release on error in nfs_lock_and_join_requests()
  NFSv4: Check for delegation validity in nfs_start_delegation_return_locked()
  NFS: Don't allow waiting for exiting tasks
  SUNRPC: Don't allow waiting for exiting tasks
  NFSv4: Treat ENETUNREACH errors as fatal for state recovery
  NFSv4: clp->cl_cons_state < 0 signifies an invalid nfs_client
  NFSv4: Further cleanups to shutdown loops
  NFS: Shut down the nfs_client only after all the superblocks
  SUNRPC: rpc_clnt_set_transport() must not change the autobind setting
  SUNRPC: rpcbind should never reset the port to the value '0'
  pNFS/flexfiles: Report ENETDOWN as a connection error
  pNFS/flexfiles: Treat ENETUNREACH errors as fatal in containers
  NFS: Treat ENETUNREACH errors as fatal in containers
  NFS: Add a mount option to make ENETUNREACH errors fatal
  sunrpc: Add a sysfs file for one-step xprt deletion
  sunrpc: Add a sysfs file for adding a new xprt
  sunrpc: Add a sysfs files for rpc_clnt information
  sunrpc: Add a sysfs attr for xprtsec
  NFS: Add implid to sysfs
  NFS: Extend rdirplus mount option with "force|none"
  ...
parents 5e17b5c7 8e5419d6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -546,6 +546,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
		args.flags |= RPC_CLNT_CREATE_NOPING;
	if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_REUSEPORT;
	if (test_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_NETUNREACH_FATAL;

	if (!IS_ERR(clp->cl_rpcclient))
		return 0;
@@ -709,6 +711,9 @@ static int nfs_init_server(struct nfs_server *server,
	if (ctx->flags & NFS_MOUNT_NORESVPORT)
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	if (ctx->flags & NFS_MOUNT_NETUNREACH_FATAL)
		__set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init.init_flags);

	/* Allocate or find a client reference we can use */
	clp = nfs_get_client(&cl_init);
	if (IS_ERR(clp))
+44 −22
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
				       struct nfs_delegation *delegation)
{
	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
	set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags);
	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
}

@@ -306,7 +307,8 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
	if (delegation == NULL)
		goto out;
	spin_lock(&delegation->lock);
	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
	if (delegation->inode &&
	    !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);
@@ -330,14 +332,16 @@ nfs_start_delegation_return(struct nfs_inode *nfsi)
}

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

	spin_lock(&delegation->lock);
	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
	if (err == -EAGAIN) {
		set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags);
		set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state);
		set_bit(NFS4SERV_DELEGRETURN_DELAYED,
			&server->delegation_flags);
		set_bit(NFS4CLNT_DELEGRETURN_DELAYED,
			&server->nfs_client->cl_state);
	}
	spin_unlock(&delegation->lock);
}
@@ -547,7 +551,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
 */
static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
	struct nfs_server *server = NFS_SERVER(inode);
	unsigned int mode = O_WRONLY | O_RDWR;
	int err = 0;

@@ -569,11 +573,11 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
		/*
		 * Guard against state recovery
		 */
		err = nfs4_wait_clnt_recover(clp);
		err = nfs4_wait_clnt_recover(server->nfs_client);
	}

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

@@ -590,17 +594,6 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)

	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
		ret = true;
	else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
		struct inode *inode;

		spin_lock(&delegation->lock);
		inode = delegation->inode;
		if (inode && list_empty(&NFS_I(inode)->open_files))
			ret = true;
		spin_unlock(&delegation->lock);
	}
	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))
@@ -619,6 +612,9 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server,
	struct nfs_delegation *place_holder_deleg = NULL;
	int err = 0;

	if (!test_and_clear_bit(NFS4SERV_DELEGRETURN,
				&server->delegation_flags))
		return 0;
restart:
	/*
	 * To avoid quadratic looping we hold a reference
@@ -670,6 +666,7 @@ static int nfs_server_return_marked_delegations(struct nfs_server *server,
		cond_resched();
		if (!err)
			goto restart;
		set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags);
		set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
		goto out;
	}
@@ -684,6 +681,9 @@ static bool nfs_server_clear_delayed_delegations(struct nfs_server *server)
	struct nfs_delegation *d;
	bool ret = false;

	if (!test_and_clear_bit(NFS4SERV_DELEGRETURN_DELAYED,
				&server->delegation_flags))
		goto out;
	list_for_each_entry_rcu (d, &server->delegations, super_list) {
		if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags))
			continue;
@@ -691,6 +691,7 @@ static bool nfs_server_clear_delayed_delegations(struct nfs_server *server)
		clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags);
		ret = true;
	}
out:
	return ret;
}

@@ -878,11 +879,25 @@ int nfs4_inode_make_writeable(struct inode *inode)
	return nfs4_inode_return_delegation(inode);
}

static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
static void
nfs_mark_return_if_closed_delegation(struct nfs_server *server,
				     struct nfs_delegation *delegation)
{
	struct inode *inode;

	if (test_bit(NFS_DELEGATION_RETURN, &delegation->flags) ||
	    test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags))
		return;
	spin_lock(&delegation->lock);
	inode = delegation->inode;
	if (!inode)
		goto out;
	if (list_empty(&NFS_I(inode)->open_files))
		nfs_mark_return_delegation(server, delegation);
	else
		set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
out:
	spin_unlock(&delegation->lock);
}

static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
@@ -1276,6 +1291,7 @@ static void nfs_mark_test_expired_delegation(struct nfs_server *server,
		return;
	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
	set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
	set_bit(NFS4SERV_DELEGATION_EXPIRED, &server->delegation_flags);
	set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
}

@@ -1354,6 +1370,9 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
	nfs4_stateid stateid;
	unsigned long gen = ++server->delegation_gen;

	if (!test_and_clear_bit(NFS4SERV_DELEGATION_EXPIRED,
				&server->delegation_flags))
		return 0;
restart:
	rcu_read_lock();
	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
@@ -1383,6 +1402,9 @@ static int nfs_server_reap_expired_delegations(struct nfs_server *server,
			goto restart;
		}
		nfs_inode_mark_test_expired_delegation(server,inode);
		set_bit(NFS4SERV_DELEGATION_EXPIRED, &server->delegation_flags);
		set_bit(NFS4CLNT_DELEGATION_EXPIRED,
			&server->nfs_client->cl_state);
		iput(inode);
		return -EAGAIN;
	}
+2 −0
Original line number Diff line number Diff line
@@ -666,6 +666,8 @@ static bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx,
{
	if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
		return false;
	if (NFS_SERVER(dir)->flags & NFS_MOUNT_FORCE_RDIRPLUS)
		return true;
	if (ctx->pos == 0 ||
	    cache_hits + cache_misses > NFS_READDIR_CACHE_USAGE_THRESHOLD)
		return true;
+22 −2
Original line number Diff line number Diff line
@@ -1154,10 +1154,14 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
		rpc_wake_up(&tbl->slot_tbl_waitq);
		goto reset;
	/* RPC connection errors */
	case -ENETDOWN:
	case -ENETUNREACH:
		if (test_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags))
			return -NFS4ERR_FATAL_IOERROR;
		fallthrough;
	case -ECONNREFUSED:
	case -EHOSTDOWN:
	case -EHOSTUNREACH:
	case -ENETUNREACH:
	case -EIO:
	case -ETIMEDOUT:
	case -EPIPE:
@@ -1183,6 +1187,7 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,

/* Retry all errors through either pNFS or MDS except for -EJUKEBOX */
static int ff_layout_async_handle_error_v3(struct rpc_task *task,
					   struct nfs_client *clp,
					   struct pnfs_layout_segment *lseg,
					   u32 idx)
{
@@ -1200,6 +1205,11 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task,
	case -EJUKEBOX:
		nfs_inc_stats(lseg->pls_layout->plh_inode, NFSIOS_DELAY);
		goto out_retry;
	case -ENETDOWN:
	case -ENETUNREACH:
		if (test_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags))
			return -NFS4ERR_FATAL_IOERROR;
		fallthrough;
	default:
		dprintk("%s DS connection error %d\n", __func__,
			task->tk_status);
@@ -1234,7 +1244,7 @@ static int ff_layout_async_handle_error(struct rpc_task *task,

	switch (vers) {
	case 3:
		return ff_layout_async_handle_error_v3(task, lseg, idx);
		return ff_layout_async_handle_error_v3(task, clp, lseg, idx);
	case 4:
		return ff_layout_async_handle_error_v4(task, state, clp,
						       lseg, idx);
@@ -1264,6 +1274,7 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
		case -ECONNRESET:
		case -EHOSTDOWN:
		case -EHOSTUNREACH:
		case -ENETDOWN:
		case -ENETUNREACH:
		case -EADDRINUSE:
		case -ENOBUFS:
@@ -1337,6 +1348,9 @@ static int ff_layout_read_done_cb(struct rpc_task *task,
		return task->tk_status;
	case -EAGAIN:
		goto out_eagain;
	case -NFS4ERR_FATAL_IOERROR:
		task->tk_status = -EIO;
		return 0;
	}

	return 0;
@@ -1507,6 +1521,9 @@ static int ff_layout_write_done_cb(struct rpc_task *task,
		return task->tk_status;
	case -EAGAIN:
		return -EAGAIN;
	case -NFS4ERR_FATAL_IOERROR:
		task->tk_status = -EIO;
		return 0;
	}

	if (hdr->res.verf->committed == NFS_FILE_SYNC ||
@@ -1551,6 +1568,9 @@ static int ff_layout_commit_done_cb(struct rpc_task *task,
	case -EAGAIN:
		rpc_restart_call_prepare(task);
		return -EAGAIN;
	case -NFS4ERR_FATAL_IOERROR:
		task->tk_status = -EIO;
		return 0;
	}

	ff_layout_set_layoutcommit(data->inode, data->lseg, data->lwb);
+67 −4
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ enum nfs_param {
	Opt_clientaddr,
	Opt_cto,
	Opt_alignwrite,
	Opt_fatal_neterrors,
	Opt_fg,
	Opt_fscache,
	Opt_fscache_flag,
@@ -72,6 +73,8 @@ enum nfs_param {
	Opt_posix,
	Opt_proto,
	Opt_rdirplus,
	Opt_rdirplus_none,
	Opt_rdirplus_force,
	Opt_rdma,
	Opt_resvport,
	Opt_retrans,
@@ -95,6 +98,20 @@ enum nfs_param {
	Opt_xprtsec,
};

enum {
	Opt_fatal_neterrors_default,
	Opt_fatal_neterrors_enetunreach,
	Opt_fatal_neterrors_none,
};

static const struct constant_table nfs_param_enums_fatal_neterrors[] = {
	{ "default",			Opt_fatal_neterrors_default },
	{ "ENETDOWN:ENETUNREACH",	Opt_fatal_neterrors_enetunreach },
	{ "ENETUNREACH:ENETDOWN",	Opt_fatal_neterrors_enetunreach },
	{ "none",			Opt_fatal_neterrors_none },
	{}
};

enum {
	Opt_local_lock_all,
	Opt_local_lock_flock,
@@ -151,6 +168,8 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
	fsparam_string("clientaddr",	Opt_clientaddr),
	fsparam_flag_no("cto",		Opt_cto),
	fsparam_flag_no("alignwrite",	Opt_alignwrite),
	fsparam_enum("fatal_neterrors", Opt_fatal_neterrors,
		     nfs_param_enums_fatal_neterrors),
	fsparam_flag  ("fg",		Opt_fg),
	fsparam_flag_no("fsc",		Opt_fscache_flag),
	fsparam_string("fsc",		Opt_fscache),
@@ -174,7 +193,8 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
	fsparam_u32   ("port",		Opt_port),
	fsparam_flag_no("posix",	Opt_posix),
	fsparam_string("proto",		Opt_proto),
	fsparam_flag_no("rdirplus",	Opt_rdirplus),
	fsparam_flag_no("rdirplus", Opt_rdirplus), // rdirplus|nordirplus
	fsparam_string("rdirplus",  Opt_rdirplus), // rdirplus=...
	fsparam_flag  ("rdma",		Opt_rdma),
	fsparam_flag_no("resvport",	Opt_resvport),
	fsparam_u32   ("retrans",	Opt_retrans),
@@ -288,6 +308,12 @@ static const struct constant_table nfs_xprtsec_policies[] = {
	{}
};

static const struct constant_table nfs_rdirplus_tokens[] = {
	{ "none",	Opt_rdirplus_none },
	{ "force",	Opt_rdirplus_force },
	{}
};

/*
 * Sanity-check a server address provided by the mount command.
 *
@@ -636,10 +662,25 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
			ctx->flags &= ~NFS_MOUNT_NOACL;
		break;
	case Opt_rdirplus:
		if (result.negated)
		if (result.negated) {
			ctx->flags &= ~NFS_MOUNT_FORCE_RDIRPLUS;
			ctx->flags |= NFS_MOUNT_NORDIRPLUS;
		else
		} else if (!param->string) {
			ctx->flags &= ~(NFS_MOUNT_NORDIRPLUS | NFS_MOUNT_FORCE_RDIRPLUS);
		} else {
			switch (lookup_constant(nfs_rdirplus_tokens, param->string, -1)) {
			case Opt_rdirplus_none:
				ctx->flags &= ~NFS_MOUNT_FORCE_RDIRPLUS;
				ctx->flags |= NFS_MOUNT_NORDIRPLUS;
				break;
			case Opt_rdirplus_force:
				ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
				ctx->flags |= NFS_MOUNT_FORCE_RDIRPLUS;
				break;
			default:
				goto out_invalid_value;
			}
		}
		break;
	case Opt_sharecache:
		if (result.negated)
@@ -872,6 +913,25 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
			goto out_of_bounds;
		ctx->nfs_server.max_connect = result.uint_32;
		break;
	case Opt_fatal_neterrors:
		trace_nfs_mount_assign(param->key, param->string);
		switch (result.uint_32) {
		case Opt_fatal_neterrors_default:
			if (fc->net_ns != &init_net)
				ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;
			else
				ctx->flags &= ~NFS_MOUNT_NETUNREACH_FATAL;
			break;
		case Opt_fatal_neterrors_enetunreach:
			ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;
			break;
		case Opt_fatal_neterrors_none:
			ctx->flags &= ~NFS_MOUNT_NETUNREACH_FATAL;
			break;
		default:
			goto out_invalid_value;
		}
		break;
	case Opt_lookupcache:
		trace_nfs_mount_assign(param->key, param->string);
		switch (result.uint_32) {
@@ -1651,6 +1711,9 @@ static int nfs_init_fs_context(struct fs_context *fc)
		ctx->xprtsec.cert_serial	= TLS_NO_CERT;
		ctx->xprtsec.privkey_serial	= TLS_NO_PRIVKEY;

		if (fc->net_ns != &init_net)
			ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;

		fc->s_iflags		|= SB_I_STABLE_WRITES;
	}
	fc->fs_private = ctx;
Loading