1b03d0d558
Add memory operations for file-backed and tmpfs memory. Call existing tests with these new memory operations to test collapse functionality of khugepaged and MADV_COLLAPSE on file-backed and tmpfs memory. Not all tests are reusable; for example, collapse_swapin_single_pte() which checks swap usage. Refactor test arguments. Usage is now: Usage: ./khugepaged <test type> [dir] <test type> : <context>:<mem_type> <context> : [all|khugepaged|madvise] <mem_type> : [all|anon|file] "file,all" mem_type requires [dir] argument "file,all" mem_type requires kernel built with CONFIG_READ_ONLY_THP_FOR_FS=y if [dir] is a (sub)directory of a tmpfs mount, tmpfs must be mounted with huge=madvise option for khugepaged tests to work Refactor calling tests to make it clear what collapse context / memory operations they support, but only invoke tests requested by user. Also log what test is being ran, and with what context / memory, to make test logs more human readable. A new test file is created and deleted for every test to ensure no pages remain in the page cache between tests (tests also may attempt to collapse different amount of memory). For file-backed memory where the file is stored on a block device, disable /sys/block/<device>/queue/read_ahead_kb so that pages don't find their way into the page cache without the tests faulting them in. Add file and shmem wrappers to vm_utils check for file and shmem hugepages in smaps. [zokeefe@google.com: fix "add thp collapse file and tmpfs testing" for tmpfs] Link: https://lkml.kernel.org/r/20220913212517.3163701-1-zokeefe@google.com Link: https://lkml.kernel.org/r/20220907144521.3115321-8-zokeefe@google.com Link: https://lkml.kernel.org/r/20220922224046.1143204-8-zokeefe@google.com Signed-off-by: Zach O'Keefe <zokeefe@google.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: Chris Kennelly <ckennelly@google.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: James Houghton <jthoughton@google.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Miaohe Lin <linmiaohe@huawei.com> Cc: Minchan Kim <minchan@kernel.org> Cc: Pasha Tatashin <pasha.tatashin@soleen.com> Cc: Peter Xu <peterx@redhat.com> Cc: Rongwei Wang <rongwei.wang@linux.alibaba.com> Cc: SeongJae Park <sj@kernel.org> Cc: Song Liu <songliubraving@fb.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Yang Shi <shy828301@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
127 lines
3.0 KiB
C
127 lines
3.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include "../kselftest.h"
|
|
#include "vm_util.h"
|
|
|
|
#define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
|
|
#define SMAP_FILE_PATH "/proc/self/smaps"
|
|
#define MAX_LINE_LENGTH 500
|
|
|
|
uint64_t pagemap_get_entry(int fd, char *start)
|
|
{
|
|
const unsigned long pfn = (unsigned long)start / getpagesize();
|
|
uint64_t entry;
|
|
int ret;
|
|
|
|
ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
|
|
if (ret != sizeof(entry))
|
|
ksft_exit_fail_msg("reading pagemap failed\n");
|
|
return entry;
|
|
}
|
|
|
|
bool pagemap_is_softdirty(int fd, char *start)
|
|
{
|
|
uint64_t entry = pagemap_get_entry(fd, start);
|
|
|
|
// Check if dirty bit (55th bit) is set
|
|
return entry & 0x0080000000000000ull;
|
|
}
|
|
|
|
void clear_softdirty(void)
|
|
{
|
|
int ret;
|
|
const char *ctrl = "4";
|
|
int fd = open("/proc/self/clear_refs", O_WRONLY);
|
|
|
|
if (fd < 0)
|
|
ksft_exit_fail_msg("opening clear_refs failed\n");
|
|
ret = write(fd, ctrl, strlen(ctrl));
|
|
close(fd);
|
|
if (ret != strlen(ctrl))
|
|
ksft_exit_fail_msg("writing clear_refs failed\n");
|
|
}
|
|
|
|
bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
|
|
{
|
|
while (fgets(buf, len, fp)) {
|
|
if (!strncmp(buf, pattern, strlen(pattern)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint64_t read_pmd_pagesize(void)
|
|
{
|
|
int fd;
|
|
char buf[20];
|
|
ssize_t num_read;
|
|
|
|
fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
|
|
if (fd == -1)
|
|
ksft_exit_fail_msg("Open hpage_pmd_size failed\n");
|
|
|
|
num_read = read(fd, buf, 19);
|
|
if (num_read < 1) {
|
|
close(fd);
|
|
ksft_exit_fail_msg("Read hpage_pmd_size failed\n");
|
|
}
|
|
buf[num_read] = '\0';
|
|
close(fd);
|
|
|
|
return strtoul(buf, NULL, 10);
|
|
}
|
|
|
|
bool __check_huge(void *addr, char *pattern, int nr_hpages,
|
|
uint64_t hpage_size)
|
|
{
|
|
uint64_t thp = -1;
|
|
int ret;
|
|
FILE *fp;
|
|
char buffer[MAX_LINE_LENGTH];
|
|
char addr_pattern[MAX_LINE_LENGTH];
|
|
|
|
ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
|
|
(unsigned long) addr);
|
|
if (ret >= MAX_LINE_LENGTH)
|
|
ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
|
|
|
|
fp = fopen(SMAP_FILE_PATH, "r");
|
|
if (!fp)
|
|
ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
|
|
|
|
if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
|
|
goto err_out;
|
|
|
|
/*
|
|
* Fetch the pattern in the same block and check the number of
|
|
* hugepages.
|
|
*/
|
|
if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
|
|
goto err_out;
|
|
|
|
snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
|
|
|
|
if (sscanf(buffer, addr_pattern, &thp) != 1)
|
|
ksft_exit_fail_msg("Reading smap error\n");
|
|
|
|
err_out:
|
|
fclose(fp);
|
|
return thp == (nr_hpages * (hpage_size >> 10));
|
|
}
|
|
|
|
bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
|
|
{
|
|
return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
|
|
}
|
|
|
|
bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
|
|
{
|
|
return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
|
|
}
|
|
|
|
bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
|
|
{
|
|
return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
|
|
}
|