mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
zed: let autoexpand see capacity changes on partitioned disks
Growing a disk under a whole-disk vdev never triggers autoexpand (#12505). The kernel reports a capacity change on the disk itself and nothing for the partitions, whose sizes did not change. But since zfs owns the whole disk it carries a partition table, and zed_udev_monitor() drops any disk-with-partitions event on the assumption that a partition event will follow. For a resize none ever does, so the ESC_DEV_DLE event that zfsdle_vdev_online() needs is never generated and the pool stays at the old size until someone runs zpool online -e by hand. This is the common case for cloud disks grown online. Pass change events through when udev marks them RESIZE=1. On the matching side a disk-level event has no vdev guid to search by (the label lives on the partition), and udev provides no ID_PATH on some buses, so the physical path lookup can also come up empty. When that happens, read the ZFS label off the whole-disk partition and match by the pool and vdev guids stored in it. Unlike matching the config path textually, this works no matter which name the pool was imported with (by-id, by-path or a bare device node), and a stale device path in an unrelated pool's config cannot steal the event, since the label names the owning pool. The fallback only runs for guid-less disk events and only accepts a whole-disk vdev that is not a spare or l2cache device. The new zpool_expand_006_pos test covers this end to end on a scsi_debug disk. zpool_expand_001_pos already grows a scsi_debug disk the same way but keeps passing on an unpatched zed, because block_device_wait issues a bare udevadm trigger, which re-sends change events for the zfs_member partitions and hands zed the vdev guid the resize itself never delivered. The new test drains the pool-creation udev traffic and then only settles, so zed sees what a production resize generates: one RESIZE=1 change event on the disk. Multipath maps take a different path through zed_udev_monitor() and still need the manual online; that is unchanged here, and the same goes for other device-mapper vdevs, whose partitions live on separate dm nodes the fallback cannot derive from the map's name. Disks with no devid source at all (virtio-blk, Xen) also stay out of scope: their events are dropped earlier for lack of any device identifier, and widening that is its own discussion. Tests: zpool_expand_006_pos fails against unpatched zed and passes with the fix; a pool imported by /dev/disk/by-id expands from the bare disk event with "matched vdev ... by the label" in the zed log; the zpool_expand group passes. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: MorganaFuture <[email protected]> Closes #12505 Closes #18833
This commit is contained in:
1 parent
a8458ef364
commit
466d90e15d
5 files changed
+179
-3
No files matched your search
@@ -1080,8 +1080,63 @@ zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
|
||||
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: searching for '%s' in '%s'",
|
||||
devname, zpool_get_name(zhp));
|
||||
|
||||
if ((tgt = zpool_find_vdev_by_physpath(zhp, devname,
|
||||
&avail_spare, &l2cache, NULL)) != NULL) {
|
||||
tgt = zpool_find_vdev_by_physpath(zhp, devname,
|
||||
&avail_spare, &l2cache, NULL);
|
||||
|
||||
/*
|
||||
* A whole-disk event carries no vdev guid (the label lives on
|
||||
* the partition), and udev provides no ID_PATH on some buses,
|
||||
* so neither lookup above can match. Identify the vdev by
|
||||
* reading the label off the whole-disk partition instead: the
|
||||
* pool and vdev guids stored there don't depend on which name
|
||||
* the pool was imported with (by-id, by-path or a bare device
|
||||
* node), and they can't be forged by a stale config path left
|
||||
* behind in an unrelated pool.
|
||||
*/
|
||||
if (tgt == NULL && nvlist_lookup_uint64(udev_nvl, ZFS_EV_VDEV_GUID,
|
||||
&guid) != 0 && nvlist_lookup_string(udev_nvl, DEV_NAME,
|
||||
&tmp_devname) == 0 && !nvlist_exists(udev_nvl, DEV_IS_PART)) {
|
||||
nvlist_t *label = NULL;
|
||||
uint64_t label_pool_guid = 0, label_vdev_guid = 0;
|
||||
int fd;
|
||||
|
||||
strlcpy(devname, tmp_devname, MAXPATHLEN);
|
||||
zfs_append_partition(devname, MAXPATHLEN);
|
||||
|
||||
if ((fd = open(devname, O_RDONLY | O_CLOEXEC)) >= 0) {
|
||||
if (zpool_read_label(fd, &label, NULL) == 0 &&
|
||||
label != NULL &&
|
||||
nvlist_lookup_uint64(label,
|
||||
ZPOOL_CONFIG_POOL_GUID, &label_pool_guid) == 0 &&
|
||||
nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID,
|
||||
&label_vdev_guid) == 0 &&
|
||||
label_pool_guid == zpool_get_prop_int(zhp,
|
||||
ZPOOL_PROP_GUID, NULL)) {
|
||||
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: "
|
||||
"matched vdev %llu by the label on '%s'",
|
||||
(u_longlong_t)label_vdev_guid, devname);
|
||||
|
||||
(void) snprintf(devname, MAXPATHLEN, "%llu",
|
||||
(u_longlong_t)label_vdev_guid);
|
||||
tgt = zpool_find_vdev(zhp, devname,
|
||||
&avail_spare, &l2cache, NULL);
|
||||
if (tgt != NULL && (avail_spare || l2cache))
|
||||
tgt = NULL;
|
||||
if (tgt != NULL) {
|
||||
uint64_t wd = 0;
|
||||
|
||||
(void) nvlist_lookup_uint64(tgt,
|
||||
ZPOOL_CONFIG_WHOLE_DISK, &wd);
|
||||
if (!wd)
|
||||
tgt = NULL;
|
||||
}
|
||||
}
|
||||
nvlist_free(label);
|
||||
(void) close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (tgt != NULL) {
|
||||
const char *path;
|
||||
char fullpath[MAXPATHLEN];
|
||||
uint64_t wholedisk = 0;
|
||||
|
||||
@@ -242,6 +242,9 @@ zed_udev_monitor(void *arg)
|
||||
part != NULL && part[0] != '\0') {
|
||||
const char *devname =
|
||||
udev_device_get_property_value(dev, "DEVNAME");
|
||||
const char *resize =
|
||||
udev_device_get_property_value(dev, "RESIZE");
|
||||
const char *dev_action = udev_device_get_action(dev);
|
||||
|
||||
if (strcmp(part, "atari") == 0) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
@@ -249,6 +252,19 @@ zed_udev_monitor(void *arg)
|
||||
"but we're going to assume it's a false "
|
||||
"positive and still use it (issue #13497)",
|
||||
__func__, devname);
|
||||
} else if (dev_action != NULL &&
|
||||
strcmp(dev_action, "change") == 0 &&
|
||||
resize != NULL && strcmp(resize, "1") == 0) {
|
||||
/*
|
||||
* A capacity change is reported on the disk
|
||||
* itself, not on its partitions, so this is
|
||||
* the only chance to notice it. Pass the
|
||||
* event on for a possible autoexpand of a
|
||||
* whole-disk vdev (issue #12505).
|
||||
*/
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: %s capacity change on partitioned "
|
||||
"disk", __func__, devname);
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: skip %s since it has a %s partition "
|
||||
|
||||
@@ -70,7 +70,8 @@ tags = ['functional', 'cli_root', 'zpool_add']
|
||||
|
||||
[tests/functional/cli_root/zpool_expand:Linux]
|
||||
tests = ['zpool_expand_001_pos', 'zpool_expand_002_pos',
|
||||
'zpool_expand_003_neg', 'zpool_expand_004_pos', 'zpool_expand_005_pos']
|
||||
'zpool_expand_003_neg', 'zpool_expand_004_pos', 'zpool_expand_005_pos',
|
||||
'zpool_expand_006_pos']
|
||||
tags = ['functional', 'cli_root', 'zpool_expand']
|
||||
|
||||
[tests/functional/cli_root/zpool_import:Linux]
|
||||
|
||||
@@ -1185,6 +1185,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
functional/cli_root/zpool_expand/zpool_expand_003_neg.ksh \
|
||||
functional/cli_root/zpool_expand/zpool_expand_004_pos.ksh \
|
||||
functional/cli_root/zpool_expand/zpool_expand_005_pos.ksh \
|
||||
functional/cli_root/zpool_expand/zpool_expand_006_pos.ksh \
|
||||
functional/cli_root/zpool_export/cleanup.ksh \
|
||||
functional/cli_root/zpool_export/setup.ksh \
|
||||
functional/cli_root/zpool_export/zpool_export_001_pos.ksh \
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/bin/ksh -p
|
||||
# SPDX-License-Identifier: CDDL-1.0
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# This file and its contents are supplied under the terms of the
|
||||
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
# You may only use this file in accordance with the terms of version
|
||||
# 1.0 of the CDDL.
|
||||
#
|
||||
# A full copy of the text of the CDDL should have accompanied this
|
||||
# source. A copy of the CDDL is also available via the Internet at
|
||||
# http://www.illumos.org/license/CDDL.
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2026 by Andrew Mochalskyi. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_expand/zpool_expand.cfg
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# A pool on a whole-disk vdev autoexpands from the kernel's own
|
||||
# capacity change event alone (issue #12505).
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1) Create a pool with autoexpand=on on a single scsi_debug device,
|
||||
# so zfs owns the whole disk and puts a partition table on it
|
||||
# 2) Grow the disk and let the SCSI layer pick up the new capacity
|
||||
# 3) Wait for the pool to expand
|
||||
#
|
||||
# The kernel reports a resize with a single change event on the disk;
|
||||
# nothing is generated for the partitions. zpool_expand_001_pos
|
||||
# passes this point on an unpatched zed only because
|
||||
# block_device_wait runs a bare udevadm trigger, which resends change
|
||||
# events for the zfs_member partitions and hands zed the vdev guid
|
||||
# the resize never delivered. For the same reason the udev events
|
||||
# left over from pool creation must be drained before growing the
|
||||
# disk: only udevadm settle may be used after that point, so that zed
|
||||
# sees the resize event and nothing else, just like with a disk grown
|
||||
# in production.
|
||||
#
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1
|
||||
unload_scsi_debug
|
||||
}
|
||||
|
||||
# Wait for the pool size to grow past $1.
|
||||
function wait_for_autoexpand
|
||||
{
|
||||
typeset prev_size=$1
|
||||
typeset -i i
|
||||
|
||||
for (( i = 0; i < 60; i++ )); do
|
||||
typeset new_size=$(get_pool_prop size $TESTPOOL1)
|
||||
if [[ $new_size -gt $prev_size ]]; then
|
||||
return
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_fail "$TESTPOOL1 never grew past $prev_size (still $new_size)"
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "whole-disk pool autoexpands from the disk's own resize event"
|
||||
|
||||
MINVDEVSIZE_MB=$((MINVDEVSIZE / 1048576))
|
||||
load_scsi_debug $MINVDEVSIZE_MB 1 1 1 '512b'
|
||||
block_device_wait
|
||||
DEV=$(get_debug_device)
|
||||
|
||||
log_must zpool create -o autoexpand=on $TESTPOOL1 $DEV
|
||||
|
||||
# Let the udev fallout of the pool creation (partition table writes,
|
||||
# device node closes) fully play out before the resize, so the only
|
||||
# event left for zed to see below is the resize itself.
|
||||
udevadm settle
|
||||
sleep 2
|
||||
udevadm settle
|
||||
|
||||
typeset prev_size=$(get_pool_prop size $TESTPOOL1)
|
||||
|
||||
# Grow the device and have the SCSI disk driver re-read the capacity.
|
||||
# This makes the kernel emit a change event with RESIZE=1 for the
|
||||
# disk, and nothing for its partitions.
|
||||
echo "2" > /sys/bus/pseudo/drivers/scsi_debug/virtual_gb
|
||||
echo "1" > /sys/class/block/$DEV/device/rescan
|
||||
udevadm settle
|
||||
|
||||
wait_for_autoexpand $prev_size
|
||||
|
||||
log_note "$TESTPOOL1 grew from $prev_size to $(get_pool_prop size $TESTPOOL1)"
|
||||
|
||||
log_pass "whole-disk pool autoexpanded from the disk's own resize event"
|
||||
Reference in new issue
Block a user