xref: /linux/tools/testing/selftests/mm/hugetlb_reparenting_test.sh (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Kselftest framework requirement - SKIP code is 4.
5ksft_skip=4
6
7set -e
8
9if [[ $(id -u) -ne 0 ]]; then
10  echo "This test must be run as root. Skipping..."
11  exit $ksft_skip
12fi
13
14nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages)
15trap 'echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages' EXIT INT TERM
16
17usage_file=usage_in_bytes
18
19if [[ "$1" == "-cgroup-v2" ]]; then
20  cgroup2=1
21  usage_file=current
22fi
23
24
25if [[ $cgroup2 ]]; then
26  CGROUP_ROOT=$(mount -t cgroup2 | head -1 | awk '{print $3}')
27  if [[ -z "$CGROUP_ROOT" ]]; then
28    CGROUP_ROOT=$(mktemp -d)
29    mount -t cgroup2 none $CGROUP_ROOT
30    do_umount=1
31  fi
32  echo "+hugetlb +memory" >$CGROUP_ROOT/cgroup.subtree_control
33else
34  CGROUP_ROOT=$(mount -t cgroup | grep ",hugetlb" | awk '{print $3}')
35  if [[ -z "$CGROUP_ROOT" ]]; then
36    CGROUP_ROOT=/dev/cgroup/memory
37    mount -t cgroup memory,hugetlb $CGROUP_ROOT
38    do_umount=1
39  fi
40fi
41MNT='/mnt/huge'
42
43function get_machine_hugepage_size() {
44  hpz=$(grep -i hugepagesize /proc/meminfo)
45  kb=${hpz:14:-3}
46  mb=$(($kb / 1024))
47  echo $mb
48}
49
50MB=$(get_machine_hugepage_size)
51if (( MB >= 1024 )); then
52  UNIT="GB"
53  MB_DISPLAY=$((MB / 1024))
54else
55  UNIT="MB"
56  MB_DISPLAY=$MB
57fi
58
59function cleanup() {
60  echo cleanup
61  set +e
62  rm -rf "$MNT"/* 2>/dev/null
63  umount "$MNT" 2>/dev/null
64  rmdir "$MNT" 2>/dev/null
65  rmdir "$CGROUP_ROOT"/a/b 2>/dev/null
66  rmdir "$CGROUP_ROOT"/a 2>/dev/null
67  rmdir "$CGROUP_ROOT"/test1 2>/dev/null
68  set -e
69}
70
71function assert_with_retry() {
72  local actual_path="$1"
73  local expected="$2"
74  local tolerance=$((7 * 1024 * 1024))
75  local timeout=20
76  local interval=1
77  local start_time
78  local now
79  local elapsed
80  local actual
81
82  start_time=$(date +%s)
83
84  while true; do
85    actual="$(cat "$actual_path")"
86
87    if [[ $actual -ge $(($expected - $tolerance)) ]] &&
88        [[ $actual -le $(($expected + $tolerance)) ]]; then
89      return 0
90    fi
91
92    now=$(date +%s)
93    elapsed=$((now - start_time))
94
95    if [[ $elapsed -ge $timeout ]]; then
96      echo "actual = $((${actual%% *} / 1024 / 1024)) MB"
97      echo "expected = $((${expected%% *} / 1024 / 1024)) MB"
98      echo FAIL
99      cleanup
100      exit 1
101    fi
102
103    sleep $interval
104  done
105}
106
107function assert_state() {
108  local expected_a_hugetlb="$1"
109  local expected_b_hugetlb=""
110
111  if [ ! -z ${2:-} ]; then
112    expected_b_hugetlb="$2"
113  fi
114
115  assert_with_retry \
116	  "$CGROUP_ROOT/a/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_a_hugetlb"
117
118  if [[ -n "$expected_b_hugetlb" ]]; then
119    assert_with_retry \
120	  "$CGROUP_ROOT/a/b/hugetlb.${MB_DISPLAY}${UNIT}.$usage_file" "$expected_b_hugetlb"
121  fi
122}
123
124function setup() {
125  echo 100 >/proc/sys/vm/nr_hugepages
126  mkdir "$CGROUP_ROOT"/a
127  sleep 1
128  if [[ $cgroup2 ]]; then
129    echo "+hugetlb +memory" >$CGROUP_ROOT/a/cgroup.subtree_control
130  else
131    echo 0 >$CGROUP_ROOT/a/cpuset.mems
132    echo 0 >$CGROUP_ROOT/a/cpuset.cpus
133  fi
134
135  mkdir "$CGROUP_ROOT"/a/b
136
137  if [[ ! $cgroup2 ]]; then
138    echo 0 >$CGROUP_ROOT/a/b/cpuset.mems
139    echo 0 >$CGROUP_ROOT/a/b/cpuset.cpus
140  fi
141
142  mkdir -p "$MNT"
143  mount -t hugetlbfs none "$MNT"
144}
145
146write_hugetlbfs() {
147  local cgroup="$1"
148  local path="$2"
149  local size="$3"
150
151  if [[ $cgroup2 ]]; then
152    cg_file="$CGROUP_ROOT/$cgroup/cgroup.procs"
153  else
154    echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems
155    echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus
156    cg_file="$CGROUP_ROOT/$cgroup/tasks"
157  fi
158
159  # Spawn helper to join cgroup before exec to ensure correct cgroup accounting
160  bash -c 'echo $$ > "$1"; exec ./write_to_hugetlbfs -p "$2" -s "$3" -m 0 -o' _ \
161	  "$cg_file" "$path" "$size" & pid=$!
162  wait "$pid"
163  echo
164}
165
166set -e
167
168size=$((${MB} * 1024 * 1024 * 25)) # 50MB = 25 * 2MB hugepages.
169
170cleanup
171
172echo
173echo Test charge, rmdir, uncharge
174setup
175echo mkdir
176mkdir $CGROUP_ROOT/test1
177
178echo write
179write_hugetlbfs test1 "$MNT"/test $size
180
181echo rmdir
182rmdir $CGROUP_ROOT/test1
183mkdir $CGROUP_ROOT/test1
184
185echo uncharge
186rm -rf /mnt/huge/*
187
188cleanup
189
190echo done
191echo
192if [[ ! $cgroup2 ]]; then
193  echo "Test parent and child hugetlb usage"
194  setup
195
196  echo write
197  write_hugetlbfs a "$MNT"/test $size
198
199  echo Assert memory charged correctly for parent use.
200  assert_state $size 0
201
202  write_hugetlbfs a/b "$MNT"/test2 $size
203
204  echo Assert memory charged correctly for child use.
205  assert_state $(($size * 2)) $size
206
207  rmdir "$CGROUP_ROOT"/a/b
208  echo Assert memory reparent correctly.
209  assert_state $(($size * 2))
210
211  rm -rf "$MNT"/*
212  umount "$MNT"
213  echo Assert memory uncharged correctly.
214  assert_state 0
215
216  cleanup
217fi
218
219echo
220echo "Test child only hugetlb usage"
221echo setup
222setup
223
224echo write
225write_hugetlbfs a/b "$MNT"/test2 $size
226
227echo Assert memory charged correctly for child only use.
228assert_state $(($size)) $size
229
230rmdir "$CGROUP_ROOT"/a/b
231echo Assert memory reparent correctly.
232assert_state $size
233
234rm -rf "$MNT"/*
235umount "$MNT"
236echo Assert memory uncharged correctly.
237assert_state 0
238
239cleanup
240
241echo ALL PASS
242
243if [[ $do_umount ]]; then
244  umount $CGROUP_ROOT
245  rm -rf $CGROUP_ROOT
246fi
247
248