Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 05 Apr 2026 16:04:43 +0000
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 8b9775912cbc - main - nfs_diskless: Add support for an NFSv4 root fs
Message-ID:  <69d2881b.4511d.5f34dcf8@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=8b9775912cbc7bb3c05c1fdfc3597dc4b68a9b9e

commit 8b9775912cbc7bb3c05c1fdfc3597dc4b68a9b9e
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-04-05 16:00:24 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-04-05 16:02:39 +0000

    nfs_diskless: Add support for an NFSv4 root fs
    
    Without this patch, diskless root NFS file systems
    could only be mounted via NFSv3 (or NFSv2).
    This patch adds the basic support needed to mount
    a root fs via NFSv4.
    
    At this time, the NFSv4 mount will only work if
    the following is done on the NFS server configuration:
    - The root directory specified in the "V4:" line in
      /etc/exports must be "/".  This is needed since the
      path to mount must be the same for NFSv3 and NFSv4.
    - The NFS server must be configured to do both NFSv3
      and NFSv4, since the bootstrap code still uses NFSv3.
    - The NFSv4 server must be configured with:
      vfs.nfs.enable_uidtostring=1
      vfs.nfsd.enable_stringtouid=1
      since the NFSv4 root fs cannot be running nfsuserd(8)
      when it is booting.  (This limitation may be removed
      in a future commit by hard-wiring enough id<-->name
      mapping entries to handle things until the nfsuserd(8)
      is running.)
    
    To enable the root fs to be mounted via NFSv4, it needs:
    - in the root file system's /boot/loader.conf
      boot.nfsroot.options="nfsv4"
      (Additional options like rsize=65536,wsize=65536 can
       also be specified.)
    - in the root file system's /etc/sysctl.conf
      vfs.nfs.enable_uidtostring=1
    
    Requested by:   Dan Shelton <dan.f.dhelton@gmail.com>
    MFC after:      1 week
---
 sys/fs/nfs/nfs_var.h            |  1 +
 sys/fs/nfsclient/nfs_clport.c   | 31 +++++++++++++++++++++++++++++++
 sys/fs/nfsclient/nfs_clrpcops.c |  3 ++-
 sys/fs/nfsclient/nfs_clstate.c  | 17 +++++++++++++++--
 sys/fs/nfsclient/nfs_clvfsops.c |  7 +++++--
 sys/nfs/nfs_diskless.c          | 37 +++++++++++++++++++++++++------------
 6 files changed, 79 insertions(+), 17 deletions(-)

diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 28088c12d7e7..145cbf984464 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -609,6 +609,7 @@ int nfscl_relbytelock(vnode_t, u_int64_t, u_int64_t,
 int nfscl_checkwritelocked(vnode_t, struct flock *,
     struct ucred *, NFSPROC_T *, void *, int);
 void nfscl_lockrelease(struct nfscllockowner *, int, int);
+void nfscl_uuidcheck(char *);
 void nfscl_fillclid(u_int64_t, char *, u_int8_t *, u_int16_t);
 void nfscl_filllockowner(void *, u_int8_t *, int);
 void nfscl_freeopen(struct nfsclopen *, int, bool);
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index cf163adc02de..d23051058ce4 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -659,6 +659,37 @@ ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep)
 	return (setnsize);
 }
 
+/*
+ * If the uuid passed in is the DEFAULT_UUID, try and find an
+ * alternate to replace it with.
+ * If no alternate is available, set uuid to "" so that nfscl_fillclid()
+ * will use random bytes.
+ */
+void
+nfscl_uuidcheck(char *uuid)
+{
+	int ucplen, uuidlen;
+	char *ucp;
+
+	/*
+	 * If the uuid is the DEFAULT_UUID, try and get an alternative.
+	 */
+	uuidlen = strlen(uuid);
+	ucp = NULL;
+	if (uuidlen == strlen(DEFAULT_HOSTUUID) &&
+	    NFSBCMP(uuid, DEFAULT_HOSTUUID, uuidlen) == 0) {
+		*uuid = '\0';
+		/* Use smbios.system.uuid if it exists. */
+		if ((ucp = kern_getenv("smbios.system.uuid")) != NULL) {
+			ucplen = strlen(ucp);
+			if (ucplen < HOSTUUIDLEN && ucplen > 0)
+				strlcpy(uuid, ucp, HOSTUUIDLEN);
+		}
+	}
+	if (ucp != NULL)
+		freeenv(ucp);
+}
+
 /*
  * Fill in the client id name. For these bytes:
  * 1 - they must be unique
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 974d08611a00..130cc4990152 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -67,6 +67,7 @@ SYSCTL_U64(_vfs_nfs, OID_AUTO, maxcopyrange, CTLFLAG_RW,
 /*
  * Global variables
  */
+uint32_t nfs_exchangeboot = 0;
 extern struct nfsstatsv1 nfsstatsv1;
 extern int nfs_numnfscbd;
 extern struct timeval nfsboottime;
@@ -5537,7 +5538,7 @@ nfsrpc_exchangeid(struct nfsmount *nmp, struct nfsclclient *clp,
 	nfscl_reqstart(nd, NFSPROC_EXCHANGEID, nmp, NULL, 0, NULL, NULL,
 	    NFS_VER4, minorvers, NULL);
 	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
-	*tl++ = txdr_unsigned(nfsboottime.tv_sec);	/* Client owner */
+	*tl++ = txdr_unsigned(nfs_exchangeboot);	/* Client owner */
 	*tl = txdr_unsigned(clp->nfsc_rev);
 	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
 
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 6dc97142b77f..a511c31d5202 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -89,6 +89,8 @@ extern u_int32_t newnfs_false, newnfs_true;
 extern int nfscl_debuglevel;
 extern int nfscl_enablecallb;
 extern int nfs_numnfscbd;
+extern struct timeval nfsboottime;
+extern uint32_t nfs_exchangeboot;
 NFSREQSPINLOCK;
 NFSCLSTATEMUTEX;
 int nfscl_inited = 0;
@@ -883,9 +885,11 @@ nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
 	if (cred != NULL) {
 		getcredhostuuid(cred, uuid, sizeof uuid);
 		idlen = strlen(uuid);
-		if (idlen > 0)
+		if (idlen > 0) {
+			nfscl_uuidcheck(uuid);
+			idlen = strlen(uuid);
 			idlen += sizeof (u_int64_t);
-		else
+		} else
 			idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
 		newclp = malloc(
 		    sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
@@ -996,6 +1000,15 @@ nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
 		 * such that the server throws away the clientid before
 		 * receiving the SetClientIDConfirm.
 		 */
+		/*
+		 * Must be done here while locked and before calling
+		 * nfsrpc_setclient().
+		 */
+		if (nfs_exchangeboot == 0) {
+			nfs_exchangeboot = nfsboottime.tv_sec;
+			if (nfs_exchangeboot == 0)
+				nfs_exchangeboot = arc4random();
+		}
 		if (clp->nfsc_renew > 0)
 			clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
 		else
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 212c88f28930..74e5e2dc9b1b 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -567,7 +567,7 @@ nfs_mountdiskless(char *path,
     struct vnode **vpp, struct mount *mp)
 {
 	struct sockaddr *nam;
-	int dirlen, error;
+	int dirlen, error, minvers;
 	char *dirpath;
 
 	/*
@@ -580,9 +580,12 @@ nfs_mountdiskless(char *path,
 	else
 		dirlen = 0;
 	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
+	minvers = 0;
+	if ((args->flags & NFSMNT_NFSV4) != 0)
+		minvers = -1;
 	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
 	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, 
-	    NFS_DEFAULT_NEGNAMETIMEO, 0, 0, NULL, 0)) != 0) {
+	    NFS_DEFAULT_NEGNAMETIMEO, minvers, 0, NULL, 0)) != 0) {
 		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
 		return (error);
 	}
diff --git a/sys/nfs/nfs_diskless.c b/sys/nfs/nfs_diskless.c
index 0f0cf80feeec..d5278612d8d9 100644
--- a/sys/nfs/nfs_diskless.c
+++ b/sys/nfs/nfs_diskless.c
@@ -119,6 +119,10 @@ nfs_parse_options(const char *envopts, struct nfs_args *nd)
 		else if (strcmp(o, "nfsv3") == 0) {
 			nd->flags &= ~NFSMNT_NFSV4;
 			nd->flags |= NFSMNT_NFSV3;
+		} else if (strcmp(o, "nfsv4") == 0) {
+			nd->flags &= ~NFSMNT_NFSV3;
+			nd->flags |= NFSMNT_NFSV4;
+			nd->sotype = SOCK_STREAM;
 		} else if (strcmp(o, "tcp") == 0)
 			nd->sotype = SOCK_STREAM;
 		else if (strcmp(o, "udp") == 0)
@@ -271,24 +275,33 @@ match_done:
 			return;
 		}
 		nd3->root_saddr.sin_port = htons(NFS_PORT);
-		fhlen = decode_nfshandle("boot.nfsroot.nfshandle",
-		    &nd3->root_fh[0], NFSX_V3FHMAX);
-		if (fhlen == 0) {
-			printf("nfs_diskless: no NFS handle\n");
-			return;
+		if ((cp = kern_getenv("boot.nfsroot.options")) != NULL) {
+			nfs_parse_options(cp, &nd3->root_args);
+			freeenv(cp);
 		}
-		if (fhlen != nd3->root_fhsize) {
-			printf("nfs_diskless: bad NFS handle len=%d\n", fhlen);
-			return;
+		if ((nd3->root_args.flags & NFSMNT_NFSV4) == 0) {
+			fhlen = decode_nfshandle("boot.nfsroot.nfshandle",
+			    &nd3->root_fh[0], NFSX_V3FHMAX);
+			if (fhlen == 0) {
+				printf("nfs_diskless: no NFS handle\n");
+				return;
+			}
+			if (fhlen != nd3->root_fhsize) {
+				printf("nfs_diskless: bad NFS handle len=%d\n",
+				    fhlen);
+				return;
+			}
+		} else {
+			/*
+			 * For NFSv4, the file handle is derived from the
+			 * boot.nfsroot.path during mounting by NFSv4.
+			 */
+			nd3->root_fhsize = 0;
 		}
 		if ((cp = kern_getenv("boot.nfsroot.path")) != NULL) {
 			strncpy(nd3->root_hostnam, cp, MNAMELEN - 1);
 			freeenv(cp);
 		}
-		if ((cp = kern_getenv("boot.nfsroot.options")) != NULL) {
-			nfs_parse_options(cp, &nd3->root_args);
-			freeenv(cp);
-		}
 
 		nfs_diskless_valid = 3;
 	} else {


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69d2881b.4511d.5f34dcf8>