[PATCH v2 2/2] selftests: liveupdate: Add tests for LIVEUPDATE_SESSION_FREEZE
From: Pasha Tatashin
Date: Tue Oct 20 2026 - 10:58:05 EST
Provide luo_session_freeze() in the liveupdate test library and add unit
tests in liveupdate.c covering the LIVEUPDATE_SESSION_FREEZE ioctl. The
tests verify that non-zero reserved bits are rejected with -EINVAL, an
outgoing session with preserved files can be frozen early, freezing an
already frozen session returns -EBUSY, and attempting to preserve an
additional file descriptor into a frozen session fails with -EBUSY.
Also freeze the test session early in luo_kexec_simple.c to exercise
LIVEUPDATE_SESSION_FREEZE across a kexec reboot, and verify that
attempting to freeze a retrieved incoming session returns -EINVAL.
Signed-off-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
---
.../liveupdate/lib/include/libliveupdate.h | 1 +
.../selftests/liveupdate/lib/lu_utils.c | 10 ++++
.../testing/selftests/liveupdate/liveupdate.c | 49 +++++++++++++++++++
.../selftests/liveupdate/luo_kexec_simple.c | 8 +++
4 files changed, 68 insertions(+)
diff --git a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
index fa07fed08364..15a7db3a6b3e 100644
--- a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
+++ b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
@@ -26,6 +26,7 @@ int luo_create_session(int luo_fd, const char *name);
int luo_retrieve_session(int luo_fd, const char *name);
int luo_session_finish(int session_fd);
int luo_get_session_name(int session_fd, char *name, size_t name_len);
+int luo_session_freeze(int session_fd);
int luo_ensure_nofile_limit(long min_limit);
int luo_session_preserve_fd(int session_fd, int fd, __u64 token);
diff --git a/tools/testing/selftests/liveupdate/lib/lu_utils.c b/tools/testing/selftests/liveupdate/lib/lu_utils.c
index 74d41115c281..6315746ff08f 100644
--- a/tools/testing/selftests/liveupdate/lib/lu_utils.c
+++ b/tools/testing/selftests/liveupdate/lib/lu_utils.c
@@ -198,6 +198,16 @@ int luo_session_finish(int session_fd)
return 0;
}
+int luo_session_freeze(int session_fd)
+{
+ struct liveupdate_session_freeze arg = { .size = sizeof(arg) };
+
+ if (ioctl(session_fd, LIVEUPDATE_SESSION_FREEZE, &arg) < 0)
+ return -errno;
+
+ return 0;
+}
+
void create_state_file(int luo_fd, const char *session_name, int token,
int next_stage)
{
diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index 2dedd5fc2534..74004e4276f5 100644
--- a/tools/testing/selftests/liveupdate/liveupdate.c
+++ b/tools/testing/selftests/liveupdate/liveupdate.c
@@ -530,4 +530,53 @@ TEST_F(liveupdate_device, preserve_many_files)
ASSERT_EQ(close(session_fd), 0);
}
+/*
+ * Test Case: Session Freeze
+ *
+ * Verifies that an outgoing session can be frozen early via
+ * LIVEUPDATE_SESSION_FREEZE, rejects non-zero reserved bits with -EINVAL,
+ * that subsequent freeze attempts return -EBUSY, and that attempting to
+ * preserve an additional file descriptor into a frozen session fails with
+ * -EBUSY.
+ */
+TEST_F(liveupdate_device, session_freeze)
+{
+ struct liveupdate_session_freeze bad_arg = {
+ .size = sizeof(bad_arg),
+ .reserved = 1,
+ };
+ int session_fd, mem_fd1, mem_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);
+
+ session_fd = luo_create_session(self->fd1, "session-freeze-test");
+ ASSERT_GE(session_fd, 0);
+
+ mem_fd1 = memfd_create("freeze-memfd-1", 0);
+ ASSERT_GE(mem_fd1, 0);
+ ASSERT_EQ(luo_session_preserve_fd(session_fd, mem_fd1, 0x1111), 0);
+
+ /* Non-zero reserved field must fail with -EINVAL */
+ EXPECT_EQ(ioctl(session_fd, LIVEUPDATE_SESSION_FREEZE, &bad_arg), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Freeze the session early */
+ ASSERT_EQ(luo_session_freeze(session_fd), 0);
+
+ /* Subsequent freeze attempt on an already frozen session must fail with -EBUSY */
+ EXPECT_EQ(luo_session_freeze(session_fd), -EBUSY);
+
+ /* Preserving a file after freeze must fail with -EBUSY */
+ mem_fd2 = memfd_create("freeze-memfd-2", 0);
+ ASSERT_GE(mem_fd2, 0);
+ EXPECT_EQ(luo_session_preserve_fd(session_fd, mem_fd2, 0x2222), -EBUSY);
+
+ ASSERT_EQ(close(session_fd), 0);
+ ASSERT_EQ(close(mem_fd1), 0);
+ ASSERT_EQ(close(mem_fd2), 0);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/liveupdate/luo_kexec_simple.c b/tools/testing/selftests/liveupdate/luo_kexec_simple.c
index 786ac93b9ae3..9bc8dab2e074 100644
--- a/tools/testing/selftests/liveupdate/luo_kexec_simple.c
+++ b/tools/testing/selftests/liveupdate/luo_kexec_simple.c
@@ -40,6 +40,11 @@ static void run_stage_1(int luo_fd)
TEST_MEMFD_TOKEN);
}
+ ksft_print_msg("[STAGE 1] Freezing session '%s' early...\n",
+ TEST_SESSION_NAME);
+ if (luo_session_freeze(session_fd) < 0)
+ fail_exit("luo_session_freeze for '%s'", TEST_SESSION_NAME);
+
close(luo_fd);
daemonize_and_wait();
}
@@ -60,6 +65,9 @@ static void run_stage_2(int luo_fd, int state_session_fd)
if (session_fd < 0)
fail_exit("luo_retrieve_session for '%s'", TEST_SESSION_NAME);
+ if (luo_session_freeze(session_fd) != -EINVAL)
+ fail_exit("luo_session_freeze on incoming session did not return -EINVAL");
+
ksft_print_msg("[STAGE 2] Restoring and verifying memfd (token %#x)...\n",
TEST_MEMFD_TOKEN);
mfd = restore_and_verify_memfd(session_fd, TEST_MEMFD_TOKEN,
--
2.56.0.212.g129ede20d9-goog