Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dracut module & gentoo init tweaks, doc'd & cleaned up #307

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dracut/90zfs/90-zfs.rules
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ ACTION!="add|change", GOTO="zfs_end"
ENV{ID_FS_TYPE}=="zfs", RUN+="/sbin/modprobe zfs"
ENV{ID_FS_TYPE}=="zfs_member", RUN+="/sbin/modprobe zfs"

KERNEL=="null", SYMLINK+="root"
SYMLINK=="null", SYMLINK+="root"

LABEL="zfs_end"
5 changes: 1 addition & 4 deletions dracut/90zfs/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
pkgdracutdir = $(datadir)/dracut/modules.d/90zfs
dist_pkgdracut_SCRIPTS = \
$(top_srcdir)/dracut/90zfs/90-zfs.rules \
$(top_srcdir)/dracut/90zfs/check \
$(top_srcdir)/dracut/90zfs/install \
$(top_srcdir)/dracut/90zfs/installkernel \
$(top_srcdir)/dracut/90zfs/module-setup.sh \
$(top_srcdir)/dracut/90zfs/mount-zfs.sh \
$(top_srcdir)/dracut/90zfs/zfs-genrules.sh \
$(top_srcdir)/dracut/90zfs/parse-zfs.sh

all:
Expand Down
5 changes: 1 addition & 4 deletions dracut/90zfs/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,8 @@ top_srcdir = @top_srcdir@
pkgdracutdir = $(datadir)/dracut/modules.d/90zfs
dist_pkgdracut_SCRIPTS = \
$(top_srcdir)/dracut/90zfs/90-zfs.rules \
$(top_srcdir)/dracut/90zfs/check \
$(top_srcdir)/dracut/90zfs/install \
$(top_srcdir)/dracut/90zfs/installkernel \
$(top_srcdir)/dracut/90zfs/module-setup.sh \
$(top_srcdir)/dracut/90zfs/mount-zfs.sh \
$(top_srcdir)/dracut/90zfs/zfs-genrules.sh \
$(top_srcdir)/dracut/90zfs/parse-zfs.sh

all: all-am
Expand Down
10 changes: 0 additions & 10 deletions dracut/90zfs/check

This file was deleted.

17 changes: 0 additions & 17 deletions dracut/90zfs/install

This file was deleted.

10 changes: 0 additions & 10 deletions dracut/90zfs/installkernel

This file was deleted.

46 changes: 46 additions & 0 deletions dracut/90zfs/module-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/sh

check() {
# We depend on udev-rules being loaded
[ "$1" = "-d" ] && return 0

# Verify the zfs tool chain
which zpool >/dev/null 2>&1 || return 1
which zfs >/dev/null 2>&1 || return 1

return 0
}

depends() {
echo udev-rules
return 0
}

installkernel() {
instmods zfs
instmods zcommon
instmods znvpair
instmods zavl
instmods zunicode
instmods spl
instmods zlib_deflate
instmods zlib_inflate
}

install() {
inst_rules "$moddir/90-zfs.rules"
inst_rules /etc/udev/rules.d/60-zpool.rules
inst_rules /etc/udev/rules.d/60-zvol.rules
inst /etc/zfs/zdev.conf
inst /etc/zfs/zpool.cache
inst /etc/hostid
dracut_install zfs
dracut_install zpool
dracut_install zpool_layout
dracut_install zpool_id
dracut_install zvol_id
dracut_install mount.zfs
dracut_install hostid
inst_hook cmdline 95 "$moddir/parse-zfs.sh"
inst_hook mount 98 "$moddir/mount-zfs.sh"
}
69 changes: 59 additions & 10 deletions dracut/90zfs/mount-zfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,64 @@

. /lib/dracut-lib.sh

if [ "$rootfs" = "zfs" ]; then
zfsrootfs=`echo "$root" | sed 's|^zfs:||'`
zfspool=`echo "$zfsrootfs" | sed 's|/.*||g'`
zpool import -N "$zfspool"
mount -o zfsutil -t "$rootfs" "$zfsrootfs" "$NEWROOT"
if [ "$?" = "0" ]
then
ROOTFS_MOUNTED=yes
ZPOOL_FORCE=""
if getargbool 0 zfs_force -y zfs.force -y zfsforce ; then
warn "ZFS: Will force-import pools if necessary."
ZPOOL_FORCE="-f"
fi

