Add unit tests exercising CPU preservation sysfs interface: - Verify /sys/devices/system/cpu/cpu<N>/preserve operations. - Test CPU offline and preserve sequencing. - Verify status reporting and error handling.
Signed-off-by: Pasha Tatashin <[email protected]> --- tools/testing/selftests/liveupdate/config | 6 + .../testing/selftests/liveupdate/liveupdate.c | 251 ++++++++++++++++++ 2 files changed, 257 insertions(+) diff --git a/tools/testing/selftests/liveupdate/config b/tools/testing/selftests/liveupdate/config index 016d009dba13..9458b9c70eeb 100644 --- a/tools/testing/selftests/liveupdate/config +++ b/tools/testing/selftests/liveupdate/config @@ -5,8 +5,14 @@ CONFIG_KEXEC_HANDOVER=y CONFIG_KEXEC_HANDOVER_ENABLE_DEFAULT=y CONFIG_KEXEC_HANDOVER_DEBUGFS=y CONFIG_KEXEC_HANDOVER_DEBUG=y +CONFIG_SMP=y +CONFIG_HOTPLUG_CPU=y CONFIG_LIVEUPDATE=y CONFIG_LIVEUPDATE_TEST=y +CONFIG_LIVEUPDATE_CPU=y +CONFIG_ACPI=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_HOTPLUG_CPU=y CONFIG_MEMFD_CREATE=y CONFIG_TMPFS=y CONFIG_SHMEM=y diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c index 2dedd5fc2534..b3c9cd7ea653 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -530,4 +530,255 @@ TEST_F(liveupdate_device, preserve_many_files) ASSERT_EQ(close(session_fd), 0); } +static int find_hotplug_cpu(void) +{ + char path[128]; + int cpu; + + for (cpu = 1; cpu < 256; cpu++) { + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/preserve", cpu); + if (access(path, R_OK) == 0) { + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu); + if (access(path, R_OK) == 0) + return cpu; + } + } + return -1; +} + +static int read_cpu_online(int cpu) +{ + char path[128], buf[16]; + int fd, val; + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu); + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + memset(buf, 0, sizeof(buf)); + if (read(fd, buf, sizeof(buf) - 1) <= 0) { + close(fd); + return -1; + } + close(fd); + val = atoi(buf); + return val; +} + +static int write_cpu_online(int cpu, int val) +{ + char path[128], buf[16]; + int fd, ret; + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu); + fd = open(path, O_WRONLY); + if (fd < 0) + return -errno; + snprintf(buf, sizeof(buf), "%d\n", val); + ret = write(fd, buf, strlen(buf)); + if (ret < 0) { + ret = -errno; + close(fd); + return ret; + } + close(fd); + return 0; +} + +/* + * Test Case: CPU Preservation Basic + * + * Verifies that a CPU preserve sysfs file descriptor can be preserved in a + * LUO session, offlining the CPU from scheduler and returning it online upon + * session teardown. + */ +TEST_F(liveupdate_device, cpu_preserve_basic) +{ + char path[128]; + int target_cpu; + int session_fd, cpu_fd; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + target_cpu = find_hotplug_cpu(); + if (target_cpu < 0) + SKIP(return, "No hotpluggable CPU with preserve attribute found"); + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/preserve", target_cpu); + cpu_fd = open(path, O_RDONLY); + ASSERT_GE(cpu_fd, 0); + + session_fd = luo_create_session(self->fd1, "cpu-preserve-test"); + ASSERT_GE(session_fd, 0); + + ASSERT_EQ(luo_session_preserve_fd(session_fd, cpu_fd, 0x42), 0); + close(cpu_fd); + + /* Verify target CPU is now offline */ + ASSERT_EQ(read_cpu_online(target_cpu), 0); + + /* Closing session unpreserves and brings CPU back online */ + ASSERT_EQ(close(session_fd), 0); + ASSERT_EQ(read_cpu_online(target_cpu), 1); +} + +/* + * Test Case: CPU Preservation Sysfs Online/Offline Rejection + * + * Verifies that while a CPU is preserved, attempting to bring it online + * via sysfs fails with -EBUSY. After session closure, normal online/offline + * hotplug operations succeed. + */ +TEST_F(liveupdate_device, cpu_preserve_sysfs_online_offline) +{ + char path[128]; + int target_cpu; + int session_fd, cpu_fd; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + target_cpu = find_hotplug_cpu(); + if (target_cpu < 0) + SKIP(return, "No hotpluggable CPU with preserve attribute found"); + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/preserve", target_cpu); + cpu_fd = open(path, O_RDONLY); + ASSERT_GE(cpu_fd, 0); + + session_fd = luo_create_session(self->fd1, "cpu-preserve-sysfs"); + ASSERT_GE(session_fd, 0); + + ASSERT_EQ(luo_session_preserve_fd(session_fd, cpu_fd, 0x100), 0); + close(cpu_fd); + + /* Target CPU is offline while preserved */ + ASSERT_EQ(read_cpu_online(target_cpu), 0); + + /* Attempting to bring the preserved CPU online via sysfs must fail with EBUSY */ + EXPECT_EQ(write_cpu_online(target_cpu, 1), -EBUSY); + ASSERT_EQ(read_cpu_online(target_cpu), 0); + + /* Writing 0 to online (offline) should succeed since it is already offline */ + EXPECT_EQ(write_cpu_online(target_cpu, 0), 0); + + /* Close session -> unpreserve and restore online */ + ASSERT_EQ(close(session_fd), 0); + ASSERT_EQ(read_cpu_online(target_cpu), 1); + + /* Verify normal offline and online now succeed */ + ASSERT_EQ(write_cpu_online(target_cpu, 0), 0); + ASSERT_EQ(read_cpu_online(target_cpu), 0); + ASSERT_EQ(write_cpu_online(target_cpu, 1), 0); + ASSERT_EQ(read_cpu_online(target_cpu), 1); +} + +/* + * Test Case: Prevent Double CPU Preservation + * + * Verifies that a CPU cannot be preserved twice across the same or different + * sessions simultaneously. + */ +TEST_F(liveupdate_device, cpu_preserve_prevent_double) +{ + char path[128]; + int target_cpu; + int session_fd1, session_fd2, cpu_fd1, cpu_fd2; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + target_cpu = find_hotplug_cpu(); + if (target_cpu < 0) + SKIP(return, "No hotpluggable CPU with preserve attribute found"); + + session_fd1 = luo_create_session(self->fd1, "cpu-double-1"); + ASSERT_GE(session_fd1, 0); + session_fd2 = luo_create_session(self->fd1, "cpu-double-2"); + ASSERT_GE(session_fd2, 0); + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/preserve", target_cpu); + cpu_fd1 = open(path, O_RDONLY); + ASSERT_GE(cpu_fd1, 0); + + ASSERT_EQ(luo_session_preserve_fd(session_fd1, cpu_fd1, 0x10), 0); + close(cpu_fd1); + + /* Attempting to preserve the same CPU in session2 must fail with -EBUSY */ + cpu_fd2 = open(path, O_RDONLY); + ASSERT_GE(cpu_fd2, 0); + EXPECT_EQ(luo_session_preserve_fd(session_fd2, cpu_fd2, 0x20), -EBUSY); + close(cpu_fd2); + + /* Attempting to preserve again in session1 must also fail with -EBUSY */ + cpu_fd2 = open(path, O_RDONLY); + ASSERT_GE(cpu_fd2, 0); + EXPECT_EQ(luo_session_preserve_fd(session_fd1, cpu_fd2, 0x30), -EBUSY); + close(cpu_fd2); + + ASSERT_EQ(close(session_fd1), 0); + ASSERT_EQ(close(session_fd2), 0); + ASSERT_EQ(read_cpu_online(target_cpu), 1); +} + +/* + * Test Case: Multiple Preserved CPUs + * + * Verifies preserving multiple distinct CPUs simultaneously in the same session. + */ +TEST_F(liveupdate_device, cpu_preserve_multiple_cpus) +{ + char path[128]; + int target_cpus[8]; + int num_targets = 0; + int session_fd, cpu_fd, cpu, i; + + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno == ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + for (cpu = 1; cpu < 256 && num_targets < 8; cpu++) { + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/preserve", cpu); + if (access(path, R_OK) == 0) { + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu); + if (access(path, R_OK) == 0) + target_cpus[num_targets++] = cpu; + } + } + + if (num_targets < 2) + SKIP(return, "Need at least 2 hotpluggable CPUs for multiple CPU preserve test"); + + session_fd = luo_create_session(self->fd1, "multi-cpu-preserve"); + ASSERT_GE(session_fd, 0); + + for (i = 0; i < num_targets; i++) { + snprintf(path, sizeof(path), + "/sys/devices/system/cpu/cpu%d/preserve", target_cpus[i]); + cpu_fd = open(path, O_RDONLY); + ASSERT_GE(cpu_fd, 0); + ASSERT_EQ(luo_session_preserve_fd(session_fd, cpu_fd, 0x1000 + i), 0); + close(cpu_fd); + } + + /* Verify all targets are offline and reject sysfs online */ + for (i = 0; i < num_targets; i++) { + ASSERT_EQ(read_cpu_online(target_cpus[i]), 0); + EXPECT_EQ(write_cpu_online(target_cpus[i], 1), -EBUSY); + } + + /* Close session and verify all targets return online */ + ASSERT_EQ(close(session_fd), 0); + for (i = 0; i < num_targets; i++) + ASSERT_EQ(read_cpu_online(target_cpus[i]), 1); +} + TEST_HARNESS_MAIN -- 2.55.0.1082.g2b9226bbc0-goog

