forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftest: add simple test for soft-dirty bit
It creates a mapping of 3 pages and checks that reads, writes and clear-refs result in present and soft-dirt bits reported from pagemap2 set as expected. [[email protected]: alphasort the Makefile TARGETS to reduce rejects] Signed-off-by: Pavel Emelyanov <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
3 changed files
with
128 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
CFLAGS += -iquote../../../../include/uapi -Wall | ||
soft-dirty: soft-dirty.c | ||
|
||
all: soft-dirty | ||
|
||
clean: | ||
rm -f soft-dirty | ||
|
||
run_tests: all | ||
@./soft-dirty || echo "soft-dirty selftests: [FAIL]" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <sys/mman.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
#include <sys/types.h> | ||
|
||
typedef unsigned long long u64; | ||
|
||
#define PME_PRESENT (1ULL << 63) | ||
#define PME_SOFT_DIRTY (1Ull << 55) | ||
|
||
#define PAGES_TO_TEST 3 | ||
#ifndef PAGE_SIZE | ||
#define PAGE_SIZE 4096 | ||
#endif | ||
|
||
static void get_pagemap2(char *mem, u64 *map) | ||
{ | ||
int fd; | ||
|
||
fd = open("/proc/self/pagemap2", O_RDONLY); | ||
if (fd < 0) { | ||
perror("Can't open pagemap2"); | ||
exit(1); | ||
} | ||
|
||
lseek(fd, (unsigned long)mem / PAGE_SIZE * sizeof(u64), SEEK_SET); | ||
read(fd, map, sizeof(u64) * PAGES_TO_TEST); | ||
close(fd); | ||
} | ||
|
||
static inline char map_p(u64 map) | ||
{ | ||
return map & PME_PRESENT ? 'p' : '-'; | ||
} | ||
|
||
static inline char map_sd(u64 map) | ||
{ | ||
return map & PME_SOFT_DIRTY ? 'd' : '-'; | ||
} | ||
|
||
static int check_pte(int step, int page, u64 *map, u64 want) | ||
{ | ||
if ((map[page] & want) != want) { | ||
printf("Step %d Page %d has %c%c, want %c%c\n", | ||
step, page, | ||
map_p(map[page]), map_sd(map[page]), | ||
map_p(want), map_sd(want)); | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static void clear_refs(void) | ||
{ | ||
int fd; | ||
char *v = "4"; | ||
|
||
fd = open("/proc/self/clear_refs", O_WRONLY); | ||
if (write(fd, v, 3) < 3) { | ||
perror("Can't clear soft-dirty bit"); | ||
exit(1); | ||
} | ||
close(fd); | ||
} | ||
|
||
int main(void) | ||
{ | ||
char *mem, x; | ||
u64 map[PAGES_TO_TEST]; | ||
|
||
mem = mmap(NULL, PAGES_TO_TEST * PAGE_SIZE, | ||
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); | ||
|
||
x = mem[0]; | ||
mem[2 * PAGE_SIZE] = 'c'; | ||
get_pagemap2(mem, map); | ||
|
||
if (check_pte(1, 0, map, PME_PRESENT)) | ||
return 1; | ||
if (check_pte(1, 1, map, 0)) | ||
return 1; | ||
if (check_pte(1, 2, map, PME_PRESENT | PME_SOFT_DIRTY)) | ||
return 1; | ||
|
||
clear_refs(); | ||
get_pagemap2(mem, map); | ||
|
||
if (check_pte(2, 0, map, PME_PRESENT)) | ||
return 1; | ||
if (check_pte(2, 1, map, 0)) | ||
return 1; | ||
if (check_pte(2, 2, map, PME_PRESENT)) | ||
return 1; | ||
|
||
mem[0] = 'a'; | ||
mem[PAGE_SIZE] = 'b'; | ||
x = mem[2 * PAGE_SIZE]; | ||
get_pagemap2(mem, map); | ||
|
||
if (check_pte(3, 0, map, PME_PRESENT | PME_SOFT_DIRTY)) | ||
return 1; | ||
if (check_pte(3, 1, map, PME_PRESENT | PME_SOFT_DIRTY)) | ||
return 1; | ||
if (check_pte(3, 2, map, PME_PRESENT)) | ||
return 1; | ||
|
||
(void)x; /* gcc warn */ | ||
|
||
printf("PASS\n"); | ||
return 0; | ||
} |