git: 564ed8e806e7 - main - nfsd: Allow multiple instances of rpc.tlsservd

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Mon, 22 Aug 2022 20:56:17 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=564ed8e806e7abb640775b1b3d253a7a6eb452f7

commit 564ed8e806e7abb640775b1b3d253a7a6eb452f7
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2022-08-22 20:54:24 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2022-08-22 20:54:24 +0000

    nfsd: Allow multiple instances of rpc.tlsservd
    
    During a discussion with someone working on NFS-over-TLS
    for a non-FreeBSD platform, we agreed that a single server
    daemon for TLS handshakes could become a bottleneck when
    an NFS server first boots, if many concurrent NFS-over-TLS
    connections are attempted.
    
    This patch modifies the kernel RPC code so that it can
    handle multiple rpc.tlsservd daemons.  A separate commit
    currently under review as D35886 for the rpc.tlsservd
    daemon.
---
 sys/rpc/rpcsec_tls.h             |   8 +-
 sys/rpc/rpcsec_tls/rpctls_impl.c | 156 ++++++++++++++++++++++++++++-----------
 sys/rpc/svc.h                    |   1 +
 sys/rpc/svc_vc.c                 |   4 +-
 4 files changed, 122 insertions(+), 47 deletions(-)

diff --git a/sys/rpc/rpcsec_tls.h b/sys/rpc/rpcsec_tls.h
index 49a7e71b7514..6c49f9577cc8 100644
--- a/sys/rpc/rpcsec_tls.h
+++ b/sys/rpc/rpcsec_tls.h
@@ -37,6 +37,10 @@
 #define	RPCTLS_SYSC_SRVSETPATH	4
 #define	RPCTLS_SYSC_SRVSOCKET	5
 #define	RPCTLS_SYSC_SRVSHUTDOWN	6
+#define	RPCTLS_SYSC_SRVSTARTUP	7
+
+/* Max nprocs for SRV startup */
+#define	RPCTLS_SRV_MAXNPROCS	16
 
 /* System call used by the rpctlscd, rpctlssd daemons. */
 int	rpctls_syscall(int, const char *);
@@ -63,11 +67,11 @@ enum clnt_stat	rpctls_connect(CLIENT *newclient, char *certname,
 enum clnt_stat	rpctls_cl_handlerecord(uint64_t sec, uint64_t usec,
 		    uint64_t ssl, uint32_t *reterr);
 enum clnt_stat	rpctls_srv_handlerecord(uint64_t sec, uint64_t usec,
-		    uint64_t ssl, uint32_t *reterr);
+		    uint64_t ssl, int procpos, uint32_t *reterr);
 enum clnt_stat	rpctls_cl_disconnect(uint64_t sec, uint64_t usec,
 		    uint64_t ssl, uint32_t *reterr);
 enum clnt_stat	rpctls_srv_disconnect(uint64_t sec, uint64_t usec,
-		    uint64_t ssl, uint32_t *reterr);
+		    uint64_t ssl, int procpos, uint32_t *reterr);
 
 /* Initialization function for rpcsec_tls. */
 int		rpctls_init(void);
diff --git a/sys/rpc/rpcsec_tls/rpctls_impl.c b/sys/rpc/rpcsec_tls/rpctls_impl.c
index c495213b08e2..9d7f686af768 100644
--- a/sys/rpc/rpcsec_tls/rpctls_impl.c
+++ b/sys/rpc/rpcsec_tls/rpctls_impl.c
@@ -74,22 +74,26 @@ static CLIENT		*rpctls_connect_handle;
 static struct mtx	rpctls_connect_lock;
 static struct socket	*rpctls_connect_so = NULL;
 static CLIENT		*rpctls_connect_cl = NULL;
-static CLIENT		*rpctls_server_handle;
+static CLIENT		*rpctls_server_handle[RPCTLS_SRV_MAXNPROCS];
 static struct mtx	rpctls_server_lock;
 static struct socket	*rpctls_server_so = NULL;
 static SVCXPRT		*rpctls_server_xprt = NULL;
+static bool		rpctls_srv_newdaemon = false;
+static int		rpctls_srv_prevproc = 0;
+static bool		rpctls_server_busy[RPCTLS_SRV_MAXNPROCS];
 static struct opaque_auth rpctls_null_verf;
 
 static CLIENT		*rpctls_connect_client(void);
-static CLIENT		*rpctls_server_client(void);
+static CLIENT		*rpctls_server_client(int procpos);
 static enum clnt_stat	rpctls_server(SVCXPRT *xprt, struct socket *so,
 			    uint32_t *flags, uint64_t *sslp,
-			    uid_t *uid, int *ngrps, gid_t **gids);
+			    uid_t *uid, int *ngrps, gid_t **gids,
+			    int *procposp);
 
 int
 rpctls_init(void)
 {
-	int error;
+	int error, i;
 
 	error = syscall_helper_register(rpctls_syscalls, SY_THR_STATIC_KLD);
 	if (error != 0) {
@@ -103,6 +107,8 @@ rpctls_init(void)
 	rpctls_null_verf.oa_flavor = AUTH_NULL;
 	rpctls_null_verf.oa_base = RPCTLS_START_STRING;
 	rpctls_null_verf.oa_length = strlen(RPCTLS_START_STRING);
+	for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++)
+		rpctls_server_busy[i] = false;
 	return (0);
 }
 
@@ -115,8 +121,8 @@ sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
 	struct socket *so;
 	SVCXPRT *xprt;
 	char path[MAXPATHLEN];
-	int fd = -1, error, try_count;
-	CLIENT *cl, *oldcl, *concl;
+	int fd = -1, error, i, try_count;
+	CLIENT *cl, *oldcl[RPCTLS_SRV_MAXNPROCS], *concl;
 	uint64_t ssl[3];
 	struct timeval timeo;
 #ifdef KERN_TLS
@@ -128,6 +134,24 @@ sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
 		return (error);
 
 	switch (uap->op) {
+	case RPCTLS_SYSC_SRVSTARTUP:
+		/* Get rid of all old CLIENTs. */
+		mtx_lock(&rpctls_server_lock);
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+			oldcl[i] = rpctls_server_handle[i];
+			rpctls_server_handle[i] = NULL;
+			rpctls_server_busy[i] = false;
+		}
+		rpctls_srv_newdaemon = true;
+		rpctls_srv_prevproc = 0;
+		mtx_unlock(&rpctls_server_lock);
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+			if (oldcl[i] != NULL) {
+				CLNT_CLOSE(oldcl[i]);
+				CLNT_RELEASE(oldcl[i]);
+			}
+		}
+		break;
 	case RPCTLS_SYSC_CLSETPATH:
 		error = copyinstr(uap->path, path, sizeof(path), NULL);
 		if (error == 0) {
@@ -175,13 +199,13 @@ sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
 		}
 	
 		mtx_lock(&rpctls_connect_lock);
-		oldcl = rpctls_connect_handle;
+		oldcl[0] = rpctls_connect_handle;
 		rpctls_connect_handle = cl;
 		mtx_unlock(&rpctls_connect_lock);
 	
-		if (oldcl != NULL) {
-			CLNT_CLOSE(oldcl);
-			CLNT_RELEASE(oldcl);
+		if (oldcl[0] != NULL) {
+			CLNT_CLOSE(oldcl[0]);
+			CLNT_RELEASE(oldcl[0]);
 		}
 		break;
 	case RPCTLS_SYSC_SRVSETPATH:
@@ -227,36 +251,66 @@ sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
 				error = EINVAL;
 		}
 	
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++)
+			oldcl[i] = NULL;
 		mtx_lock(&rpctls_server_lock);
-		oldcl = rpctls_server_handle;
-		rpctls_server_handle = cl;
+		if (rpctls_srv_newdaemon) {
+			/*
+			 * For a new daemon, the rpctls_srv_handles have
+			 * already been cleaned up by RPCTLS_SYSC_SRVSTARTUP.
+			 * Scan for an available array entry to use.
+			 */
+			for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+				if (rpctls_server_handle[i] == NULL)
+					break;
+			}
+			if (i == RPCTLS_SRV_MAXNPROCS && error == 0)
+				error = ENXIO;
+		} else {
+			/* For an old daemon, clear out old CLIENTs. */
+			for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+				oldcl[i] = rpctls_server_handle[i];
+				rpctls_server_handle[i] = NULL;
+				rpctls_server_busy[i] = false;
+			}
+			i = 0;	/* Set to use rpctls_server_handle[0]. */
+		}
+		if (error == 0)
+			rpctls_server_handle[i] = cl;
 		mtx_unlock(&rpctls_server_lock);
-	
-		if (oldcl != NULL) {
-			CLNT_CLOSE(oldcl);
-			CLNT_RELEASE(oldcl);
+
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+			if (oldcl[i] != NULL) {
+				CLNT_CLOSE(oldcl[i]);
+				CLNT_RELEASE(oldcl[i]);
+			}
 		}
 		break;
 	case RPCTLS_SYSC_CLSHUTDOWN:
 		mtx_lock(&rpctls_connect_lock);
