NFS client updates for Linux 6.15

Highlights include:
 
 Bugfixes:
 - 3 Fixes for looping in the NFSv4 state manager delegation code.
 - Fix for the NFSv4 state XDR code from Neil Brown.
 - Fix a leaked reference in nfs_lock_and_join_requests().
 - Fix a use-after-free in the delegation return code.
 
 Features:
 - Implemenation of 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().
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEESQctxSBg8JpV8KqEZwvnipYKAPIFAmftuE0ACgkQZwvnipYK
 APIAAhAAqFdJnh88UUT0/R184Qzpd021lR9XhxkwNA3TzhOIzmpuTgBzNE1iMG1j
 EHveYqCpTU2orA1aisAyw5c8meJlsCQREPDvUOQ2i4BTCCmsBHOMxg7KDWwwRdNh
 SVDCezFWrHYz4An81jpgBe3/x6RJaEyAhKC45ZzQruiBtSMeoOX1TAV/DTWwEo0j
 JcLdAUSGVBsfyrj3qT0oJXoj+96o7rbB80loCdNKy8m8PBWHWp0oILwuU00XdXgu
 7jYyjZfxW1013It+vfVFsjTYRVfJ92pq3wiz/U9HXYDe3Arc4oPRw509/Jo3xEWW
 tdUljc/HepD3459ahiubTCLY39JxILl8/GapWe2Fn0J/JJuOGgZX9lqIMKDn4QCA
 6TBOqWK7OEwImj4M7cfPptJQWd+hp91T4AR13xWJeQgp19AR8yOqEW0YX6hVlaBg
 UrBwdR+l6ys5lJJBReUW+JMDCYZmbH9RjuwcqzXn71JmlACHNFi6odwLnQ1mInvF
 P5pEf7aXaZkF6kEz2kmZ1eUgdkERAaIGCNFQTui6intlCSlQodNurrEU7Vx146os
 OvowJYM0HvnVBDOnERrJD04HADKZeDS8jt59ev0uXbP/NFxEJnPRRQgIdiZbfISV
 beQrc2fpUgwdjYAURbW1qWO7XNTJzK9LHJzn02SytfCazX0IQO0=
 =zPX4
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-6.15-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

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"
  ...
This commit is contained in:
Linus Torvalds 2025-04-02 17:06:31 -07:00
commit 94d471a4f4
31 changed files with 807 additions and 74 deletions

View File

@ -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))

View File

@ -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 @@ out:
*/
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 @@ restart:
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,
struct nfs_delegation *delegation)
static void
nfs_mark_return_if_closed_delegation(struct nfs_server *server,
struct nfs_delegation *delegation)
{
set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
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);
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 @@ restart:
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;
}

View File

@ -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;

View File

@ -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 @@ reset:
/* 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);

View File

@ -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
ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
} 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;

View File

@ -74,6 +74,8 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
int nfs_wait_bit_killable(struct wait_bit_key *key, int mode)
{
if (unlikely(nfs_current_task_exiting()))
return -EINTR;
schedule();
if (signal_pending_state(mode, current))
return -ERESTARTSYS;

View File

@ -912,6 +912,11 @@ static inline u32 nfs_stateid_hash(nfs4_stateid *stateid)
}
#endif
static inline bool nfs_current_task_exiting(void)
{
return (current->flags & PF_EXITING) != 0;
}
static inline bool nfs_error_is_fatal(int err)
{
switch (err) {

View File

@ -120,6 +120,8 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
if (test_bit(NFS_CS_NETUNREACH_FATAL, &mds_clp->cl_flags))
__set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init.init_flags);
__set_bit(NFS_CS_DS, &cl_init.init_flags);

View File

@ -39,7 +39,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
__set_current_state(TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
schedule_timeout(NFS_JUKEBOX_RETRY_TIME);
res = -ERESTARTSYS;
} while (!fatal_signal_pending(current));
} while (!fatal_signal_pending(current) && !nfs_current_task_exiting());
return res;
}

View File

@ -21,6 +21,8 @@
#define NFSDBG_FACILITY NFSDBG_PROC
static int nfs42_do_offload_cancel_async(struct file *dst, nfs4_stateid *std);
static int nfs42_proc_offload_status(struct file *file, nfs4_stateid *stateid,
u64 *copied);
static void nfs42_set_netaddr(struct file *filep, struct nfs42_netaddr *naddr)
{
@ -173,6 +175,20 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
return err;
}
static void nfs4_copy_dequeue_callback(struct nfs_server *dst_server,
struct nfs_server *src_server,
struct nfs4_copy_state *copy)
{
spin_lock(&dst_server->nfs_client->cl_lock);
list_del_init(&copy->copies);
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);
}
}
static int handle_async_copy(struct nfs42_copy_res *res,
struct nfs_server *dst_server,
struct nfs_server *src_server,
@ -182,9 +198,12 @@ static int handle_async_copy(struct nfs42_copy_res *res,
bool *restart)
{
struct nfs4_copy_state *copy, *tmp_copy = NULL, *iter;
int status = NFS4_OK;
struct nfs_open_context *dst_ctx = nfs_file_open_context(dst);
struct nfs_open_context *src_ctx = nfs_file_open_context(src);
struct nfs_client *clp = dst_server->nfs_client;
unsigned long timeout = 3 * HZ;
int status = NFS4_OK;
u64 copied;
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_KERNEL);
if (!copy)
@ -222,15 +241,12 @@ static int handle_async_copy(struct nfs42_copy_res *res,
spin_unlock(&src_server->nfs_client->cl_lock);
}
status = wait_for_completion_interruptible(&copy->completion);
spin_lock(&dst_server->nfs_client->cl_lock);
list_del_init(&copy->copies);
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);
}
wait:
status = wait_for_completion_interruptible_timeout(&copy->completion,
timeout);
if (!status)
goto timeout;
nfs4_copy_dequeue_callback(dst_server, src_server, copy);
if (status == -ERESTARTSYS) {
goto out_cancel;
} else if (copy->flags || copy->error == NFS4ERR_PARTNER_NO_AUTH) {
@ -240,6 +256,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
}
out:
res->write_res.count = copy->count;
/* Copy out the updated write verifier provided by CB_OFFLOAD. */
memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
status = -copy->error;
@ -251,6 +268,39 @@ out_cancel:
if (!nfs42_files_from_same_server(src, dst))
nfs42_do_offload_cancel_async(src, src_stateid);
goto out_free;
timeout:
timeout <<= 1;
if (timeout > (clp->cl_lease_time >> 1))
timeout = clp->cl_lease_time >> 1;
status = nfs42_proc_offload_status(dst, &copy->stateid, &copied);
if (status == -EINPROGRESS)
goto wait;
nfs4_copy_dequeue_callback(dst_server, src_server, copy);
switch (status) {
case 0:
/* The server recognized the copy stateid, so it hasn't
* rebooted. Don't overwrite the verifier returned in the
* COPY result. */
res->write_res.count = copied;
goto out_free;
case -EREMOTEIO:
/* COPY operation failed on the server. */
status = -EOPNOTSUPP;
res->write_res.count = copied;
goto out_free;
case -EBADF:
/* Server did not recognize the copy stateid. It has
* probably restarted and lost the plot. */
res->write_res.count = 0;
status = -EOPNOTSUPP;
break;
case -EOPNOTSUPP:
/* RFC 7862 REQUIREs server to support OFFLOAD_STATUS when
* it has signed up for an async COPY, so server is not
* spec-compliant. */
res->write_res.count = 0;
}
goto out_free;
}
static int process_copy_commit(struct file *dst, loff_t pos_dst,
@ -582,6 +632,108 @@ static int nfs42_do_offload_cancel_async(struct file *dst,
return status;
}
static int
_nfs42_proc_offload_status(struct nfs_server *server, struct file *file,
struct nfs42_offload_data *data)
{
struct nfs_open_context *ctx = nfs_file_open_context(file);
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OFFLOAD_STATUS],
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = ctx->cred,
};
int status;
status = nfs4_call_sync(server->client, server, &msg,
&data->args.osa_seq_args,
&data->res.osr_seq_res, 1);
trace_nfs4_offload_status(&data->args, status);
switch (status) {
case 0:
break;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_OLD_STATEID:
/*
* Server does not recognize the COPY stateid. CB_OFFLOAD
* could have purged it, or server might have rebooted.
* Since COPY stateids don't have an associated inode,
* avoid triggering state recovery.
*/
status = -EBADF;
break;
case -NFS4ERR_NOTSUPP:
case -ENOTSUPP:
case -EOPNOTSUPP:
server->caps &= ~NFS_CAP_OFFLOAD_STATUS;
status = -EOPNOTSUPP;
break;
}
return status;
}
/**
* nfs42_proc_offload_status - Poll completion status of an async copy operation
* @dst: handle of file being copied into
* @stateid: copy stateid (from async COPY result)
* @copied: OUT: number of bytes copied so far
*
* Return values:
* %0: Server returned an NFS4_OK completion status
* %-EINPROGRESS: Server returned no completion status
* %-EREMOTEIO: Server returned an error completion status
* %-EBADF: Server did not recognize the copy stateid
* %-EOPNOTSUPP: Server does not support OFFLOAD_STATUS
* %-ERESTARTSYS: Wait interrupted by signal
*
* Other negative errnos indicate the client could not complete the
* request.
*/
static int
nfs42_proc_offload_status(struct file *dst, nfs4_stateid *stateid, u64 *copied)
{
struct inode *inode = file_inode(dst);
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = {
.inode = inode,
};
struct nfs42_offload_data *data;
int status;
if (!(server->caps & NFS_CAP_OFFLOAD_STATUS))
return -EOPNOTSUPP;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->seq_server = server;
data->args.osa_src_fh = NFS_FH(inode);
memcpy(&data->args.osa_stateid, stateid,
sizeof(data->args.osa_stateid));
exception.stateid = &data->args.osa_stateid;
do {
status = _nfs42_proc_offload_status(server, dst, data);
if (status == -EOPNOTSUPP)
goto out;
status = nfs4_handle_exception(server, status, &exception);
} while (exception.retry);
if (status)
goto out;
*copied = data->res.osr_count;
if (!data->res.complete_count)
status = -EINPROGRESS;
else if (data->res.osr_complete != NFS_OK)
status = -EREMOTEIO;
out:
kfree(data);
return status;
}
static int _nfs42_proc_copy_notify(struct file *src, struct file *dst,
struct nfs42_copy_notify_args *args,
struct nfs42_copy_notify_res *res)

View File

