[PATCH 3/5] selftests/liveupdate: Test session and file limit removal

Pasha Tatashin
2026-04-14T20:02:45Z

With the removal of static limits on the number of sessions and files per
session, the orchestrator now uses dynamic allocation.

Add new test cases to verify that the system can handle a large number of
sessions and files. These tests ensure that the dynamic block allocation
and reuse logic for session metadata and outgoing files work correctly
beyond the previous static limits.

Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
.../testing/selftests/liveupdate/liveupdate.c | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)

diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index 37c808fbe1e9..0eaf97b19267 100644
--- a/tools/testing/selftests/liveupdate/liveupdate.c
+++ b/tools/testing/selftests/liveupdate/liveupdate.c
@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/resource.h>
#include <unistd.h>

#include <linux/liveupdate.h>
@@ -386,4 +387,102 @@ TEST_F(liveupdate_device, prevent_double_preservation)
ASSERT_EQ(close(session_fd2), 0);
}

+static void ensure_nofile_limit(struct __test_metadata *_metadata,
+ long min_limit)
+{
+ struct rlimit hl;
+
+ if (getrlimit(RLIMIT_NOFILE, &hl) < 0)
+ ksft_exit_fail_msg("getrlimit failed: %s\n", strerror(errno));
+
+ if (hl.rlim_cur >= min_limit)
+ return;
+
+ hl.rlim_cur = min_limit;
+ if (hl.rlim_cur > hl.rlim_max)
+ hl.rlim_max = hl.rlim_cur;
+
+ if (setrlimit(RLIMIT_NOFILE, &hl) < 0) {
+ if (errno == EPERM) {
+ SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE to %ld",
+ hl.rlim_cur);
+ }
+ ksft_exit_fail_msg("setrlimit to %ld failed: %s\n",
+ hl.rlim_cur, strerror(errno));
+ }
+}
+
+/*
+ * Test Case: Manage Many Sessions
+ *
+ * Verifies that a large number of sessions can be created and then
+ * destroyed during normal system operation. This specifically tests the
+ * dynamic block allocation and reuse logic for session metadata management
+ * without preserving any files.
+ */
+TEST_F(liveupdate_device, preserve_many_sessions)
+{
+#define MANY_SESSIONS 2000
+ int session_fds[MANY_SESSIONS];
+ int 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);
+
+ ensure_nofile_limit(_metadata, MANY_SESSIONS + 10);
+ if (_metadata->exit_code == KSFT_SKIP)
+ return;
+
+ for (i = 0; i < MANY_SESSIONS; i++) {
+ char name[64];
+
+ snprintf(name, sizeof(name), "many-session-%d", i);
+ session_fds[i] = create_session(self->fd1, name);
+ ASSERT_GE(session_fds[i], 0);
+ }
+
+ for (i = 0; i < MANY_SESSIONS; i++)
+ ASSERT_EQ(close(session_fds[i]), 0);
+}
+
+/*
+ * Test Case: Preserve Many Files
+ *
+ * Verifies that a large number of files can be preserved in a single session
+ * and then destroyed during normal system operation. This tests the dynamic
+ * block allocation and management for outgoing files.
+ */
+TEST_F(liveupdate_device, preserve_many_files)
+{
+#define MANY_FILES 500
+ int mem_fds[MANY_FILES];
+ int session_fd;
+ int 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);
+
+ session_fd = create_session(self->fd1, "many-files-test");
+ ASSERT_GE(session_fd, 0);
+
+ ensure_nofile_limit(_metadata, MANY_FILES + 10);
+ if (_metadata->exit_code == KSFT_SKIP)
+ return;
+
+ for (i = 0; i < MANY_FILES; i++) {
+ mem_fds[i] = memfd_create("test-memfd", 0);
+ ASSERT_GE(mem_fds[i], 0);
+ ASSERT_EQ(preserve_fd(session_fd, mem_fds[i], i), 0);
+ }
+
+ for (i = 0; i < MANY_FILES; i++)
+ ASSERT_EQ(close(mem_fds[i]), 0);
+
+ ASSERT_EQ(close(session_fd), 0);
+}
+
TEST_HARNESS_MAIN
--
2.43.0