case "$root" in
zfs:*)
# We have ZFS modules loaded, so we're able to import pools now.
if [ "$root" = "zfs:AUTO" ] ; then
# Need to parse bootfs attribute
info "ZFS: Attempting to detect root from imported ZFS pools."

# Might be imported by the kernel module, so try searching before we import anything.
zfsbootfs=`zpool list -H -o bootfs | sed 'q'`
if [ "$zfsbootfs" = "" ] ; then
# Not there, so we need to import everything.
info "ZFS: Attempting to import additional pools."
zpool import -N -a ${ZPOOL_FORCE}
zfsbootfs=`zpool list -H -o bootfs | sed 'q'`
if [ "$zfsbootfs" = "" ] ; then
rootok=0
pool=""

warn "ZFS: No bootfs attribute found in importable pools."

# Re-export everything since we're not prepared to take responsibility for them
zpool list -H | while read fs rest ; do
zpool export "$fs"
done

return 1
fi
fi
info "ZFS: Using ${zfsbootfs} as root."
else
mount -t "$rootfs" "$zfsrootfs" "$NEWROOT" && ROOTFS_MOUNTED=yes
# Should have an explicit pool set, so just import it and we're done.
zfsbootfs="${root#zfs:}"
pool="${zfsbootfs%%/*}"
if ! zpool list -H $pool > /dev/null ; then
# pool wasn't imported automatically by the kernel module, so try it manually.
info "ZFS: Importing pool ${pool}..."
if ! zpool import -N ${ZPOOL_FORCE} $pool ; then
warn "ZFS: Unable to import pool ${pool}."
rootok=0

return 1
fi
fi
fi
fi

# Above should have left our rpool imported and pool/dataset in $root.
# We need zfsutil for non-legacy mounts and not for legacy mounts.
mountpoint=`zfs get -H -o value mountpoint $zfsbootfs`
if [ "$mountpoint" = "legacy" ] ; then
mount -t zfs "$zfsbootfs" "$NEWROOT" && ROOTFS_MOUNTED=yes
else
mount -o zfsutil -t zfs "$zfsbootfs" "$NEWROOT" && ROOTFS_MOUNTED=yes
fi
;;
esac
64 changes: 44 additions & 20 deletions dracut/90zfs/parse-zfs.sh
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
#!/bin/sh

. /lib/dracut-lib.sh

# Let the command line override our host id.
spl_hostid=`getarg spl_hostid=`
if [ "${spl_hostid}" != "" ] ; then
info "ZFS: Using hostid from command line: ${spl_hostid}"
echo "${spl_hostid}" > /etc/hostid
elif [ -f /etc/hostid ] ; then
info "ZFS: Using hostid from /etc/hostid: `cat /etc/hostid`"
else
warn "ZFS: No hostid found on kernel command line or /etc/hostid. ZFS pools may not import correctly."
fi

case "$root" in
zfs:FILESYSTEM=*|FILESYSTEM=*)
root="${root#zfs:}"
root="zfs:${root#FILESYSTEM=}"
rootfs="zfs"
rootok=1 ;;
zfs:ZFS=*|ZFS=*)
root="${root#zfs:}"
root="zfs:${root#ZFS=}"
rootfs="zfs"
rootok=1 ;;
""|zfs|zfs:)
# We'll take root unset, root=zfs, or root=zfs:
# No root set, so we want to read the bootfs attribute. We can't do that until udev settles,
# so we'll set dummy values and hope for the best later on.
root="zfs:AUTO"
rootok=1

info "ZFS: Enabling autodetection of bootfs after udev settles."
;;

ZFS\=*|zfs:*|zfs:FILESYSTEM\=*|FILESYSTEM\=*)
# root is explicit ZFS root. Parse it now.
# We can handle a root=... param in any of the following formats:
# root=ZFS=rpool/ROOT
# root=zfs:rpool/ROOT
# root=zfs:FILESYSTEM=rpool/ROOT
# root=FILESYSTEM=rpool/ROOT

