git: 455c354dd4db - main - gss_impl.c: Fix a nfsd hang when the kgssapi.ko is loaded, but no gssd
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 21 Jun 2026 23:17:10 UTC
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=455c354dd4dbba9b9ec9c68e335ab3aa46dca438
commit 455c354dd4dbba9b9ec9c68e335ab3aa46dca438
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-06-21 23:15:23 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-06-21 23:15:23 +0000
gss_impl.c: Fix a nfsd hang when the kgssapi.ko is loaded, but no gssd
After the conversion to using netlink, the kgssapi had
no way of knowing if the gssd daemon was running.
As such, a boot where the kgssapi is loaded, but the
gssd is not enabled would hang the nfsd for a very
long time. (Many timeouts at 300sec each.)
This patch adds a Null RPC upcall with a 200msec
timeout to check to see if the gssd is running.
If the gssd is not running, the nfsd starts up
(without Kerberos support) with only a 200msec
delay.)
Also, move the svc_svc_nl_create() and svc_reg() calls in gssd.c
to before the daemon() call, so they are guaranteed to have
been done before the nfsd(8) daemon is started by
the rc scripts.
PR: 295714
Reviewed by: glebius
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D57455
---
sys/kgssapi/gss_impl.c | 42 ++++++++++++++++++++++++++++++++++++++++--
usr.sbin/gssd/gssd.c | 22 +++++-----------------
2 files changed, 45 insertions(+), 19 deletions(-)
diff --git a/sys/kgssapi/gss_impl.c b/sys/kgssapi/gss_impl.c
index 97b85a034071..4c7147fc3879 100644
--- a/sys/kgssapi/gss_impl.c
+++ b/sys/kgssapi/gss_impl.c
@@ -57,6 +57,7 @@ static int
kgss_load(void)
{
CLIENT *cl;
+ struct timeval nulltimo = {.tv_sec = 0, .tv_usec = 200000};
LIST_INIT(&kgss_mechs);
@@ -65,13 +66,16 @@ kgss_load(void)
/*
* The transport default is no retries at all, since there could
- * be no userland listener to our messages. We will retry for 5
+ * be no userland listener to our messages. Initially, a short timeout
+ * is set, so that a Null RPC upcall will fail quickly if the daemon
+ * is not running. After the Null RPC succeeds, we will retry for 5
* minutes with 10 second interval. This will potentially cure hosts
* with misconfigured startup, where kernel starts sending GSS queries
* before userland had started up the gssd(8) daemon.
+ * The initial timeout of 200usec is for the Null RPC.
*/
clnt_control(cl, CLSET_RETRIES, &(int){30});
- clnt_control(cl, CLSET_TIMEOUT, &(struct timeval){.tv_sec = 300});
+ clnt_control(cl, CLSET_TIMEOUT, &nulltimo);
/*
* We literally wait on gssd(8), let's see that in top(1).
@@ -237,9 +241,43 @@ CLIENT *
kgss_gssd_client(void)
{
CLIENT *cl;
+ struct rpc_callextra ext;
+ enum clnt_stat stat;
+ struct timeval rpctimo = {.tv_sec = 300, .tv_usec = 0};
+ static time_t nextrpc = 0;
+ static bool done_upcall = false;
mtx_lock(&kgss_gssd_lock);
cl = kgss_gssd_handle;
+ if (!done_upcall) {
+ mtx_unlock(&kgss_gssd_lock);
+ /*
+ * Can only be NULL if client_nl_create() called from
+ * kgss_load() returns NULL.
+ */
+ if (cl == NULL)
+ return (cl);
+ if (time_uptime < nextrpc)
+ return (NULL);
+ /*
+ * Do a Null RPC with a short timeout. If the RPC succeeds,
+ * the gssd daemon is running. If the Null RPC fails, don't
+ * again for 5sec.
+ */
+ memset(&ext, 0, sizeof(ext));
+ ext.rc_auth = authnone_create();
+ stat = clnt_call_private(cl, &ext, NULLPROC,
+ (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL,
+ rpctimo); /* rpctimo is ignored by clnt_nl_call(). */
+ AUTH_DESTROY(ext.rc_auth);
+ if (stat != RPC_SUCCESS) {
+ nextrpc = time_uptime + 5;
+ return (NULL);
+ }
+ CLNT_CONTROL(cl, CLSET_TIMEOUT, &rpctimo);
+ mtx_lock(&kgss_gssd_lock);
+ done_upcall = true;
+ }
if (cl != NULL)
CLNT_ACQUIRE(cl);
mtx_unlock(&kgss_gssd_lock);
diff --git a/usr.sbin/gssd/gssd.c b/usr.sbin/gssd/gssd.c
index 54d2062dd29a..5a00dbdd7c97 100644
--- a/usr.sbin/gssd/gssd.c
+++ b/usr.sbin/gssd/gssd.c
@@ -167,6 +167,11 @@ main(int argc, char **argv)
gssd_load_mech();
+ if ((xprt = svc_nl_create("kgss")) == NULL)
+ errx(1, "Can't create transport for local gssd socket");
+ if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL))
+ errx(1, "Can't register service for local gssd socket");
+
if (!debug_level) {
if (daemon(0, 0) != 0)
err(1, "Can't daemonize");
@@ -177,23 +182,6 @@ main(int argc, char **argv)
signal(SIGTERM, gssd_terminate);
signal(SIGPIPE, gssd_terminate);
- if ((xprt = svc_nl_create("kgss")) == NULL) {
- if (debug_level == 0) {
- syslog(LOG_ERR,
- "Can't create transport for local gssd socket");
- exit(1);
- }
- err(1, "Can't create transport for local gssd socket");
- }
- if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
- if (debug_level == 0) {
- syslog(LOG_ERR,
- "Can't register service for local gssd socket");
- exit(1);
- }
- err(1, "Can't register service for local gssd socket");
- }
-
LIST_INIT(&gss_resources);
gss_next_id = 1;
gss_start_time = time(0);