You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
hugetlb_cgroup: add hugetlb_cgroup reservation tests
The tests use both shared and private mapped hugetlb memory, and monitors the hugetlb usage counter as well as the hugetlb reservation counter. They test different configurations such as hugetlb memory usage via hugetlbfs, or MAP_HUGETLB, or shmget/shmat, and with and without MAP_POPULATE. Also add test for hugetlb reservation reparenting, since this is a subtle issue. Signed-off-by: Mina Almasry <almasrymina@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Tested-by: Sandipan Das <sandipan@linux.ibm.com> [powerpc64] Acked-by: Mike Kravetz <mike.kravetz@oracle.com> Cc: Sandipan Das <sandipan@linux.ibm.com> Cc: David Rientjes <rientjes@google.com> Cc: Greg Thelen <gthelen@google.com> Cc: Shakeel Butt <shakeelb@google.com> Cc: Shuah Khan <shuah@kernel.org> Link: http://lkml.kernel.org/r/20200211213128.73302-8-almasrymina@google.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
committed by
Linus Torvalds
parent
a9b3f86740
commit
29750f71a9
1
tools/testing/selftests/vm/.gitignore
vendored
1
tools/testing/selftests/vm/.gitignore
vendored
@@ -14,3 +14,4 @@ virtual_address_range
|
||||
gup_benchmark
|
||||
va_128TBswitch
|
||||
map_fixed_noreplace
|
||||
write_to_hugetlbfs
|
||||
|
||||
@@ -23,6 +23,7 @@ TEST_GEN_FILES += userfaultfd
|
||||
ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64))
|
||||
TEST_GEN_FILES += va_128TBswitch
|
||||
TEST_GEN_FILES += virtual_address_range
|
||||
TEST_GEN_FILES += write_to_hugetlbfs
|
||||
endif
|
||||
|
||||
TEST_PROGS := run_vmtests
|
||||
|
||||
575
tools/testing/selftests/vm/charge_reserved_hugetlb.sh
Normal file
575
tools/testing/selftests/vm/charge_reserved_hugetlb.sh
Normal file
File diff suppressed because it is too large
Load Diff
244
tools/testing/selftests/vm/hugetlb_reparenting_test.sh
Normal file
244
tools/testing/selftests/vm/hugetlb_reparenting_test.sh
Normal file
@@ -0,0 +1,244 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
if [[ $(id -u) -ne 0 ]]; then
|
||||
echo "This test must be run as root. Skipping..."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
usage_file=usage_in_bytes
|
||||
|
||||
if [[ "$1" == "-cgroup-v2" ]]; then
|
||||
cgroup2=1
|
||||
usage_file=current
|
||||
fi
|
||||
|
||||
CGROUP_ROOT='/dev/cgroup/memory'
|
||||
MNT='/mnt/huge/'
|
||||
|
||||
if [[ ! -e $CGROUP_ROOT ]]; then
|
||||
mkdir -p $CGROUP_ROOT
|
||||
if [[ $cgroup2 ]]; then
|
||||
mount -t cgroup2 none $CGROUP_ROOT
|
||||
sleep 1
|
||||
echo "+hugetlb +memory" >$CGROUP_ROOT/cgroup.subtree_control
|
||||
else
|
||||
mount -t cgroup memory,hugetlb $CGROUP_ROOT
|
||||
fi
|
||||
fi
|
||||
|
||||
function get_machine_hugepage_size() {
|
||||
hpz=$(grep -i hugepagesize /proc/meminfo)
|
||||
kb=${hpz:14:-3}
|
||||
mb=$(($kb / 1024))
|
||||
echo $mb
|
||||
}
|
||||
|
||||
MB=$(get_machine_hugepage_size)
|
||||
|
||||
function cleanup() {
|
||||
echo cleanup
|
||||
set +e
|
||||
rm -rf "$MNT"/* 2>/dev/null
|
||||
umount "$MNT" 2>/dev/null
|
||||
rmdir "$MNT" 2>/dev/null
|
||||
rmdir "$CGROUP_ROOT"/a/b 2>/dev/null
|
||||
rmdir "$CGROUP_ROOT"/a 2>/dev/null
|
||||
rmdir "$CGROUP_ROOT"/test1 2>/dev/null
|
||||
echo 0 >/proc/sys/vm/nr_hugepages
|
||||
set -e
|
||||
}
|
||||
|
||||
function assert_state() {
|
||||
local expected_a="$1"
|
||||
local expected_a_hugetlb="$2"
|
||||
local expected_b=""
|
||||
local expected_b_hugetlb=""
|
||||
|
||||
if [ ! -z ${3:-} ] && [ ! -z ${4:-} ]; then
|
||||
expected_b="$3"
|
||||
expected_b_hugetlb="$4"
|
||||
fi
|
||||
local tolerance=$((5 * 1024 * 1024))
|
||||
|
||||
local actual_a
|
||||
actual_a="$(cat "$CGROUP_ROOT"/a/memory.$usage_file)"
|
||||
if [[ $actual_a -lt $(($expected_a - $tolerance)) ]] ||
|
||||
[[ $actual_a -gt $(($expected_a + $tolerance)) ]]; then
|
||||
echo actual a = $((${actual_a%% *} / 1024 / 1024)) MB
|
||||
echo expected a = $((${expected_a%% *} / 1024 / 1024)) MB
|
||||
echo fail
|
||||
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local actual_a_hugetlb
|
||||
actual_a_hugetlb="$(cat "$CGROUP_ROOT"/a/hugetlb.${MB}MB.$usage_file)"
|
||||
if [[ $actual_a_hugetlb -lt $(($expected_a_hugetlb - $tolerance)) ]] ||
|
||||
[[ $actual_a_hugetlb -gt $(($expected_a_hugetlb + $tolerance)) ]]; then
|
||||
echo actual a hugetlb = $((${actual_a_hugetlb%% *} / 1024 / 1024)) MB
|
||||
echo expected a hugetlb = $((${expected_a_hugetlb%% *} / 1024 / 1024)) MB
|
||||
echo fail
|
||||
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$expected_b" || -z "$expected_b_hugetlb" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local actual_b
|
||||
actual_b="$(cat "$CGROUP_ROOT"/a/b/memory.$usage_file)"
|
||||
if [[ $actual_b -lt $(($expected_b - $tolerance)) ]] ||
|
||||
[[ $actual_b -gt $(($expected_b + $tolerance)) ]]; then
|
||||
echo actual b = $((${actual_b%% *} / 1024 / 1024)) MB
|
||||
echo expected b = $((${expected_b%% *} / 1024 / 1024)) MB
|
||||
echo fail
|
||||
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local actual_b_hugetlb
|
||||
actual_b_hugetlb="$(cat "$CGROUP_ROOT"/a/b/hugetlb.${MB}MB.$usage_file)"
|
||||
if [[ $actual_b_hugetlb -lt $(($expected_b_hugetlb - $tolerance)) ]] ||
|
||||
[[ $actual_b_hugetlb -gt $(($expected_b_hugetlb + $tolerance)) ]]; then
|
||||
echo actual b hugetlb = $((${actual_b_hugetlb%% *} / 1024 / 1024)) MB
|
||||
echo expected b hugetlb = $((${expected_b_hugetlb%% *} / 1024 / 1024)) MB
|
||||
echo fail
|
||||
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function setup() {
|
||||
echo 100 >/proc/sys/vm/nr_hugepages
|
||||
mkdir "$CGROUP_ROOT"/a
|
||||
sleep 1
|
||||
if [[ $cgroup2 ]]; then
|
||||
echo "+hugetlb +memory" >$CGROUP_ROOT/a/cgroup.subtree_control
|
||||
else
|
||||
echo 0 >$CGROUP_ROOT/a/cpuset.mems
|
||||
echo 0 >$CGROUP_ROOT/a/cpuset.cpus
|
||||
fi
|
||||
|
||||
mkdir "$CGROUP_ROOT"/a/b
|
||||
|
||||
if [[ ! $cgroup2 ]]; then
|
||||
echo 0 >$CGROUP_ROOT/a/b/cpuset.mems
|
||||
echo 0 >$CGROUP_ROOT/a/b/cpuset.cpus
|
||||
fi
|
||||
|
||||
mkdir -p "$MNT"
|
||||
mount -t hugetlbfs none "$MNT"
|
||||
}
|
||||
|
||||
write_hugetlbfs() {
|
||||
local cgroup="$1"
|
||||
local path="$2"
|
||||
local size="$3"
|
||||
|
||||
if [[ $cgroup2 ]]; then
|
||||
echo $$ >$CGROUP_ROOT/$cgroup/cgroup.procs
|
||||
else
|
||||
echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems
|
||||
echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus
|
||||
echo $$ >"$CGROUP_ROOT/$cgroup/tasks"
|
||||
fi
|
||||
./write_to_hugetlbfs -p "$path" -s "$size" -m 0 -o
|
||||
if [[ $cgroup2 ]]; then
|
||||
echo $$ >$CGROUP_ROOT/cgroup.procs
|
||||
else
|
||||
echo $$ >"$CGROUP_ROOT/tasks"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
set -e
|
||||
|
||||
size=$((${MB} * 1024 * 1024 * 25)) # 50MB = 25 * 2MB hugepages.
|
||||
|
||||
cleanup
|
||||
|
||||
echo
|
||||
echo
|
||||
echo Test charge, rmdir, uncharge
|
||||
setup
|
||||
echo mkdir
|
||||
mkdir $CGROUP_ROOT/test1
|
||||
|
||||
echo write
|
||||
write_hugetlbfs test1 "$MNT"/test $size
|
||||
|
||||
echo rmdir
|
||||
rmdir $CGROUP_ROOT/test1
|
||||
mkdir $CGROUP_ROOT/test1
|
||||
|
||||
echo uncharge
|
||||
rm -rf /mnt/huge/*
|
||||
|
||||
cleanup
|
||||
|
||||
echo done
|
||||
echo
|
||||
echo
|
||||
if [[ ! $cgroup2 ]]; then
|
||||
echo "Test parent and child hugetlb usage"
|
||||
setup
|
||||
|
||||
echo write
|
||||
write_hugetlbfs a "$MNT"/test $size
|
||||
|
||||
echo Assert memory charged correctly for parent use.
|
||||
assert_state 0 $size 0 0
|
||||
|
||||
write_hugetlbfs a/b "$MNT"/test2 $size
|
||||
|
||||
echo Assert memory charged correctly for child use.
|
||||
assert_state 0 $(($size * 2)) 0 $size
|
||||
|
||||
rmdir "$CGROUP_ROOT"/a/b
|
||||
sleep 5
|
||||
echo Assert memory reparent correctly.
|
||||
assert_state 0 $(($size * 2))
|
||||
|
||||
rm -rf "$MNT"/*
|
||||
umount "$MNT"
|
||||
echo Assert memory uncharged correctly.
|
||||
assert_state 0 0
|
||||
|
||||
cleanup
|
||||
fi
|
||||
|
||||
echo
|
||||
echo
|
||||
echo "Test child only hugetlb usage"
|
||||
echo setup
|
||||
setup
|
||||
|
||||
echo write
|
||||
write_hugetlbfs a/b "$MNT"/test2 $size
|
||||
|
||||
echo Assert memory charged correctly for child only use.
|
||||
assert_state 0 $(($size)) 0 $size
|
||||
|
||||
rmdir "$CGROUP_ROOT"/a/b
|
||||
echo Assert memory reparent correctly.
|
||||
assert_state 0 $size
|
||||
|
||||
rm -rf "$MNT"/*
|
||||
umount "$MNT"
|
||||
echo Assert memory uncharged correctly.
|
||||
assert_state 0 0
|
||||
|
||||
cleanup
|
||||
|
||||
echo ALL PASS
|
||||
|
||||
umount $CGROUP_ROOT
|
||||
rm -rf $CGROUP_ROOT
|
||||
23
tools/testing/selftests/vm/write_hugetlb_memory.sh
Normal file
23
tools/testing/selftests/vm/write_hugetlb_memory.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
size=$1
|
||||
populate=$2
|
||||
write=$3
|
||||
cgroup=$4
|
||||
path=$5
|
||||
method=$6
|
||||
private=$7
|
||||
want_sleep=$8
|
||||
reserve=$9
|
||||
|
||||
echo "Putting task in cgroup '$cgroup'"
|
||||
echo $$ > /dev/cgroup/memory/"$cgroup"/cgroup.procs
|
||||
|
||||
echo "Method is $method"
|
||||
|
||||
set +e
|
||||
./write_to_hugetlbfs -p "$path" -s "$size" "$write" "$populate" -m "$method" \
|
||||
"$private" "$want_sleep" "$reserve"
|
||||
242
tools/testing/selftests/vm/write_to_hugetlbfs.c
Normal file
242
tools/testing/selftests/vm/write_to_hugetlbfs.c
Normal file
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* This program reserves and uses hugetlb memory, supporting a bunch of
|
||||
* scenarios needed by the charged_reserved_hugetlb.sh test.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* Global definitions. */
|
||||
enum method {
|
||||
HUGETLBFS,
|
||||
MMAP_MAP_HUGETLB,
|
||||
SHM,
|
||||
MAX_METHOD
|
||||
};
|
||||
|
||||
|
||||
/* Global variables. */
|
||||
static const char *self;
|
||||
static char *shmaddr;
|
||||
static int shmid;
|
||||
|
||||
/*
|
||||
* Show usage and exit.
|
||||
*/
|
||||
static void exit_usage(void)
|
||||
{
|
||||
printf("Usage: %s -p <path to hugetlbfs file> -s <size to map> "
|
||||
"[-m <0=hugetlbfs | 1=mmap(MAP_HUGETLB)>] [-l] [-r] "
|
||||
"[-o] [-w] [-n]\n",
|
||||
self);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
printf("Received %d.\n", signo);
|
||||
if (signo == SIGINT) {
|
||||
printf("Deleting the memory\n");
|
||||
if (shmdt((const void *)shmaddr) != 0) {
|
||||
perror("Detach failure");
|
||||
shmctl(shmid, IPC_RMID, NULL);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
shmctl(shmid, IPC_RMID, NULL);
|
||||
printf("Done deleting the memory\n");
|
||||
}
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd = 0;
|
||||
int key = 0;
|
||||
int *ptr = NULL;
|
||||
int c = 0;
|
||||
int size = 0;
|
||||
char path[256] = "";
|
||||
enum method method = MAX_METHOD;
|
||||
int want_sleep = 0, private = 0;
|
||||
int populate = 0;
|
||||
int write = 0;
|
||||
int reserve = 1;
|
||||
|
||||
unsigned long i;
|
||||
|
||||
if (signal(SIGINT, sig_handler) == SIG_ERR)
|
||||
err(1, "\ncan't catch SIGINT\n");
|
||||
|
||||
/* Parse command-line arguments. */
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
self = argv[0];
|
||||
|
||||
while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
size = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
strncpy(path, optarg, sizeof(path));
|
||||
break;
|
||||
case 'm':
|
||||
if (atoi(optarg) >= MAX_METHOD) {
|
||||
errno = EINVAL;
|
||||
perror("Invalid -m.");
|
||||
exit_usage();
|
||||
}
|
||||
method = atoi(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
populate = 1;
|
||||
break;
|
||||
case 'w':
|
||||
write = 1;
|
||||
break;
|
||||
case 'l':
|
||||
want_sleep = 1;
|
||||
break;
|
||||
case 'r':
|
||||
private
|
||||
= 1;
|
||||
break;
|
||||
case 'n':
|
||||
reserve = 0;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
perror("Invalid arg");
|
||||
exit_usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp(path, "", sizeof(path)) != 0) {
|
||||
printf("Writing to this path: %s\n", path);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
perror("path not found");
|
||||
exit_usage();
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
printf("Writing this size: %d\n", size);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
perror("size not found");
|
||||
exit_usage();
|
||||
}
|
||||
|
||||
if (!populate)
|
||||
printf("Not populating.\n");
|
||||
else
|
||||
printf("Populating.\n");
|
||||
|
||||
if (!write)
|
||||
printf("Not writing to memory.\n");
|
||||
|
||||
if (method == MAX_METHOD) {
|
||||
errno = EINVAL;
|
||||
perror("-m Invalid");
|
||||
exit_usage();
|
||||
} else
|
||||
printf("Using method=%d\n", method);
|
||||
|
||||
if (!private)
|
||||
printf("Shared mapping.\n");
|
||||
else
|
||||
printf("Private mapping.\n");
|
||||
|
||||
if (!reserve)
|
||||
printf("NO_RESERVE mapping.\n");
|
||||
else
|
||||
printf("RESERVE mapping.\n");
|
||||
|
||||
switch (method) {
|
||||
case HUGETLBFS:
|
||||
printf("Allocating using HUGETLBFS.\n");
|
||||
fd = open(path, O_CREAT | O_RDWR, 0777);
|
||||
if (fd == -1)
|
||||
err(1, "Failed to open file.");
|
||||
|
||||
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
(private ? MAP_PRIVATE : MAP_SHARED) |
|
||||
(populate ? MAP_POPULATE : 0) |
|
||||
(reserve ? 0 : MAP_NORESERVE),
|
||||
fd, 0);
|
||||
|
||||
if (ptr == MAP_FAILED) {
|
||||
close(fd);
|
||||
err(1, "Error mapping the file");
|
||||
}
|
||||
break;
|
||||
case MMAP_MAP_HUGETLB:
|
||||
printf("Allocating using MAP_HUGETLB.\n");
|
||||
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
(private ? (MAP_PRIVATE | MAP_ANONYMOUS) :
|
||||
MAP_SHARED) |
|
||||
MAP_HUGETLB | (populate ? MAP_POPULATE : 0) |
|
||||
(reserve ? 0 : MAP_NORESERVE),
|
||||
-1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
err(1, "mmap");
|
||||
|
||||
printf("Returned address is %p\n", ptr);
|
||||
break;
|
||||
case SHM:
|
||||
printf("Allocating using SHM.\n");
|
||||
shmid = shmget(key, size,
|
||||
SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
|
||||
if (shmid < 0) {
|
||||
shmid = shmget(++key, size,
|
||||
SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
|
||||
if (shmid < 0)
|
||||
err(1, "shmget");
|
||||
}
|
||||
printf("shmid: 0x%x, shmget key:%d\n", shmid, key);
|
||||
|
||||
ptr = shmat(shmid, NULL, 0);
|
||||
if (ptr == (int *)-1) {
|
||||
perror("Shared memory attach failure");
|
||||
shmctl(shmid, IPC_RMID, NULL);
|
||||
exit(2);
|
||||
}
|
||||
printf("shmaddr: %p\n", ptr);
|
||||
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
err(1, "Invalid method.");
|
||||
}
|
||||
|
||||
if (write) {
|
||||
printf("Writing to memory.\n");
|
||||
memset(ptr, 1, size);
|
||||
}
|
||||
|
||||
if (want_sleep) {
|
||||
/* Signal to caller that we're done. */
|
||||
printf("DONE\n");
|
||||
|
||||
/* Hold memory until external kill signal is delivered. */
|
||||
while (1)
|
||||
sleep(100);
|
||||
}
|
||||
|
||||
if (method == HUGETLBFS)
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user