mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 15:04:45 +00:00
selftests/mm: factor out thp settings management
The khugepaged test has a useful framework for save/restore/pop/push of all thp settings via the sysfs interface. This will be useful to explicitly control multi-size THP settings in other tests, so let's move it out of khugepaged and into its own thp_settings.[c|h] utility. Link: https://lkml.kernel.org/r/20231207161211.2374093-7-ryan.roberts@arm.com Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> Tested-by: Alistair Popple <apopple@nvidia.com> Acked-by: David Hildenbrand <david@redhat.com> Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com> Tested-by: John Hubbard <jhubbard@nvidia.com> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Barry Song <v-songbaohua@oppo.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: David Rientjes <rientjes@google.com> Cc: "Huang, Ying" <ying.huang@intel.com> Cc: Hugh Dickins <hughd@google.com> Cc: Itaru Kitayama <itaru.kitayama@gmail.com> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Luis Chamberlain <mcgrof@kernel.org> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Yang Shi <shy828301@gmail.com> Cc: Yin Fengwei <fengwei.yin@intel.com> Cc: Yu Zhao <yuzhao@google.com> Cc: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
b6aab3384c
commit
00679a183a
@ -117,8 +117,8 @@ TEST_FILES += va_high_addr_switch.sh
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
$(TEST_GEN_PROGS): vm_util.c
|
||||
$(TEST_GEN_FILES): vm_util.c
|
||||
$(TEST_GEN_PROGS): vm_util.c thp_settings.c
|
||||
$(TEST_GEN_FILES): vm_util.c thp_settings.c
|
||||
|
||||
$(OUTPUT)/uffd-stress: uffd-common.c
|
||||
$(OUTPUT)/uffd-unit-tests: uffd-common.c
|
||||
|
@ -22,13 +22,13 @@
|
||||
#include "linux/magic.h"
|
||||
|
||||
#include "vm_util.h"
|
||||
#include "thp_settings.h"
|
||||
|
||||
#define BASE_ADDR ((void *)(1UL << 30))
|
||||
static unsigned long hpage_pmd_size;
|
||||
static unsigned long page_size;
|
||||
static int hpage_pmd_nr;
|
||||
|
||||
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
|
||||
#define PID_SMAPS "/proc/self/smaps"
|
||||
#define TEST_FILE "collapse_test_file"
|
||||
|
||||
@ -71,78 +71,7 @@ struct file_info {
|
||||
};
|
||||
|
||||
static struct file_info finfo;
|
||||
|
||||
enum thp_enabled {
|
||||
THP_ALWAYS,
|
||||
THP_MADVISE,
|
||||
THP_NEVER,
|
||||
};
|
||||
|
||||
static const char *thp_enabled_strings[] = {
|
||||
"always",
|
||||
"madvise",
|
||||
"never",
|
||||
NULL
|
||||
};
|
||||
|
||||
enum thp_defrag {
|
||||
THP_DEFRAG_ALWAYS,
|
||||
THP_DEFRAG_DEFER,
|
||||
THP_DEFRAG_DEFER_MADVISE,
|
||||
THP_DEFRAG_MADVISE,
|
||||
THP_DEFRAG_NEVER,
|
||||
};
|
||||
|
||||
static const char *thp_defrag_strings[] = {
|
||||
"always",
|
||||
"defer",
|
||||
"defer+madvise",
|
||||
"madvise",
|
||||
"never",
|
||||
NULL
|
||||
};
|
||||
|
||||
enum shmem_enabled {
|
||||
SHMEM_ALWAYS,
|
||||
SHMEM_WITHIN_SIZE,
|
||||
SHMEM_ADVISE,
|
||||
SHMEM_NEVER,
|
||||
SHMEM_DENY,
|
||||
SHMEM_FORCE,
|
||||
};
|
||||
|
||||
static const char *shmem_enabled_strings[] = {
|
||||
"always",
|
||||
"within_size",
|
||||
"advise",
|
||||
"never",
|
||||
"deny",
|
||||
"force",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct khugepaged_settings {
|
||||
bool defrag;
|
||||
unsigned int alloc_sleep_millisecs;
|
||||
unsigned int scan_sleep_millisecs;
|
||||
unsigned int max_ptes_none;
|
||||
unsigned int max_ptes_swap;
|
||||
unsigned int max_ptes_shared;
|
||||
unsigned long pages_to_scan;
|
||||
};
|
||||
|
||||
struct settings {
|
||||
enum thp_enabled thp_enabled;
|
||||
enum thp_defrag thp_defrag;
|
||||
enum shmem_enabled shmem_enabled;
|
||||
bool use_zero_page;
|
||||
struct khugepaged_settings khugepaged;
|
||||
unsigned long read_ahead_kb;
|
||||
};
|
||||
|
||||
static struct settings saved_settings;
|
||||
static bool skip_settings_restore;
|
||||
|
||||
static int exit_status;
|
||||
|
||||
static void success(const char *msg)
|
||||
@ -161,226 +90,13 @@ static void skip(const char *msg)
|
||||
printf(" \e[33m%s\e[0m\n", msg);
|
||||
}
|
||||
|
||||
static int read_file(const char *path, char *buf, size_t buflen)
|
||||
{
|
||||
int fd;
|
||||
ssize_t numread;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
|
||||
numread = read(fd, buf, buflen - 1);
|
||||
if (numread < 1) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[numread] = '\0';
|
||||
close(fd);
|
||||
|
||||
return (unsigned int) numread;
|
||||
}
|
||||
|
||||
static int write_file(const char *path, const char *buf, size_t buflen)
|
||||
{
|
||||
int fd;
|
||||
ssize_t numwritten;
|
||||
|
||||
fd = open(path, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
printf("open(%s)\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
numwritten = write(fd, buf, buflen - 1);
|
||||
close(fd);
|
||||
if (numwritten < 1) {
|
||||
printf("write(%s)\n", buf);
|
||||
exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned int) numwritten;
|
||||
}
|
||||
|
||||
static int read_string(const char *name, const char *strings[])
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[256];
|
||||
char *c;
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!read_file(path, buf, sizeof(buf))) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
c = strchr(buf, '[');
|
||||
if (!c) {
|
||||
printf("%s: Parse failure\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
c++;
|
||||
memmove(buf, c, sizeof(buf) - (c - buf));
|
||||
|
||||
c = strchr(buf, ']');
|
||||
if (!c) {
|
||||
printf("%s: Parse failure\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*c = '\0';
|
||||
|
||||
ret = 0;
|
||||
while (strings[ret]) {
|
||||
if (!strcmp(strings[ret], buf))
|
||||
return ret;
|
||||
ret++;
|
||||
}
|
||||
|
||||
printf("Failed to parse %s\n", name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void write_string(const char *name, const char *val)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file(path, val, strlen(val) + 1)) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned long _read_num(const char *path)
|
||||
{
|
||||
char buf[21];
|
||||
|
||||
if (read_file(path, buf, sizeof(buf)) < 0) {
|
||||
perror("read_file(read_num)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return strtoul(buf, NULL, 10);
|
||||
}
|
||||
|
||||
static const unsigned long read_num(const char *name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return _read_num(path);
|
||||
}
|
||||
|
||||
static void _write_num(const char *path, unsigned long num)
|
||||
{
|
||||
char buf[21];
|
||||
|
||||
sprintf(buf, "%ld", num);
|
||||
if (!write_file(path, buf, strlen(buf) + 1)) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_num(const char *name, unsigned long num)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
_write_num(path, num);
|
||||
}
|
||||
|
||||
static void write_settings(struct settings *settings)
|
||||
{
|
||||
struct khugepaged_settings *khugepaged = &settings->khugepaged;
|
||||
|
||||
write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
|
||||
write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
|
||||
write_string("shmem_enabled",
|
||||
shmem_enabled_strings[settings->shmem_enabled]);
|
||||
write_num("use_zero_page", settings->use_zero_page);
|
||||
|
||||
write_num("khugepaged/defrag", khugepaged->defrag);
|
||||
write_num("khugepaged/alloc_sleep_millisecs",
|
||||
khugepaged->alloc_sleep_millisecs);
|
||||
write_num("khugepaged/scan_sleep_millisecs",
|
||||
khugepaged->scan_sleep_millisecs);
|
||||
write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
|
||||
write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
|
||||
write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
|
||||
write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
|
||||
|
||||
if (file_ops && finfo.type == VMA_FILE)
|
||||
_write_num(finfo.dev_queue_read_ahead_path,
|
||||
settings->read_ahead_kb);
|
||||
}
|
||||
|
||||
#define MAX_SETTINGS_DEPTH 4
|
||||
static struct settings settings_stack[MAX_SETTINGS_DEPTH];
|
||||
static int settings_index;
|
||||
|
||||
static struct settings *current_settings(void)
|
||||
{
|
||||
if (!settings_index) {
|
||||
printf("Fail: No settings set");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return settings_stack + settings_index - 1;
|
||||
}
|
||||
|
||||
static void push_settings(struct settings *settings)
|
||||
{
|
||||
if (settings_index >= MAX_SETTINGS_DEPTH) {
|
||||
printf("Fail: Settings stack exceeded");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
settings_stack[settings_index++] = *settings;
|
||||
write_settings(current_settings());
|
||||
}
|
||||
|
||||
static void pop_settings(void)
|
||||
{
|
||||
if (settings_index <= 0) {
|
||||
printf("Fail: Settings stack empty");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
--settings_index;
|
||||
write_settings(current_settings());
|
||||
}
|
||||
|
||||
static void restore_settings_atexit(void)
|
||||
{
|
||||
if (skip_settings_restore)
|
||||
return;
|
||||
|
||||
printf("Restore THP and khugepaged settings...");
|
||||
write_settings(&saved_settings);
|
||||
thp_restore_settings();
|
||||
success("OK");
|
||||
|
||||
skip_settings_restore = true;
|
||||
@ -395,27 +111,9 @@ static void restore_settings(int sig)
|
||||
static void save_settings(void)
|
||||
{
|
||||
printf("Save THP and khugepaged settings...");
|
||||
saved_settings = (struct settings) {
|
||||
.thp_enabled = read_string("enabled", thp_enabled_strings),
|
||||
.thp_defrag = read_string("defrag", thp_defrag_strings),
|
||||
.shmem_enabled =
|
||||
read_string("shmem_enabled", shmem_enabled_strings),
|
||||
.use_zero_page = read_num("use_zero_page"),
|
||||
};
|
||||
saved_settings.khugepaged = (struct khugepaged_settings) {
|
||||
.defrag = read_num("khugepaged/defrag"),
|
||||
.alloc_sleep_millisecs =
|
||||
read_num("khugepaged/alloc_sleep_millisecs"),
|
||||
.scan_sleep_millisecs =
|
||||
read_num("khugepaged/scan_sleep_millisecs"),
|
||||
.max_ptes_none = read_num("khugepaged/max_ptes_none"),
|
||||
.max_ptes_swap = read_num("khugepaged/max_ptes_swap"),
|
||||
.max_ptes_shared = read_num("khugepaged/max_ptes_shared"),
|
||||
.pages_to_scan = read_num("khugepaged/pages_to_scan"),
|
||||
};
|
||||
if (file_ops && finfo.type == VMA_FILE)
|
||||
saved_settings.read_ahead_kb =
|
||||
_read_num(finfo.dev_queue_read_ahead_path);
|
||||
thp_set_read_ahead_path(finfo.dev_queue_read_ahead_path);
|
||||
thp_save_settings();
|
||||
|
||||
success("OK");
|
||||
|
||||
@ -798,7 +496,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
|
||||
struct mem_ops *ops, bool expect)
|
||||
{
|
||||
int ret;
|
||||
struct settings settings = *current_settings();
|
||||
struct thp_settings settings = *thp_current_settings();
|
||||
|
||||
printf("%s...", msg);
|
||||
|
||||
@ -808,7 +506,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
|
||||
*/
|
||||
settings.thp_enabled = THP_NEVER;
|
||||
settings.shmem_enabled = SHMEM_NEVER;
|
||||
push_settings(&settings);
|
||||
thp_push_settings(&settings);
|
||||
|
||||
/* Clear VM_NOHUGEPAGE */
|
||||
madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE);
|
||||
@ -820,7 +518,7 @@ static void __madvise_collapse(const char *msg, char *p, int nr_hpages,
|
||||
else
|
||||
success("OK");
|
||||
|
||||
pop_settings();
|
||||
thp_pop_settings();
|
||||
}
|
||||
|
||||
static void madvise_collapse(const char *msg, char *p, int nr_hpages,
|
||||
@ -850,13 +548,13 @@ static bool wait_for_scan(const char *msg, char *p, int nr_hpages,
|
||||
madvise(p, nr_hpages * hpage_pmd_size, MADV_HUGEPAGE);
|
||||
|
||||
/* Wait until the second full_scan completed */
|
||||
full_scans = read_num("khugepaged/full_scans") + 2;
|
||||
full_scans = thp_read_num("khugepaged/full_scans") + 2;
|
||||
|
||||
printf("%s...", msg);
|
||||
while (timeout--) {
|
||||
if (ops->check_huge(p, nr_hpages))
|
||||
break;
|
||||
if (read_num("khugepaged/full_scans") >= full_scans)
|
||||
if (thp_read_num("khugepaged/full_scans") >= full_scans)
|
||||
break;
|
||||
printf(".");
|
||||
usleep(TICK);
|
||||
@ -911,11 +609,11 @@ static bool is_tmpfs(struct mem_ops *ops)
|
||||
|
||||
static void alloc_at_fault(void)
|
||||
{
|
||||
struct settings settings = *current_settings();
|
||||
struct thp_settings settings = *thp_current_settings();
|
||||
char *p;
|
||||
|
||||
settings.thp_enabled = THP_ALWAYS;
|
||||
push_settings(&settings);
|
||||
thp_push_settings(&settings);
|
||||
|
||||
p = alloc_mapping(1);
|
||||
*p = 1;
|
||||
@ -925,7 +623,7 @@ static void alloc_at_fault(void)
|
||||
else
|
||||
fail("Fail");
|
||||
|
||||
pop_settings();
|
||||
thp_pop_settings();
|
||||
|
||||
madvise(p, page_size, MADV_DONTNEED);
|
||||
printf("Split huge PMD on MADV_DONTNEED...");
|
||||
@ -973,11 +671,11 @@ static void collapse_single_pte_entry(struct collapse_context *c, struct mem_ops
|
||||
static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *ops)
|
||||
{
|
||||
int max_ptes_none = hpage_pmd_nr / 2;
|
||||
struct settings settings = *current_settings();
|
||||
struct thp_settings settings = *thp_current_settings();
|
||||
void *p;
|
||||
|
||||
settings.khugepaged.max_ptes_none = max_ptes_none;
|
||||
push_settings(&settings);
|
||||
thp_push_settings(&settings);
|
||||
|
||||
p = ops->setup_area(1);
|
||||
|
||||
@ -1002,7 +700,7 @@ static void collapse_max_ptes_none(struct collapse_context *c, struct mem_ops *o
|
||||
}
|
||||
skip:
|
||||
ops->cleanup_area(p, hpage_pmd_size);
|
||||
pop_settings();
|
||||
thp_pop_settings();
|
||||
}
|
||||
|
||||
static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_ops *ops)
|
||||
@ -1033,7 +731,7 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op
|
||||
|
||||
static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *ops)
|
||||
{
|
||||
int max_ptes_swap = read_num("khugepaged/max_ptes_swap");
|
||||
int max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap");
|
||||
void *p;
|
||||
|
||||
p = ops->setup_area(1);
|
||||
@ -1250,11 +948,11 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o
|
||||
fail("Fail");
|
||||
ops->fault(p, 0, page_size);
|
||||
|
||||
write_num("khugepaged/max_ptes_shared", hpage_pmd_nr - 1);
|
||||
thp_write_num("khugepaged/max_ptes_shared", hpage_pmd_nr - 1);
|
||||
c->collapse("Collapse PTE table full of compound pages in child",
|
||||
p, 1, ops, true);
|
||||
write_num("khugepaged/max_ptes_shared",
|
||||
current_settings()->khugepaged.max_ptes_shared);
|
||||
thp_write_num("khugepaged/max_ptes_shared",
|
||||
thp_current_settings()->khugepaged.max_ptes_shared);
|
||||
|
||||
validate_memory(p, 0, hpage_pmd_size);
|
||||
ops->cleanup_area(p, hpage_pmd_size);
|
||||
@ -1275,7 +973,7 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o
|
||||
|
||||
static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops *ops)
|
||||
{
|
||||
int max_ptes_shared = read_num("khugepaged/max_ptes_shared");
|
||||
int max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared");
|
||||
int wstatus;
|
||||
void *p;
|
||||
|
||||
@ -1443,7 +1141,7 @@ static void parse_test_type(int argc, const char **argv)
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct settings default_settings = {
|
||||
struct thp_settings default_settings = {
|
||||
.thp_enabled = THP_MADVISE,
|
||||
.thp_defrag = THP_DEFRAG_ALWAYS,
|
||||
.shmem_enabled = SHMEM_ADVISE,
|
||||
@ -1484,7 +1182,7 @@ int main(int argc, const char **argv)
|
||||
default_settings.khugepaged.pages_to_scan = hpage_pmd_nr * 8;
|
||||
|
||||
save_settings();
|
||||
push_settings(&default_settings);
|
||||
thp_push_settings(&default_settings);
|
||||
|
||||
alloc_at_fault();
|
||||
|
||||
|
296
tools/testing/selftests/mm/thp_settings.c
Normal file
296
tools/testing/selftests/mm/thp_settings.c
Normal file
@ -0,0 +1,296 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "thp_settings.h"
|
||||
|
||||
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
|
||||
#define MAX_SETTINGS_DEPTH 4
|
||||
static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
|
||||
static int settings_index;
|
||||
static struct thp_settings saved_settings;
|
||||
static char dev_queue_read_ahead_path[PATH_MAX];
|
||||
|
||||
static const char * const thp_enabled_strings[] = {
|
||||
"always",
|
||||
"madvise",
|
||||
"never",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const thp_defrag_strings[] = {
|
||||
"always",
|
||||
"defer",
|
||||
"defer+madvise",
|
||||
"madvise",
|
||||
"never",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const shmem_enabled_strings[] = {
|
||||
"always",
|
||||
"within_size",
|
||||
"advise",
|
||||
"never",
|
||||
"deny",
|
||||
"force",
|
||||
NULL
|
||||
};
|
||||
|
||||
int read_file(const char *path, char *buf, size_t buflen)
|
||||
{
|
||||
int fd;
|
||||
ssize_t numread;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
|
||||
numread = read(fd, buf, buflen - 1);
|
||||
if (numread < 1) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[numread] = '\0';
|
||||
close(fd);
|
||||
|
||||
return (unsigned int) numread;
|
||||
}
|
||||
|
||||
int write_file(const char *path, const char *buf, size_t buflen)
|
||||
{
|
||||
int fd;
|
||||
ssize_t numwritten;
|
||||
|
||||
fd = open(path, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
printf("open(%s)\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
numwritten = write(fd, buf, buflen - 1);
|
||||
close(fd);
|
||||
if (numwritten < 1) {
|
||||
printf("write(%s)\n", buf);
|
||||
exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned int) numwritten;
|
||||
}
|
||||
|
||||
const unsigned long read_num(const char *path)
|
||||
{
|
||||
char buf[21];
|
||||
|
||||
if (read_file(path, buf, sizeof(buf)) < 0) {
|
||||
perror("read_file()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return strtoul(buf, NULL, 10);
|
||||
}
|
||||
|
||||
void write_num(const char *path, unsigned long num)
|
||||
{
|
||||
char buf[21];
|
||||
|
||||
sprintf(buf, "%ld", num);
|
||||
if (!write_file(path, buf, strlen(buf) + 1)) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int thp_read_string(const char *name, const char * const strings[])
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[256];
|
||||
char *c;
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!read_file(path, buf, sizeof(buf))) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
c = strchr(buf, '[');
|
||||
if (!c) {
|
||||
printf("%s: Parse failure\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
c++;
|
||||
memmove(buf, c, sizeof(buf) - (c - buf));
|
||||
|
||||
c = strchr(buf, ']');
|
||||
if (!c) {
|
||||
printf("%s: Parse failure\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*c = '\0';
|
||||
|
||||
ret = 0;
|
||||
while (strings[ret]) {
|
||||
if (!strcmp(strings[ret], buf))
|
||||
return ret;
|
||||
ret++;
|
||||
}
|
||||
|
||||
printf("Failed to parse %s\n", name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void thp_write_string(const char *name, const char *val)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file(path, val, strlen(val) + 1)) {
|
||||
perror(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned long thp_read_num(const char *name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return read_num(path);
|
||||
}
|
||||
|
||||
void thp_write_num(const char *name, unsigned long num)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
|
||||
if (ret >= PATH_MAX) {
|
||||
printf("%s: Pathname is too long\n", __func__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
write_num(path, num);
|
||||
}
|
||||
|
||||
void thp_read_settings(struct thp_settings *settings)
|
||||
{
|
||||
*settings = (struct thp_settings) {
|
||||
.thp_enabled = thp_read_string("enabled", thp_enabled_strings),
|
||||
.thp_defrag = thp_read_string("defrag", thp_defrag_strings),
|
||||
.shmem_enabled =
|
||||
thp_read_string("shmem_enabled", shmem_enabled_strings),
|
||||
.use_zero_page = thp_read_num("use_zero_page"),
|
||||
};
|
||||
settings->khugepaged = (struct khugepaged_settings) {
|
||||
.defrag = thp_read_num("khugepaged/defrag"),
|
||||
.alloc_sleep_millisecs =
|
||||
thp_read_num("khugepaged/alloc_sleep_millisecs"),
|
||||
.scan_sleep_millisecs =
|
||||
thp_read_num("khugepaged/scan_sleep_millisecs"),
|
||||
.max_ptes_none = thp_read_num("khugepaged/max_ptes_none"),
|
||||
.max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap"),
|
||||
.max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"),
|
||||
.pages_to_scan = thp_read_num("khugepaged/pages_to_scan"),
|
||||
};
|
||||
if (dev_queue_read_ahead_path[0])
|
||||
settings->read_ahead_kb = read_num(dev_queue_read_ahead_path);
|
||||
}
|
||||
|
||||
void thp_write_settings(struct thp_settings *settings)
|
||||
{
|
||||
struct khugepaged_settings *khugepaged = &settings->khugepaged;
|
||||
|
||||
thp_write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
|
||||
thp_write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
|
||||
thp_write_string("shmem_enabled",
|
||||
shmem_enabled_strings[settings->shmem_enabled]);
|
||||
thp_write_num("use_zero_page", settings->use_zero_page);
|
||||
|
||||
thp_write_num("khugepaged/defrag", khugepaged->defrag);
|
||||
thp_write_num("khugepaged/alloc_sleep_millisecs",
|
||||
khugepaged->alloc_sleep_millisecs);
|
||||
thp_write_num("khugepaged/scan_sleep_millisecs",
|
||||
khugepaged->scan_sleep_millisecs);
|
||||
thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
|
||||
thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
|
||||
thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
|
||||
thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
|
||||
|
||||
if (dev_queue_read_ahead_path[0])
|
||||
write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
|
||||
}
|
||||
|
||||
struct thp_settings *thp_current_settings(void)
|
||||
{
|
||||
if (!settings_index) {
|
||||
printf("Fail: No settings set");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return settings_stack + settings_index - 1;
|
||||
}
|
||||
|
||||
void thp_push_settings(struct thp_settings *settings)
|
||||
{
|
||||
if (settings_index >= MAX_SETTINGS_DEPTH) {
|
||||
printf("Fail: Settings stack exceeded");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
settings_stack[settings_index++] = *settings;
|
||||
thp_write_settings(thp_current_settings());
|
||||
}
|
||||
|
||||
void thp_pop_settings(void)
|
||||
{
|
||||
if (settings_index <= 0) {
|
||||
printf("Fail: Settings stack empty");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
--settings_index;
|
||||
thp_write_settings(thp_current_settings());
|
||||
}
|
||||
|
||||
void thp_restore_settings(void)
|
||||
{
|
||||
thp_write_settings(&saved_settings);
|
||||
}
|
||||
|
||||
void thp_save_settings(void)
|
||||
{
|
||||
thp_read_settings(&saved_settings);
|
||||
}
|
||||
|
||||
void thp_set_read_ahead_path(char *path)
|
||||
{
|
||||
if (!path) {
|
||||
dev_queue_read_ahead_path[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(dev_queue_read_ahead_path, path,
|
||||
sizeof(dev_queue_read_ahead_path));
|
||||
dev_queue_read_ahead_path[sizeof(dev_queue_read_ahead_path) - 1] = '\0';
|
||||
}
|
71
tools/testing/selftests/mm/thp_settings.h
Normal file
71
tools/testing/selftests/mm/thp_settings.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __THP_SETTINGS_H__
|
||||
#define __THP_SETTINGS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum thp_enabled {
|
||||
THP_ALWAYS,
|
||||
THP_MADVISE,
|
||||
THP_NEVER,
|
||||
};
|
||||
|
||||
enum thp_defrag {
|
||||
THP_DEFRAG_ALWAYS,
|
||||
THP_DEFRAG_DEFER,
|
||||
THP_DEFRAG_DEFER_MADVISE,
|
||||
THP_DEFRAG_MADVISE,
|
||||
THP_DEFRAG_NEVER,
|
||||
};
|
||||
|
||||
enum shmem_enabled {
|
||||
SHMEM_ALWAYS,
|
||||
SHMEM_WITHIN_SIZE,
|
||||
SHMEM_ADVISE,
|
||||
SHMEM_NEVER,
|
||||
SHMEM_DENY,
|
||||
SHMEM_FORCE,
|
||||
};
|
||||
|
||||
struct khugepaged_settings {
|
||||
bool defrag;
|
||||
unsigned int alloc_sleep_millisecs;
|
||||
unsigned int scan_sleep_millisecs;
|
||||
unsigned int max_ptes_none;
|
||||
unsigned int max_ptes_swap;
|
||||
unsigned int max_ptes_shared;
|
||||
unsigned long pages_to_scan;
|
||||
};
|
||||
|
||||
struct thp_settings {
|
||||
enum thp_enabled thp_enabled;
|
||||
enum thp_defrag thp_defrag;
|
||||
enum shmem_enabled shmem_enabled;
|
||||
bool use_zero_page;
|
||||
struct khugepaged_settings khugepaged;
|
||||
unsigned long read_ahead_kb;
|
||||
};
|
||||
|
||||
int read_file(const char *path, char *buf, size_t buflen);
|
||||
int write_file(const char *path, const char *buf, size_t buflen);
|
||||
const unsigned long read_num(const char *path);
|
||||
void write_num(const char *path, unsigned long num);
|
||||
|
||||
int thp_read_string(const char *name, const char * const strings[]);
|
||||
void thp_write_string(const char *name, const char *val);
|
||||
const unsigned long thp_read_num(const char *name);
|
||||
void thp_write_num(const char *name, unsigned long num);
|
||||
|
||||
void thp_write_settings(struct thp_settings *settings);
|
||||
void thp_read_settings(struct thp_settings *settings);
|
||||
struct thp_settings *thp_current_settings(void);
|
||||
void thp_push_settings(struct thp_settings *settings);
|
||||
void thp_pop_settings(void);
|
||||
void thp_restore_settings(void);
|
||||
void thp_save_settings(void);
|
||||
|
||||
void thp_set_read_ahead_path(char *path);
|
||||
|
||||
#endif /* __THP_SETTINGS_H__ */
|
Loading…
Reference in New Issue
Block a user