summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/rpc/clnt_vc.c22
1 files changed, 20 insertions, 2 deletions
diff --git a/sys/rpc/clnt_vc.c b/sys/rpc/clnt_vc.c
index 7d6ca94..6bc6a06 100644
--- a/sys/rpc/clnt_vc.c
+++ b/sys/rpc/clnt_vc.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
@@ -107,6 +108,8 @@ static struct clnt_ops clnt_vc_ops = {
static void clnt_vc_upcallsdone(struct ct_data *);
+static int fake_wchan;
+
/*
* Create a client handle for a connection.
* Default options are set, which the user can change using clnt_control()'s.
@@ -298,7 +301,7 @@ clnt_vc_call(
uint32_t xid;
struct mbuf *mreq = NULL, *results;
struct ct_request *cr;
- int error;
+ int error, trycnt;
cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK);
@@ -328,8 +331,20 @@ clnt_vc_call(
timeout = ct->ct_wait; /* use default timeout */
}
+ /*
+ * After 15sec of looping, allow it to return RPC_CANTSEND, which will
+ * cause the clnt_reconnect layer to create a new TCP connection.
+ */
+ trycnt = 15 * hz;
call_again:
mtx_assert(&ct->ct_lock, MA_OWNED);
+ if (ct->ct_closing || ct->ct_closed) {
+ ct->ct_threads--;
+ wakeup(ct);
+ mtx_unlock(&ct->ct_lock);
+ free(cr, M_RPC);
+ return (RPC_CANTSEND);
+ }
ct->ct_xid++;
xid = ct->ct_xid;
@@ -397,13 +412,16 @@ call_again:
*/
error = sosend(ct->ct_socket, NULL, NULL, mreq, NULL, 0, curthread);
mreq = NULL;
- if (error == EMSGSIZE) {
+ if (error == EMSGSIZE || (error == ERESTART &&
+ (ct->ct_waitflag & PCATCH) == 0 && trycnt-- > 0)) {
SOCKBUF_LOCK(&ct->ct_socket->so_snd);
sbwait(&ct->ct_socket->so_snd);
SOCKBUF_UNLOCK(&ct->ct_socket->so_snd);
AUTH_VALIDATE(auth, xid, NULL, NULL);
mtx_lock(&ct->ct_lock);
TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
+ /* Sleep for 1 clock tick before trying the sosend() again. */
+ msleep(&fake_wchan, &ct->ct_lock, 0, "rpclpsnd", 1);
goto call_again;
}
OpenPOWER on IntegriCloud