@ -35,6 +35,11 @@
#define encode_offload_cancel_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE))
#define decode_offload_cancel_maxsz (op_decode_hdr_maxsz)
#define encode_offload_status_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE))
#define decode_offload_status_maxsz (op_decode_hdr_maxsz + \
2 /* osr_count */ + \
2 /* osr_complete */)
#define encode_copy_notify_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
1 + /* nl4_type */ \
@ -143,6 +148,14 @@
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_offload_cancel_maxsz)
#define NFS4_enc_offload_status_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_offload_status_maxsz)
#define NFS4_dec_offload_status_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_offload_status_maxsz)
#define NFS4_enc_copy_notify_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
@ -345,6 +358,14 @@ static void encode_offload_cancel(struct xdr_stream *xdr,
encode_nfs4_stateid(xdr, &args->osa_stateid);
}
static void encode_offload_status(struct xdr_stream *xdr,
const struct nfs42_offload_status_args *args,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_OFFLOAD_STATUS, decode_offload_status_maxsz, hdr);
encode_nfs4_stateid(xdr, &args->osa_stateid);
}
static void encode_copy_notify(struct xdr_stream *xdr,
const struct nfs42_copy_notify_args *args,
struct compound_hdr *hdr)
@ -569,6 +590,25 @@ static void nfs4_xdr_enc_offload_cancel(struct rpc_rqst *req,
encode_nops(&hdr);
}
/*
* Encode OFFLOAD_STATUS request
*/
static void nfs4_xdr_enc_offload_status(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_offload_status_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->osa_seq_args),
};
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->osa_seq_args, &hdr);
encode_putfh(xdr, args->osa_src_fh, &hdr);
encode_offload_status(xdr, args, &hdr);
encode_nops(&hdr);
}
/*
* Encode COPY_NOTIFY request
*/
@ -921,6 +961,26 @@ static int decode_offload_cancel(struct xdr_stream *xdr,
return decode_op_hdr(xdr, OP_OFFLOAD_CANCEL);
}
static int decode_offload_status(struct xdr_stream *xdr,
struct nfs42_offload_status_res *res)
{
ssize_t result;
int status;
status = decode_op_hdr(xdr, OP_OFFLOAD_STATUS);
if (status)
return status;
/* osr_count */
if (xdr_stream_decode_u64(xdr, &res->osr_count) < 0)
return -EIO;
/* osr_complete<1> */
result = xdr_stream_decode_uint32_array(xdr, &res->osr_complete, 1);
if (result < 0)
return -EIO;
res->complete_count = result;
return 0;
}
static int decode_copy_notify(struct xdr_stream *xdr,
struct nfs42_copy_notify_res *res)
{
@ -1370,6 +1430,32 @@ out:
return status;
}
/*
* Decode OFFLOAD_STATUS response
*/
static int nfs4_xdr_dec_offload_status(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_offload_status_res *res = data;
struct compound_hdr hdr;
int status;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->osr_seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
if (status)
goto out;
status = decode_offload_status(xdr, res);
out:
return status;
}
/*
* Decode COPY_NOTIFY response
*/

View File

