#define _GNU_SOURCE
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#ifndef MADV_PAGEOUT
#define MADV_PAGEOUT 21
#endif
#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"
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 long pages_to_scan;
};
struct settings {
enum thp_enabled thp_enabled;
enum thp_defrag thp_defrag;
enum shmem_enabled shmem_enabled;
bool debug_cow;
bool use_zero_page;
struct khugepaged_settings khugepaged;
};
static struct settings default_settings = {
.thp_enabled = THP_MADVISE,
.thp_defrag = THP_DEFRAG_ALWAYS,
.shmem_enabled = SHMEM_NEVER,
.debug_cow = 0,
.use_zero_page = 0,
.khugepaged = {
.defrag = 1,
.alloc_sleep_millisecs = 10,
.scan_sleep_millisecs = 10,
},
};
static struct settings saved_settings;
static bool skip_settings_restore;
static int exit_status;
static void success(const char *msg)
{
printf(" \e[32m%s\e[0m\n", msg);
}
static void fail(const char *msg)
{
printf(" \e[31m%s\e[0m\n", msg);
exit_status++;
}
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)
return 0;
numwritten = write(fd, buf, buflen - 1);
close(fd);
if (numwritten < 1)
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