Skip to content

Commit

Permalink
[LibOS] Allow madvise(MADV_DONTNEED) on non-writable mappings
Browse files Browse the repository at this point in the history
Previously, it was not implemented though there is no blocker for this
(only need to temporarily change permissions on the mapping to be
writable). This fixes Node.js v20 workloads.

Signed-off-by: Dmitrii Kuvaiskii <dmitrii.kuvaiskii@intel.com>
  • Loading branch information
dimakuv committed Feb 8, 2024
1 parent 6c868c3 commit 915691a
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 7 deletions.
1 change: 0 additions & 1 deletion Documentation/devel/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,6 @@ applications that would use these flags. In case of SGX backend, `mprotect()` be
`madvise()` implements only a minimal subset of functionality:
- `MADV_DONTNEED` is partially supported:
- resetting writable file-backed mappings is not implemented;
- zeroing non-writable mappings is not implemented;
- all other cases are implemented.
- `MADV_NORMAL`, `MADV_RANDOM`, `MADV_SEQUENTIAL`, `MADV_WILLNEED`, `MADV_FREE`,
`MADV_SOFT_OFFLINE`, `MADV_MERGEABLE`, `MADV_UNMERGEABLE`, `MADV_HUGEPAGE`, `MADV_NOHUGEPAGE` are
Expand Down
28 changes: 23 additions & 5 deletions libos/src/bookkeep/libos_vma.c
Original file line number Diff line number Diff line change
Expand Up @@ -1326,14 +1326,32 @@ static bool madvise_dontneed_visitor(struct libos_vma* vma, void* visitor_arg) {
return true;
}

if (!(vma->prot & PROT_WRITE)) {
ctx->error = -ENOSYS; // Zeroing non-writable mappings is not yet implemented.
return false;
}

uintptr_t zero_start = MAX(ctx->begin, vma->begin);
uintptr_t zero_end = MIN(ctx->end, vma->end);

pal_prot_flags_t pal_prot = LINUX_PROT_TO_PAL(vma->prot, vma->flags);
pal_prot_flags_t pal_prot_writable = pal_prot | PAL_PROT_WRITE;

if (pal_prot != pal_prot_writable) {
/* make the area writable so that it can be memset-to-zero */
int ret = PalVirtualMemoryProtect((void*)zero_start, zero_end - zero_start,
pal_prot_writable);
if (ret < 0) {
ctx->error = pal_to_unix_errno(ret);
return false;
}
}

memset((void*)zero_start, 0, zero_end - zero_start);

if (pal_prot != pal_prot_writable) {
/* the area was made writable above; restore the original permissions */
int ret = PalVirtualMemoryProtect((void*)zero_start, zero_end - zero_start, pal_prot);
if (ret < 0) {
log_error("restoring original permissions failed: %s", pal_strerror(ret));
BUG();
}
}
return true;
}

Expand Down
28 changes: 27 additions & 1 deletion libos/test/regression/madvise.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#define PAGES_CNT 128

int main(void) {
static void test_madvise_rw(void) {
size_t page_size = getpagesize();

char* m = (char*)mmap(NULL, PAGES_CNT * page_size,
Expand Down Expand Up @@ -53,6 +53,32 @@ int main(void) {
}
}
}

if (munmap(m, PAGES_CNT * page_size) < 0)
err(1, "munmap");
}

/* this test emulates Node.js v20 behavior */
static void test_madvise_none(void) {
size_t page_size = getpagesize();

char* m = (char*)mmap(NULL, PAGES_CNT * page_size,
PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (m == MAP_FAILED)
err(1, "mmap()");

int res = madvise(m, PAGES_CNT * page_size, MADV_DONTNEED);
if (res)
err(1, "madvise(%p, 0x%zx, MADV_DONTNEED) failed", m, PAGES_CNT * page_size);

if (munmap(m, PAGES_CNT * page_size) < 0)
err(1, "munmap");
}

int main(void) {
test_madvise_rw();
test_madvise_none();
puts("TEST OK");
return 0;
}

0 comments on commit 915691a

Please sign in to comment.