# Strip down to just the pool/fs
root="${root#zfs:}"
root="${root#FILESYSTEM=}"
root="zfs:${root#ZFS=}"
rootok=1

info "ZFS: Set ${root} as bootfs."
;;
esac

if [ "$rootok" != "1" ] ; then
zpool import -aN
zfsbootfs=`zpool list -H -o bootfs | grep -v ^-$ -m 1`
if [ -n "$zfsbootfs" ] ; then
root="zfs:$zfsbootfs"
rootfs="zfs"
rootok=1
fi
zpool list -H | while read fs rest ; do zpool export "$fs" ; done
fi
# Make sure Dracut is happy that we have a root and will wait for ZFS modules to settle before mounting.
ln -s /dev/null /dev/root 2>/dev/null
echo '[ -e /dev/zfs ]' > $hookdir/initqueue/finished/zfs.sh
12 changes: 0 additions & 12 deletions dracut/90zfs/zfs-genrules.sh

This file was deleted.

60 changes: 60 additions & 0 deletions dracut/README.dracut.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,63 @@ zfs_arc_max, zfs_prefetch_disable, and zfs_vdev_max_pending.
4) Finally, create your new initramfs by running dracut.

$ dracut --force /path/to/initramfs kernel_version

Kernel Command Line
-------------------

The initramfs' behavior is influenced by the following kernel command line parameters passed in from the boot loader:

* `root=...`: If not set, importable pools are searched for a bootfs attribute. If an explicitly set root is desired, you may use `root=ZFS:pool/dataset`
* `zfs_force=0`: If set to 1, the initramfs will run `zpool import -f` when attempting to import pools if the required pool isn't automatically imported by the zfs module. This can save you a trip to a bootcd if hostid has changed, but is dangerous and can lead to zpool corruption, particularly in cases where storage is on a shared fabric such as iSCSI where multiple hosts can access storage devices concurrently. _Please understand the implications of force-importing a pool before enabling this option!_
* `spl_hostid`: By default, the hostid used by the SPL module is read from /etc/hostid inside the initramfs. This file is placed there from the host system when the initramfs is built which effectively ties the ramdisk to the host which builds it. If a different hostid is desired, one may be set in this attribute and will override any file present in the ramdisk. The format should be hex exactly as found in the `/etc/hostid` file, IE `spl_hostid=00bab10c`.

Note that changing the hostid between boots will most likely lead to an un-importable pool since the last importing hostid won't match. In order to recover from this, you may use the `zfs_force` option or boot from a different filesystem and `zpool import -f` then `zpool export` the pool before rebooting with the new hostid.

How it Works
============

