| #!/bin/sh |
| ############################################################################## |
| # # |
| # Copyright (c) International Business Machines Corp., 2007 # |
| # Sivakumar Chinnaiah, Sivakumar.C@in.ibm.com # |
| # Copyright (c) Linux Test Project, 2016 # |
| # # |
| # This program is free software: you can redistribute it and/or modify # |
| # it under the terms of the GNU General Public License as published by # |
| # the Free Software Foundation, either version 3 of the License, or # |
| # (at your option) any later version. # |
| # # |
| # This program is distributed in the hope that it will be useful, # |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of # |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # |
| # GNU General Public License for more details. # |
| # # |
| # You should have received a copy of the GNU General Public License # |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. # |
| # # |
| ############################################################################## |
| # # |
| # Description: Test Basic functionality of numactl command. # |
| # Test #1: Verifies cpunodebind and membind # |
| # Test #2: Verifies preferred node bind for memory allocation # |
| # Test #3: Verifies share memory allocation on preferred node # |
| # Test #4: Verifies memory interleave on all nodes # |
| # Test #5: Verifies share memory interleave on all nodes # |
| # Test #6: Verifies physcpubind # |
| # Test #7: Verifies localalloc # |
| # Test #8: Verifies memhog # |
| # Test #9: Verifies numa_node_size api # |
| # Test #10:Verifies Migratepages # |
| # Test #11:Verifies hugepage alloacted on specified node # |
| # Test #12:Verifies THP memory allocated on preferred node # |
| # # |
| ############################################################################## |
| |
| TST_CNT=12 |
| TST_SETUP=setup |
| TST_TESTFUNC=test |
| TST_NEEDS_TMPDIR=1 |
| TST_NEEDS_ROOT=1 |
| TST_NEEDS_CMDS="numactl numastat awk" |
| |
| . tst_test.sh |
| |
| # |
| # Extracts the value of given numa node from the `numastat -p` output. |
| # |
| # $1 - Pid number. |
| # $2 - Node number. |
| # |
| extract_numastat_p() |
| { |
| local pid=$1 |
| local node=$(($2 + 2)) |
| |
| echo $(numastat -p $pid |awk '/^Total/ {print $'$node'}') |
| } |
| |
| check_for_support_numa() |
| { |
| local pid=$1 |
| |
| local state=$(awk '{print $3}' /proc/$pid/stat) |
| |
| if [ $state = 'T' ]; then |
| return 0 |
| fi |
| |
| return 1 |
| } |
| |
| setup() |
| { |
| export MB=$((1024*1024)) |
| export PAGE_SIZE=$(tst_getconf PAGESIZE) |
| export HPAGE_SIZE=$(awk '/Hugepagesize:/ {print $2}' /proc/meminfo) |
| |
| total_nodes=0 |
| |
| nodes_list=$(numactl --show | grep nodebind | cut -d ':' -f 2) |
| for node in $nodes_list; do |
| total_nodes=$((total_nodes+1)) |
| done |
| |
| tst_res TINFO "The system contains $total_nodes nodes: $nodes_list" |
| if [ $total_nodes -le 1 ]; then |
| tst_brk TCONF "your machine does not support numa policy |
| or your machine is not a NUMA machine" |
| fi |
| } |
| |
| # Verification of memory allocated on a node |
| test1() |
| { |
| Mem_curr=0 |
| |
| for node in $nodes_list; do |
| numactl --cpunodebind=$node --membind=$node support_numa alloc_1MB & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $node) * $MB" |bc) |
| if [ $(echo "$Mem_curr < $MB" | bc) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA memory allocated in node$node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA local node and memory affinity" |
| } |
| |
| # Verification of memory allocated on preferred node |
| test2() |
| { |
| Mem_curr=0 |
| |
| COUNTER=1 |
| for node in $nodes_list; do |
| |
| if [ $COUNTER -eq $total_nodes ]; then #wrap up for last node |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f 1) |
| else |
| # always next node is preferred node |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f $((COUNTER+1))) |
| fi |
| |
| numactl --cpunodebind=$node --preferred=$Preferred_node support_numa alloc_1MB & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $Preferred_node) * $MB" |bc) |
| if [ $(echo "$Mem_curr < $MB" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA memory allocated in node$Preferred_node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| COUNTER=$((COUNTER+1)) |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA preferred node policy" |
| } |
| |
| # Verification of share memory allocated on preferred node |
| test3() |
| { |
| Mem_curr=0 |
| COUNTER=1 |
| |
| for node in $nodes_list; do |
| |
| if [ $COUNTER -eq $total_nodes ] #wrap up for last node |
| then |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f 1) |
| else |
| # always next node is preferred node |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f $((COUNTER+1))) |
| fi |
| |
| numactl --cpunodebind=$node --preferred=$Preferred_node support_numa alloc_1MB_shared & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $Preferred_node) * $MB" |bc) |
| if [ $(echo "$Mem_curr < $MB" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA share memory allocated in node$Preferred_node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| COUNTER=$((COUNTER+1)) |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA share memory allocated in preferred node" |
| } |
| |
| # Verification of memory interleaved on all nodes |
| test4() |
| { |
| Mem_curr=0 |
| # Memory will be allocated using round robin on nodes. |
| Exp_incr=$(echo "$MB / $total_nodes" |bc) |
| |
| numactl --interleave=all support_numa alloc_1MB & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| for node in $nodes_list; do |
| Mem_curr=$(echo "$(extract_numastat_p $pid $node) * $MB" |bc) |
| |
| if [ $(echo "$Mem_curr < $Exp_incr" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA interleave memory allocated in node$node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| done |
| |
| kill -CONT $pid >/dev/null 2>&1 |
| tst_res TPASS "NUMA interleave policy" |
| } |
| |
| # Verification of shared memory interleaved on all nodes |
| test5() |
| { |
| Mem_curr=0 |
| # Memory will be allocated using round robin on nodes. |
| Exp_incr=$(echo "$MB / $total_nodes" |bc) |
| |
| numactl --interleave=all support_numa alloc_1MB_shared & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| for node in $nodes_list; do |
| Mem_curr=$(echo "$(extract_numastat_p $pid $node) * $MB" |bc) |
| |
| if [ $(echo "$Mem_curr < $Exp_incr" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA interleave share memory allocated in node$node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| done |
| |
| kill -CONT $pid >/dev/null 2>&1 |
| |
| tst_res TPASS "NUMA interleave policy on shared memory" |
| } |
| |
| # Verification of physical cpu bind |
| test6() |
| { |
| no_of_cpus=0 #no. of cpu's exist |
| run_on_cpu=0 |
| running_on_cpu=0 |
| |
| no_of_cpus=$(tst_ncpus) |
| # not sure whether cpu's can't be in odd number |
| run_on_cpu=$(($((no_of_cpus+1))/2)) |
| numactl --physcpubind=$run_on_cpu support_numa pause & #just waits for sigint |
| pid=$! |
| var=`awk '{ print $2 }' /proc/$pid/stat` |
| while [ $var = '(numactl)' ]; do |
| var=`awk '{ print $2 }' /proc/$pid/stat` |
| tst_sleep 100ms |
| done |
| # Warning !! 39 represents cpu number, on which process pid is currently running and |
| # this may change if Some more fields are added in the middle, may be in future |
| running_on_cpu=$(awk '{ print $39; }' /proc/$pid/stat) |
| if [ $running_on_cpu -ne $run_on_cpu ]; then |
| tst_res TFAIL \ |
| "Process running on cpu$running_on_cpu but expected to run on cpu$run_on_cpu" |
| ROD kill -INT $pid |
| return |
| fi |
| |
| ROD kill -INT $pid |
| |
| tst_res TPASS "NUMA phycpubind policy" |
| } |
| |
| # Verification of local node allocation |
| test7() |
| { |
| Mem_curr=0 |
| |
| for node in $nodes_list; do |
| numactl --cpunodebind=$node --localalloc support_numa alloc_1MB & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $node) * $MB" |bc) |
| if [ $(echo "$Mem_curr < $MB" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA localnode memory allocated in node$node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA local node allocation" |
| } |
| |
| check_ltp_numa_test8_log() |
| { |
| grep -m1 -q '.' ltp_numa_test8.log |
| } |
| |
| # Verification of memhog with interleave policy |
| test8() |
| { |
| Mem_curr=0 |
| # Memory will be allocated using round robin on nodes. |
| Exp_incr=$(echo "$MB / $total_nodes" |bc) |
| |
| numactl --interleave=all memhog -r1000000 1MB >ltp_numa_test8.log 2>&1 & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_ltp_numa_test8_log" 0 |
| |
| for node in $nodes_list; do |
| Mem_curr=$(echo "$(extract_numastat_p $pid $node) * $MB" |bc) |
| |
| if [ $(echo "$Mem_curr < $Exp_incr" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA interleave memhog in node$node is less than expected" |
| kill -KILL $pid >/dev/null 2>&1 |
| return |
| fi |
| done |
| |
| kill -KILL $pid >/dev/null 2>&1 |
| tst_res TPASS "NUMA MEMHOG policy" |
| } |
| |
| # Function: hardware cheking with numa_node_size api |
| # |
| # Description: - Returns the size of available nodes if success. |
| # |
| # Input: - o/p of numactl --hardware command which is expected in the format |
| # shown below |
| # available: 2 nodes (0-1) |
| # node 0 size: 7808 MB |
| # node 0 free: 7457 MB |
| # node 1 size: 5807 MB |
| # node 1 free: 5731 MB |
| # node distances: |
| # node 0 1 |
| # 0: 10 20 |
| # 1: 20 10 |
| # |
| test9() |
| { |
| RC=0 |
| |
| numactl --hardware > gavail_nodes |
| RC=$(awk '{ if ( NR == 1 ) {print $1;} }' gavail_nodes) |
| if [ $RC = "available:" ]; then |
| RC=$(awk '{ if ( NR == 1 ) {print $3;} }' gavail_nodes) |
| if [ $RC = "nodes" ]; then |
| RC=$(awk '{ if ( NR == 1 ) {print $2;} }' gavail_nodes) |
| tst_res TPASS "NUMA policy on lib NUMA_NODE_SIZE API" |
| else |
| tst_res TFAIL "Failed with numa policy" |
| fi |
| else |
| tst_res TFAIL "Failed with numa policy" |
| fi |
| } |
| |
| # Verification of migratepages |
| test10() |
| { |
| Mem_curr=0 |
| COUNTER=1 |
| |
| for node in $nodes_list; do |
| |
| if [ $COUNTER -eq $total_nodes ]; then |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f 1) |
| else |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f $((COUNTER+1))) |
| fi |
| |
| numactl --preferred=$node support_numa alloc_1MB & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| migratepages $pid $node $Preferred_node |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $Preferred_node) * $MB" |bc) |
| if [ $(echo "$Mem_curr < $MB" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA migratepages is not working fine" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| COUNTER=$((COUNTER+1)) |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA MIGRATEPAGES policy" |
| } |
| |
| # Verification of hugepage memory allocated on a node |
| test11() |
| { |
| Mem_huge=0 |
| Sys_node=/sys/devices/system/node |
| |
| if [ ! -d "/sys/kernel/mm/hugepages/" ]; then |
| tst_res TCONF "hugepage is not supported" |
| return |
| fi |
| |
| for node in $nodes_list; do |
| Ori_hpgs=$(cat ${Sys_node}/node${node}/hugepages/hugepages-${HPAGE_SIZE}kB/nr_hugepages) |
| New_hpgs=$((Ori_hpgs + 1)) |
| echo $New_hpgs >${Sys_node}/node${node}/hugepages/hugepages-${HPAGE_SIZE}kB/nr_hugepages |
| |
| Chk_hpgs=$(cat ${Sys_node}/node${node}/hugepages/hugepages-${HPAGE_SIZE}kB/nr_hugepages) |
| if [ "$Chk_hpgs" -ne "$New_hpgs" ]; then |
| tst_res TCONF "hugepage is not enough to test" |
| return |
| fi |
| |
| numactl --cpunodebind=$node --membind=$node support_numa alloc_1huge_page & |
| pid=$! |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_huge=$(echo $(numastat -p $pid |awk '/^Huge/ {print $'$((node+2))'}')) |
| Mem_huge=$((${Mem_huge%.*} * 1024)) |
| |
| if [ "$Mem_huge" -lt "$HPAGE_SIZE" ]; then |
| tst_res TFAIL \ |
| "NUMA memory allocated in node$node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| echo $Ori_hpgs >${Sys_node}/node${node}/hugepages/hugepages-${HPAGE_SIZE}kB/nr_hugepages |
| return |
| fi |
| |
| kill -CONT $pid >/dev/null 2>&1 |
| echo $Ori_hpgs >${Sys_node}/node${node}/hugepages/hugepages-${HPAGE_SIZE}kB/nr_hugepages |
| done |
| |
| tst_res TPASS "NUMA local node hugepage memory allocated" |
| } |
| |
| # Verification of THP memory allocated on preferred node |
| test12() |
| { |
| Mem_curr=0 |
| |
| if ! grep -q '\[always\]' /sys/kernel/mm/transparent_hugepage/enabled; then |
| tst_res TCONF "THP is not supported/enabled" |
| return |
| fi |
| |
| COUNTER=1 |
| for node in $nodes_list; do |
| |
| if [ $COUNTER -eq $total_nodes ]; then #wrap up for last node |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f 1) |
| else |
| # always next node is preferred node |
| Preferred_node=$(echo $nodes_list | cut -d ' ' -f $((COUNTER+1))) |
| fi |
| |
| numactl --cpunodebind=$node --preferred=$Preferred_node support_numa alloc_2HPSZ_THP & |
| pid=$! |
| |
| TST_RETRY_FUNC "check_for_support_numa $pid" 0 |
| |
| Mem_curr=$(echo "$(extract_numastat_p $pid $Preferred_node) * 1024" |bc) |
| if [ $(echo "$Mem_curr < $HPAGE_SIZE * 2" |bc ) -eq 1 ]; then |
| tst_res TFAIL \ |
| "NUMA memory allocated in node$Preferred_node is less than expected" |
| kill -CONT $pid >/dev/null 2>&1 |
| return |
| fi |
| |
| COUNTER=$((COUNTER+1)) |
| kill -CONT $pid >/dev/null 2>&1 |
| done |
| |
| tst_res TPASS "NUMA preferred node policy verified with THP enabled" |
| } |
| |
| tst_run |