@ -233,6 +233,8 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
__set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
if (test_bit(NFS_CS_PNFS, &cl_init->init_flags))
__set_bit(NFS_CS_PNFS, &clp->cl_flags);
if (test_bit(NFS_CS_NETUNREACH_FATAL, &cl_init->init_flags))
__set_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags);
/*
* Set up the connection to the server before we add add to the
* global list.
@ -937,6 +939,9 @@ static int nfs4_set_client(struct nfs_server *server,
__set_bit(NFS_CS_TSM_POSSIBLE, &cl_init.init_flags);
server->port = rpc_get_port((struct sockaddr *)addr);
if (server->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))
@ -1011,6 +1016,8 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
if (test_bit(NFS_CS_NETUNREACH_FATAL, &mds_clp->cl_flags))
__set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init.init_flags);
__set_bit(NFS_CS_PNFS, &cl_init.init_flags);
cl_init.max_connect = NFS_MAX_TRANSPORTS;

View File

@ -195,6 +195,9 @@ static int nfs4_map_errors(int err)
return -EBUSY;
case -NFS4ERR_NOT_SAME:
return -ENOTSYNC;
case -ENETDOWN:
case -ENETUNREACH:
break;
default:
dprintk("%s could not handle NFSv4 error %d\n",
__func__, -err);
@ -443,6 +446,8 @@ static int nfs4_delay_killable(long *timeout)
{
might_sleep();
if (unlikely(nfs_current_task_exiting()))
return -EINTR;
__set_current_state(TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
schedule_timeout(nfs4_update_delay(timeout));
if (!__fatal_signal_pending(current))
@ -454,6 +459,8 @@ static int nfs4_delay_interruptible(long *timeout)
{
might_sleep();
if (unlikely(nfs_current_task_exiting()))
return -EINTR;
__set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE_UNSAFE);
schedule_timeout(nfs4_update_delay(timeout));
if (!signal_pending(current))
@ -1774,7 +1781,8 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
rcu_read_unlock();
trace_nfs4_open_stateid_update_wait(state->inode, stateid, 0);
if (!fatal_signal_pending(current)) {
if (!fatal_signal_pending(current) &&
!nfs_current_task_exiting()) {
if (schedule_timeout(5*HZ) == 0)
status = -EAGAIN;
else
@ -3576,7 +3584,7 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
write_sequnlock(&state->seqlock);
trace_nfs4_close_stateid_update_wait(state->inode, dst, 0);
if (fatal_signal_pending(current))
if (fatal_signal_pending(current) || nfs_current_task_exiting())
status = -EINTR;
else
if (schedule_timeout(5*HZ) != 0)
@ -9594,7 +9602,7 @@ static void nfs41_sequence_call_done(struct rpc_task *task, void *data)
return;
trace_nfs4_sequence(clp, task->tk_status);
if (task->tk_status < 0 && !task->tk_client->cl_shutdown) {
if (task->tk_status < 0 && clp->cl_cons_state >= 0) {
dprintk("%s ERROR %d\n", __func__, task->tk_status);
if (refcount_read(&clp->cl_count) == 1)
return;
@ -10798,7 +10806,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
| NFS_CAP_CLONE
| NFS_CAP_LAYOUTERROR
| NFS_CAP_READ_PLUS
| NFS_CAP_MOVEABLE,
| NFS_CAP_MOVEABLE
| NFS_CAP_OFFLOAD_STATUS,
.init_client = nfs41_init_client,
.shutdown_client = nfs41_shutdown_client,
.match_stateid = nfs41_match_stateid,

View File

@ -1198,7 +1198,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
struct rpc_clnt *clnt = clp->cl_rpcclient;
bool swapon = false;
if (clnt->cl_shutdown)
if (clp->cl_cons_state < 0)
return;
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
@ -1403,7 +1403,7 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
clp->cl_hostname);
nfs4_schedule_state_manager(clp);
return 0;
return clp->cl_cons_state < 0 ? clp->cl_cons_state : 0;
}
EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
@ -2739,7 +2739,15 @@ out_error:
pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
" with error %d\n", section_sep, section,
clp->cl_hostname, -status);
ssleep(1);
switch (status) {
case -ENETDOWN:
case -ENETUNREACH:
nfs_mark_client_ready(clp, -EIO);
break;
default:
ssleep(1);
break;
}
out_drain:
memalloc_nofs_restore(memflags);
nfs4_end_drain_session(clp);

View File

@ -2608,7 +2608,7 @@ TRACE_EVENT(nfs4_copy_notify,
)
);
TRACE_EVENT(nfs4_offload_cancel,
DECLARE_EVENT_CLASS(nfs4_offload_class,
TP_PROTO(
const struct nfs42_offload_status_args *args,
int error
@ -2640,6 +2640,15 @@ TRACE_EVENT(nfs4_offload_cancel,
__entry->stateid_seq, __entry->stateid_hash
)
);
#define DEFINE_NFS4_OFFLOAD_EVENT(name) \
DEFINE_EVENT(nfs4_offload_class, name, \
TP_PROTO( \
const struct nfs42_offload_status_args *args, \
int error \
), \
TP_ARGS(args, error))
DEFINE_NFS4_OFFLOAD_EVENT(nfs4_offload_cancel);
DEFINE_NFS4_OFFLOAD_EVENT(nfs4_offload_status);
DECLARE_EVENT_CLASS(nfs4_xattr_event,
TP_PROTO(

View File

@ -82,9 +82,8 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
* we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2)
*/
#define pagepad_maxsz (1)
#define open_owner_id_maxsz (1 + 2 + 1 + 1 + 2)
#define lock_owner_id_maxsz (1 + 1 + 4)
#define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
#define open_owner_id_maxsz (2 + 1 + 2 + 2)
#define lock_owner_id_maxsz (2 + 1 + 2)
#define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define op_encode_hdr_maxsz (1)
@ -185,7 +184,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#define encode_claim_null_maxsz (1 + nfs4_name_maxsz)
#define encode_open_maxsz (op_encode_hdr_maxsz + \
2 + encode_share_access_maxsz + 2 + \
open_owner_id_maxsz + \
1 + open_owner_id_maxsz + \
encode_opentype_maxsz + \
encode_claim_null_maxsz)
#define decode_space_limit_maxsz (3)
@ -255,13 +254,14 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#define encode_link_maxsz (op_encode_hdr_maxsz + \
nfs4_name_maxsz)
#define decode_link_maxsz (op_decode_hdr_maxsz + decode_change_info_maxsz)
#define encode_lockowner_maxsz (7)
#define encode_lockowner_maxsz (2 + 1 + lock_owner_id_maxsz)
#define encode_lock_maxsz (op_encode_hdr_maxsz + \
7 + \
1 + encode_stateid_maxsz + 1 + \
encode_lockowner_maxsz)
#define decode_lock_denied_maxsz \
(8 + decode_lockowner_maxsz)
(2 + 2 + 1 + 2 + 1 + lock_owner_id_maxsz)
#define decode_lock_maxsz (op_decode_hdr_maxsz + \
decode_lock_denied_maxsz)
#define encode_lockt_maxsz (op_encode_hdr_maxsz + 5 + \
@ -617,7 +617,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
encode_lockowner_maxsz)
#define NFS4_dec_release_lockowner_sz \
(compound_decode_hdr_maxsz + \
decode_lockowner_maxsz)
decode_release_lockowner_maxsz)
#define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
@ -1412,7 +1412,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
__be32 *p;
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4 = 32
* owner 28
*/
encode_nfs4_seqid(xdr, arg->seqid);
encode_share_access(xdr, arg->share_access);
@ -5077,7 +5077,7 @@ static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
/*
* We create the owner, so we know a proper owner.id length is 4.
*/
static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
static int decode_lock_denied(struct xdr_stream *xdr, struct file_lock *fl)
{
uint64_t offset, length, clientid;
__be32 *p;
@ -7702,6 +7702,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
PROC42(CLONE, enc_clone, dec_clone),
PROC42(COPY, enc_copy, dec_copy),
PROC42(OFFLOAD_CANCEL, enc_offload_cancel, dec_offload_cancel),
PROC42(OFFLOAD_STATUS, enc_offload_status, dec_offload_status),
PROC42(COPY_NOTIFY, enc_copy_notify, dec_copy_notify),
PROC(LOOKUPP, enc_lookupp, dec_lookupp),
PROC42(LAYOUTERROR, enc_layouterror, dec_layouterror),

View File

@ -454,8 +454,12 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
{ NFS_MOUNT_FORCE_RDIRPLUS, ",rdirplus=force", "" },
{ NFS_MOUNT_UNSHARED, ",nosharecache", "" },
{ NFS_MOUNT_NORESVPORT, ",noresvport", "" },
{ NFS_MOUNT_NETUNREACH_FATAL,
",fatal_neterrors=ENETDOWN:ENETUNREACH",
",fatal_neterrors=none" },
{ 0, NULL, NULL }
};
const struct proc_nfs_info *nfs_infop;

View File