-		oldcl = rpctls_connect_handle;
+		oldcl[0] = rpctls_connect_handle;
 		rpctls_connect_handle = NULL;
 		mtx_unlock(&rpctls_connect_lock);
 	
-		if (oldcl != NULL) {
-			CLNT_CLOSE(oldcl);
-			CLNT_RELEASE(oldcl);
+		if (oldcl[0] != NULL) {
+			CLNT_CLOSE(oldcl[0]);
+			CLNT_RELEASE(oldcl[0]);
 		}
 		break;
 	case RPCTLS_SYSC_SRVSHUTDOWN:
 		mtx_lock(&rpctls_server_lock);
-		oldcl = rpctls_server_handle;
-		rpctls_server_handle = NULL;
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+			oldcl[i] = rpctls_server_handle[i];
+			rpctls_server_handle[i] = NULL;
+		}
+		rpctls_srv_newdaemon = false;
 		mtx_unlock(&rpctls_server_lock);
 	
-		if (oldcl != NULL) {
-			CLNT_CLOSE(oldcl);
-			CLNT_RELEASE(oldcl);
+		for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) {
+			if (oldcl[i] != NULL) {
+				CLNT_CLOSE(oldcl[i]);
+				CLNT_RELEASE(oldcl[i]);
+			}
 		}
 		break;
 	case RPCTLS_SYSC_CLSOCKET:
@@ -342,12 +396,12 @@ rpctls_connect_client(void)
  * if it is available.
  */
 static CLIENT *
-rpctls_server_client(void)
+rpctls_server_client(int procpos)
 {
 	CLIENT *cl;
 
 	mtx_lock(&rpctls_server_lock);
-	cl = rpctls_server_handle;
+	cl = rpctls_server_handle[procpos];
 	if (cl != NULL)
 		CLNT_ACQUIRE(cl);
 	mtx_unlock(&rpctls_server_lock);
@@ -467,7 +521,7 @@ rpctls_cl_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
 }
 
 enum clnt_stat
-rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
+rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl, int procpos,
     uint32_t *reterr)
 {
 	struct rpctlssd_handlerecord_arg arg;
@@ -475,7 +529,7 @@ rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
 	enum clnt_stat stat;
 	CLIENT *cl;
 
-	cl = rpctls_server_client();
+	cl = rpctls_server_client(procpos);
 	if (cl == NULL) {
 		*reterr = RPCTLSERR_NOSSL;
 		return (RPC_SUCCESS);
@@ -520,7 +574,7 @@ rpctls_cl_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
 }
 
 enum clnt_stat
-rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
+rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl, int procpos,
     uint32_t *reterr)
 {
 	struct rpctlssd_disconnect_arg arg;
@@ -528,7 +582,7 @@ rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
 	enum clnt_stat stat;
 	CLIENT *cl;
 
-	cl = rpctls_server_client();
+	cl = rpctls_server_client(procpos);
 	if (cl == NULL) {
 		*reterr = RPCTLSERR_NOSSL;
 		return (RPC_SUCCESS);
@@ -548,26 +602,40 @@ rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
 /* Do an upcall for a new server socket using TLS. */
 static enum clnt_stat
 rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
-    uid_t *uid, int *ngrps, gid_t **gids)
+    uid_t *uid, int *ngrps, gid_t **gids, int *procposp)
 {
 	enum clnt_stat stat;
 	CLIENT *cl;
 	struct rpctlssd_connect_res res;
 	gid_t *gidp;
 	uint32_t *gidv;
-	int i;
-	static bool rpctls_server_busy = false;
+	int i, procpos;
 
-	cl = rpctls_server_client();
+	cl = NULL;
+	procpos = -1;
+	mtx_lock(&rpctls_server_lock);
+	for (i = (rpctls_srv_prevproc + 1) % RPCTLS_SRV_MAXNPROCS;
+	    i != rpctls_srv_prevproc; i = (i + 1) % RPCTLS_SRV_MAXNPROCS) {
+		if (rpctls_server_handle[i] != NULL)
+			break;
+	}
+	if (i == rpctls_srv_prevproc) {
+		if (rpctls_server_handle[i] != NULL)
+			procpos = i;
+	} else
+		rpctls_srv_prevproc = procpos = i;
+	mtx_unlock(&rpctls_server_lock);
+	if (procpos >= 0)
+		cl = rpctls_server_client(procpos);
 	if (cl == NULL)
 		return (RPC_SYSTEMERROR);
 
 	/* Serialize the server upcalls. */
 	mtx_lock(&rpctls_server_lock);
-	while (rpctls_server_busy)
-		msleep(&rpctls_server_busy, &rpctls_server_lock, PVFS,
+	while (rpctls_server_busy[procpos])
+		msleep(&rpctls_server_busy[procpos], &rpctls_server_lock, PVFS,
 		    "rtlssn", 0);
-	rpctls_server_busy = true;
+	rpctls_server_busy[procpos] = true;
 	rpctls_server_so = so;
 	rpctls_server_xprt = xprt;
 	mtx_unlock(&rpctls_server_lock);
@@ -580,6 +648,7 @@ rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
 		*sslp++ = res.sec;
 		*sslp++ = res.usec;
 		*sslp = res.ssl;
+		*procposp = procpos;
 		if ((*flags & (RPCTLS_FLAGS_CERTUSER |
 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
 			*ngrps = res.gid.gid_len;
@@ -605,8 +674,8 @@ rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
 	mtx_lock(&rpctls_server_lock);
 	rpctls_server_so = NULL;
 	rpctls_server_xprt = NULL;
-	rpctls_server_busy = false;
-	wakeup(&rpctls_server_busy);
+	rpctls_server_busy[procpos] = false;
+	wakeup(&rpctls_server_busy[procpos]);
 	mtx_unlock(&rpctls_server_lock);
 
 	return (stat);
@@ -626,7 +695,7 @@ _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
 	SVCXPRT *xprt;
 	uint32_t flags;
 	uint64_t ssl[3];
-	int ngrps;
+	int ngrps, procpos;
 	uid_t uid;
 	gid_t *gidp;
 #ifdef KERN_TLS
@@ -677,7 +746,7 @@ _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
 
 	/* Do an upcall to do the TLS handshake. */
 	stat = rpctls_server(xprt, xprt->xp_socket, &flags,
-	    ssl, &uid, &ngrps, &gidp);
+	    ssl, &uid, &ngrps, &gidp, &procpos);
 
 	/* Re-enable reception on the socket within the krpc. */
 	sx_xlock(&xprt->xp_lock);
@@ -687,6 +756,7 @@ _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
 		xprt->xp_sslsec = ssl[0];
 		xprt->xp_sslusec = ssl[1];
 		xprt->xp_sslrefno = ssl[2];
+		xprt->xp_sslproc = procpos;
 		if ((flags & (RPCTLS_FLAGS_CERTUSER |
 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
 			xprt->xp_ngrps = ngrps;
@@ -725,7 +795,7 @@ rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
 		return (false);
 	if (rpctlscd_run && rpctls_connect_handle == NULL)
 		return (false);
-	if (rpctlssd_run && rpctls_server_handle == NULL)
+	if (rpctlssd_run && rpctls_server_handle[0] == NULL)
 		return (false);
 	*maxlenp = maxlen;
 	return (enable);
diff --git a/sys/rpc/svc.h b/sys/rpc/svc.h
index 7f6d7c948193..f3f29977f463 100644
--- a/sys/rpc/svc.h
+++ b/sys/rpc/svc.h
@@ -185,6 +185,7 @@ typedef struct __rpc_svcxprt {
 	uint64_t	xp_sslsec;	/* Userland SSL * */
 	uint64_t	xp_sslusec;
 	uint64_t	xp_sslrefno;
+	int		xp_sslproc;	/* Which upcall daemon being used */
 	int		xp_ngrps;	/* Cred. from TLS cert. */
 	uid_t		xp_uid;
 	gid_t		*xp_gidp;
diff --git a/sys/rpc/svc_vc.c b/sys/rpc/svc_vc.c
index 8b11cdf82e8b..9d2d9c49502b 100644
--- a/sys/rpc/svc_vc.c
+++ b/sys/rpc/svc_vc.c
@@ -463,7 +463,7 @@ svc_vc_destroy_common(SVCXPRT *xprt)
 				 */
 				rpctls_srv_disconnect(xprt->xp_sslsec,
 				    xprt->xp_sslusec, xprt->xp_sslrefno,
-				    &reterr);
+				    xprt->xp_sslproc, &reterr);
 			}
 			/* Must sorele() to get rid of reference. */
 			CURVNET_SET(xprt->xp_socket->so_vnet);
@@ -817,7 +817,7 @@ tryagain:
 			sx_xunlock(&xprt->xp_lock);
 			ret = rpctls_srv_handlerecord(xprt->xp_sslsec,
 			    xprt->xp_sslusec, xprt->xp_sslrefno,
-			    &reterr);
+			    xprt->xp_sslproc, &reterr);
 			sx_xlock(&xprt->xp_lock);
 			xprt->xp_dontrcv = FALSE;
 			if (ret != RPC_SUCCESS || reterr != RPCTLSERR_OK) {