Changes to allow using BOOTP_NFSROOT and mounting an nfs root filesystem

other than the one specified by the BOOTP server.  This configures NFS
using the BOOTP protocol while also respecting other root-path options such
as setting vfs.root.mountfrom in the environment or using the RB_DFLTROOT
boot option.  It allows you to override the root path provided by the
server, or to supply a root path when the server provides IP configuration
but no root path info.

This maintains the historical BOOTP_NFSROOT behavior of panicking on a
failure to mount the root path provided by the server, unless you've
provided an alternative via the ROOTDEVNAME kernel option or by setting
vfs.root.mountfrom.  The behavior of panicking when given no other options
is preserved because it amounts to a bit of a retry loop that could
eventually recover from a transient network or server problem.

The user can now override the root path from loader(8) even if the
kernel is compiled with BOOTP_NFSROOT.  If vfs.root.mountfrom is set in
the environment it is used unconditionally -- it always overrides the
BOOTP info.  If it begins with [old]nfs: then the BOOTP code uses it
instead of the server-provided info.  If it specifies some other
filesystem then the bootp code will not panic like it used to and the code
in vfs_mountroot.c will invoke the right filesystem to do the mount.

If the kernel is compiled with the ROOTDEVNAME option, then that name is
used by the BOOTP code if either
      * The server doesn't provide a pathname.
      * The boothowto flags include RB_DFLTROOT.
The latter allows the user to compile in alternate path in ROOTDEVNAME
such as ufs:/dev/da0s1a and boot from that path by setting
boot_dftlroot=1 in loader(8) or using the '-r' option in boot(8).

The one thing not provided here is automatic failover from a
server-provided path to a compiled-in one without the user manually
requesting that.  The code just isn't currently structured in a way that
makes that possible with a lot of rewrite.  I think the ability to set
vfs.root.mountfrom and to use ROOTDEVNAME automatically when the server
doesn't provide a name covers the most common needs.

A set of patches submitted by Lars Eggert provided the part I couldn't
figure out by myself when I tried to do this last year; many thanks.

Reviewed by:	rodrigc
This commit is contained in:
Ian Lepore 2013-07-31 19:14:00 +00:00
parent cac0f72e5d
commit 6cbd933b37
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=253847
2 changed files with 56 additions and 21 deletions

View File

@ -714,8 +714,8 @@ parse_mount(char **conf)
goto out;
}
if (strcmp(fs, "zfs") != 0 && dev[0] != '\0' &&
!parse_mount_dev_present(dev)) {
if (strcmp(fs, "zfs") != 0 && strstr(fs, "nfs") == NULL &&
dev[0] != '\0' && !parse_mount_dev_present(dev)) {
printf("mountroot: waiting for device %s ...\n", dev);
delay = hz / 10;
timeout = root_mount_timeout * hz;

View File

@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include "opt_bootp.h"
#include "opt_nfs.h"
#include "opt_rootdevname.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
@ -167,6 +169,7 @@ struct bootpc_tagcontext {
struct bootpc_globalcontext {
STAILQ_HEAD(, bootpc_ifcontext) interfaces;
u_int32_t xid;
int any_root_overrides;
int gotrootpath;
int gotgw;
int ifnum;
@ -865,13 +868,14 @@ bootpc_call(struct bootpc_globalcontext *gctx, struct thread *td)
BOOTP_SETTLE_DELAY;
} else
printf(" (ignored)");
if (ifctx->gotrootpath) {
if (ifctx->gotrootpath ||
gctx->any_root_overrides) {
gotrootpath = 1;
rtimo = time_second +
BOOTP_SETTLE_DELAY;
printf(" (got root path)");
} else
printf(" (no root path)");
if (ifctx->gotrootpath)
printf(" (got root path)");
}
printf("\n");
}
} /* while secs */
@ -1371,7 +1375,7 @@ static void
bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx,
struct bootpc_globalcontext *gctx)
{
char *p;
char *p, *s;
unsigned int ip;
ifctx->gotgw = 0;
@ -1438,8 +1442,30 @@ bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx,
}
}
p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
/*
* Choose a root filesystem. If a value is forced in the environment
* and it contains "nfs:", use it unconditionally. Otherwise, if the
* kernel is compiled with the ROOTDEVNAME option, then use it if:
* - The server doesn't provide a pathname.
* - The boothowto flags include RB_DFLTROOT (user said to override
* the server value).
*/
p = NULL;
if ((s = getenv("vfs.root.mountfrom")) != NULL) {
if ((p = strstr(s, "nfs:")) != NULL)
p = strdup(p + 4, M_TEMP);
freeenv(s);
}
if (p == NULL) {
p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
TAG_ROOT);
}
#ifdef ROOTDEVNAME
if ((p == NULL || (boothowto & RB_DFLTROOT) != 0) &&
(p = strstr(ROOTDEVNAME, "nfs:")) != NULL) {
p += 4;
}
#endif
if (p != NULL) {
if (gctx->setrootfs != NULL) {
printf("rootfs %s (ignored) ", p);
@ -1543,6 +1569,17 @@ bootpc_init(void)
gctx->xid = ~0xFFFF;
gctx->starttime = time_second;
/*
* If ROOTDEVNAME is defined or vfs.root.mountfrom is set then we have
* root-path overrides that can potentially let us boot even if we don't
* get a root path from the server, so we can treat that as a non-error.
*/
#ifdef ROOTDEVNAME
gctx->any_root_overrides = 1;
#else
gctx->any_root_overrides = testenv("vfs.root.mountfrom");
#endif
/*
* Find a network interface.
*/
@ -1652,19 +1689,10 @@ bootpc_init(void)
bootpc_compose_query(ifctx, td);
error = bootpc_call(gctx, td);
if (error != 0) {
#ifdef BOOTP_NFSROOT
panic("BOOTP call failed");
#else
printf("BOOTP call failed\n");
#endif
}
rootdevnames[0] = "nfs:";
#ifdef NFSCLIENT
rootdevnames[1] = "oldnfs:";
#endif
mountopts(&nd->root_args, NULL);
STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
@ -1672,7 +1700,7 @@ bootpc_init(void)
bootpc_decode_reply(nd, ifctx, gctx);
#ifdef BOOTP_NFSROOT
if (gctx->gotrootpath == 0)
if (gctx->gotrootpath == 0 && gctx->any_root_overrides == 0)
panic("bootpc: No root path offered");
#endif
@ -1699,9 +1727,16 @@ bootpc_init(void)
error = md_mount(&nd->root_saddr, nd->root_hostnam,
nd->root_fh, &nd->root_fhsize,
&nd->root_args, td);
if (error != 0)
panic("nfs_boot: mountd root, error=%d", error);
if (error != 0) {
if (gctx->any_root_overrides == 0)
panic("nfs_boot: mount root, error=%d", error);
else
goto out;
}
rootdevnames[0] = "nfs:";
#ifdef NFSCLIENT
rootdevnames[1] = "oldnfs:";
#endif
nfs_diskless_valid = 3;
}