@ -14,6 +14,7 @@
#include <linux/rcupdate.h>
#include <linux/lockd/lockd.h>
#include "internal.h"
#include "nfs4_fs.h"
#include "netns.h"
#include "sysfs.h"
@ -228,6 +229,25 @@ static void shutdown_client(struct rpc_clnt *clnt)
rpc_cancel_tasks(clnt, -EIO, shutdown_match_client, NULL);
}
/*
* Shut down the nfs_client only once all the superblocks
* have been shut down.
*/
static void shutdown_nfs_client(struct nfs_client *clp)
{
struct nfs_server *server;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
if (!(server->flags & NFS_MOUNT_SHUTDOWN)) {
rcu_read_unlock();
return;
}
}
rcu_read_unlock();
nfs_mark_client_ready(clp, -EIO);
shutdown_client(clp->cl_rpcclient);
}
static ssize_t
shutdown_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
@ -259,7 +279,6 @@ shutdown_store(struct kobject *kobj, struct kobj_attribute *attr,
server->flags |= NFS_MOUNT_SHUTDOWN;
shutdown_client(server->client);
shutdown_client(server->nfs_client->cl_rpcclient);
if (!IS_ERR(server->client_acl))
shutdown_client(server->client_acl);
@ -267,11 +286,44 @@ shutdown_store(struct kobject *kobj, struct kobj_attribute *attr,
if (server->nlm_host)
shutdown_client(server->nlm_host->h_rpcclnt);
out:
shutdown_nfs_client(server->nfs_client);
return count;
}
static struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW(shutdown);
#if IS_ENABLED(CONFIG_NFS_V4_1)
static ssize_t
implid_domain_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
struct nfs41_impl_id *impl_id = server->nfs_client->cl_implid;
if (!impl_id || strlen(impl_id->domain) == 0)
return 0; //sysfs_emit(buf, "");
return sysfs_emit(buf, "%s\n", impl_id->domain);
}
static struct kobj_attribute nfs_sysfs_attr_implid_domain = __ATTR_RO(implid_domain);
static ssize_t
implid_name_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
struct nfs41_impl_id *impl_id = server->nfs_client->cl_implid;
if (!impl_id || strlen(impl_id->name) == 0)
return 0; //sysfs_emit(buf, "");
return sysfs_emit(buf, "%s\n", impl_id->name);
}
static struct kobj_attribute nfs_sysfs_attr_implid_name = __ATTR_RO(implid_name);
#endif /* IS_ENABLED(CONFIG_NFS_V4_1) */
#define RPC_CLIENT_NAME_SIZE 64
void nfs_sysfs_link_rpc_client(struct nfs_server *server,
@ -309,6 +361,32 @@ static struct kobj_type nfs_sb_ktype = {
.child_ns_type = nfs_netns_object_child_ns_type,
};
#if IS_ENABLED(CONFIG_NFS_V4_1)
static void nfs_sysfs_add_nfsv41_server(struct nfs_server *server)
{
int ret;
if (!server->nfs_client->cl_implid)
return;
ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_implid_domain.attr,
nfs_netns_server_namespace(&server->kobj));
if (ret < 0)
pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
server->s_sysfs_id, ret);
ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_implid_name.attr,
nfs_netns_server_namespace(&server->kobj));
if (ret < 0)
pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
server->s_sysfs_id, ret);
}
#else /* CONFIG_NFS_V4_1 */
static inline void nfs_sysfs_add_nfsv41_server(struct nfs_server *server)
{
}
#endif /* CONFIG_NFS_V4_1 */
void nfs_sysfs_add_server(struct nfs_server *server)
{
int ret;
@ -325,6 +403,8 @@ void nfs_sysfs_add_server(struct nfs_server *server)
if (ret < 0)
pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
server->s_sysfs_id, ret);
nfs_sysfs_add_nfsv41_server(server);
}
EXPORT_SYMBOL_GPL(nfs_sysfs_add_server);

View File

@ -579,8 +579,10 @@ retry:
while (!nfs_lock_request(head)) {
ret = nfs_wait_on_request(head);
if (ret < 0)
if (ret < 0) {
nfs_release_request(head);
return ERR_PTR(ret);
}
}
/* Ensure that nobody removed the request before we locked it */

View File

