Re: git: 4a30d7bb373c - main - growfs script: add swap partition as well as growing root

From: Mike Karels <mike_at_karels.net>
Date: Sun, 11 Dec 2022 13:26:46 UTC
On 11 Dec 2022, at 4:09, Gordon Bergling wrote:

> Hi Mike,
>
> thanks for implementation. After the MFC and the new -STABLE snapshots
> I am really looking forward to try this out an my RPi4B.

You are welcome, but I hadn’t decided yet whether this should be MFC’d.
That depends in part on how it seems to work out, but I am interested
in opinions on that.

		Mike

> --Gordon
>
> On Sat, Dec 10, 2022 at 07:41:13PM +0000, Mike Karels wrote:
>> The branch main has been updated by karels:
>>
>> URL: https://cgit.FreeBSD.org/src/commit/?id=4a30d7bb373c08f42f953b9cd1e793e236b4cd92
>>
>> commit 4a30d7bb373c08f42f953b9cd1e793e236b4cd92
>> Author:     Mike Karels <karels@FreeBSD.org>
>> AuthorDate: 2022-12-10 19:38:36 +0000
>> Commit:     Mike Karels <karels@FreeBSD.org>
>> CommitDate: 2022-12-10 19:38:36 +0000
>>
>>     growfs script: add swap partition as well as growing root
>>
>>     Add the ability to create a swap partition in the course of growing
>>     the root file system on first boot, enabling by default.  The default
>>     rules are: add swap if the disk is at least 15 GB (decimal), and the
>>     existing root is less than 40% of the disk.  The default size is 10%
>>     of the disk, but is limited by the memory size.  The limit is twice
>>     memory size up to 4 GB, 8 GB up to 8 GB memory, and memory size over
>>     8 GB memory. Swap size is clamped at vm.swap_maxpages/2 as well.
>>     The new swap partition is labeled as "growfs_swap".
>>
>>     The default behavior can be overridden by setting growfs_swap_size in
>>     /etc/rc.conf or in the kernel environment, with kenv taking priority.
>>     A value of 0 inhibits the addition of swap, an empty value specifies
>>     the default, and other values indicate a swap size in bytes.
>>
>>     By default, addition of swap is inhibited if a swap partition is found
>>     in the output of the sysctl kern.geom.conftxt before the current root
>>     partition, usually meaning that there is another disk present.
>>     Swap space is not added if one is already present in /etc/fstab.
>>
>>     The root partition is read-only when growfs runs, so /etc/fstab can
>>     not be modified.  That step is handled by a new growfs_fstab script,
>>     added in a separate commit.  Set the value "growfs_swap_pdev" in kenv
>>     to indicate that this should be done, as well as for internal use.
>>
>>     There is optional verbose output meant for debugging; it can only be
>>     enabled by modifying the script (in two places, for sh and awk).
>>     This should be removed before release, after testing on -current.
>>
>>     Discussed with: cperciva
>>     Reviewed by:    imp (previous version)
>>     Differential Revision:  https://reviews.freebsd.org/D37462
>> ---
>>  libexec/rc/rc.d/growfs | 178 +++++++++++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 172 insertions(+), 6 deletions(-)
>>
>> diff --git a/libexec/rc/rc.d/growfs b/libexec/rc/rc.d/growfs
>> index 5402bd442279..3c48a7dca6b2 100755
>> --- a/libexec/rc/rc.d/growfs
>> +++ b/libexec/rc/rc.d/growfs
>> @@ -1,5 +1,6 @@
>>  #!/bin/sh
>>  #
>> +# Copyright 2022 Michael J. Karels
>>  # Copyright 2014 John-Mark Gurney
>>  # All rights reserved.
>>  #
>> @@ -32,8 +33,9 @@
>>  # BEFORE: root
>>  # KEYWORD: firstboot
>>
>> -# This allows us to distribute an image
>> -# and have it work on essentially any size drive.
>> +# Grow root partition to fill available space, optionally adding a swap
>> +# partition at the end.  This allows us to distribute an image and
>> +# have it work on essentially any size drive.
>>
>>  # Note that this uses awk(1), and thus will not work if /usr is on a separate
>>  # filesystem.  We need to run early, because there might be not enough free
>> @@ -48,7 +50,7 @@ start_cmd="growfs_start"
>>  stop_cmd=":"
>>  rcvar="growfs_enable"
>>
>> -growfs_get_diskdev ()
>> +growfs_get_diskdev()
>>  {
>>  	local _search=${1}
>>  	sysctl -b kern.geom.conftxt |
>> @@ -61,8 +63,51 @@ growfs_get_diskdev ()
>>  	done
>>  }
>>
>> -growfs_start ()
>> +# Compute upper bound on swap partition size (if added), based on physmem
>> +# and vm.swap_maxpages / 2 (the limit that elicits a warning).
>> +# Rule for swap size based on memory size:
>> +#	up to 4 GB	twice memory size
>> +#	4 GB - 8 GB	8 GB
>> +#	over 8 GB	memory size
>> +growfs_swap_max()
>>  {
>> +	memsize=$(sysctl -n hw.physmem)
>> +	memsizeMB=$(($memsize / (1024 * 1024)))
>> +
>> +	if  [ $memsizeMB -lt 4096 ]
>> +	then
>> +		swapmax=$(($memsize * 2))
>> +	elif  [ $memsizeMB -lt 8192 ]
>> +	then
>> +		swapmax=$((8192 * 1024 * 1024))
>> +	else
>> +		swapmax=$memsize
>> +	fi
>> +
>> +	pagesize=$(sysctl -n hw.pagesize)
>> +	vm_swap_max=$(($(sysctl -n vm.swap_maxpages) / 2 * $pagesize))
>> +
>> +	if [ $swapmax -gt $vm_swap_max ]
>> +	then
>> +		$swapmax=$vm_swap_max
>> +	fi
>> +	echo -n "$swapmax"
>> +}
>> +
>> +# Find newly-added swap partition on parent device ($1).
>> +growfs_last_swap()
>> +{
>> +	swapdev=$(gpart list $1 | awk '
>> +		$2 == "Name:" { dev = $3 }
>> +		$1 == "type:" && $2 == "freebsd-swap" { swapdev = dev }
>> +		END { print swapdev }
>> +	    ')
>> +	echo -n $swapdev
>> +}
>> +
>> +growfs_start()
>> +{
>> +	verbose=0
>>  	echo "Growing root partition to fill device"
>>  	FSTYPE=$(mount -p | awk '{ if ( $2 == "/") { print $3 }}')
>>  	FSDEV=$(mount -p | awk '{ if ( $2 == "/") { print $1 }}')
>> @@ -100,19 +145,126 @@ growfs_start ()
>>  		diskdev=${rootdev}
>>  	fi
>>
>> +	# Check kenv for growfs_swap_size; if not present,
>> +	# check $growfs_swap_size from /etc/rc.conf.
>> +	# A value of 0 suppresses swap addition,
>> +	# "" (or unset) specifies the default;
>> +	# other values indicate the size in bytes.
>> +	# If default, check whether swap is already in fstab;
>> +	# if so, don't add another.
>> +	addswap=1
>> +	swapsize="$(kenv -q growfs_swap_size 2>/dev/null)"
>> +	case "$swapsize" in
>> +	"0")	addswap=0
>> +		;;
>> +	"")	case "$growfs_swap_size" in
>> +		"0")	addswap=0
>> +			;;
>> +		"")
>> +			if ! awk '
>> +				/^#/ { next }
>> +				$3 == "swap" { exit 1 }
>> +			    ' < /etc/fstab
>> +			then
>> +				addswap=0
>> +			fi
>> +			;;
>> +		*)	swapsize="$growfs_swap_size"
>> +			;;
>> +		esac
>> +		;;
>> +	*)	;;
>> +	esac
>> +
>> +	swaplim=$(growfs_swap_max)
>> +
>> +	[ $verbose -eq 1 ] && {
>> +		echo "diskdev is $diskdev"
>> +		echo "search is $search"
>> +		echo "swapsize is $swapsize"
>> +		echo "swaplim is $swaplim"
>> +	}
>> +
>>  	sysctl -b kern.geom.conftxt | awk '
>>  {
>> +	verbose = 0
>>  	lvl=$1
>>  	device[lvl] = $3
>>  	type[lvl] = $2
>>  	idx[lvl] = $7
>> +	offset[lvl] = $9
>>  	parttype[lvl] = $13
>> +	size[lvl] = $4
>> +	if (verbose) print lvl, type[lvl], $3
>> +	if (type[lvl] == "DISK") {
>> +		disksize = size[lvl]
>> +		if (verbose)
>> +			print "disksize ", disksize
>> +		# Don't add swap on disks under 15 GB (decimal) by default.
>> +		if (addswap == 1 && (size[lvl] > 15000000000 || swapsize > 0))
>> +			doing_swap = 1
>> +		else
>> +			doing_swap = 0
>> +	} else if (type[lvl] == "PART" && $11 == "freebsd-swap" && \
>> +	    int(swapsize) == 0) {
>> +		# This finds swap only if it precedes root, e.g. preceding disk.
>> +		addswap = 0
>> +		doing_swap = 0
>> +		print "swap device exists, not adding swap"
>> +	}
>>  	if (dev == $3) {
>>  		for (i = 1; i <= lvl; i++) {
>>  			# resize
>>  			if (type[i] == "PART") {
>>  				pdev = device[i - 1]
>> -				cmd[i] = "gpart resize -i " idx[i] " " pdev
>> +				if (verbose)
>> +					print i, pdev, addswap, disksize, \
>> +					    doing_swap
>> +				swapcmd = ""
>> +				# Allow swap if current root is < 40% of disk.
>> +				if (parttype[i] != "MBR" && doing_swap == 1 && \
>> +				    (size[i] / disksize < 0.4 || \
>> +				    swapsize > 0)) {
>> +					print "Adding swap partition"
>> +					if (int(swapsize) == 0) {
>> +						swapsize = int(disksize / 10)
>> +						if (swapsize > swaplim)
>> +							swapsize = swaplim
>> +					}
>> +					sector = $5
>> +					swapsize /= sector
>> +					if (verbose)
>> +						print "swapsize sectors",
>> +						    swapsize
>> +					align = 4 * 1024 * 1024 / sector
>> +
>> +					# Estimate offset for swap; let
>> +					# gpart compute actual start and size.
>> +					# Assume expansion all goes into this
>> +					# partition for MBR case.
>> +					if (parttype[i - 1] == "MBR") {
>> +					    if (verbose)
>> +						print "sz ", size[i - 1], \
>> +						    " off ", offset[i - 1]
>> +					    expand = size[0] - \
>> +						(size[i - 1] + offset[i - 1])
>> +					} else {
>> +					    if (verbose)
>> +						print "sz ", size[i], \
>> +						    " off ", offset[i]
>> +					    expand = size[0] - \
>> +						(size[i] + offset[i])
>> +					}
>> +					if (verbose)
>> +					    print "expand ", expand, \
>> +						" sz ", size[i]
>> +					swapbase = (expand + size[i]) / sector
>> +					swapbase -= swapsize + align
>> +					swapcmd = "gpart add -t freebsd-swap -a " align " -b " swapbase " " pdev "; kenv growfs_swap_pdev=" pdev " >/dev/null; "
>> +					if (verbose)
>> +						swapcmd = "set -x; " swapcmd
>> +				}
>> +				cmd[i] = swapcmd "gpart resize -i " idx[i] " " pdev
>>  				if (parttype[i] == "GPT")
>>  					cmd[i] = "gpart recover " pdev " ; " cmd[i]
>>  			} else if (type[i] == "LABEL") {
>> @@ -128,7 +280,7 @@ growfs_start ()
>>  		}
>>  		exit 0
>>  	}
>> -}' dev="$search"
>> +}' dev="$search" addswap="$addswap" swapsize="$swapsize" swaplim="$swaplim"
>>  	gpart commit "$diskdev" 2> /dev/null
>>  	case "$FSTYPE" in
>>  	ufs)
>> @@ -138,6 +290,20 @@ growfs_start ()
>>  		zpool online -e $pool $rootdev
>>  		;;
>>  	esac
>> +
>> +	# Get parent device of swap partition if one was added;
>> +	# if so, find swap device and label it.
>> +	pdev=$(kenv -q growfs_swap_pdev)
>> +	if [ -n "$pdev" ]
>> +	then
>> +		dev=$(growfs_last_swap "$pdev")
>> +		if [ -z "$dev" ]
>> +		then
>> +			echo "Swap partition not found on $pdev"
>> +			exit 0
>> +		fi
>> +		glabel label -v growfs_swap $dev
>> +	fi
>>  }
>>
>>  load_rc_config $name
>>
>
> --