diff options
author | Brian Behlendorf <[email protected]> | 2015-07-09 11:41:14 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2015-07-09 13:59:37 -0700 |
commit | cc49250563b65c80d87afa5273ae350d06aa8d3b (patch) | |
tree | 9fe3928c6279f518c82d0f5fd8a813e8a3ed7d75 /contrib | |
parent | 2cac7f5f11756663525a5d4604d9f0a3202d4024 (diff) |
Move dracut directory to contrib
The dracut code is analogous to the initramfs code and as such
it should be located in the contrib with initramfs for consistency.
Signed-off-by: Brian Behlendorf <[email protected]>
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/Makefile.am | 4 | ||||
-rw-r--r-- | contrib/dracut/90zfs/.gitignore | 5 | ||||
-rw-r--r-- | contrib/dracut/90zfs/Makefile.am | 25 | ||||
-rwxr-xr-x | contrib/dracut/90zfs/export-zfs.sh.in | 29 | ||||
-rwxr-xr-x | contrib/dracut/90zfs/module-setup.sh.in | 61 | ||||
-rwxr-xr-x | contrib/dracut/90zfs/mount-zfs.sh.in | 44 | ||||
-rwxr-xr-x | contrib/dracut/90zfs/parse-zfs.sh.in | 59 | ||||
-rwxr-xr-x | contrib/dracut/90zfs/zfs-lib.sh.in | 87 | ||||
-rw-r--r-- | contrib/dracut/Makefile.am | 3 | ||||
-rw-r--r-- | contrib/dracut/README.dracut.markdown | 207 |
10 files changed, 522 insertions, 2 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 4a13cfa98..b05e5c45b 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = bash_completion.d initramfs -DIST_SUBDIRS = bash_completion.d initramfs +SUBDIRS = bash_completion.d dracut initramfs +DIST_SUBDIRS = bash_completion.d dracut initramfs diff --git a/contrib/dracut/90zfs/.gitignore b/contrib/dracut/90zfs/.gitignore new file mode 100644 index 000000000..9502be985 --- /dev/null +++ b/contrib/dracut/90zfs/.gitignore @@ -0,0 +1,5 @@ +export-zfs.sh +module-setup.sh +mount-zfs.sh +parse-zfs.sh +zfs-lib.sh diff --git a/contrib/dracut/90zfs/Makefile.am b/contrib/dracut/90zfs/Makefile.am new file mode 100644 index 000000000..30880bfc5 --- /dev/null +++ b/contrib/dracut/90zfs/Makefile.am @@ -0,0 +1,25 @@ +pkgdracutdir = $(dracutdir)/modules.d/90zfs +pkgdracut_SCRIPTS = \ + $(top_srcdir)/contrib/dracut/90zfs/export-zfs.sh \ + $(top_srcdir)/contrib/dracut/90zfs/module-setup.sh \ + $(top_srcdir)/contrib/dracut/90zfs/mount-zfs.sh \ + $(top_srcdir)/contrib/dracut/90zfs/parse-zfs.sh \ + $(top_srcdir)/contrib/dracut/90zfs/zfs-lib.sh + +EXTRA_DIST = \ + $(top_srcdir)/contrib/dracut/90zfs/export-zfs.sh.in \ + $(top_srcdir)/contrib/dracut/90zfs/module-setup.sh.in \ + $(top_srcdir)/contrib/dracut/90zfs/mount-zfs.sh.in \ + $(top_srcdir)/contrib/dracut/90zfs/parse-zfs.sh.in \ + $(top_srcdir)/contrib/dracut/90zfs/zfs-lib.sh.in + +$(pkgdracut_SCRIPTS): + -$(SED) -e 's,@bindir\@,$(bindir),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@udevdir\@,$(udevdir),g' \ + -e 's,@udevruledir\@,$(udevruledir),g' \ + -e 's,@sysconfdir\@,$(sysconfdir),g' \ + '[email protected]' >'$@' + +distclean-local:: + -$(RM) $(pkgdracut_SCRIPTS) diff --git a/contrib/dracut/90zfs/export-zfs.sh.in b/contrib/dracut/90zfs/export-zfs.sh.in new file mode 100755 index 000000000..393753fbf --- /dev/null +++ b/contrib/dracut/90zfs/export-zfs.sh.in @@ -0,0 +1,29 @@ +#!/bin/sh + +. /lib/dracut-zfs-lib.sh + +_do_zpool_export() { + local ret=0 + local final="${1}" + local opts="" + + if [ "x${final}" != "x" ]; then + opts="-f" + fi + + info "Exporting ZFS storage pools." + export_all ${opts} || ret=$? + + if [ "x${final}" != "x" ]; then + info "zpool list" + zpool list 2>&1 | vinfo + fi + + return ${ret} +} + +if command -v zpool >/dev/null; then + _do_zpool_export "${1}" +else + : +fi diff --git a/contrib/dracut/90zfs/module-setup.sh.in b/contrib/dracut/90zfs/module-setup.sh.in new file mode 100755 index 000000000..9eb9f5765 --- /dev/null +++ b/contrib/dracut/90zfs/module-setup.sh.in @@ -0,0 +1,61 @@ +#!/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 @udevruledir@/90-zfs.rules + inst_rules @udevruledir@/69-vdev.rules + inst_rules @udevruledir@/60-zvol.rules + dracut_install @sbindir@/zfs + dracut_install @sbindir@/zpool + dracut_install @udevdir@/vdev_id + dracut_install @udevdir@/zvol_id + dracut_install mount.zfs + dracut_install hostid + dracut_install awk + dracut_install head + inst_hook cmdline 95 "${moddir}/parse-zfs.sh" + inst_hook mount 98 "${moddir}/mount-zfs.sh" + inst_hook shutdown 30 "${moddir}/export-zfs.sh" + + inst_simple "${moddir}/zfs-lib.sh" "/lib/dracut-zfs-lib.sh" + if [ -e @sysconfdir@/zfs/zpool.cache ]; then + inst @sysconfdir@/zfs/zpool.cache + fi + + if [ -e @sysconfdir@/zfs/vdev_id.conf ]; then + inst @sysconfdir@/zfs/vdev_id.conf + fi + + # Synchronize initramfs and system hostid + AA=`hostid | cut -b 1,2` + BB=`hostid | cut -b 3,4` + CC=`hostid | cut -b 5,6` + DD=`hostid | cut -b 7,8` + printf "\x${DD}\x${CC}\x${BB}\x${AA}" > "${initdir}/etc/hostid" +} diff --git a/contrib/dracut/90zfs/mount-zfs.sh.in b/contrib/dracut/90zfs/mount-zfs.sh.in new file mode 100755 index 000000000..2abc8766b --- /dev/null +++ b/contrib/dracut/90zfs/mount-zfs.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh + +. /lib/dracut-zfs-lib.sh + +ZFS_DATASET="" +ZFS_POOL="" + +case "${root}" in + zfs:*) ;; + *) return ;; +esac + +# Delay until all required block devices are present. +udevadm settle + +if [ "${root}" = "zfs:AUTO" ] ; then + ZFS_DATASET="$(find_bootfs)" + if [ $? -ne 0 ] ; then + zpool import -N -a ${ZPOOL_IMPORT_OPTS} + ZFS_DATASET="$(find_bootfs)" + if [ $? -ne 0 ] ; then + warn "ZFS: No bootfs attribute found in importable pools." + export_all || export_all "-f" + + rootok=0 + return 1 + fi + fi + info "ZFS: Using ${ZFS_DATASET} as root." +fi + +ZFS_DATASET="${ZFS_DATASET:-${root#zfs:}}" +ZFS_POOL="${ZFS_DATASET%%/*}" + +if import_pool "${ZFS_POOL}" ; then + info "ZFS: Mounting dataset ${ZFS_DATASET}..." + if mount_dataset "${ZFS_DATASET}" ; then + ROOTFS_MOUNTED=yes + return 0 + fi +fi + +rootok=0 +need_shutdown diff --git a/contrib/dracut/90zfs/parse-zfs.sh.in b/contrib/dracut/90zfs/parse-zfs.sh.in new file mode 100755 index 000000000..c305c7821 --- /dev/null +++ b/contrib/dracut/90zfs/parse-zfs.sh.in @@ -0,0 +1,59 @@ +#!/bin/sh + +. /lib/dracut-lib.sh + +# Let the command line override our host id. +spl_hostid=`getarg spl_hostid=` +if [ -n "${spl_hostid}" ] ; then + info "ZFS: Using hostid from command line: ${spl_hostid}" + AA=`echo ${spl_hostid} | cut -b 1,2` + BB=`echo ${spl_hostid} | cut -b 3,4` + CC=`echo ${spl_hostid} | cut -b 5,6` + DD=`echo ${spl_hostid} | cut -b 7,8` + printf "\x${DD}\x${CC}\x${BB}\x${AA}" >/etc/hostid +elif [ -f "/etc/hostid" ] ; then + info "ZFS: Using hostid from /etc/hostid: `hostid`" +else + warn "ZFS: No hostid found on kernel command line or /etc/hostid." + warn "ZFS: Pools may not import correctly." +fi + +wait_for_zfs=0 +case "${root}" in + ""|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 + wait_for_zfs=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 + wait_for_zfs=1 + + info "ZFS: Set ${root} as bootfs." + ;; +esac + +# Make sure Dracut is happy that we have a root and will wait for ZFS +# modules to settle before mounting. +if [ ${wait_for_zfs} -eq 1 ]; then + ln -s /dev/null /dev/root 2>/dev/null + echo '[ -e /dev/zfs ]' > "${hookdir}/initqueue/finished/zfs.sh" +fi diff --git a/contrib/dracut/90zfs/zfs-lib.sh.in b/contrib/dracut/90zfs/zfs-lib.sh.in new file mode 100755 index 000000000..1c223befd --- /dev/null +++ b/contrib/dracut/90zfs/zfs-lib.sh.in @@ -0,0 +1,87 @@ +#!/bin/sh + +command -v getarg >/dev/null || . /lib/dracut-lib.sh + +OLDIFS="${IFS}" +NEWLINE=" +" + +ZPOOL_IMPORT_OPTS="" +if getargbool 0 zfs_force -y zfs.force -y zfsforce ; then + warn "ZFS: Will force-import pools if necessary." + ZPOOL_IMPORT_OPTS="${ZPOOL_IMPORT_OPTS} -f" +fi + +# find_bootfs +# returns the first dataset with the bootfs attribute. +find_bootfs() { + IFS="${NEWLINE}" + for dataset in $(zpool list -H -o bootfs); do + case "${dataset}" in + "" | "-") + continue + ;; + "no pools available") + IFS="${OLDIFS}" + return 1 + ;; + *) + IFS="${OLDIFS}" + echo "${dataset}" + return 0 + ;; + esac + done + + IFS="${OLDIFS}" + return 1 +} + +# import_pool POOL +# imports the given zfs pool if it isn't imported already. +import_pool() { + local pool="${1}" + + if ! zpool list -H "${pool}" 2>&1 > /dev/null ; then + info "ZFS: Importing pool ${pool}..." + if ! zpool import -N ${ZPOOL_IMPORT_OPTS} "${pool}" ; then + warn "ZFS: Unable to import pool ${pool}" + return 1 + fi + fi + + return 0 +} + +# mount_dataset DATASET +# mounts the given zfs dataset. +mount_dataset() { + local dataset="${1}" + local mountpoint="$(zfs get -H -o value mountpoint "${dataset}")" + + # We need zfsutil for non-legacy mounts and not for legacy mounts. + if [ "${mountpoint}" = "legacy" ] ; then + mount -t zfs "${dataset}" "${NEWROOT}" + else + mount -o zfsutil -t zfs "${dataset}" "${NEWROOT}" + fi + + return $? +} + +# export_all OPTS +# exports all imported zfs pools. +export_all() { + local opts="${1}" + local ret=0 + + IFS="${NEWLINE}" + for pool in `zpool list -H -o name` ; do + if zpool list -H "${pool}" 2>&1 > /dev/null ; then + zpool export "${pool}" ${opts} || ret=$? + fi + done + IFS="${OLDIFS}" + + return ${ret} +} diff --git a/contrib/dracut/Makefile.am b/contrib/dracut/Makefile.am new file mode 100644 index 000000000..35b88c36f --- /dev/null +++ b/contrib/dracut/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = 90zfs + +EXTRA_DIST = README.dracut.markdown diff --git a/contrib/dracut/README.dracut.markdown b/contrib/dracut/README.dracut.markdown new file mode 100644 index 000000000..46d032f70 --- /dev/null +++ b/contrib/dracut/README.dracut.markdown @@ -0,0 +1,207 @@ +How to setup a zfs root filesystem using dracut +----------------------------------------------- + +1) Install the zfs-dracut package. This package adds a zfs dracut module +to the /usr/share/dracut/modules.d/ directory which allows dracut to +create an initramfs which is zfs aware. + +2) Set the bootfs property for the bootable dataset in the pool. Then set +the dataset mountpoint property to '/'. + + $ zpool set bootfs=pool/dataset pool + $ zfs set mountpoint=/ pool/dataset + +It is also possible to set the bootfs property for an entire pool, just in +case you are not using a dedicated dataset for '/'. + + $ zpool set bootfs=pool pool + $ zfs set mountpoint=/ pool + +Alternately, legacy mountpoints can be used by setting the 'root=' option +on the kernel line of your grub.conf/menu.lst configuration file. Then +set the dataset mountpoint property to 'legacy'. + + $ grub.conf/menu.lst: kernel ... root=ZFS=pool/dataset + $ zfs set mountpoint=legacy pool/dataset + +3) To set zfs module options put them in /etc/modprobe.d/zfs.conf file. +The complete list of zfs module options is available by running the +_modinfo zfs_ command. Commonly set options include: zfs_arc_min, +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=0x00bab10c`. + +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 +and 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. + +* `zfs-lib.sh`: Utility functions used by the other files. + +* `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. + +* `export-zfs.sh`: Run on shutdown after dracut has restored the initramfs +and pivoted to it, allowing for a clean unmount and export of the ZFS root. + +`zfs-lib.sh` +------------ + +This file provides a few handy functions for working with ZFS. Those +functions are used by the `mount-zfs.sh` and `export-zfs.sh` files. +However, they could be used by any other file as well, as long as the file +sources `/lib/dracut-zfs-lib.sh`. + +`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 and 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 and 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. + +`export-zfs.sh` +--------------- + +Normally the zpool containing the root dataset cannot be exported on +shutdown as it is still in use by the init process. To work around this, +Dracut is able to restore the initramfs on shutdown and pivot to it. +All remaining process are then running from a ramdisk, allowing for a +clean unmount and export of the ZFS root. The theory of operation is +described in detail in the [Dracut manual](https://www.kernel.org/pub/linux/utils/boot/dracut/dracut.html#_dracut_on_shutdown). + +This script will try to export all remaining zpools after Dracut has +pivoted to the initramfs. If an initial regular export is not successful, +Dracut will call this script once more with the `final` option, +in which case a forceful export is attempted. + +Other Dracut modules include similar shutdown scripts and Dracut +invokes these scripts round-robin until they succeed. In particular, +the `90dm` module installs a script which tries to close and remove +all device mapper targets. Thus, if there are ZVOLs containing +dm-crypt volumes or if the zpool itself is backed by a dm-crypt +volume, the shutdown scripts will try to untangle this. |