Re: git: 5a31987d4c39 - main - rc+devd: Add growfs_postboot

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Mon, 24 Aug 2026 12:25:36 UTC
On Mon 24 Aug 05:02, Colin Percival wrote:
> The branch main has been updated by cperciva:
> 
> URL: https://cgit.FreeBSD.org/src/commit/?id=5a31987d4c395ec4d3ed303cbc2e1d12ea54a039
> 
> commit 5a31987d4c395ec4d3ed303cbc2e1d12ea54a039
> Author:     Colin Percival <cperciva@FreeBSD.org>
> AuthorDate: 2026-07-30 20:20:51 +0000
> Commit:     Colin Percival <cperciva@FreeBSD.org>
> CommitDate: 2026-08-24 05:02:16 +0000
> 
>     rc+devd: Add growfs_postboot
>     
>     In VM and cloud environments it is often possible to enlarge virtual
>     disks; this can be useful, for example, if a system is launched with a
>     small root disk and it later becomes clear that more space is needed.
>     
>     On kernels which support run-time resizing of disks (for NVMe, this was
>     added in November 2025; some other disk types have supported this for
>     longer) a SIZECHANGE notification is sent to userland via devd.
>     
>     Add a "nostart" rc.d script (runnable manually but not automatically at
>     boot time) and a devd script which invokes it when a notification
>     arrives.  The rc.d script enlarges the "final partition" on partitioned
>     geoms, or the UFS filesystem or zpool device when triggered on a disk
>     containing either of those.
>     
>     Reviewed by:    imp, ziaee
>     MFC after:      2 weeks
>     Relnotes:       Disk partitions and filesystems can be enlarged
>                     automatically when disks grow by setting
>                     growfs_postboot_enable=YES in /etc/rc.conf.
>     Sponsored by:   Amazon
>     Differential Revision:  https://reviews.freebsd.org/D58582
> ---
>  libexec/rc/rc.conf               |   3 +
>  libexec/rc/rc.d/Makefile         |   1 +
>  libexec/rc/rc.d/growfs_postboot  | 116 +++++++++++++++++++++++++++++++++++++++
>  sbin/devd/Makefile               |   1 +
>  sbin/devd/growfs_postboot.conf   |   8 +++
>  share/man/man5/rc.conf.5         |   1 +
>  share/man/man7/Makefile          |   1 +
>  share/man/man7/growfs.7          |   1 +
>  share/man/man7/growfs_postboot.7 |  69 +++++++++++++++++++++++
>  9 files changed, 201 insertions(+)
> 
> diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf
> index d5d1a198b143..e7ce22822515 100644
> --- a/libexec/rc/rc.conf
> +++ b/libexec/rc/rc.conf
> @@ -122,6 +122,9 @@ background_fsck_delay="60" # Time to wait (seconds) before starting the fsck.
>  growfs_enable="NO"	# Set to YES to attempt to grow the root filesystem on boot
>  growfs_swap_size=""	# Set to 0 to disable growfs swap, "" to default size,
>  			# size in bytes to specify swap size.
> +growfs_postboot_enable="NO" # Set to YES to attempt to grow partitions and
> +			# filesystems when devd notifies about disks growing
> +			# after the system boots.
>  netfs_types="nfs:NFS smbfs:SMB" # Net filesystems.
>  extra_netfs_types="NO"	# List of network extra filesystem types for delayed
>  			# mount at startup (or NO).
> diff --git a/libexec/rc/rc.d/Makefile b/libexec/rc/rc.d/Makefile
> index cb94380aff32..f6646208dc03 100644
> --- a/libexec/rc/rc.d/Makefile
> +++ b/libexec/rc/rc.d/Makefile
> @@ -24,6 +24,7 @@ CONFS=	DAEMON \
>  	fsck \
>  	growfs \
>  	growfs_fstab \
> +	growfs_postboot \
>  	hostid \
>  	hostid_save \
>  	hostname \
> diff --git a/libexec/rc/rc.d/growfs_postboot b/libexec/rc/rc.d/growfs_postboot
> new file mode 100755
> index 000000000000..346c8a0d7162
> --- /dev/null
> +++ b/libexec/rc/rc.d/growfs_postboot
> @@ -0,0 +1,116 @@
> +#!/bin/sh
> +#
> +# Copyright 2026 Colin Percival
> +#
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# PROVIDE: growfs_postboot
> +# KEYWORD: nostart
> +
> +# Triggered by /etc/devd/growfs_postboot.conf: When a disk grows, enlarge the
> +# final partition to fill the available space (if the disk is partitioned) or
> +# enlarge the UFS filesystem or ZFS zpool (if in use for one of those).  Note
> +# that in the common case of "a disk is partitioned and one of the partitions
> +# contains a filesystem" this script is invoked twice -- the repartitioning
> +# done on the first call generates a new devd event which triggers the second
> +# invocation.
> +#
> +# Enable by setting
> +# growfs_postboot_enable=YES
> +# in /etc/rc.conf.  Note that on systems with growfs_enable=YES, it will often
> +# be necessary to set growfs_swap_size=0 in order to disable swap creation;
> +# otherwise the "final partition" is the swap space, not the filesystem and
> +# growing the disk will not cause the most likely desired results.
> +
> +. /etc/rc.subr
> +
> +name="growfs_postboot"
> +desc="automatically grow file systems during runtime"
> +rcvar="growfs_postboot_enable"
> +start_cmd="growfs_postboot_start"
> +
> +disktype() {
> +	sysctl -n kern.geom.conftxt | awk -v dev="$1" '
> +	BEGIN {
> +		partitioned=0
> +		indev=0
> +	}
> +	{
> +		if (dev == $3) {
> +			indev=1
> +			devlevel=$1
> +		} else if (devlevel >= $1) {
> +			indev=0
> +		} else if (indev == 1) {
> +			if ($2 == "PART") {
> +				partitioned=1
> +			}
> +		}
> +	}
> +	END {
> +		if (partitioned == 1) {
> +			print "partitioned"
> +		} else {
> +			print "unpartitioned"
> +		}
> +	}'
> +}
> +
> +growpart() {
> +	# Run 'gpart recover' if GPT
> +	if gpart list "$1" | grep -qi 'scheme: GPT'; then
> +		gpart recover "$1" || true
> +	fi
> +
> +	# Find the index of the last partition; note that we need to compare
> +	# the starting sectors in case partitions are not numbered sensibly.
> +	idx=$(gpart backup "$1" | awk '
> +		NR == 1 { next }
> +		$3 + 0 > start { start = $3 + 0; idx = $1 }
> +		END { print idx }')
> +	if [ -z "$idx" ]; then
> +		echo "Error finding final partition of $1"
> +		return 1
> +	fi
> +
> +	# Expand that partition
> +	gpart resize -i "$idx" "$1"
> +}
> +
> +growfilesystem() {
> +	case $(fstyp -u "/dev/$1" 2>/dev/null) in
> +	ufs)	growfs -y "/dev/$1"
> +		;;
> +	zfs)	zpool list -H -o name |
> +		    while read -r pool; do
> +			zpool list -vH -o name "$pool" |
> +			    grep "^[[:space:]]" |
> +			    while read -r dev; do
> +				if [ "$dev" = "$1" ]; then
> +					zpool online -e "$pool" "$1"
> +				fi
> +			    done
> +		    done
> +		;;
> +	"")	# Nothing to grow here
> +		;;
> +	*)	echo "Don't know how to grow filesystem on /dev/$1"
> +		;;
> +	esac
> +}
> +
> +growfs_postboot_start() {
> +	# Run either growpart or growfilesystem depending on whether this is
> +	# something which is partitioned or not.
> +	case $(disktype "$1") in
> +	partitioned)
> +		growpart "$1"
> +		;;
> +	unpartitioned)
> +		growfilesystem "$1"
> +		;;
> +	esac
> +}
> +
> +load_rc_config $name
> +run_rc_command "$@"
> diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile
> index 78b2b039ac6e..864df3604d64 100644
> --- a/sbin/devd/Makefile
> +++ b/sbin/devd/Makefile
> @@ -5,6 +5,7 @@ PACKAGE=devd
>  CONFGROUPS=	CONFS DEVD
>  CONFS=	devd.conf
>  DEVD=	devmatch.conf
> +DEVD+=	growfs_postboot.conf
>  DEVDDIR=	/etc/devd
>  .if ${MK_ACPI} != "no"
>  DEVD+=	asus.conf
> diff --git a/sbin/devd/growfs_postboot.conf b/sbin/devd/growfs_postboot.conf
> new file mode 100644
> index 000000000000..0c6b817755ec
> --- /dev/null
> +++ b/sbin/devd/growfs_postboot.conf
> @@ -0,0 +1,8 @@
> +# Trigger growfs on GEOM size changes.
> +
> +notify 200 {
> +	match "system" "GEOM";
> +	match "subsystem" "DEV";
> +	match "type" "SIZECHANGE";
> +	action "lockf -k /var/run/growfs_postboot.lock /etc/rc.d/growfs_postboot quietstart $cdev 2>&1 | logger -t growfs_postboot &";
> +};

Please avoid calling /etc/rc.d/* directly and prefer using service(8) like all
other devd from base. (beside consistency, it is also simplify my life on rcd)

Best regards,
Bapt