Created
October 4, 2013 14:43
-
-
Save rosstimson/6827095 to your computer and use it in GitHub Desktop.
Makefile that automates installation of FreeBSD on ZFS root (compatible with BEADM boot environments).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ---- Makefile for Vermaden's FreeBSD 9.1 root on ZFS manual install | |
# $Id: Makefile,v 1.18 2013/08/07 13:31:29 root Exp root $ | |
# | |
# Copyright (c) 2013 Adriaan van Roosmalen <j65nko daemonforums.org>> | |
# | |
# Permission to use, copy, modify, and distribute this software for any | |
# purpose with or without fee is hereby granted, provided that the above | |
# copyright notice and this permission notice appear in all copies. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
# | |
# This BSD makefile merely automates Vermaden's "'ZFS madness / boot | |
# environments" mirrored disk setup. | |
# | |
# For a complete description of this particular ZFS only setup see below. | |
# | |
# The ZFS configuration described by Vermaden does not align the | |
# disk partitions for 'Avanced Format' 4K disks and also does not | |
# instruct ZFS to use 4K blocks for the write and read operations. | |
# | |
# The procedure implemented here does align the EFI/gpt partitions | |
# used on a 4K (8 x 512) # sector boundary. By using the 'gnop' trick | |
# the FreeBSD ZFS implementation is coached to use an 'ashift' value | |
# of 12 (2^12 = 4096) | |
# This value makes ZFS read and write on 4K boundaries | |
# | |
# Credits for the ZFS install procedure: | |
# | |
# Slawomir Wojciech Wojtczak (vermaden) | |
# http://forums.freebsd.org/showthread.php?t=31662 | |
# http://www.daemonforums.org/showthread.php?t=7099 | |
# | |
# Sources and credits for the 4K alignment issue and gnop hack: | |
# | |
# Warren Block (wblock@) for his numerous posts about partition alignment: | |
# http://forums.freebsd.org (Installation and Storage sections) | |
# http://www.wonkity.com/~wblock/docs/html/disksetup.html#_the_new_standard_gpt | |
# Ivan Voras for the gnop workaround: | |
# http://ivoras.sharanet.org/blog/tree/2011-01-01.freebsd-on-4k-sector-drives.html | |
# George Kontostanos (gkontos) for using gnop and a ZFS cache file: | |
# http://forums.freebsd.org/showthread.php?t=23544 | |
# http://www.aisecure.net/2012/01/16/rootzfs/ | |
# | |
# --- variables start | |
#DEBUG = echo | |
POOL = rpool | |
ROOT_SET = ${POOL}/ROOT | |
BOOT_SET = ${ROOT_SET}/default | |
# --- installation file sets | |
SETS = base.txz kernel.txz | |
DIR = /usr/freebsd-dist # On FreeBSD liveCD/DVD/memstick | |
.for X in ${SETS} | |
INSTALL_SETS += ${DIR}/${X} | |
.endfor | |
DISKTYPE = ada | |
DISKTYPE = md | |
.if ${DISKTYPE} == "md" | |
# --- memory disks see md(4) and mdconfig(8) | |
# size of disk needs to be at least 64M else you encounter this error: | |
# "cannot create 'pool': one or more devices is less than the minimum size (64M)" | |
MD_SIZE = 2g | |
SWAPSIZE = 256m | |
DISK_1 = /dev/md1 | |
DISK_2 = /dev/md2 | |
DISKS = ${DISK_1} ${DISK_2} | |
# --- gpt/EFI labels | |
LABEL_ZFS = mdisk_ | |
LABEL_BOOT = mdboot | |
.else | |
# --- real spinning rust disks | |
DISK_1 = /dev/ada1 | |
DISK_2 = /dev/ada2 | |
DISKS = ${DISK_1} ${DISK_2} | |
SWAPSIZE = 4G | |
# --- gpt/EFI labels | |
LABEL_ZFS = SYSTEM_ | |
LABEL_BOOT = BOOT_ | |
.endif | |
TMP = /tmp | |
CACHEFILE = ${TMP}/zpool.cache | |
TEMPLATE_RC_CONF= ${TMP}/template_rc.conf | |
MOUNT = /mnt | |
RC_CONF = ${MOUNT}/etc/rc.conf | |
LOADER_CONF = ${MOUNT}/boot/loader.conf | |
FSTAB = ${MOUNT}/etc/fstab | |
# --- variables end | |
show: | |
@echo ------------------------- | |
@echo Current variable settings | |
@echo ------------------------- | |
@printf "%-20s : [%s]\n" DEBUG "${DEBUG}" | |
@printf "%-20s : [%s]\n" POOL "${POOL}" | |
@printf "%-20s : [%s]\n" ROOT_SET "${ROOT_SET}" | |
@printf "%-20s : [%s]\n" BOOT_SET "${BOOT_SET}" | |
@printf "%-20s : [%s]\n" SETS "${SETS}" | |
@printf "%-20s : [%s]\n" DIR "${DIR}" | |
@printf "%-20s : [%s]\n" INSTALL_SETS "${INSTALL_SETS}" | |
@printf "%-20s : [%s]\n" DISKTYPE "${DISKTYPE}" | |
@printf "%-20s : [%s]\n" MD_SIZE "${MD_SIZE}" | |
@printf "%-20s : [%s]\n" SWAPSIZE "${SWAPSIZE}" | |
@printf "%-20s : [%s]\n" DISK_1 "${DISK_1}" | |
@printf "%-20s : [%s]\n" DISK_2 "${DISK_2}" | |
@printf "%-20s : [%s]\n" DISKS "${DISKS}" | |
@printf "%-20s : [%s]\n" LABEL_ZFS "${LABEL_ZFS}" | |
@printf "%-20s : [%s]\n" LABEL_BOOT "${LABEL_BOOT}" | |
@printf "%-20s : [%s]\n" SWAPSIZE "${SWAPSIZE}" | |
@printf "%-20s : [%s]\n" TMP "${TMP}" | |
@printf "%-20s : [%s]\n" CACHEFILE "${CACHEFILE}" | |
@printf "%-20s : [%s]\n" MOUNT "${MOUNT}" | |
@printf "%-20s : [%s]\n" TEMPLATE_RC_CONF "${TEMPLATE_RC_CONF}" | |
@printf "%-20s : [%s]\n" RC_CONF "${RC_CONF}" | |
@printf "%-20s : [%s]\n" LOADER_CONF "${LOADER_CONF}" | |
@printf "%-20s : [%s]\n" FSTAB "${FSTAB}" | |
@echo ------------------------- | |
@echo "Checking for installation sets ..." | |
.for X in ${INSTALL_SETS} | |
@printf "Set $X : " | |
@if [ -f ${X} ] ; then echo found! ; else echo missing! ; fi | |
.endfor | |
@echo "Checking for 'rc.conf' template ..." | |
.if exists(${TEMPLATE_RC_CONF}) | |
@echo ----------- rc.conf template ---------- | |
cat ${TEMPLATE_RC_CONF} | |
.else | |
@echo ${TEMPLATE_RC_CONF} not found | |
exit 100 | |
.endif | |
# ----------------------------------------------------------------------------- | |
# WARNING: you can 'make' all targets except the 'trailer' target!! | |
# 'trailer' is a makefile macro and only works as prerequisite of other targets | |
# Scroll down to the end of the Makefile and read the reason why ..... | |
# ----------------------------------------------------------------------------- | |
diskinfo: trailer | |
.for X in ${DISKS} | |
if [ -e ${X} ] ; then diskinfo -v ${X} ; fi | |
.endfor | |
# --- Load FreeBSD ZFS kernel modules | |
zfsload: trailer | |
kldload -n zfs | |
kldload -n opensolaris | |
kldstat | |
# --------- using make '.for .. in ... .endfor' construct | |
# make variables you can refer to with a single '$' as in ${X} | |
# for shell variables you must use a double $$ as $${NR} | |
partition: trailer | |
.for X in ${DISKS} | |
if gpart show ${X} ; then gpart destroy -F ${X} ; fi | |
gpart create -s gpt ${X} | |
NR=$$( echo ${X} | tr -c -d '0-9' ) ;\ | |
gpart add -b 40 -s 128k -t freebsd-boot -l ${LABEL_BOOT}$${NR} ${X} ;\ | |
gpart add -t freebsd-zfs -l ${LABEL_ZFS}$${NR} ${X} | |
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ${X} | |
gpart show ${X} | |
.endfor | |
ls -l /dev/gpt | |
# --- Procedure to coach ZFS to use 4K sector aligned read/writes | |
gnop4k: trailer | |
@echo Creating gnop devices with 4K sectors ..... | |
for X in ${DISKS} ; do \ | |
NR=$$( echo $${X} | tr -c -d '0-9' ) ;\ | |
gnop create -S 4096 /dev/gpt/${LABEL_ZFS}$${NR} ;\ | |
done | |
ls -l /dev/gpt | |
gnop list | |
gnop status | |
pool4k: trailer | |
@echo Creating zpool with 4K gnop devices | |
if [ -f ${CACHEFILE} ] ; then mv ${CACHEFILE} ${CACHEFILE}.prev ; fi | |
ls -l /dev/gpt | |
# ---- creating a ZFS mirror pool without automatically mounting it (-m none) ..... | |
zpool create -f -o cachefile=${CACHEFILE} -m none ${POOL} mirror /dev/gpt/${LABEL_ZFS}*nop | |
zpool list ${POOL} | |
zpool status ${POOL} | |
zfs list | |
export: trailer | |
@echo Exporting ${POOL} ...... | |
@echo --------------------------------- | |
zpool export ${POOL} | |
@echo --------------------------------- | |
@echo Showing import status of ${POOL} ...... | |
@echo --------------------------------- | |
zpool import | |
gnop_destroy: trailer | |
@echo Destroy the 4K gnop devices | |
for X in ${DISKS} ; do \ | |
NR=$$( echo $${X} | tr -c -d '0-9' ) ;\ | |
gnop destroy /dev/gpt/${LABEL_ZFS}$${NR}.nop ;\ | |
done | |
ls -l /dev/gpt | |
for X in ${DISKS} ; do gpart show $${X} ; done | |
import: trailer | |
@echo Import the pool | |
zpool import -o cachefile=${CACHEFILE} ${POOL} | |
zpool list ${POOL} | |
@echo --------------------------------- | |
zpool status ${POOL} | |
@echo --------------------------------- | |
mount | |
chk_ashift: trailer | |
@echo "Verify that ashift value is 12 (2^12 = 4096 ; 2^9 = 512)" | |
@echo ========================================================= | |
zdb -C -U ${CACHEFILE} ${POOL} | |
@echo ==================================== | |
zdb -C -U ${CACHEFILE} ${POOL} | grep ashift | |
# --- Having a 4K aligned pool we now continue with the Vermaden 'ZFS madness' procedure | |
zfs_options: trailer | |
zfs set mountpoint=none ${POOL} | |
zfs set checksum=fletcher4 ${POOL} | |
zfs set atime=off ${POOL} | |
zpool list ${POOL} | |
zpool status ${POOL} | |
zfs list | |
zfs_fs: trailer | |
@echo --------------------------------- | |
zfs create ${ROOT_SET} | |
zfs create -o mountpoint=${MOUNT} ${BOOT_SET} | |
zpool set bootfs=${BOOT_SET} ${POOL} | |
zfs list | |
@echo --------------------------------- | |
mount | |
# --- alternatively you can configure ZFS swap after reboot | |
zfs_swap: trailer | |
zfs create -V ${SWAPSIZE} ${POOL}/swap | |
zfs set org.freebsd:swap=on ${POOL}/swap | |
zfs set checksum=off ${POOL}/swap | |
zfs set sync=disabled ${POOL}/swap | |
zfs set primarycache=none ${POOL}/swap | |
zfs set secondarycache=none ${POOL}/swap | |
zfs list | |
# ========================================================== | |
pre_install: trailer partition gnop4k pool4k export gnop_destroy import \ | |
chk_ashift zfs_options zfs_fs zfs_swap | |
install: trailer | |
for THIS in ${INSTALL_SETS} ; do \ | |
tar --unlink -xvpJf $${THIS} -C ${MOUNT} ;\ | |
done | |
ls -l ${MOUNT} | |
zpool status ${POOL} | |
zpool iostat ${POOL} | |
zfs list | |
post_install: trailer loader.conf fstab rc.conf zfs_boot zfs_umount mountpoint | |
all: trailer pre_install install post_install | |
# ========================================================== | |
loader.conf: trailer | |
echo 'zfs_load=YES' >> ${LOADER_CONF} | |
echo 'vfs.root.mountfrom="zfs:${BOOT_SET}"' >> ${LOADER_CONF} | |
@echo =========== loader.conf ========================= | |
@cat ${LOADER_CONF} | |
fstab: trailer | |
# ---- create empty /etc/fstab file | |
touch ${FSTAB} | |
rc.conf: trailer | |
cat ${TEMPLATE_RC_CONF} >> ${RC_CONF} | |
echo 'zfs_enable="YES"' >> ${RC_CONF} | |
@echo =========== rc.conf ========================= | |
@cat ${RC_CONF} | |
zfs_boot: trailer | |
if [ ! -d ${MOUNT}/boot/zfs ] ; then echo Cannot copy ${CACHEFILE} to ${MOUNT}/boot/zfs ! ; exit 2 ; fi | |
cp -p ${CACHEFILE} ${MOUNT}/boot/zfs/ | |
ls -l ${MOUNT}/boot/zfs | |
zfs_umount: trailer | |
zfs unmount -a | |
zfs list | |
@echo ------ | |
mount | |
mountpoint: trailer | |
ls -l ${TMP} | |
zfs set mountpoint=legacy ${BOOT_SET} | |
zfs list | |
@echo Setup is finished ............ | |
@echo ----------------- done ! --------------------------------- | |
@echo you can now reboot into your new ZFS only system now ..... | |
@echo ---------------------------------------------------------- | |
@echo Or you could finish the elementary setup with: | |
@echo | |
@echo "1. Remount your ZFS file system : mount -t zfs ${BOOT_SET} ${MOUNT}" | |
@echo "2. Chroot into ${MOUNT} : chroot ${MOUNT}" | |
@echo "3. Change the root password : passwd" | |
@echo "4. Create the /etc/mail/aliases.db : newaliases" | |
@echo "5. Set up your time zone : tzsetup" | |
@echo | |
@echo "6. Exit the chroot : exit" | |
@echo "7. Unmount : umount ${MOUNT}" | |
# ====================== U t i l i t i e s ================================ | |
# --- if something went wrong with the pool/zfs creation you clean up with: | |
pool_destroy: trailer | |
zfs destroy -r ${POOL} | |
zfs list | |
zpool destroy ${POOL} | |
zpool status | |
zfs list | |
# --- destroy gpart partition scheme and zero out first 256 MB of disk(s) | |
gpart_destroy: trailer | |
.for X in ${DISKS} | |
if gpart show ${X} ; then gpart destroy -F ${X} ; fi | |
dd if=/dev/zero of=${X} bs=1m count=256 | |
.endfor | |
# --- create FreeBSD memory disks (for testing Makefile modifications) | |
md_create: trailer | |
@${DEBUG} echo Creating Memory disk devices ${DISKS}: | |
@${DEBUG} mdconfig -a -t swap -s ${MD_SIZE} -u 1 | |
@${DEBUG} mdconfig -a -t swap -s ${MD_SIZE} -u 2 | |
@${DEBUG} echo Memory disk devices: | |
@${DEBUG} mdconfig -lv -u 1 | |
@${DEBUG} mdconfig -lv -u 2 | |
@${DEBUG} ls -l /dev/md* | |
md_destroy: trailer | |
@${DEBUG} mdconfig -d -u 1 | |
@${DEBUG} mdconfig -d -u 2 | |
@${DEBUG} echo Memory disk devices: | |
@${DEBUG} ls -l /dev/md* | |
# --- don't make the target 'trailer'. I once tried for fun and ran out of swap space! | |
# From 'dmesg' | |
# swap_pager: out of swap space | |
# swap_pager_getswapspace(16): failed | |
# pid 1812 (make), uid 0, was killed: out of swap space | |
trailer: .USE | |
@echo -------------end of ${.TARGET} -------------------- | |
.PHONY: show diskinfo zfsload md_create md_destroy partition gnop4k pool4k export | |
.PHONY: gnop_destroy import chk_ashift zfs_options zfs_fs zfs_swap install | |
.PHONY: loader.conf fstab rc.conf zfs_boot zfs_umount mountpoint pool_destroy | |
.PHONY: gpart_destroy pre_install post_install all trailer | |
# ---- end of Makefile |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment