From e205ae0d9cf15bc5bec07cfd0e9e504be1f37c60 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 8 Sep 2021 14:57:55 +0200 Subject: [PATCH 1/5] test: use one call to install necessary modules --- test/TEST-64-UDEV-STORAGE/test.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index 62e0041af8..8b2591fbac 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -20,10 +20,7 @@ fi test_append_files() { ( - instmods "=block" - instmods "=md" - instmods "=scsi" - instmods "=nvme" + instmods "=block" "=md" "=nvme" "=scsi" install_dmevent generate_module_dependencies inst_binary lsblk From 9e7c3bd48c7ee3b3ba62837cf305f2f1c4346932 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 8 Sep 2021 18:17:46 +0200 Subject: [PATCH 2/5] test: add a multipath helper --- test/test-functions | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/test-functions b/test/test-functions index e732d99d5b..c07073bf9d 100644 --- a/test/test-functions +++ b/test/test-functions @@ -909,6 +909,32 @@ install_dmevent() { fi } +install_multipath() { + instmods "=md" multipath + image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx + image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket} + if get_bool "$LOOKS_LIKE_DEBIAN"; then + inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules + else + inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules + fi + mkdir -p "${initdir:?}/etc/multipath" + + local file + while read -r file; do + # Install libraries required by the given library + inst_libs "$file" + # Install the library itself and create necessary symlinks + inst_library "$file" + done < <(find /lib*/multipath -type f) + + if get_bool "$LOOKS_LIKE_ARCH"; then + # On Arch the multipath libraries are not linked against libgcc_s.so.1, + # but it's still required at runtime + inst_library "/lib64/libgcc_s.so.1" + fi +} + install_compiled_systemd() { dinfo "Install compiled systemd" From d0cbad16c554043aa19ece6d94707fbffa7eb234 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 8 Sep 2021 18:26:02 +0200 Subject: [PATCH 3/5] test: add a basic multipath test + failover --- test/TEST-64-UDEV-STORAGE/test.sh | 52 +++++++++++++++- test/units/testsuite-64.sh | 99 +++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index 8b2591fbac..accbe09255 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -23,11 +23,16 @@ test_append_files() { instmods "=block" "=md" "=nvme" "=scsi" install_dmevent generate_module_dependencies - inst_binary lsblk - inst_binary wc + image_install lsblk wc + + # Configure multipath + if command -v multipath && command -v multipathd; then + install_multipath + fi for i in {0..127}; do dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1 + echo "device$i" >"${TESTDIR:?}/disk$i.img" done ) } @@ -182,6 +187,49 @@ EOF QEMU_SMP=1 QEMU_TIMEOUT=60 test_run_one "${1:?}" } +testcase_multipath_basic_failover() { + if ! command -v multipath || ! command -v multipathd; then + echo "Missing multipath tools, skipping the test..." + return 77 + fi + + local qemu_opts=("-device virtio-scsi-pci,id=scsi") + local partdisk="${TESTDIR:?}/multipathpartitioned.img" + local image lodev nback ndisk wwn + + if [[ ! -e "$partdisk" ]]; then + dd if=/dev/zero of="$partdisk" bs=1M count=16 + lodev="$(losetup --show -f -P "$partdisk")" + sfdisk "${lodev:?}" </etc/multipath.conf <<\EOF +defaults { + # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX + user_friendly_names no + find_multipaths yes + enable_foreign "^$" +} + +blacklist_exceptions { + property "(SCSI_IDENT_|ID_WWN)" +} + +blacklist { +} +EOF + modprobe -v dm_multipath + systemctl start multipathd.service + systemctl status multipathd.service + multipath -ll + ls -l /dev/disk/by-id/ + + for i in {0..63}; do + wwid="deaddeadbeef$(printf "%.4d" "$i")" + path="/dev/disk/by-id/wwn-0x$wwid" + dmpath="$(readlink -f "$path")" + + lsblk "$path" + multipath -C "$dmpath" + # We should have 4 active paths for each multipath device + [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]] + done + + # Test failover (with the first multipath device that has a partitioned disk) + echo "${FUNCNAME[0]}: test failover" + local device expected link mpoint part + local -a devices + mpoint="$(mktemp -d /mnt/mpathXXX)" + wwid="deaddeadbeef0000" + path="/dev/disk/by-id/wwn-0x$wwid" + + # All following symlinks should exists and should be valid + local -a part_links=( + "/dev/disk/by-id/wwn-0x$wwid-part2" + "/dev/disk/by-partlabel/failover_part" + "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000" + "/dev/disk/by-label/failover_vol" + "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111" + ) + for link in "${part_links[@]}"; do + test -e "$link" + done + + # Choose a random symlink to the failover data partition each time, for + # a better coverage + part="${part_links[$RANDOM % ${#part_links[@]}]}" + + # Get all devices attached to a specific multipath device (in H:C:T:L format) + # and sort them in a random order, so we cut off different paths each time + mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R) + if [[ "${#devices[@]}" -ne 4 ]]; then + echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead" + return 1 + fi + # Drop the last path from the array, since we want to leave at least one path active + unset "devices[3]" + # Mount the first multipath partition, write some data we can check later, + # and then disconnect the remaining paths one by one while checking if we + # can still read/write from the mount + mount -t ext4 "$part" "$mpoint" + expected=0 + echo -n "$expected" >"$mpoint/test" + # Sanity check we actually wrote what we wanted + [[ "$(<"$mpoint/test")" == "$expected" ]] + + for device in "${devices[@]}"; do + echo offline >"/sys/class/scsi_device/$device/device/state" + [[ "$(<"$mpoint/test")" == "$expected" ]] + expected="$((expected + 1))" + echo -n "$expected" >"$mpoint/test" + + # Make sure all symlinks are still valid + for link in "${part_links[@]}"; do + test -e "$link" + done + done + + multipath -l "$path" + # Three paths should be now marked as 'offline' and one as 'running' + [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]] + [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]] + + umount "$mpoint" + rm -fr "$mpoint" +} + : >/failed udevadm settle From d430e451c900d924bc8e738b649e11979832d211 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 10 Sep 2021 13:04:58 +0200 Subject: [PATCH 4/5] test: make sure all symlinks under /dev/disk/ are valid --- test/units/testsuite-64.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh index 64859f1c89..8e8f27d23c 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/testsuite-64.sh @@ -4,6 +4,28 @@ set -eux set -o pipefail +# Check if all symlinks under /dev/disk/ are valid +helper_check_device_symlinks() { + local dev link target + + while read -r link; do + target="$(readlink -f "$link")" + # Both checks should do virtually the same thing, but check both to be + # on the safe side + if [[ ! -e "$link" || ! -e "$target" ]]; then + echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist" + return 1 + fi + + # Check if the symlink points to the correct device in /dev + dev="/dev/$(udevadm info -q name "$link")" + if [[ "$target" != "$dev" ]]; then + echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected" + return 1 + fi + done < <(find /dev/disk -type l) +} + testcase_megasas2_basic() { lsblk -S [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] @@ -121,9 +143,11 @@ EOF : >/failed udevadm settle - lsblk -a +echo "Check if all symlinks under /dev/disk/ are valid (pre-test)" +helper_check_device_symlinks + # TEST_FUNCTION_NAME is passed on the kernel command line via systemd.setenv= # in the respective test.sh file if ! command -v "${TEST_FUNCTION_NAME:?}"; then @@ -134,6 +158,9 @@ fi echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME" "$TEST_FUNCTION_NAME" +echo "Check if all symlinks under /dev/disk/ are valid (post-test)" +helper_check_device_symlinks + systemctl status systemd-udevd touch /testok From 35497c7c3369718cae08bb5dcf95dd487d214008 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Fri, 10 Sep 2021 17:16:51 +0200 Subject: [PATCH 5/5] test: coverage for #19946 --- test/TEST-64-UDEV-STORAGE/test.sh | 16 +++++++++++++ test/units/testsuite-64.sh | 37 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index accbe09255..2b9d0735e3 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -230,6 +230,22 @@ EOF test_run_one "${1:?}" } +# Test case for issue https://github.com/systemd/systemd/issues/19946 +testcase_simultaneous_events() { + local qemu_opts=("-device virtio-scsi-pci,id=scsi") + local partdisk="${TESTDIR:?}/simultaneousevents.img" + + dd if=/dev/zero of="$partdisk" bs=1M count=110 + qemu_opts+=( + "-device scsi-hd,drive=drive1,serial=deadbeeftest" + "-drive format=raw,cache=unsafe,file=$partdisk,if=none,id=drive1" + ) + + KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" + QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" + test_run_one "${1:?}" +} + # Allow overriding which tests should be run from the "outside", useful for manual # testing (make -C test/... TESTCASES="testcase1 testcase2") if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh index 8e8f27d23c..ab688207bf 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/testsuite-64.sh @@ -140,6 +140,43 @@ EOF rm -fr "$mpoint" } +testcase_simultaneous_events() { + local blockdev part partscript + + blockdev="$(readlink -f /dev/disk/by-id/scsi-*_deadbeeftest)" + partscript="$(mktemp)" + + if [[ ! -b "$blockdev" ]]; then + echo "ERROR: failed to find the test SCSI block device" + return 1 + fi + + cat >"$partscript" </failed udevadm settle