mm: huge_memory: debugfs for file-backed THP split
Further extend <debugfs>/split_huge_pages to accept "<path>,<pgoff_start>,<pgoff_end>" for file-backed THP split tests since tmpfs may have file backed by THP that mapped nowhere. Update selftest program to test file-backed THP split too. Link: https://lkml.kernel.org/r/20210331235309.332292-2-zi.yan@sent.com Signed-off-by: Zi Yan <ziy@nvidia.com> Suggested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reviewed-by: Yang Shi <shy828301@gmail.com> Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com> Cc: Shuah Khan <shuah@kernel.org> Cc: John Hubbard <jhubbard@nvidia.com> Cc: Sandipan Das <sandipan@linux.ibm.com> Cc: David Hildenbrand <david@redhat.com> Cc: Mika Penttila <mika.penttila@nextfour.com> Cc: David Rientjes <rientjes@google.com> Cc: Matthew Wilcox <willy@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Родитель
fa6c02315f
Коммит
fbe37501b2
|
@ -3050,6 +3050,65 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int split_huge_pages_in_file(const char *file_path, pgoff_t off_start,
|
||||
pgoff_t off_end)
|
||||
{
|
||||
struct filename *file;
|
||||
struct file *candidate;
|
||||
struct address_space *mapping;
|
||||
int ret = -EINVAL;
|
||||
pgoff_t index;
|
||||
int nr_pages = 1;
|
||||
unsigned long total = 0, split = 0;
|
||||
|
||||
file = getname_kernel(file_path);
|
||||
if (IS_ERR(file))
|
||||
return ret;
|
||||
|
||||
candidate = file_open_name(file, O_RDONLY, 0);
|
||||
if (IS_ERR(candidate))
|
||||
goto out;
|
||||
|
||||
pr_debug("split file-backed THPs in file: %s, page offset: [0x%lx - 0x%lx]\n",
|
||||
file_path, off_start, off_end);
|
||||
|
||||
mapping = candidate->f_mapping;
|
||||
|
||||
for (index = off_start; index < off_end; index += nr_pages) {
|
||||
struct page *fpage = pagecache_get_page(mapping, index,
|
||||
FGP_ENTRY | FGP_HEAD, 0);
|
||||
|
||||
nr_pages = 1;
|
||||
if (xa_is_value(fpage) || !fpage)
|
||||
continue;
|
||||
|
||||
if (!is_transparent_hugepage(fpage))
|
||||
goto next;
|
||||
|
||||
total++;
|
||||
nr_pages = thp_nr_pages(fpage);
|
||||
|
||||
if (!trylock_page(fpage))
|
||||
goto next;
|
||||
|
||||
if (!split_huge_page(fpage))
|
||||
split++;
|
||||
|
||||
unlock_page(fpage);
|
||||
next:
|
||||
put_page(fpage);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
filp_close(candidate, NULL);
|
||||
ret = 0;
|
||||
|
||||
pr_debug("%lu of %lu file-backed THP split\n", split, total);
|
||||
out:
|
||||
putname(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_INPUT_BUF_SZ 255
|
||||
|
||||
static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,
|
||||
|
@ -3057,7 +3116,8 @@ static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,
|
|||
{
|
||||
static DEFINE_MUTEX(split_debug_mutex);
|
||||
ssize_t ret;
|
||||
char input_buf[MAX_INPUT_BUF_SZ]; /* hold pid, start_vaddr, end_vaddr */
|
||||
/* hold pid, start_vaddr, end_vaddr or file_path, off_start, off_end */
|
||||
char input_buf[MAX_INPUT_BUF_SZ];
|
||||
int pid;
|
||||
unsigned long vaddr_start, vaddr_end;
|
||||
|
||||
|
@ -3072,6 +3132,34 @@ static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,
|
|||
goto out;
|
||||
|
||||
input_buf[MAX_INPUT_BUF_SZ - 1] = '\0';
|
||||
|
||||
if (input_buf[0] == '/') {
|
||||
char *tok;
|
||||
char *buf = input_buf;
|
||||
char file_path[MAX_INPUT_BUF_SZ];
|
||||
pgoff_t off_start = 0, off_end = 0;
|
||||
size_t input_len = strlen(input_buf);
|
||||
|
||||
tok = strsep(&buf, ",");
|
||||
if (tok) {
|
||||
strncpy(file_path, tok, MAX_INPUT_BUF_SZ);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sscanf(buf, "0x%lx,0x%lx", &off_start, &off_end);
|
||||
if (ret != 2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = split_huge_pages_in_file(file_path, off_start, off_end);
|
||||
if (!ret)
|
||||
ret = input_len;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sscanf(input_buf, "%d,0x%lx,0x%lx", &pid, &vaddr_start, &vaddr_end);
|
||||
if (ret == 1 && pid == 1) {
|
||||
split_huge_pages_all();
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
@ -24,6 +26,9 @@ uint64_t pmd_pagesize;
|
|||
#define SMAP_PATH "/proc/self/smaps"
|
||||
#define INPUT_MAX 80
|
||||
|
||||
#define PID_FMT "%d,0x%lx,0x%lx"
|
||||
#define PATH_FMT "%s,0x%lx,0x%lx"
|
||||
|
||||
#define PFN_MASK ((1UL<<55)-1)
|
||||
#define KPF_THP (1UL<<22)
|
||||
|
||||
|
@ -87,13 +92,16 @@ static int write_file(const char *path, const char *buf, size_t buflen)
|
|||
return (unsigned int) numwritten;
|
||||
}
|
||||
|
||||
static void write_debugfs(int pid, uint64_t vaddr_start, uint64_t vaddr_end)
|
||||
static void write_debugfs(const char *fmt, ...)
|
||||
{
|
||||
char input[INPUT_MAX];
|
||||
int ret;
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, fmt);
|
||||
ret = vsnprintf(input, INPUT_MAX, fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
ret = snprintf(input, INPUT_MAX, "%d,0x%lx,0x%lx", pid, vaddr_start,
|
||||
vaddr_end);
|
||||
if (ret >= INPUT_MAX) {
|
||||
printf("%s: Debugfs input is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -183,7 +191,8 @@ void split_pmd_thp(void)
|
|||
}
|
||||
|
||||
/* split all THPs */
|
||||
write_debugfs(getpid(), (uint64_t)one_page, (uint64_t)one_page + len);
|
||||
write_debugfs(PID_FMT, getpid(), (uint64_t)one_page,
|
||||
(uint64_t)one_page + len);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (one_page[i] != (char)i) {
|
||||
|
@ -274,7 +283,7 @@ void split_pte_mapped_thp(void)
|
|||
}
|
||||
|
||||
/* split all remapped THPs */
|
||||
write_debugfs(getpid(), (uint64_t)pte_mapped,
|
||||
write_debugfs(PID_FMT, getpid(), (uint64_t)pte_mapped,
|
||||
(uint64_t)pte_mapped + pagesize * 4);
|
||||
|
||||
/* smap does not show THPs after mremap, use kpageflags instead */
|
||||
|
@ -300,6 +309,68 @@ void split_pte_mapped_thp(void)
|
|||
close(kpageflags_fd);
|
||||
}
|
||||
|
||||
void split_file_backed_thp(void)
|
||||
{
|
||||
int status;
|
||||
int fd;
|
||||
ssize_t num_written;
|
||||
char tmpfs_template[] = "/tmp/thp_split_XXXXXX";
|
||||
const char *tmpfs_loc = mkdtemp(tmpfs_template);
|
||||
char testfile[INPUT_MAX];
|
||||
uint64_t pgoff_start = 0, pgoff_end = 1024;
|
||||
|
||||
printf("Please enable pr_debug in split_huge_pages_in_file() if you need more info.\n");
|
||||
|
||||
status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m");
|
||||
|
||||
if (status) {
|
||||
printf("Unable to create a tmpfs for testing\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc);
|
||||
if (status >= INPUT_MAX) {
|
||||
printf("Fail to create file-backed THP split testing file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fd = open(testfile, O_CREAT|O_WRONLY);
|
||||
if (fd == -1) {
|
||||
perror("Cannot open testing file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* write something to the file, so a file-backed THP can be allocated */
|
||||
num_written = write(fd, tmpfs_loc, sizeof(tmpfs_loc));
|
||||
close(fd);
|
||||
|
||||
if (num_written < 1) {
|
||||
printf("Fail to write data to testing file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* split the file-backed THP */
|
||||
write_debugfs(PATH_FMT, testfile, pgoff_start, pgoff_end);
|
||||
|
||||
status = unlink(testfile);
|
||||
if (status)
|
||||
perror("Cannot remove testing file\n");
|
||||
|
||||
cleanup:
|
||||
status = umount(tmpfs_loc);
|
||||
if (status) {
|
||||
printf("Unable to umount %s\n", tmpfs_loc);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
status = rmdir(tmpfs_loc);
|
||||
if (status) {
|
||||
perror("cannot remove tmp dir");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("file-backed THP split test done, please check dmesg for more information\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (geteuid() != 0) {
|
||||
|
@ -313,6 +384,7 @@ int main(int argc, char **argv)
|
|||
|
||||
split_pmd_thp();
|
||||
split_pte_mapped_thp();
|
||||
split_file_backed_thp();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче