summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2016-01-12 10:11:29 +0000
committertrasz <trasz@FreeBSD.org>2016-01-12 10:11:29 +0000
commit355c35a189c2f841c4660bf6e63e376f77a2c04b (patch)
tree80e73a1e8e4e3f10ec17f3c3b146a796efee3373 /sys/kern
parenta35cfac66cd13b8a8193084a71a760710f87703e (diff)
downloadFreeBSD-src-355c35a189c2f841c4660bf6e63e376f77a2c04b.zip
FreeBSD-src-355c35a189c2f841c4660bf6e63e376f77a2c04b.tar.gz
MFC r287964:
Kernel part of reroot support - a way to change rootfs without reboot. Note that the mountlist manipulations are somewhat fragile, and not very pretty. The reason for this is to avoid changing vfs_mountroot(), which is (obviously) rather mission-critical, but not very well documented, and thus hard to test properly. It might be possible to rework it to use its own simple root mount mechanism instead of vfs_mountroot(). Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D2698
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_shutdown.c133
-rw-r--r--sys/kern/vfs_mountroot.c47
2 files changed, 159 insertions, 21 deletions
diff --git a/sys/kern/kern_shutdown.c b/sys/kern/kern_shutdown.c
index 5eba047..8139c8c 100644
--- a/sys/kern/kern_shutdown.c
+++ b/sys/kern/kern_shutdown.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/eventhandler.h>
+#include <sys/filedesc.h>
#include <sys/jail.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
@@ -157,10 +158,16 @@ static struct dumperinfo dumper; /* our selected dumper */
static struct pcb dumppcb; /* Registers. */
lwpid_t dumptid; /* Thread ID. */
+static struct cdevsw reroot_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "reroot",
+};
+
static void poweroff_wait(void *, int);
static void shutdown_halt(void *junk, int howto);
static void shutdown_panic(void *junk, int howto);
static void shutdown_reset(void *junk, int howto);
+static int kern_reroot(void);
/* register various local shutdown events */
static void
@@ -180,6 +187,26 @@ shutdown_conf(void *unused)
SYSINIT(shutdown_conf, SI_SUB_INTRINSIC, SI_ORDER_ANY, shutdown_conf, NULL);
/*
+ * The only reason this exists is to create the /dev/reroot/ directory,
+ * used by reroot code in init(8) as a mountpoint for tmpfs.
+ */
+static void
+reroot_conf(void *unused)
+{
+ int error;
+ struct cdev *cdev;
+
+ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &cdev,
+ &reroot_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "reroot/reroot");
+ if (error != 0) {
+ printf("%s: failed to create device node, error %d",
+ __func__, error);
+ }
+}
+
+SYSINIT(reroot_conf, SI_SUB_DEVFS, SI_ORDER_ANY, reroot_conf, NULL);
+
+/*
* The system call that results in a reboot.
*/
/* ARGSUSED */
@@ -195,9 +222,13 @@ sys_reboot(struct thread *td, struct reboot_args *uap)
if (error == 0)
error = priv_check(td, PRIV_REBOOT);
if (error == 0) {
- mtx_lock(&Giant);
- kern_reboot(uap->opt);
- mtx_unlock(&Giant);
+ if (uap->opt & RB_REROOT) {
+ error = kern_reroot();
+ } else {
+ mtx_lock(&Giant);
+ kern_reboot(uap->opt);
+ mtx_unlock(&Giant);
+ }
}
return (error);
}
@@ -462,6 +493,102 @@ kern_reboot(int howto)
}
/*
+ * The system call that results in changing the rootfs.
+ */
+static int
+kern_reroot(void)
+{
+ struct vnode *oldrootvnode, *vp;
+ struct mount *mp, *devmp;
+ int error;
+
+ if (curproc != initproc)
+ return (EPERM);
+
+ /*
+ * Mark the filesystem containing currently-running executable
+ * (the temporary copy of init(8)) busy.
+ */
+ vp = curproc->p_textvp;
+ error = vn_lock(vp, LK_SHARED);
+ if (error != 0)
+ return (error);
+ mp = vp->v_mount;
+ error = vfs_busy(mp, MBF_NOWAIT);
+ if (error != 0) {
+ vfs_ref(mp);
+ VOP_UNLOCK(vp, 0);
+ error = vfs_busy(mp, 0);
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ vfs_rel(mp);
+ if (error != 0) {
+ VOP_UNLOCK(vp, 0);
+ return (ENOENT);
+ }
+ if (vp->v_iflag & VI_DOOMED) {
+ VOP_UNLOCK(vp, 0);
+ vfs_unbusy(mp);
+ return (ENOENT);
+ }
+ }
+ VOP_UNLOCK(vp, 0);
+
+ /*
+ * Remove the filesystem containing currently-running executable
+ * from the mount list, to prevent it from being unmounted
+ * by vfs_unmountall(), and to avoid confusing vfs_mountroot().
+ *
+ * Also preserve /dev - forcibly unmounting it could cause driver
+ * reinitialization.
+ */
+
+ vfs_ref(rootdevmp);
+ devmp = rootdevmp;
+ rootdevmp = NULL;
+
+ mtx_lock(&mountlist_mtx);
+ TAILQ_REMOVE(&mountlist, mp, mnt_list);
+ TAILQ_REMOVE(&mountlist, devmp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+
+ oldrootvnode = rootvnode;
+
+ /*
+ * Unmount everything except for the two filesystems preserved above.
+ */
+ vfs_unmountall();
+
+ /*
+ * Add /dev back; vfs_mountroot() will move it into its new place.
+ */
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_HEAD(&mountlist, devmp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+ rootdevmp = devmp;
+ vfs_rel(rootdevmp);
+
+ /*
+ * Mount the new rootfs.
+ */
+ vfs_mountroot();
+
+ /*
+ * Update all references to the old rootvnode.
+ */
+ mountcheckdirs(oldrootvnode, rootvnode);
+
+ /*
+ * Add the temporary filesystem back and unbusy it.
+ */
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+ vfs_unbusy(mp);
+
+ return (0);
+}
+
+/*
* If the shutdown was a clean halt, behave accordingly.
*/
static void
diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c
index 5c49ff7..184976a 100644
--- a/sys/kern/vfs_mountroot.c
+++ b/sys/kern/vfs_mountroot.c
@@ -220,28 +220,39 @@ vfs_mountroot_devfs(struct thread *td, struct mount **mpp)
*mpp = NULL;
- vfsp = vfs_byname("devfs");
- KASSERT(vfsp != NULL, ("Could not find devfs by name"));
- if (vfsp == NULL)
- return (ENOENT);
-
- mp = vfs_mount_alloc(NULLVP, vfsp, "/dev", td->td_ucred);
+ if (rootdevmp != NULL) {
+ /*
+ * Already have /dev; this happens during rerooting.
+ */
+ error = vfs_busy(rootdevmp, 0);
+ if (error != 0)
+ return (error);
+ *mpp = rootdevmp;
+ } else {
+ vfsp = vfs_byname("devfs");
+ KASSERT(vfsp != NULL, ("Could not find devfs by name"));
+ if (vfsp == NULL)
+ return (ENOENT);
+
+ mp = vfs_mount_alloc(NULLVP, vfsp, "/dev", td->td_ucred);
+
+ error = VFS_MOUNT(mp);
+ KASSERT(error == 0, ("VFS_MOUNT(devfs) failed %d", error));
+ if (error)
+ return (error);
- error = VFS_MOUNT(mp);
- KASSERT(error == 0, ("VFS_MOUNT(devfs) failed %d", error));
- if (error)
- return (error);
+ opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
+ TAILQ_INIT(opts);
+ mp->mnt_opt = opts;
- opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
- TAILQ_INIT(opts);
- mp->mnt_opt = opts;
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
- mtx_lock(&mountlist_mtx);
- TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
- mtx_unlock(&mountlist_mtx);
+ *mpp = mp;
+ rootdevmp = mp;
+ }
- *mpp = mp;
- rootdevmp = mp;
set_rootvnode();
error = kern_symlink(td, "/", "dev", UIO_SYSSPACE);
OpenPOWER on IntegriCloud