From: "Pratyush Yadav (Google)" <[email protected]> When memory is added to a memfd via fallocate(), it does not get zeroed immediately. This is tracked by the absence of the uptodate folio flag. Initially, memfd preservation simply saved the folio flags at preserve time. This led to a bug, where all writes to un-initialized fallocated memory after preserve were lost after live update. This is fixed by patch [0] (not in mainline as of writing this).
Add a test that fallocates some memory in a memfd, preserves it, writes to it. Then in stage 2 it verifies the written content is still present. [0] https://lore.kernel.org/linux-mm/[email protected]/ Signed-off-by: Pratyush Yadav (Google) <[email protected]> --- .../testing/selftests/liveupdate/luo_memfd.c | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tools/testing/selftests/liveupdate/luo_memfd.c b/tools/testing/selftests/liveupdate/luo_memfd.c index 75f88101e7b5..dde3c78db50e 100644 --- a/tools/testing/selftests/liveupdate/luo_memfd.c +++ b/tools/testing/selftests/liveupdate/luo_memfd.c @@ -43,6 +43,11 @@ #define PRESERVED_MEMFD_TOKEN 1 #define PRESERVED_BUFFER_SIZE SZ_1M +#define FALLOCATE_SESSION_NAME "fallocate_session" +#define FALLOCATE_MEMFD_TOKEN 1 +#define FALLOCATE_BUFFER_SIZE SZ_1M +#define RANDOM_DATA_FILE_FALLOCATE "luo_random_data_fallocate.bin" + #define LIVEUPDATE_DEV "/dev/liveupdate" static int luo_fd = -1, stage; @@ -193,6 +198,65 @@ TEST(preserved_ops) ASSERT_EQ(lseek(fd, 0, SEEK_END), PRESERVED_BUFFER_SIZE); } +/* + * Test that an fallocated memfd is preserved across live update and can be + * written to after being preserved. + */ +TEST(fallocate_memfd) +{ + int fd, session; + char *buffer; + struct liveupdate_session_preserve_fd preserve_arg = { .size = sizeof(preserve_arg) }; + struct liveupdate_session_retrieve_fd retrieve_arg = { .size = sizeof(retrieve_arg) }; + + buffer = malloc(FALLOCATE_BUFFER_SIZE); + ASSERT_NE(buffer, NULL); + + switch (stage) { + case 1: + session = luo_create_session(luo_fd, FALLOCATE_SESSION_NAME); + ASSERT_GE(session, 0); + + fd = memfd_create("fallocate_memfd", 0); + ASSERT_GE(fd, 0); + + /* Fallocate memory but do not write to it yet */ + ASSERT_EQ(fallocate(fd, 0, 0, FALLOCATE_BUFFER_SIZE), 0); + + preserve_arg.fd = fd; + preserve_arg.token = FALLOCATE_MEMFD_TOKEN; + ASSERT_GE(ioctl(session, LIVEUPDATE_SESSION_PRESERVE_FD, &preserve_arg), 0); + + /* Now write to it after preserving */ + ASSERT_GE(generate_random_data(buffer, FALLOCATE_BUFFER_SIZE), 0); + ASSERT_EQ(save_test_data(RANDOM_DATA_FILE_FALLOCATE, buffer, FALLOCATE_BUFFER_SIZE), 0); + + ASSERT_GE(lseek(fd, 0, SEEK_SET), 0); + ASSERT_EQ(write_size(fd, buffer, FALLOCATE_BUFFER_SIZE), 0); + + daemonize_and_wait(); + break; + case 2: + session = luo_retrieve_session(luo_fd, FALLOCATE_SESSION_NAME); + ASSERT_GE(session, 0); + + ASSERT_EQ(load_test_data(RANDOM_DATA_FILE_FALLOCATE, buffer, FALLOCATE_BUFFER_SIZE), 0); + + retrieve_arg.token = FALLOCATE_MEMFD_TOKEN; + ASSERT_GE(ioctl(session, LIVEUPDATE_SESSION_RETRIEVE_FD, &retrieve_arg), 0); + fd = retrieve_arg.fd; + ASSERT_GE(fd, 0); + + ASSERT_EQ(verify_fd_content(fd, buffer, FALLOCATE_BUFFER_SIZE), 0); + + ASSERT_EQ(luo_session_finish(session), 0); + break; + default: + TH_LOG("Unknown stage %d\n", stage); + ASSERT_FALSE(true); + } +} + int main(int argc, char *argv[]) { int session, expected_stage = 0; -- 2.53.0.473.g4a7958ca14-goog

