diff --git a/src/libpmem2/deep_flush_linux.c b/src/libpmem2/deep_flush_linux.c index 3d8431a3286606a383709ce25745f8c347766e25..567174b4253bfb0bddb6316e22b54fa664594c21 100644 --- a/src/libpmem2/deep_flush_linux.c +++ b/src/libpmem2/deep_flush_linux.c @@ -83,6 +83,19 @@ pmem2_deep_flush_dax(struct pmem2_map *map, void *ptr, size_t size) enum pmem2_file_type type = map->source.value.ftype; if (type == PMEM2_FTYPE_REG) { + /* + * Flushing using OS-provided mechanisms requires that + * the address be a multiple of the page size. + * Align address down and change len so that [addr, addr + len) + * still contains the initial range. + */ + + /* round address down to page boundary */ + uintptr_t new_addr = ALIGN_DOWN((uintptr_t)ptr, Pagesize); + /* increase len by the amount we gain when we round addr down */ + size += (uintptr_t)ptr - new_addr; + ptr = (void *)new_addr; + ret = pmem2_flush_file_buffers_os(map, ptr, size, 0); if (ret) { LOG(1, "cannot flush buffers addr %p len %zu", diff --git a/src/test/pmem2_integration/TESTS.py b/src/test/pmem2_integration/TESTS.py index e8178e32f81c1acbcc455063b8c9c7518f3fda85..24ba2991c8a6d06b0187d7f046fb82e71eb9212c 100755 --- a/src/test/pmem2_integration/TESTS.py +++ b/src/test/pmem2_integration/TESTS.py @@ -282,3 +282,8 @@ class TEST36(PMEM2_INTEGRATION_DEV_DAXES): class TEST37(PMEM2_INTEGRATION_DEV_DAXES): """test deep flush with overlaping part""" test_case = "test_deep_flush_overlap" + + +class TEST38(PMEM2_INTEGRATION): + """test for unaligned persists""" + test_case = "test_unaligned_persist" diff --git a/src/test/pmem2_integration/pmem2_integration.c b/src/test/pmem2_integration/pmem2_integration.c index 68f3654ae38ac71bdf0fea3fe1d9e77303257412..fe50d073568e1b12007619e1d9927bd93134a48c 100644 --- a/src/test/pmem2_integration/pmem2_integration.c +++ b/src/test/pmem2_integration/pmem2_integration.c @@ -736,6 +736,47 @@ test_deep_flush_overlap(const struct test_case *tc, int argc, char *argv[]) return 1; } +/* + * test_unaligned_persist -- try flushing on non-page-aligned addresses + */ +static int +test_unaligned_persist(const struct test_case *tc, int argc, char *argv[]) +{ + char *file = argv[0]; + int fd = OPEN(file, O_RDWR); + +#define FLUSH_OFFSET 256 + + struct pmem2_config *cfg; + struct pmem2_source *src; + PMEM2_PREPARE_CONFIG_INTEGRATION(&cfg, &src, fd, + PMEM2_GRANULARITY_PAGE); + + size_t len; + PMEM2_SOURCE_SIZE(src, &len); + + struct pmem2_map *map = map_valid(cfg, src, len); + + size_t map_size = pmem2_map_get_size(map); + char *addr = pmem2_map_get_address(map); + pmem2_persist_fn persist_fn = pmem2_get_persist_fn(map); + memset(addr, 0, len); + persist_fn(addr + FLUSH_OFFSET, len - FLUSH_OFFSET); + + int ret = pmem2_deep_flush(map, addr + FLUSH_OFFSET, + map_size - FLUSH_OFFSET); + UT_PMEM2_EXPECT_RETURN(ret, 0); + + pmem2_unmap(&map); + PMEM2_CONFIG_DELETE(&cfg); + PMEM2_SOURCE_DELETE(&src); + CLOSE(fd); + +#undef FLUSH_OFFSET + + return 1; +} + /* * test_source_anon -- tests map/config/source functions in combination * with anonymous source. @@ -880,6 +921,7 @@ static struct test_case test_cases[] = { TEST_CASE(test_source_anon_page), TEST_CASE(test_source_anon_too_small), TEST_CASE(test_source_anon_zero_len), + TEST_CASE(test_unaligned_persist), }; #define NTESTS (sizeof(test_cases) / sizeof(test_cases[0]))