Use a different technique to detect whether to mount-zfs

The behavior of the Dracut module was very wrong before.

The correct behavior: initramfs should not run `zfs-mount` to completion
if the two generator files exist.  If, however, one of them is missing,
it indicates one of three cases:

* The kernel command line did not specify a root ZFS file system, and
  another Dracut module is already handling root mount (via systemd).
  `mount-zfs` can run, but it will do nothing.
* There is no systemd to run `sysroot.mount` to begin with.
  `mount-zfs` must run.
* The root parameter is zfs:AUTO, which cannot be run in sysroot.mount.
  `mount-zfs` must run.

In any of these three cases, it is safe to run `zfs-mount` to completion.

`zfs-mount` must also delete itself if it determines it should not run,
or else Dracut will do the insane thing of running it over and over again.
Literally, the definition of insanity, doing the same thing that did not
work before, expecting different results.  Doing that may have had a great
result before, when we had a race between devices appearing and pools
being mounted, and `mount-zfs` was tasked with the full responsibility
of importing the needed pool, but nowadays it is wrong behavior and
should be suppressed.

I deduced that self-deletion was the correct thing to do by looking at
other Dracut code, because (as we all are very fully aware of) Dracut
is entirely, ahem, "implementation-defined".

Tested-by: @wphilips 
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
Closes #5157 
Closes #5204
This commit is contained in:
Rudd-O 2016-10-06 17:26:47 +00:00 committed by Brian Behlendorf
parent 64c688d716
commit b0f578a807
2 changed files with 52 additions and 19 deletions

View File

