#include #include #include #include #include #include #define PAGE_SIZE (4096) #define PMD_SIZE (PAGE_SIZE * 512) #define ULONG_SIZE (sizeof(unsigned long)) #define ULONGS_PER_PAGE ((PAGE_SIZE) / (ULONG_SIZE)) #define MAGIC_VALUE 0xabcd int write_file(char *name, unsigned long size) { unsigned long *buf; unsigned long i, p; int fd; int ret; buf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (buf == MAP_FAILED) { printf("mmap failed\n"); exit(1); } fd = creat(name, S_IRWXU); if (fd == -1) { perror("creat"); exit(1); } for (i = 0; i < size / PAGE_SIZE; i++) { buf[0] = MAGIC_VALUE + i; ret = write(fd, buf, PAGE_SIZE); // yeah, sloppy if (ret != PAGE_SIZE) { perror("write"); exit(1); } } close(fd); munmap(buf, PAGE_SIZE); } void touch(char * buf, unsigned long start, unsigned long size) { volatile char foo; for (char * ptr = buf + start; ptr < buf + start + size; ptr += PAGE_SIZE) { foo = *ptr; } } void test_read(int fd, off_t off, size_t count, size_t bufsize) { char *buf = NULL; int ret; buf = mmap(NULL, bufsize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if (buf == MAP_FAILED) { perror("mmap"); exit(1); } off = lseek(fd, off, SEEK_SET); if (off == -1) { perror("lseek"); exit(1); } for (size_t done = 0; done < count; done += bufsize) { size_t to_read = MIN(bufsize, count - done); size_t buf_read = 0; while (to_read > 0) { ssize_t read_once = read(fd, buf + buf_read, to_read); if (read_once == -1) { perror("read"); exit(1); } if (read_once == 0) { printf("EOF while reading\n"); return; } to_read -= read_once; buf_read += read_once; } for (size_t pos = 0; pos < buf_read; pos += PMD_SIZE) { unsigned long abs_pos = off + done + pos; unsigned long found_val = *((unsigned long *)(buf + pos)) - MAGIC_VALUE; unsigned long expected_val = abs_pos / PAGE_SIZE; if (found_val != expected_val) { printf("mismatch at offset %llu: read value expected for offset %llu instead of %llu\n", abs_pos, found_val * PAGE_SIZE, expected_val * PAGE_SIZE); } } } munmap(buf, bufsize); } #define MAP_SIZE (2*PMD_SIZE) int main(int argc, char **argv) { int fd, ret; unsigned char *buf; pid_t pid; if (argc != 2) { printf("usage: %s /path/to/test_file\n", argv[0]); printf("use a filesystem using generic_file_read_iter() such as ext3 or xfs\n"); exit(1); } write_file(argv[1], MAP_SIZE); fd = open(argv[1], O_RDONLY); if (fd == -1) { perror("open"); exit(1); } buf = mmap((void *)0x700000000000UL, MAP_SIZE, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); if (buf == MAP_FAILED) { perror("mmap failed"); exit(1); } ret = madvise(buf, MAP_SIZE, MADV_HUGEPAGE); if (ret) perror("madvise"); touch(buf, 0, MAP_SIZE); printf("pid: %d\n", getpid()); printf("press enter to continue after khugepaged has collapsed the huge pages\n"); printf("check /proc/meminfo FileHugePages: to verify\n"); printf("use 'echo 100 > /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs' to speed khugepaged up\n"); getchar(); touch(buf, 0, MAP_SIZE); ret = munmap(buf, MAP_SIZE); if (ret) { perror("munmap"); exit(1); } ret = close(fd); if (ret) { perror("close"); exit(1); } fd = open(argv[1], O_RDONLY); if (fd == -1) { perror("open"); exit(1); } test_read(fd, 0, MAP_SIZE, MAP_SIZE); close(fd); }