mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
SUNRPC: report local rpcbind calls that get no answer
A caller that creates many listeners in one operation calls svc_register() once for each of them. Every call waits for the local rpcbind on its own, so a rpcbind that never answers costs the caller one timeout per listener. The caller has no way to learn that the first call already failed. Split rpcb_register_call() failures by whether rpcbind answered: - answered: a FALSE reply, or an RPC-level rejection that rpc_decode_header() derives from the reply. -EACCES (FALSE reply or AUTH_ERROR), -EPROTONOSUPPORT, -EPFNOSUPPORT, -EOPNOTSUPP. - never sent: -ENOMEM, -EMSGSIZE, -ERESTARTSYS. - no answer: everything else, i.e. transport errors. Any no-answer error gets represented by -EIO, which is already what the RPC layer reports for most of it: rpc_check_timeout() returns -EIO for a soft timeout without RPC_TASK_TIMEOUT, and call_status() documents -EIO as "shutdown or soft timeout". Keep a count of the number of rpcbind failures in the serv. Later patches will use that to watch for hard rpcbind failures, and alter their behavior accordingly. Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260901-nfsd-nl-hang-v5-6-a540d86668b0@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
parent
8e590a1685
commit
202a557d0c
|
|
@ -199,7 +199,8 @@ struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
|
|||
|
||||
int rpcb_create_local(struct net *);
|
||||
void rpcb_put_local(struct net *);
|
||||
int rpcb_register(struct net *, u32, u32, int, unsigned short);
|
||||
int rpcb_register(struct net *net, u32 prog, u32 vers, int prot,
|
||||
unsigned short port);
|
||||
int rpcb_v4_register(struct net *net, const u32 program,
|
||||
const u32 version,
|
||||
const struct sockaddr *address,
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ struct svc_serv {
|
|||
unsigned int sv_max_payload; /* datagram payload size */
|
||||
unsigned int sv_max_mesg; /* max_payload + 1 page for overheads */
|
||||
unsigned int sv_xdrsize; /* XDR buffer size */
|
||||
atomic_t sv_rpcb_failures; /* unanswered rpcbind calls */
|
||||
struct list_head sv_permsocks; /* all permanent sockets */
|
||||
struct list_head sv_tempsocks; /* all temporary sockets */
|
||||
int sv_tmpcnt; /* count of temporary "valid" sockets */
|
||||
|
|
@ -451,6 +452,7 @@ int sunrpc_set_pool_mode(const char *val);
|
|||
int sunrpc_get_pool_mode(char *val, size_t size);
|
||||
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
|
||||
int svc_bind(struct svc_serv *serv, struct net *net);
|
||||
unsigned int svc_rpcb_failure_count(struct svc_serv *serv);
|
||||
struct svc_serv *svc_create(struct svc_program *, unsigned int,
|
||||
int (*threadfn)(void *data));
|
||||
bool svc_rqst_replace_page(struct svc_rqst *rqstp,
|
||||
|
|
@ -471,8 +473,9 @@ unsigned int svc_serv_maxthreads(const struct svc_serv *serv);
|
|||
int svc_pool_stats_open(struct svc_info *si, struct file *file);
|
||||
void svc_process(struct svc_rqst *rqstp);
|
||||
void svc_process_bc(struct rpc_rqst *req, struct svc_rqst *rqstp);
|
||||
int svc_register(const struct svc_serv *, struct net *, const int,
|
||||
const unsigned short, const unsigned short);
|
||||
int svc_register(struct svc_serv *serv, struct net *net,
|
||||
const int family, const unsigned short proto,
|
||||
const unsigned short port);
|
||||
|
||||
void svc_wake_up(struct svc_serv *);
|
||||
void svc_reserve(struct svc_rqst *rqstp, int space);
|
||||
|
|
|
|||
|
|
@ -412,7 +412,8 @@ static struct rpc_clnt *rpcb_create(struct net *net, const char *nodename,
|
|||
return rpc_create(&args);
|
||||
}
|
||||
|
||||
static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, struct rpc_message *msg, bool is_set)
|
||||
static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt,
|
||||
struct rpc_message *msg, bool is_set)
|
||||
{
|
||||
int flags = RPC_TASK_NOCONNECT;
|
||||
int error, result = 0;
|
||||
|
|
@ -422,8 +423,23 @@ static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, stru
|
|||
msg->rpc_resp = &result;
|
||||
|
||||
error = rpc_call_sync(clnt, msg, flags);
|
||||
if (error < 0)
|
||||
return error;
|
||||
if (error < 0) {
|
||||
switch (error) {
|
||||
/* rpcbind answered; the reply itself carries the error */
|
||||
case -EPROTONOSUPPORT:
|
||||
case -EPFNOSUPPORT:
|
||||
case -EOPNOTSUPP:
|
||||
case -EACCES:
|
||||
/* the call never made it onto the wire */
|
||||
case -ENOMEM:
|
||||
case -EMSGSIZE:
|
||||
/* the caller is going away; this says nothing about rpcbind */
|
||||
case -ERESTARTSYS:
|
||||
return error;
|
||||
}
|
||||
/* anything else, we assume that rpcbind isn't functional */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
return -EACCES;
|
||||
|
|
|
|||
|
|
@ -1179,10 +1179,40 @@ int svc_generic_rpcbind_set(struct net *net,
|
|||
error = svc_rpcbind_set_version(net, progp, version,
|
||||
family, proto, port);
|
||||
|
||||
/* -EIO means no answer, not a refusal, so vs_rpcb_optnl must keep it. */
|
||||
if (error == -EIO)
|
||||
return error;
|
||||
|
||||
return (vers->vs_rpcb_optnl) ? 0 : error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
|
||||
|
||||
/**
|
||||
* svc_rpcb_failure_count - local rpcbind calls for @serv that got no answer
|
||||
* @serv: RPC service to query
|
||||
*
|
||||
* svc_register() adds one for each of its calls that got no answer. A reply
|
||||
* that refuses one entry does not count, because rpcbind answered and the
|
||||
* next entry may still succeed.
|
||||
*
|
||||
* The count is kept per serv rather than per net. The local rpcbind client
|
||||
* is per-net and lockd shares it, but a count that another service can move
|
||||
* says nothing about this serv's own calls.
|
||||
*
|
||||
* This is for callers that cannot see the svc_register() return, because a
|
||||
* transport class sits in between. Such a caller reads the count before it
|
||||
* starts and compares as it goes, so there is no state to reset between
|
||||
* operations. The count never resets, and callers must not attach meaning
|
||||
* to the value itself.
|
||||
*
|
||||
* Return: the number of unanswered calls since this serv was created.
|
||||
*/
|
||||
unsigned int svc_rpcb_failure_count(struct svc_serv *serv)
|
||||
{
|
||||
return atomic_read(&serv->sv_rpcb_failures);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rpcb_failure_count);
|
||||
|
||||
/**
|
||||
* svc_register - register an RPC service with the local portmapper
|
||||
* @serv: svc_serv struct for the service to register
|
||||
|
|
@ -1193,10 +1223,11 @@ EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
|
|||
*
|
||||
* Service is registered for any address in the passed-in protocol family
|
||||
*/
|
||||
int svc_register(const struct svc_serv *serv, struct net *net,
|
||||
int svc_register(struct svc_serv *serv, struct net *net,
|
||||
const int family, const unsigned short proto,
|
||||
const unsigned short port)
|
||||
{
|
||||
bool noanswer = false;
|
||||
unsigned int p, i;
|
||||
int error = 0;
|
||||
|
||||
|
|
@ -1208,10 +1239,16 @@ int svc_register(const struct svc_serv *serv, struct net *net,
|
|||
struct svc_program *progp = &serv->sv_programs[p];
|
||||
|
||||
for (i = 0; i < progp->pg_nvers; i++) {
|
||||
const struct svc_version *vers = progp->pg_vers[i];
|
||||
int ret;
|
||||
|
||||
ret = progp->pg_rpcbind_set(net, progp, i,
|
||||
family, proto, port);
|
||||
if (ret == -EIO) {
|
||||
noanswer = true;
|
||||
if (vers && vers->vs_rpcb_optnl)
|
||||
ret = 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "svc: failed to register "
|
||||
"%sv%u RPC service (errno %d).\n",
|
||||
|
|
@ -1223,6 +1260,9 @@ int svc_register(const struct svc_serv *serv, struct net *net,
|
|||
}
|
||||
}
|
||||
|
||||
if (noanswer)
|
||||
atomic_inc(&serv->sv_rpcb_failures);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user