Skip to content

Commit 99aa772

Browse files
CmdrMoozyakpm00
authored andcommitted
selftests/mm: add uffd unit test for UFFDIO_POISON
The test is pretty basic, and exercises UFFDIO_POISON straightforwardly. We register a region with userfaultfd, in missing fault mode. For each fault, we either UFFDIO_COPY a zeroed page (odd pages) or UFFDIO_POISON (even pages). We do this mix to test "something like a real use case", where guest memory would be some mix of poisoned and non-poisoned pages. We read each page in the region, and assert that the odd pages are zeroed as expected, and the even pages yield a SIGBUS as expected. Why UFFDIO_COPY instead of UFFDIO_ZEROPAGE? Because hugetlb doesn't support UFFDIO_ZEROPAGE, and we don't want to have special case code. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Axel Rasmussen <[email protected]> Acked-by: Peter Xu <[email protected]> Cc: Al Viro <[email protected]> Cc: Brian Geffon <[email protected]> Cc: Christian Brauner <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Gaosheng Cui <[email protected]> Cc: Huang, Ying <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: James Houghton <[email protected]> Cc: Jan Alexander Steffens (heftig) <[email protected]> Cc: Jiaqi Yan <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Kefeng Wang <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Mike Kravetz <[email protected]> Cc: Mike Rapoport (IBM) <[email protected]> Cc: Muchun Song <[email protected]> Cc: Nadav Amit <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Ryan Roberts <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Suleiman Souhlal <[email protected]> Cc: Suren Baghdasaryan <[email protected]> Cc: T.J. Alumbaugh <[email protected]> Cc: Yu Zhao <[email protected]> Cc: ZhangPeng <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 7cf0f9e commit 99aa772

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

tools/testing/selftests/mm/uffd-unit-tests.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,117 @@ static void uffd_zeropage_test(uffd_test_args_t *args)
951951
uffd_test_pass();
952952
}
953953

954+
static void uffd_register_poison(int uffd, void *addr, uint64_t len)
955+
{
956+
uint64_t ioctls = 0;
957+
uint64_t expected = (1 << _UFFDIO_COPY) | (1 << _UFFDIO_POISON);
958+
959+
if (uffd_register_with_ioctls(uffd, addr, len, true,
960+
false, false, &ioctls))
961+
err("poison register fail");
962+
963+
if ((ioctls & expected) != expected)
964+
err("registered area doesn't support COPY and POISON ioctls");
965+
}
966+
967+
static void do_uffdio_poison(int uffd, unsigned long offset)
968+
{
969+
struct uffdio_poison uffdio_poison = { 0 };
970+
int ret;
971+
__s64 res;
972+
973+
uffdio_poison.range.start = (unsigned long) area_dst + offset;
974+
uffdio_poison.range.len = page_size;
975+
uffdio_poison.mode = 0;
976+
ret = ioctl(uffd, UFFDIO_POISON, &uffdio_poison);
977+
res = uffdio_poison.updated;
978+
979+
if (ret)
980+
err("UFFDIO_POISON error: %"PRId64, (int64_t)res);
981+
else if (res != page_size)
982+
err("UFFDIO_POISON unexpected size: %"PRId64, (int64_t)res);
983+
}
984+
985+
static void uffd_poison_handle_fault(
986+
struct uffd_msg *msg, struct uffd_args *args)
987+
{
988+
unsigned long offset;
989+
990+
if (msg->event != UFFD_EVENT_PAGEFAULT)
991+
err("unexpected msg event %u", msg->event);
992+
993+
if (msg->arg.pagefault.flags &
994+
(UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_MINOR))
995+
err("unexpected fault type %llu", msg->arg.pagefault.flags);
996+
997+
offset = (char *)(unsigned long)msg->arg.pagefault.address - area_dst;
998+
offset &= ~(page_size-1);
999+
1000+
/* Odd pages -> copy zeroed page; even pages -> poison. */
1001+
if (offset & page_size)
1002+
copy_page(uffd, offset, false);
1003+
else
1004+
do_uffdio_poison(uffd, offset);
1005+
}
1006+
1007+
static void uffd_poison_test(uffd_test_args_t *targs)
1008+
{
1009+
pthread_t uffd_mon;
1010+
char c;
1011+
struct uffd_args args = { 0 };
1012+
struct sigaction act = { 0 };
1013+
unsigned long nr_sigbus = 0;
1014+
unsigned long nr;
1015+
1016+
fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
1017+
1018+
uffd_register_poison(uffd, area_dst, nr_pages * page_size);
1019+
memset(area_src, 0, nr_pages * page_size);
1020+
1021+
args.handle_fault = uffd_poison_handle_fault;
1022+
if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
1023+
err("uffd_poll_thread create");
1024+
1025+
sigbuf = &jbuf;
1026+
act.sa_sigaction = sighndl;
1027+
act.sa_flags = SA_SIGINFO;
1028+
if (sigaction(SIGBUS, &act, 0))
1029+
err("sigaction");
1030+
1031+
for (nr = 0; nr < nr_pages; ++nr) {
1032+
unsigned long offset = nr * page_size;
1033+
const char *bytes = (const char *) area_dst + offset;
1034+
const char *i;
1035+
1036+
if (sigsetjmp(*sigbuf, 1)) {
1037+
/*
1038+
* Access below triggered a SIGBUS, which was caught by
1039+
* sighndl, which then jumped here. Count this SIGBUS,
1040+
* and move on to next page.
1041+
*/
1042+
++nr_sigbus;
1043+
continue;
1044+
}
1045+
1046+
for (i = bytes; i < bytes + page_size; ++i) {
1047+
if (*i)
1048+
err("nonzero byte in area_dst (%p) at %p: %u",
1049+
area_dst, i, *i);
1050+
}
1051+
}
1052+
1053+
if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
1054+
err("pipe write");
1055+
if (pthread_join(uffd_mon, NULL))
1056+
err("pthread_join()");
1057+
1058+
if (nr_sigbus != nr_pages / 2)
1059+
err("expected to receive %lu SIGBUS, actually received %lu",
1060+
nr_pages / 2, nr_sigbus);
1061+
1062+
uffd_test_pass();
1063+
}
1064+
9541065
/*
9551066
* Test the returned uffdio_register.ioctls with different register modes.
9561067
* Note that _UFFDIO_ZEROPAGE is tested separately in the zeropage test.
@@ -1126,6 +1237,12 @@ uffd_test_case_t uffd_tests[] = {
11261237
UFFD_FEATURE_PAGEFAULT_FLAG_WP |
11271238
UFFD_FEATURE_WP_HUGETLBFS_SHMEM,
11281239
},
1240+
{
1241+
.name = "poison",
1242+
.uffd_fn = uffd_poison_test,
1243+
.mem_targets = MEM_ALL,
1244+
.uffd_feature_required = UFFD_FEATURE_POISON,
1245+
},
11291246
};
11301247

11311248
static void usage(const char *prog)

0 commit comments

Comments
 (0)