@ -300,6 +300,7 @@ enum nfsstat4 {
/* error codes for internal client use */
#define NFS4ERR_RESET_TO_MDS 12001
#define NFS4ERR_RESET_TO_PNFS 12002
#define NFS4ERR_FATAL_IOERROR 12003
static inline bool seqid_mutating_err(u32 err)
{
@ -691,6 +692,7 @@ enum {
NFSPROC4_CLNT_LISTXATTRS,
NFSPROC4_CLNT_REMOVEXATTR,
NFSPROC4_CLNT_READ_PLUS,
NFSPROC4_CLNT_OFFLOAD_STATUS,
};
/* nfs41 types */

View File

@ -50,6 +50,7 @@ struct nfs_client {
#define NFS_CS_DS 7 /* - Server is a DS */
#define NFS_CS_REUSEPORT 8 /* - reuse src port on reconnect */
#define NFS_CS_PNFS 9 /* - Server used for pnfs */
#define NFS_CS_NETUNREACH_FATAL 10 /* - ENETUNREACH errors are fatal */
struct sockaddr_storage cl_addr; /* server identifier */
size_t cl_addrlen;
char * cl_hostname; /* hostname of server */
@ -167,6 +168,8 @@ struct nfs_server {
#define NFS_MOUNT_TRUNK_DISCOVERY 0x04000000
#define NFS_MOUNT_SHUTDOWN 0x08000000
#define NFS_MOUNT_NO_ALIGNWRITE 0x10000000
#define NFS_MOUNT_FORCE_RDIRPLUS 0x20000000
#define NFS_MOUNT_NETUNREACH_FATAL 0x40000000
unsigned int fattr_valid; /* Valid attributes */
unsigned int caps; /* server capabilities */
@ -250,6 +253,10 @@ struct nfs_server {
struct list_head ss_copies;
struct list_head ss_src_copies;
unsigned long delegation_flags;
#define NFS4SERV_DELEGRETURN (1)
#define NFS4SERV_DELEGATION_EXPIRED (2)
#define NFS4SERV_DELEGRETURN_DELAYED (3)
unsigned long delegation_gen;
unsigned long mig_gen;
unsigned long mig_status;
@ -289,6 +296,7 @@ struct nfs_server {
#define NFS_CAP_CASE_INSENSITIVE (1U << 6)
#define NFS_CAP_CASE_PRESERVING (1U << 7)
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
#define NFS_CAP_OPEN_XOR (1U << 12)
#define NFS_CAP_DELEGTIME (1U << 13)
#define NFS_CAP_POSIX_LOCK (1U << 14)

View File

@ -1515,8 +1515,9 @@ struct nfs42_offload_status_args {
struct nfs42_offload_status_res {
struct nfs4_sequence_res osr_seq_res;
uint64_t osr_count;
int osr_status;
u64 osr_count;
int complete_count;
u32 osr_complete;
};
struct nfs42_copy_notify_args {

View File

@ -64,7 +64,9 @@ struct rpc_clnt {
cl_noretranstimeo: 1,/* No retransmit timeouts */
cl_autobind : 1,/* use getport() */
cl_chatty : 1,/* be verbose */
cl_shutdown : 1;/* rpc immediate -EIO */
cl_shutdown : 1,/* rpc immediate -EIO */
cl_netunreach_fatal : 1;
/* Treat ENETUNREACH errors as fatal */
struct xprtsec_parms cl_xprtsec; /* transport security policy */
struct rpc_rtt * cl_rtt; /* RTO estimator data */
@ -175,6 +177,7 @@ struct rpc_add_xprt_test {
#define RPC_CLNT_CREATE_SOFTERR (1UL << 10)
#define RPC_CLNT_CREATE_REUSEPORT (1UL << 11)
#define RPC_CLNT_CREATE_CONNECTED (1UL << 12)
#define RPC_CLNT_CREATE_NETUNREACH_FATAL (1UL << 13)
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,

View File

@ -134,6 +134,7 @@ struct rpc_task_setup {
#define RPC_TASK_MOVEABLE 0x0004 /* nfs4.1+ rpc tasks */
#define RPC_TASK_NULLCREDS 0x0010 /* Use AUTH_NULL credential */
#define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
#define RPC_TASK_NETUNREACH_FATAL 0x0040 /* ENETUNREACH is fatal */
#define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
#define RPC_TASK_NO_ROUND_ROBIN 0x0100 /* send requests on "main" xprt */
#define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */

View File

@ -56,6 +56,7 @@ extern void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt);
extern void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
struct rpc_xprt *xprt, bool offline);
extern struct rpc_xprt *rpc_xprt_switch_get_main_xprt(struct rpc_xprt_switch *xps);
extern void xprt_iter_init(struct rpc_xprt_iter *xpi,
struct rpc_xprt_switch *xps);

View File

@ -343,6 +343,7 @@ TRACE_EVENT(rpc_request,
{ RPC_TASK_MOVEABLE, "MOVEABLE" }, \
{ RPC_TASK_NULLCREDS, "NULLCREDS" }, \
{ RPC_CALL_MAJORSEEN, "MAJORSEEN" }, \
{ RPC_TASK_NETUNREACH_FATAL, "NETUNREACH_FATAL"}, \
{ RPC_TASK_DYNAMIC, "DYNAMIC" }, \
{ RPC_TASK_NO_ROUND_ROBIN, "NO_ROUND_ROBIN" }, \
{ RPC_TASK_SOFT, "SOFT" }, \

View File

@ -270,9 +270,6 @@ static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt,
old = rcu_dereference_protected(clnt->cl_xprt,
lockdep_is_held(&clnt->cl_lock));
if (!xprt_bound(xprt))
clnt->cl_autobind = 1;
clnt->cl_timeout = timeout;
rcu_assign_pointer(clnt->cl_xprt, xprt);
spin_unlock(&clnt->cl_lock);
@ -512,6 +509,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
clnt->cl_discrtry = 1;
if (!(args->flags & RPC_CLNT_CREATE_QUIET))
clnt->cl_chatty = 1;
if (args->flags & RPC_CLNT_CREATE_NETUNREACH_FATAL)
clnt->cl_netunreach_fatal = 1;
return clnt;
}
@ -662,6 +661,7 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
new->cl_noretranstimeo = clnt->cl_noretranstimeo;
new->cl_discrtry = clnt->cl_discrtry;
new->cl_chatty = clnt->cl_chatty;
new->cl_netunreach_fatal = clnt->cl_netunreach_fatal;
new->cl_principal = clnt->cl_principal;
new->cl_max_connect = clnt->cl_max_connect;
return new;
@ -1195,6 +1195,8 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
task->tk_flags |= RPC_TASK_TIMEOUT;
if (clnt->cl_noretranstimeo)
task->tk_flags |= RPC_TASK_NO_RETRANS_TIMEOUT;
if (clnt->cl_netunreach_fatal)
task->tk_flags |= RPC_TASK_NETUNREACH_FATAL;
atomic_inc(&clnt->cl_task_count);
}
@ -2102,14 +2104,17 @@ call_bind_status(struct rpc_task *task)
case -EPROTONOSUPPORT:
trace_rpcb_bind_version_err(task);
goto retry_timeout;
case -ENETDOWN:
case -ENETUNREACH:
if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
break;
fallthrough;
case -ECONNREFUSED: /* connection problems */
case -ECONNRESET:
case -ECONNABORTED:
case -ENOTCONN:
case -EHOSTDOWN:
case -ENETDOWN:
case -EHOSTUNREACH:
case -ENETUNREACH:
case -EPIPE:
trace_rpcb_unreachable_err(task);
if (!RPC_IS_SOFTCONN(task)) {
@ -2191,19 +2196,22 @@ call_connect_status(struct rpc_task *task)
task->tk_status = 0;
switch (status) {
case -ENETDOWN:
case -ENETUNREACH:
if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
break;
fallthrough;
case -ECONNREFUSED:
case -ECONNRESET:
/* A positive refusal suggests a rebind is needed. */
if (RPC_IS_SOFTCONN(task))
break;
if (clnt->cl_autobind) {
rpc_force_rebind(clnt);
if (RPC_IS_SOFTCONN(task))
break;
goto out_retry;
}
fallthrough;
case -ECONNABORTED:
case -ENETDOWN:
case -ENETUNREACH:
case -EHOSTUNREACH:
case -EPIPE:
case -EPROTO:
@ -2455,10 +2463,13 @@ call_status(struct rpc_task *task)
trace_rpc_call_status(task);
task->tk_status = 0;
switch(status) {
case -EHOSTDOWN:
case -ENETDOWN:
case -EHOSTUNREACH:
case -ENETUNREACH:
if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
goto out_exit;
fallthrough;
case -EHOSTDOWN:
case -EHOSTUNREACH:
case -EPERM:
if (RPC_IS_SOFTCONN(task))
goto out_exit;

View File

@ -820,9 +820,10 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
}
trace_rpcb_setport(child, map->r_status, map->r_port);
xprt->ops->set_port(xprt, map->r_port);
if (map->r_port)
if (map->r_port) {
xprt->ops->set_port(xprt, map->r_port);
xprt_set_bound(xprt);
}
}
/*

View File

@ -276,6 +276,8 @@ EXPORT_SYMBOL_GPL(rpc_destroy_wait_queue);
static int rpc_wait_bit_killable(struct wait_bit_key *key, int mode)
{
if (unlikely(current->flags & PF_EXITING))
return -EINTR;
schedule();
if (signal_pending_state(mode, current))
return -ERESTARTSYS;

View File

@ -59,6 +59,16 @@ static struct kobject *rpc_sysfs_object_alloc(const char *name,
return NULL;
}
static inline struct rpc_clnt *
rpc_sysfs_client_kobj_get_clnt(struct kobject *kobj)
{
struct rpc_sysfs_client *c = container_of(kobj,
struct rpc_sysfs_client, kobject);
struct rpc_clnt *ret = c->clnt;
return refcount_inc_not_zero(&ret->cl_count) ? ret : NULL;
}
static inline struct rpc_xprt *
rpc_sysfs_xprt_kobj_get_xprt(struct kobject *kobj)
{
@ -86,6 +96,51 @@ rpc_sysfs_xprt_switch_kobj_get_xprt(struct kobject *kobj)
return xprt_switch_get(x->xprt_switch);
}
static ssize_t rpc_sysfs_clnt_version_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);
ssize_t ret;
if (!clnt)
return sprintf(buf, "<closed>\n");
ret = sprintf(buf, "%u", clnt->cl_vers);
refcount_dec(&clnt->cl_count);
return ret;
}
static ssize_t rpc_sysfs_clnt_program_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);
ssize_t ret;
if (!clnt)
return sprintf(buf, "<closed>\n");
ret = sprintf(buf, "%s", clnt->cl_program->name);
refcount_dec(&clnt->cl_count);
return ret;
}
static ssize_t rpc_sysfs_clnt_max_connect_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);
ssize_t ret;
if (!clnt)
return sprintf(buf, "<closed>\n");
ret = sprintf(buf, "%u\n", clnt->cl_max_connect);
refcount_dec(&clnt->cl_count);
return ret;
}
static ssize_t rpc_sysfs_xprt_dstaddr_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
@ -129,6 +184,31 @@ static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj,
return ret;
}
static const char *xprtsec_strings[] = {
[RPC_XPRTSEC_NONE] = "none",
[RPC_XPRTSEC_TLS_ANON] = "tls-anon",
[RPC_XPRTSEC_TLS_X509] = "tls-x509",
};
static ssize_t rpc_sysfs_xprt_xprtsec_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
ssize_t ret;
if (!xprt) {
ret = sprintf(buf, "<closed>\n");
goto out;
}
ret = sprintf(buf, "%s\n", xprtsec_strings[xprt->xprtsec.policy]);
xprt_put(xprt);
out:
return ret;
}
static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@ -206,6 +286,14 @@ static ssize_t rpc_sysfs_xprt_state_show(struct kobject *kobj,
return ret;
}
static ssize_t rpc_sysfs_xprt_del_xprt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "# delete this xprt\n");
}
static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
@ -225,6 +313,55 @@ static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,
return ret;
}
static ssize_t rpc_sysfs_xprt_switch_add_xprt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "# add one xprt to this xprt_switch\n");
}
static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rpc_xprt_switch *xprt_switch =
rpc_sysfs_xprt_switch_kobj_get_xprt(kobj);
struct xprt_create xprt_create_args;
struct rpc_xprt *xprt, *new;
if (!xprt_switch)
return 0;
xprt = rpc_xprt_switch_get_main_xprt(xprt_switch);
if (!xprt)
goto out;
xprt_create_args.ident = xprt->xprt_class->ident;
xprt_create_args.net = xprt->xprt_net;
xprt_create_args.dstaddr = (struct sockaddr *)&xprt->addr;
xprt_create_args.addrlen = xprt->addrlen;
xprt_create_args.servername = xprt->servername;
xprt_create_args.bc_xprt = xprt->bc_xprt;
xprt_create_args.xprtsec = xprt->xprtsec;
xprt_create_args.connect_timeout = xprt->connect_timeout;
xprt_create_args.reconnect_timeout = xprt->max_reconnect_timeout;
new = xprt_create_transport(&xprt_create_args);
if (IS_ERR_OR_NULL(new)) {
count = PTR_ERR(new);
goto out_put_xprt;
}
rpc_xprt_switch_add_xprt(xprt_switch, new);
xprt_put(new);
out_put_xprt:
xprt_put(xprt);
out:
xprt_switch_put(xprt_switch);
return count;
}
static ssize_t rpc_sysfs_xprt_dstaddr_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
@ -335,6 +472,40 @@ out_put:
return count;
}
static ssize_t rpc_sysfs_xprt_del_xprt(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
struct rpc_xprt_switch *xps = rpc_sysfs_xprt_kobj_get_xprt_switch(kobj);
if (!xprt || !xps) {
count = 0;
goto out;
}
if (xprt->main) {
count = -EINVAL;
goto release_tasks;
}
if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {
count = -EINTR;
goto out_put;
}
xprt_set_offline_locked(xprt, xps);
xprt_delete_locked(xprt, xps);
release_tasks:
xprt_release_write(xprt, NULL);
out_put:
xprt_put(xprt);
xprt_switch_put(xps);
out:
return count;
}
int rpc_sysfs_init(void)
{
rpc_sunrpc_kset = kset_create_and_add("sunrpc", NULL, kernel_kobj);
@ -398,23 +569,48 @@ static const void *rpc_sysfs_xprt_namespace(const struct kobject *kobj)
kobject)->xprt->xprt_net;
}
static struct kobj_attribute rpc_sysfs_clnt_version = __ATTR(rpc_version,
0444, rpc_sysfs_clnt_version_show, NULL);
static struct kobj_attribute rpc_sysfs_clnt_program = __ATTR(program,
0444, rpc_sysfs_clnt_program_show, NULL);
static struct kobj_attribute rpc_sysfs_clnt_max_connect = __ATTR(max_connect,
0444, rpc_sysfs_clnt_max_connect_show, NULL);
static struct attribute *rpc_sysfs_rpc_clnt_attrs[] = {
&rpc_sysfs_clnt_version.attr,
&rpc_sysfs_clnt_program.attr,
&rpc_sysfs_clnt_max_connect.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpc_sysfs_rpc_clnt);
static struct kobj_attribute rpc_sysfs_xprt_dstaddr = __ATTR(dstaddr,
0644, rpc_sysfs_xprt_dstaddr_show, rpc_sysfs_xprt_dstaddr_store);
static struct kobj_attribute rpc_sysfs_xprt_srcaddr = __ATTR(srcaddr,
0644, rpc_sysfs_xprt_srcaddr_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_xprtsec = __ATTR(xprtsec,
0644, rpc_sysfs_xprt_xprtsec_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_info = __ATTR(xprt_info,
0444, rpc_sysfs_xprt_info_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_change_state = __ATTR(xprt_state,
0644, rpc_sysfs_xprt_state_show, rpc_sysfs_xprt_state_change);
static struct kobj_attribute rpc_sysfs_xprt_del = __ATTR(del_xprt,
0644, rpc_sysfs_xprt_del_xprt_show, rpc_sysfs_xprt_del_xprt);
static struct attribute *rpc_sysfs_xprt_attrs[] = {
&rpc_sysfs_xprt_dstaddr.attr,
&rpc_sysfs_xprt_srcaddr.attr,
&rpc_sysfs_xprt_xprtsec.attr,
&rpc_sysfs_xprt_info.attr,
&rpc_sysfs_xprt_change_state.attr,
&rpc_sysfs_xprt_del.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpc_sysfs_xprt);
@ -422,14 +618,20 @@ ATTRIBUTE_GROUPS(rpc_sysfs_xprt);
static struct kobj_attribute rpc_sysfs_xprt_switch_info =
__ATTR(xprt_switch_info, 0444, rpc_sysfs_xprt_switch_info_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_switch_add_xprt =
__ATTR(add_xprt, 0644, rpc_sysfs_xprt_switch_add_xprt_show,
rpc_sysfs_xprt_switch_add_xprt_store);
static struct attribute *rpc_sysfs_xprt_switch_attrs[] = {
&rpc_sysfs_xprt_switch_info.attr,
&rpc_sysfs_xprt_switch_add_xprt.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpc_sysfs_xprt_switch);
static const struct kobj_type rpc_sysfs_client_type = {
.release = rpc_sysfs_client_release,
.default_groups = rpc_sysfs_rpc_clnt_groups,
.sysfs_ops = &kobj_sysfs_ops,
.namespace = rpc_sysfs_client_namespace,
};

View File

@ -92,6 +92,27 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
xprt_put(xprt);
}
/**
* rpc_xprt_switch_get_main_xprt - Get the 'main' xprt for an xprt switch.
* @xps: pointer to struct rpc_xprt_switch.
*/
struct rpc_xprt *rpc_xprt_switch_get_main_xprt(struct rpc_xprt_switch *xps)
{
struct rpc_xprt_iter xpi;
struct rpc_xprt *xprt;
xprt_iter_init_listall(&xpi, xps);
xprt = xprt_iter_get_next(&xpi);
while (xprt && !xprt->main) {
xprt_put(xprt);
xprt = xprt_iter_get_next(&xpi);
}
xprt_iter_destroy(&xpi);
return xprt;
}
static DEFINE_IDA(rpc_xprtswitch_ids);
void xprt_multipath_cleanup_ids(void)