The Dracut module consists of the following files (less Makefile's):

* `module-setup.sh`: Script run by the initramfs builder to create the ramdisk. Contains instructions on which files are required by the modules & z* programs. Also triggers inclusion of `/etc/hostid` and the zpool cache. This file is not included in the initramfs.
* `90-zfs.rules`: udev rules which trigger loading of the ZFS modules at boot.
* `parse-zfs.sh`: Run early in the initramfs boot process to parse kernel command line and determine if ZFS is the active root filesystem
* `mount-zfs.sh`: Run later in initramfs boot process after udev has settled to mount the root dataset.

`module-setup.sh`
---------------

This file is run by the Dracut script within the live system, not at boot time. It's not included in the final initramfs. Functions in this script describe which files are needed by ZFS at boot time.

Currently all the various z* and spl modules are included, a dependency is asserted on udev-rules, and the various zfs, zpool, etc. helpers are included. Dracut provides library functions which automatically gather the shared libs necessary to run each of these binaries, so statically built binaries are not required.

The zpool & zvol udev rules files are copied from where they are installed by the ZFS build. __PACKAGERS TAKE NOTE__: If you move `/etc/udev/rules/60-z*.rules`, you'll need to update this file to match.

Currently this file also includes `/etc/hostid` and `/etc/zfs/zpool.cache` which means the generated ramdisk is specific to the host system which built it. If a generic initramfs is required, it may be preferable to omit these files and specify the `spl_hostid` from the boot loader instead.

`parse-zfs.sh`
------------

Run during the cmdline phase of the initramfs boot process, this script performs some basic sanity checks on kernel command line parameters to determine if booting from ZFS is likely to be what is desired. Dracut requires this script to adjust the `root` variable if required and to set `rootok=1` if a mountable root filesystem is available. Unfortunately this script must run before udev is settled and kernel modules are known to be loaded, so accessing the zpool and zfs commands is unsafe.

If the root=ZFS... parameter is set on the command line, then it's at least certain that ZFS is what is desired, though this script is unable to determine if ZFS is in fact available. This script will alter the `root` parameter to replace several historical forms of specifying the pool and dataset name with the canonical form of `zfs:pool/dataset`.

If no root= parameter is set, the best this script can do is guess that ZFS is desired. At present, no other known filesystems will work with no root= parameter, though this might possibly interfere with using the compiled-in default root in the kernel image. It's considered unlikely that would ever be the case when an initramfs is in use, so this script sets `root=zfs:AUTO` and hopes for the best.

Once the root=... (or lack thereof) parameter is parsed, a dummy symlink is created from `/dev/root` -> `/dev/null` to satisfy parts of the Dracut process which check for presence of a single root device node.

Finally, an initqueue/finished hook is registered which causes the initqueue phase of Dracut to wait for `/dev/zfs` to become available before attempting to mount anything.

`mount-zfs.sh`
------------

This script is run after udev has settled and all tasks in the initqueue have succeeded. This ensures that `/dev/zfs` is available and that the various ZFS modules are successfully loaded. As it is now safe to call zpool & friends, we can proceed to find the bootfs attribute if necessary.

If the root parameter was explicitly set on the command line, no parsing is necessary. The list of imported pools is checked to see if the desired pool is already imported. If it's not, and attempt is made to import the pool explicitly, though no force is attempted. Finally the specified dataset is mounted on `$NEWROOT`, first using the `-o zfsutil` option to handle non-legacy mounts, then if that fails, without zfsutil to handle legacy mount points.

If no root parameter was specified, this script attempts to find a pool with its bootfs attribute set. First, already-imported pools are scanned and if an appropriate pool is found, no additional pools are imported. If no pool with bootfs is found, any additional pools in the system are imported with `zpool import -N -a`, and the scan for bootfs is tried again. If no bootfs is found with all pools imported, all pools are re-exported, and boot fails. Assuming a bootfs is found, an attempt is made to mount it to `$NEWROOT`, first with, then without the zfsutil option as above.

Ordinarily pools are imported _without_ the force option which may cause boot to fail if the hostid has changed or a pool has been physically moved between servers. The `zfs_force` kernel parameter is provided which when set to `1` causes `zpool import` to be run with the `-f` flag. Forcing pool import can lead to serious data corruption and loss of pools, so this option should be used with extreme caution. Note that even with this flag set, if the required zpool was auto-imported by the kernel module, no additional `zpool import` commands are run, so nothing is forced.




18 changes: 9 additions & 9 deletions etc/init.d/zfs.gentoo
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@

depend()
{
before net
# bootmisc will log to /var which may be a different zfs than root.
before net bootmisc
after udev
keyword -lxc -openvz -prefix -vserver
}

CACHEFILE=/etc/zfs/zpool.cache
ZPOOL=/usr/sbin/zpool
ZFS=/usr/sbin/zfs
ZPOOL=/sbin/zpool
ZFS=/sbin/zfs
ZFS_MODULE=zfs

checksystem() {
if [ -c /dev/zfs ]; then
einfo "ZFS modules already loaded"
return 0
else
if [ ! -c /dev/zfs ]; then
einfo "Checking if ZFS modules present"
if [ "x$(modprobe -l $ZFS_MODULE | grep $ZFS_MODULE)" == "x" ]; then
eerror "$ZFS_MODULE not found. Is the ZFS package installed?"
Expand Down Expand Up @@ -89,10 +87,12 @@ stop()
$ZFS umount -a
rv=$?
if [ $rv -ne 0 ]; then
eerror "Failed to umount ZFS filesystems."
einfo "Some ZFS filesystems not unmounted"
fi

eend $rv
# Don't fail if we couldn't umount everything. /usr might be in use.
eend 0
return 0
}

status()
Expand Down