memcg_lib.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #! /bin/sh
  2. ################################################################################
  3. ## ##
  4. ## Copyright (c) 2012 FUJITSU LIMITED ##
  5. ## ##
  6. ## This program is free software; you can redistribute it and#or modify ##
  7. ## it under the terms of the GNU General Public License as published by ##
  8. ## the Free Software Foundation; either version 2 of the License, or ##
  9. ## (at your option) any later version. ##
  10. ## ##
  11. ## This program is distributed in the hope that it will be useful, but ##
  12. ## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ##
  13. ## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ##
  14. ## for more details. ##
  15. ## ##
  16. ## You should have received a copy of the GNU General Public License ##
  17. ## along with this program; if not, write to the Free Software ##
  18. ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ##
  19. ## ##
  20. ## Author: Peng Haitao <penght@cn.fujitsu.com> ##
  21. ## ##
  22. ################################################################################
  23. TST_NEEDS_CHECKPOINTS=1
  24. . test.sh
  25. if [ "x$(grep -w memory /proc/cgroups | cut -f4)" != "x1" ]; then
  26. tst_brkm TCONF "Kernel does not support the memory resource controller"
  27. fi
  28. PAGESIZE=$(tst_getconf PAGESIZE)
  29. if [ $? -ne 0 ]; then
  30. tst_brkm TBROK "tst_getconf PAGESIZE failed"
  31. fi
  32. # Check for dependencies
  33. tst_require_cmds killall
  34. # Post 4.16 kernel updates stat in batch (> 32 pages) every time
  35. PAGESIZES=$(( $PAGESIZE * 33 ))
  36. HUGEPAGESIZE=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
  37. [ -z $HUGEPAGESIZE ] && HUGEPAGESIZE=0
  38. HUGEPAGESIZE=$(( $HUGEPAGESIZE * 1024 ))
  39. orig_memory_use_hierarchy=""
  40. MEMSW_USAGE_FLAG=0
  41. MEMSW_LIMIT_FLAG=0
  42. tst_tmpdir
  43. TMP_DIR="$PWD"
  44. cleanup()
  45. {
  46. if [ -n "$LOCAL_CLEANUP" ]; then
  47. $LOCAL_CLEANUP
  48. fi
  49. killall -9 memcg_process 2> /dev/null
  50. wait
  51. cd "$TMP_DIR"
  52. if [ -n "$TEST_ID" -a -d "/dev/memcg/$TEST_ID" ]; then
  53. for i in "/dev/memcg/$TEST_ID/"*; do
  54. if [ -d "$i" ]; then
  55. rmdir "$i"
  56. fi
  57. done
  58. rmdir "/dev/memcg/$TEST_ID"
  59. fi
  60. if [ -d "/dev/memcg" ]; then
  61. umount /dev/memcg
  62. rmdir /dev/memcg
  63. fi
  64. tst_rmdir
  65. }
  66. TST_CLEANUP=cleanup
  67. shmmax_setup()
  68. {
  69. tst_require_cmds bc
  70. shmmax=`cat /proc/sys/kernel/shmmax`
  71. if [ $(echo "$shmmax < $HUGEPAGESIZE" |bc) -eq 1 ]; then
  72. ROD echo "$HUGEPAGESIZE" \> /proc/sys/kernel/shmmax
  73. fi
  74. }
  75. shmmax_cleanup()
  76. {
  77. if [ -n "$shmmax" ]; then
  78. echo "$shmmax" > /proc/sys/kernel/shmmax
  79. fi
  80. }
  81. # Check size in memcg
  82. # $1 - Item name
  83. # $2 - Expected size
  84. check_mem_stat()
  85. {
  86. if [ -e $1 ]; then
  87. item_size=`cat $1`
  88. else
  89. item_size=`grep -w $1 memory.stat | cut -d " " -f 2`
  90. fi
  91. if [ "$2" = "$item_size" ]; then
  92. tst_resm TPASS "$1 is $2 as expected"
  93. else
  94. tst_resm TFAIL "$1 is $item_size, $2 expected"
  95. fi
  96. }
  97. signal_memcg_process()
  98. {
  99. local pid=$1
  100. local size=$2
  101. local path=$3
  102. local usage_start=$(cat ${path}memory.usage_in_bytes)
  103. kill -s USR1 $pid 2> /dev/null
  104. if [ -z "$size" ]; then
  105. return
  106. fi
  107. local loops=100
  108. while kill -0 $pid 2> /dev/null; do
  109. local usage=$(cat ${path}memory.usage_in_bytes)
  110. local diff_a=$((usage_start - usage))
  111. local diff_b=$((usage - usage_start))
  112. if [ "$diff_a" -ge "$size" -o "$diff_b" -ge "$size" ]; then
  113. return
  114. fi
  115. tst_sleep 100ms
  116. loops=$((loops - 1))
  117. if [ $loops -le 0 ]; then
  118. tst_brkm TBROK "timeouted on memory.usage_in_bytes"
  119. fi
  120. done
  121. }
  122. stop_memcg_process()
  123. {
  124. local pid=$1
  125. kill -s INT $pid 2> /dev/null
  126. wait $pid
  127. }
  128. warmup()
  129. {
  130. local pid=$1
  131. tst_resm TINFO "Warming up pid: $pid"
  132. signal_memcg_process $pid
  133. signal_memcg_process $pid
  134. sleep 1
  135. kill -0 $pid
  136. if [ $? -ne 0 ]; then
  137. wait $pid
  138. tst_resm TFAIL "Process $pid exited with $? after warm up"
  139. return 1
  140. else
  141. tst_resm TINFO "Process is still here after warm up: $pid"
  142. fi
  143. return 0
  144. }
  145. # Run test cases which checks memory.stat after make
  146. # some memory allocation
  147. test_mem_stat()
  148. {
  149. local memtypes="$1"
  150. local size=$2
  151. local total_size=$3
  152. local stat_name=$4
  153. local exp_stat_size=$5
  154. local check_after_free=$6
  155. tst_resm TINFO "Running memcg_process $memtypes -s $size"
  156. memcg_process $memtypes -s $size &
  157. TST_CHECKPOINT_WAIT 0
  158. warmup $!
  159. if [ $? -ne 0 ]; then
  160. return
  161. fi
  162. echo $! > tasks
  163. signal_memcg_process $! $size
  164. check_mem_stat $stat_name $exp_stat_size
  165. signal_memcg_process $! $size
  166. if $check_after_free; then
  167. check_mem_stat $stat_name 0
  168. fi
  169. stop_memcg_process $!
  170. }
  171. # Run test cases which checks memory.max_usage_in_bytes after make
  172. # some memory allocation
  173. # $1 - the parameters of 'process', such as --shm
  174. # $2 - the -s parameter of 'process', such as 4096
  175. # $3 - item name
  176. # $4 - the expected size
  177. # $5 - check after free ?
  178. test_max_usage_in_bytes()
  179. {
  180. tst_resm TINFO "Running memcg_process $1 -s $2"
  181. memcg_process $1 -s $2 &
  182. TST_CHECKPOINT_WAIT 0
  183. warmup $!
  184. if [ $? -ne 0 ]; then
  185. return
  186. fi
  187. echo $! > tasks
  188. signal_memcg_process $! $2
  189. signal_memcg_process $! $2
  190. check_mem_stat $3 $4
  191. if [ $5 -eq 1 ]; then
  192. echo 0 > $3
  193. check_mem_stat $3 0
  194. fi
  195. stop_memcg_process $!
  196. }
  197. # make some memory allocation
  198. # $1 - the parameters of 'process', such as --shm
  199. # $2 - the -s parameter of 'process', such as 4096
  200. malloc_free_memory()
  201. {
  202. tst_resm TINFO "Running memcg_process $1 -s $2"
  203. memcg_process $1 -s $2 &
  204. TST_CHECKPOINT_WAIT 0
  205. echo $! > tasks
  206. signal_memcg_process $! $2
  207. signal_memcg_process $! $2
  208. stop_memcg_process $!
  209. }
  210. # Test if failcnt > 0, which means page reclamation occured
  211. # $1 - item name in memcg
  212. test_failcnt()
  213. {
  214. failcnt=`cat $1`
  215. if [ $failcnt -gt 0 ]; then
  216. tst_resm TPASS "$1 is $failcnt, > 0 as expected"
  217. else
  218. tst_resm TFAIL "$1 is $failcnt, <= 0 expected"
  219. fi
  220. }
  221. # Test process will be killed due to exceed memory limit
  222. # $1 - the value of memory.limit_in_bytes
  223. # $2 - the parameters of 'process', such as --shm
  224. # $3 - the -s parameter of 'process', such as 4096
  225. # $4 - use mem+swap limitation
  226. test_proc_kill()
  227. {
  228. echo $1 > memory.limit_in_bytes
  229. if [ $4 -eq 1 ]; then
  230. if [ -e memory.memsw.limit_in_bytes ]; then
  231. echo $1 > memory.memsw.limit_in_bytes
  232. else
  233. tst_resm TCONF "mem+swap is not enabled"
  234. return
  235. fi
  236. fi
  237. memcg_process $2 -s $3 &
  238. pid=$!
  239. TST_CHECKPOINT_WAIT 0
  240. echo $pid > tasks
  241. sleep 1
  242. signal_memcg_process $pid $3
  243. tpk_pid_exists=1
  244. for tpk_iter in $(seq 20); do
  245. if [ ! -d "/proc/$pid" ] ||
  246. grep -q 'Z (zombie)' "/proc/$pid/status"; then
  247. tpk_pid_exists=0
  248. break
  249. fi
  250. tst_sleep 250ms
  251. done
  252. if [ $tpk_pid_exists -eq 0 ]; then
  253. wait $pid
  254. ret=$?
  255. if [ $ret -eq 1 ]; then
  256. tst_resm TFAIL "process $pid is killed by error"
  257. elif [ $ret -eq 2 ]; then
  258. tst_resm TPASS "Failed to lock memory"
  259. else
  260. tst_resm TPASS "process $pid is killed"
  261. fi
  262. else
  263. stop_memcg_process $!
  264. tst_resm TFAIL "process $pid is not killed"
  265. fi
  266. }
  267. # Test limit_in_bytes will be aligned to PAGESIZE
  268. # $1 - user input value
  269. # $2 - use mem+swap limitation
  270. test_limit_in_bytes()
  271. {
  272. echo $1 > memory.limit_in_bytes
  273. if [ $2 -eq 1 ]; then
  274. if [ -e memory.memsw.limit_in_bytes ]; then
  275. echo $1 > memory.memsw.limit_in_bytes
  276. limit=`cat memory.memsw.limit_in_bytes`
  277. else
  278. tst_resm TCONF "mem+swap is not enabled"
  279. return
  280. fi
  281. else
  282. limit=`cat memory.limit_in_bytes`
  283. fi
  284. # Kernels prior to 3.19 were rounding up but newer kernels
  285. # are rounding down
  286. if [ \( $(($PAGESIZE*($1/$PAGESIZE))) -eq $limit \) \
  287. -o \( $(($PAGESIZE*(($1+$PAGESIZE-1)/$PAGESIZE))) -eq $limit \) ]; then
  288. tst_resm TPASS "input=$1, limit_in_bytes=$limit"
  289. else
  290. tst_resm TFAIL "input=$1, limit_in_bytes=$limit"
  291. fi
  292. }
  293. # Never used, so untested
  294. #
  295. # Test memory controller doesn't charge hugepage
  296. # $1 - the value of /proc/sys/vm/nr_hugepages
  297. # $2 - the parameters of 'process', --mmap-file or --shm
  298. # $3 - the -s parameter of 'process', such as $HUGEPAGESIZE
  299. # $4 - 0: expected failure, 1: expected success
  300. test_hugepage()
  301. {
  302. TMP_FILE="$TMP_DIR/tmp"
  303. nr_hugepages=`cat /proc/sys/vm/nr_hugepages`
  304. mkdir /hugetlb
  305. mount -t hugetlbfs none /hugetlb
  306. echo $1 > /proc/sys/vm/nr_hugepages
  307. memcg_process $2 --hugepage -s $3 > $TMP_FILE 2>&1 &
  308. TST_CHECKPOINT_WAIT 0
  309. signal_memcg_process $! $3
  310. check_mem_stat "rss" 0
  311. echo "TMP_FILE:"
  312. cat $TMP_FILE
  313. if [ $4 -eq 0 ]; then
  314. test -s $TMP_FILE
  315. if [ $? -eq 0 ]; then
  316. tst_resm TPASS "allocate hugepage failed as expected"
  317. else
  318. signal_memcg_process $! $3
  319. stop_memcg_process $!
  320. tst_resm TFAIL "allocate hugepage should fail"
  321. fi
  322. else
  323. test ! -s $TMP_FILE
  324. if [ $? -eq 0 ]; then
  325. signal_memcg_process $! $3
  326. stop_memcg_process $!
  327. tst_resm TPASS "allocate hugepage succeeded"
  328. else
  329. tst_resm TFAIL "allocate hugepage failed"
  330. fi
  331. fi
  332. sleep 1
  333. rm -rf $TMP_FILE
  334. umount /hugetlb
  335. rmdir /hugetlb
  336. echo $nr_hugepages > /proc/sys/vm/nr_hugepages
  337. }
  338. # Test the memory charge won't move to subgroup
  339. # $1 - memory.limit_in_bytes in parent group
  340. # $2 - memory.limit_in_bytes in sub group
  341. test_subgroup()
  342. {
  343. mkdir subgroup
  344. echo $1 > memory.limit_in_bytes
  345. echo $2 > subgroup/memory.limit_in_bytes
  346. tst_resm TINFO "Running memcg_process --mmap-anon -s $PAGESIZES"
  347. memcg_process --mmap-anon -s $PAGESIZES &
  348. TST_CHECKPOINT_WAIT 0
  349. warmup $! $PAGESIZES
  350. if [ $? -ne 0 ]; then
  351. return
  352. fi
  353. echo $! > tasks
  354. signal_memcg_process $! $PAGESIZES
  355. check_mem_stat "rss" $PAGESIZES
  356. cd subgroup
  357. echo $! > tasks
  358. check_mem_stat "rss" 0
  359. # cleanup
  360. cd ..
  361. echo $! > tasks
  362. stop_memcg_process $!
  363. rmdir subgroup
  364. }
  365. # Run test cases which test memory.move_charge_at_immigrate
  366. test_move_charge()
  367. {
  368. local memtypes="$1"
  369. local size=$2
  370. local total_size=$3
  371. local move_charge_mask=$4
  372. local b_rss=$5
  373. local b_cache=$6
  374. local a_rss=$7
  375. local a_cache=$8
  376. mkdir subgroup_a
  377. tst_resm TINFO "Running memcg_process $memtypes -s $size"
  378. memcg_process $memtypes -s $size &
  379. TST_CHECKPOINT_WAIT 0
  380. warmup $!
  381. if [ $? -ne 0 ]; then
  382. rmdir subgroup_a
  383. return
  384. fi
  385. echo $! > subgroup_a/tasks
  386. signal_memcg_process $! $total_size "subgroup_a/"
  387. mkdir subgroup_b
  388. echo $move_charge_mask > subgroup_b/memory.move_charge_at_immigrate
  389. echo $! > subgroup_b/tasks
  390. cd subgroup_b
  391. check_mem_stat "rss" $b_rss
  392. check_mem_stat "cache" $b_cache
  393. cd ../subgroup_a
  394. check_mem_stat "rss" $a_rss
  395. check_mem_stat "cache" $a_cache
  396. cd ..
  397. stop_memcg_process $!
  398. rmdir subgroup_a subgroup_b
  399. }
  400. cleanup_test()
  401. {
  402. TEST_ID="$1"
  403. if [ -n "$orig_memory_use_hierarchy" ];then
  404. echo $orig_memory_use_hierarchy > \
  405. /dev/memcg/memory.use_hierarchy
  406. if [ $? -ne 0 ];then
  407. tst_resm TINFO "restore "\
  408. "/dev/memcg/memory.use_hierarchy failed"
  409. fi
  410. orig_memory_use_hierarchy=""
  411. fi
  412. killall -9 memcg_process 2>/dev/null
  413. wait
  414. ROD cd "$TMP_DIR"
  415. ROD rmdir "/dev/memcg/$TEST_ID"
  416. TEST_ID=""
  417. ROD umount /dev/memcg
  418. ROD rmdir /dev/memcg
  419. }
  420. setup_test()
  421. {
  422. TEST_ID="$1"
  423. ROD mkdir /dev/memcg
  424. ROD mount -t cgroup -omemory memcg /dev/memcg
  425. # The default value for memory.use_hierarchy is 0 and some of tests
  426. # (memcg_stat_test.sh and memcg_use_hierarchy_test.sh) expect it so
  427. # while there are distributions (RHEL7U0Beta for example) that sets
  428. # it to 1.
  429. orig_memory_use_hierarchy=$(cat /dev/memcg/memory.use_hierarchy)
  430. if [ -z "$orig_memory_use_hierarchy" ];then
  431. tst_resm TINFO "cat /dev/memcg/memory.use_hierarchy failed"
  432. elif [ "$orig_memory_use_hierarchy" = "0" ];then
  433. orig_memory_use_hierarchy=""
  434. else
  435. echo 0 > /dev/memcg/memory.use_hierarchy
  436. if [ $? -ne 0 ];then
  437. tst_resm TINFO "set /dev/memcg/memory.use_hierarchy" \
  438. "to 0 failed"
  439. fi
  440. fi
  441. ROD mkdir "/dev/memcg/$TEST_ID"
  442. ROD cd "/dev/memcg/$TEST_ID"
  443. }
  444. # Run all the test cases
  445. run_tests()
  446. {
  447. for i in $(seq 1 $TST_TOTAL); do
  448. tst_resm TINFO "Starting test $i"
  449. setup_test $i
  450. if [ -e memory.memsw.limit_in_bytes ]; then
  451. MEMSW_LIMIT_FLAG=1
  452. fi
  453. if [ -e memory.memsw.max_usage_in_bytes ]; then
  454. MEMSW_USAGE_FLAG=1
  455. fi
  456. testcase_$i
  457. cleanup_test $i
  458. done
  459. }