@ -10,17 +10,29 @@ case "${root}" in
*) return ;; *) return ;;
esac esac
if command -v systemctl >/dev/null; then GENERATOR_FILE=/run/systemd/generator/sysroot.mount
# If sysroot.mount exists, the initial RAM disk configured GENERATOR_EXTENSION=/run/systemd/generator/sysroot.mount.d/zfs-enhancement.conf
# it to mount ZFS on root. In that case, we bail early.
loadstate="$(systemctl --system --show -p LoadState sysroot.mount || true)" if [ -e "$GENERATOR_FILE" -a -e "$GENERATOR_EXTENSION" ] ; then
if [ "${loadstate}" = "LoadState=not-found" -o "${loadstate}" = "" ] ; then # If the ZFS sysroot.mount flag exists, the initial RAM disk configured
info "ZFS: sysroot.mount absent, mounting root with mount-zfs.sh" # it to mount ZFS on root. In that case, we bail early. This flag
else # file gets created by the zfs-generator program upon successful run.
info "ZFS: sysroot.mount present, delegating root mount to it" info "ZFS: There is a sysroot.mount and zfs-generator has extended it."
return info "ZFS: Delegating root mount to sysroot.mount."
# Let us tell the initrd to run on shutdown.
# We have a shutdown hook to run
# because we imported the pool.
need_shutdown
# We now prevent Dracut from running this thing again.
for zfsmounthook in "$hookdir"/mount/*zfs* ; do
if [ -f "$zfsmounthook" ] ; then
rm -f "$zfsmounthook"
fi fi
done
return
fi fi
info "ZFS: No sysroot.mount exists or zfs-generator did not extend it."
info "ZFS: Mounting root with the traditional mount-zfs.sh instead."
# Delay until all required block devices are present. # Delay until all required block devices are present.
udevadm settle udevadm settle
@ -45,6 +57,10 @@ ZFS_DATASET="${ZFS_DATASET:-${root#zfs:}}"
ZFS_POOL="${ZFS_DATASET%%/*}" ZFS_POOL="${ZFS_DATASET%%/*}"
if import_pool "${ZFS_POOL}" ; then if import_pool "${ZFS_POOL}" ; then
# Let us tell the initrd to run on shutdown.
# We have a shutdown hook to run
# because we imported the pool.
need_shutdown
info "ZFS: Mounting dataset ${ZFS_DATASET}..." info "ZFS: Mounting dataset ${ZFS_DATASET}..."
if mount_dataset "${ZFS_DATASET}" ; then if mount_dataset "${ZFS_DATASET}" ; then
ROOTFS_MOUNTED=yes ROOTFS_MOUNTED=yes
@ -53,4 +69,3 @@ if import_pool "${ZFS_POOL}" ; then
fi fi
rootok=0 rootok=0
need_shutdown

View File

@ -1,21 +1,32 @@
#!/bin/bash #!/bin/bash
echo "zfs-generator: starting" >> /dev/kmsg
GENERATOR_DIR="$1" GENERATOR_DIR="$1"
[ -z "$GENERATOR_DIR" ] && exit 1 [ -n "$GENERATOR_DIR" ] || {
echo "zfs-generator: no generator directory specified, exiting" >> /dev/kmsg
exit 1
}
[ -f /lib/dracut-lib.sh ] && dracutlib=/lib/dracut-lib.sh [ -f /lib/dracut-lib.sh ] && dracutlib=/lib/dracut-lib.sh
[ -f /usr/lib/dracut/modules.d/99base/dracut-lib.sh ] && dracutlib=/usr/lib/dracut/modules.d/99base/dracut-lib.sh [ -f /usr/lib/dracut/modules.d/99base/dracut-lib.sh ] && dracutlib=/usr/lib/dracut/modules.d/99base/dracut-lib.sh
type getarg >/dev/null 2>&1 || . "$dracutlib" type getarg >/dev/null 2>&1 || {
echo "zfs-generator: loading Dracut library from $dracutlib" >> /dev/kmsg
. "$dracutlib"
}
[ -z "$root" ] && root=$(getarg root=) [ -z "$root" ] && root=$(getarg root=)
[ -z "$rootfstype" ] && rootfstype=$(getarg rootfstype=) [ -z "$rootfstype" ] && rootfstype=$(getarg rootfstype=)
[ -z "$rootflags" ] && rootflags=$(getarg rootflags=) [ -z "$rootflags" ] && rootflags=$(getarg rootflags=)
# If root is not ZFS= or zfs: or rootfstype is not zfs
# then we are not supposed to handle it.
[ "${root##zfs:}" = "${root}" -a "${root##ZFS=}" = "${root}" -a "$rootfstype" != "zfs" ] && exit 0 [ "${root##zfs:}" = "${root}" -a "${root##ZFS=}" = "${root}" -a "$rootfstype" != "zfs" ] && exit 0
# If root is set to zfs:AUTO, then we know sysroot.mount will not be generated # If root is set to zfs:AUTO, then we are also not
# so we have no need to enhance it. # supposed to handle it, and it should be handled
# See https://github.com/zfsonlinux/zfs/pull/4558#discussion_r61118952 for details. # by the traditional Dracut mount hook.
# See https://github.com/zfsonlinux/zfs/pull/4558#discussion_r61118952
if [ "${root}" = "zfs:AUTO" ] ; then if [ "${root}" = "zfs:AUTO" ] ; then
exit 0 exit 0
fi fi
@ -32,16 +43,23 @@ fi
root="${root##zfs:}" root="${root##zfs:}"
root="${root##ZFS=}" root="${root##ZFS=}"
echo "zfs-generator: writing extension for sysroot.mount to $GENERATOR_DIR"/sysroot.mount.d/zfs-enhancement.conf >> /dev/kmsg
[ -d "$GENERATOR_DIR" ] || mkdir "$GENERATOR_DIR" [ -d "$GENERATOR_DIR" ] || mkdir "$GENERATOR_DIR"
[ -d "$GENERATOR_DIR/sysroot.mount.d" ] || mkdir "$GENERATOR_DIR/sysroot.mount.d" [ -d "$GENERATOR_DIR"/sysroot.mount.d ] || mkdir "$GENERATOR_DIR"/sysroot.mount.d
{ {
echo "[Unit]" echo "[Unit]"
echo "Before=initrd-root-fs.target"
echo "After=zfs-import-scan.service" echo "After=zfs-import-scan.service"
echo "After=zfs-import-cache.service" echo "After=zfs-import-cache.service"
echo ""
echo "[Mount]" echo "[Mount]"
echo "What=${root}" echo "What=${root}"
echo "Type=${rootfstype}" echo "Type=${rootfstype}"
echo "Options=${rootflags}" echo "Options=${rootflags}"
} > "$GENERATOR_DIR/sysroot.mount.d/zfs-enhancement.conf" } > "$GENERATOR_DIR"/sysroot.mount.d/zfs-enhancement.conf
[ -d "$GENERATOR_DIR"/initrd-root-fs.target.requires ] || mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.requires
ln -s ../sysroot.mount "$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount
echo "zfs-generator: finished" >> /dev/kmsg