用例标题
CPU指令架构支持-CPU内置功能-d.物理多路 CPU-1)支持跨路内存访问能力,允许其他路CPU访问本路内存
方案描述
1、使用preferred策略在优先指定节点上面内存分配,用来验证跨路内存访问,其他路CPU访问本地内存
2、遍历系统所有内存节点,对每个节点执行如下操作:
3、用numactl将进程support_numa绑定到当前节点
4、用所有内存节点依次顺序循环用preferred指定内存优先分配节点,进程support_numa会在优先分配节点上面分配1MB的内存
5、用numastat统计进程support_numa在优先分配节点上的总内存大小
6、如果总内存大于1MB则在优先分配节点上面内存分配成功,如果小于则用例失败
7、遍历系统所有内存节点之后如果所有的节点都分配成功则支持跨路及跨节点内存访问,用例成功。
用例步骤
1、执行./numa.sh (附件见5.2-d)
打印"PASS NUMA preferred node policy"则用例执行成功
相关测试代码:
用例包含三个源码文件:
numa.sh、 support_numa.c 、tst_getconf.c
- numa.sh 包含了测试用例
- support_numa.c 分配1M物理内存
- support_numa_case1.c 在指定节点分配1M物理内存,然后将别的节点的主线程自动迁移到指定节点
- tst_getconf.c 读取系统配置参数PAGESIZE
- Makefile support_numa,support_numa_case1和tst_getconf命令的编译脚本
使用方法
编译:make
执行:./numa.sh
PS:如果numa.sh没有可执行权限,则添加可执行权限chmod a+x numa.sh
/******************************************************************************/
/* */
/* File: support_numa.c */
/* */
/* */
/* Author: guohui */
/* */
/******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#define MB (1<<20)
#define PAGE_SIZE getpagesize()
#define barrier() __asm__ __volatile__("": : :"memory")
static void help(void)
{
printf("Input: Describe input arguments to this program\n");
printf(" argv[1] == \"alloc_1MB\" then allocate 1MB of memory\n");
printf("Exit: On failure - Exits with non-zero value\n");
printf(" On success - exits with 0 exit value\n");
exit(1);
}
int main(int argc, char *argv[])
{
int i, hpsz;
char *buf = NULL;
if (argc != 2) {
fprintf(stderr, "Here expect only one number(i.e. 2) as the parameter\n");
exit(1);
}
if (!strcmp(argv[1], "alloc_1MB")) {
buf = malloc(MB);
if (!buf) {
fprintf(stderr, "Memory is not available\n");
exit(1);
}
for (i = 0; i < MB; i += PAGE_SIZE) {
buf[i] = 'a';
barrier();
}
raise(SIGSTOP);
free(buf);
} else {
help();
}
return 0;
}
/******************************************************************************/
/* */
/* File: support_numa_case1.c */
/* */
/* Description: 分配1MB大小的内存 */
/* */
/* Author: guohui */
/* */
/******************************************************************************/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <numaif.h>
#define MB (1<<20)
#define PAGE_SIZE getpagesize()
#define barrier() __asm__ __volatile__("": : :"memory")
#define gettid() syscall(__NR_gettid)
static char *buf = NULL;
static int p1[2];
static int p2[2];
static char read_buff1[8];
static char read_buff2[8];
static int should_run = 1;
static void help(void)
{
printf("Input: Describe input arguments to this program\n");
printf(" argv[1] == \"alloc_1MB\" then allocate 1MB of memory\n");
printf(" argv[2] == \"cpus_list\" then bind sub thread to those cpus\n");
printf("Exit: On failure - Exits with non-zero value\n");
printf(" On success - exits with 0 exit value\n");
exit(1);
}
static void delay(void)
{
int i = 4600;
while (i-- > 0);
}
static void sig_usr(int signo)
{
if (signo == SIGUSR1)
{
should_run = 0;
exit(0);
}
}
static int is_digitstr(char *str)
{
return (strspn(str, "0123456789")==strlen(str));
}
static void * pthread(void *arg)
{
char *cpus = (char *)arg;
int cpu;
cpu_set_t mask;
int tid = gettid();
int i;
char v = 'a';
char *p;
char tmp[256] = { 0 };
strncpy(tmp, cpus, strlen(cpus));
CPU_ZERO(&mask);
p = strtok(tmp, " ");
while (p) {
if (!is_digitstr(p)) {
p = strtok(NULL, " ");
continue;
}
cpu = atoi(p);
CPU_SET(cpu, &mask);
p = strtok(NULL, " ");
}
if(sched_setaffinity(tid, sizeof(mask), &mask) < 0) {
perror("pthread_setaffinity_np failed.\n");
}
while (should_run) {
read(p1[1], read_buff2, 1);
sleep(1);
for (i = 0; i < MB; i += PAGE_SIZE) {
buf[i] = 'a';
barrier();
}
v += 1;
if (v > 'z')
v = 'a';
write(p2[1], "1", 1);
}
return NULL;
}
static void * pthread_node(void *arg)
{
char *cpus = (char *)arg;
int cpu;
cpu_set_t mask;
int tid = gettid();
int i;
char v = 'a';
char *p;
unsigned long count = 0;
char tmp[256] = { 0 };
strncpy(tmp, cpus, strlen(cpus));
CPU_ZERO(&mask);
p = strstr(tmp, " ");
if (p)
*p = '\0';
cpu = atoi(tmp);
CPU_SET(cpu, &mask);
if(sched_setaffinity(tid, sizeof(mask), &mask) < 0) {
perror("pthread_setaffinity_np failed.\n");
}
while (should_run) {
read(p1[1], read_buff2, 1);
sleep(10);
for (i = 0; i < MB; i += PAGE_SIZE) {
buf[i] = 'a';
barrier();
}
v += 1;
if (v > 'z')
v = 'a';
write(p2[1], "1", 1);
}
return NULL;
}
static void * pthread_local(void *arg)
{
char *cpus = (char *)arg;
int cpu;
cpu_set_t mask;
int tid = gettid();
int i;
char v = 'a';
char *p;
char tmp[256] = { 0 };
strncpy(tmp, cpus, strlen(cpus));
CPU_ZERO(&mask);
p = strtok(tmp, " ");
if (!is_digitstr(p)) {
p = strtok(NULL, " ");
}
cpu = atoi(p);
CPU_SET(cpu, &mask);
if (sched_setaffinity(tid, sizeof(mask), &mask) < 0) {
perror("pthread_setaffinity_np failed.\n");
}
while (should_run) {
read(p1[1], read_buff2, 1);
sleep(1);
while (1) {
barrier();
}
write(p2[1], "1", 1);
}
return NULL;
}
int main(int argc, char *argv[])
{
int i, hpsz;
pthread_t tidp;
char v='a';
char *tmp;
cpu_set_t mask;
if (!strcmp(argv[1], "alloc_1MB")) {
pipe(p1);
pipe(p2);
buf = malloc(MB);
if (!buf) {
fprintf(stderr, "Memory is not available\n");
exit(1);
}
if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("sig_usr signal error!\n");
}
for (i = 0; i < MB; i += PAGE_SIZE) {
buf[i] = 'a';
barrier();
}
tmp = argv[2];
while (tmp) {
tmp += 1;
if (pthread_create(&tidp, NULL, pthread, (void *)argv[2]) == -1) {
printf("create error!\n");
return 1;
}
tmp = strstr(tmp, " ");
}
tmp = argv[3];
while (tmp) {
if (pthread_create(&tidp, NULL, pthread_local, (void *)tmp) == -1) {
printf("create error!\n");
return 1;
}
tmp = strstr(tmp, " ");
if (!tmp)
break;
tmp += 1;
}
while (should_run) {
delay();
for (i = 0; i < MB; i += PAGE_SIZE) {
buf[i] = 'a';
barrier();
}
v += 1;
if (v > 'z')
v = 'a';
write(p1[0], "1", 1);
read(p2[0], read_buff1, 1);
}
free(buf);
} else {
help();
}
return 0;
}
/* tst_getconf
* Copyright (c) 2018 Myl猫ne Josserand <mylene.josserand@bootlin.com>
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static void print_help(void)
{
printf("Usage: tst_getconf variable\n\n");
printf(" variable: can be PAGESIZE/PAGE_SIZE");
printf(" or _NPROCESSORS_ONLN (for the moment)\n\n");
printf("example: tst_getconf PAGESIZE\n");
}
int main(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, ":h")) != -1) {
switch (opt) {
case 'h':
print_help();
return 0;
default:
print_help();
return 1;
}
}
if (argc != 2) {
print_help();
return 1;
}
if (!strcmp(argv[optind], "_NPROCESSORS_ONLN")) {
printf("%ld\n", sysconf(_SC_NPROCESSORS_ONLN));
} else if (!strcmp(argv[optind], "PAGESIZE") ||
!strcmp(argv[optind], "PAGE_SIZE")) {
printf("%ld\n", sysconf(_SC_PAGE_SIZE));
} else {
printf("tst_getconf: Unrecognized variable \'%s\'\n",
argv[optind]);
return -1;
}
return 0;
}
# numa.sh
#!/bin/sh
#
#*****************************************************************************#
#* *#
#* File: Makefile *#
#* *#
#* Description: 测试用例执行脚本 *#
#* Test #1: 验证 cpunodebind 和 membind 绑定测略 *#
#* 验证进程运行在本NUMA节点并在本NUMA节点分配内存 *#
#* Test #2: 验证 preferred node bind 绑定内存测略 *#
#* 验证跨路内存访问,其他路CPU访问本地内存 *#
#* Test #3: 验证 memory interleave on all nodes 测略 *#
#* Test #4: 验证 localalloc 分配测略 *#
#* *#
#* Author: guohui *#
#* *#
#******************************************************************************#
# $1 - Pid number
# $2 - Node number
get_node_index()
{
local pid=$1
local nid="Node $2"
echo $(numastat -p $pid | sed '3q;d' | awk -F '[[:space:]][[:space:]]+' \
-v node="$nid" '{ for (i = 1; i <= NF; ++i) if($i==node) print i; exit }')
}
# 转换M为B字节.
# $1 - Pid number
# $2 - Node number
# $3 - Size for multiplication (e.g. 1024, $MB)
get_mem_cur()
{
local pid=$1
local index=$(echo "$(get_node_index $pid $2)")
local size=$3
local numstat=$(numastat -p $pid |awk '/^Total/ {print $'$index'}')
if [ -z "$numstat" ]; then
echo 0
return
fi
echo $(echo "$numstat * $size" | bc)
}
get_node_cpus()
{
local ne=$1
ne=$(echo "$ne+1" | bc)
echo $(numactl --hardware | grep "cpus:" | awk -F ':' -v node="$ne" '{ if (NR == node){print $2;} }')
}
setup()
{
export MB=$((1024*1024))
export PAGE_SIZE=$(./tst_getconf PAGESIZE)
export CPU_MAX_INDEX=$(echo "$(./tst_getconf _NPROCESSORS_ONLN)-1" | bc)
total_nodes=0
nodes_list=$(numactl --show | grep nodebind | cut -d ':' -f 2)
node_max=0
for node in $nodes_list; do
total_nodes=$((total_nodes+1))
node_max=$(echo $(($node_max>$node?$node_max:$node)))
done
echo "The system contains $total_nodes nodes: $nodes_list"
if [ $total_nodes -le 1 ]; then
echo "SUT does not support NUMA policy or not a NUMA machine"
exit
fi
}
test1()
{
local ret
local i=0
echo "verifing please wait ..."
for node in $nodes_list; do
local cnt=1
local flags=0
echo "current node = " $node
for ne in $nodes_list; do
if [ $ne -eq $node ]; then
continue;
fi
local cpus_list=$(get_node_cpus $ne)
local cpus_local=$(get_node_cpus $node)
numactl --cpunodebind=$node --membind=$ne ./support_numa_case1 alloc_1MB "$cpus_list" "$cpus_local" &
pid=$!
sleep 2
taskset -pc 0-$CPU_MAX_INDEX $pid >/dev/null 2>&1
sleep 15
running_on_cpu=$(awk '{ print $39; }' /proc/$pid/stat)
for cpu in $cpus_list; do
if [ $(echo "$running_on_cpu == $cpu" | bc) -eq 1 ]; then
flags=1
break;
fi
done
if [ $flags -eq 1 ]; then
kill -USR1 $pid >/dev/null 2>&1
break;
fi
if [ $flags -eq 0 ]&&[ $ne -eq $node_max ]; then
echo "Process running on cpu $running_on_cpu but expected to run on cpu $cpus_list"
kill -USR1 $pid >/dev/null 2>&1
return
fi
kill -USR1 $pid >/dev/null 2>&1
done
done
echo "PASS NUMA local node and memory affinity"
}
test2()
{
local mem_curr
for node in $nodes_list; do
local cnt=1
for ne in $nodes_list; do
numactl --cpunodebind=$node --preferred=$ne ./support_numa alloc_1MB &
pid=$!
mem_curr=$(get_mem_cur $pid $ne $MB)
if [ $(echo "$mem_curr < $MB" |bc ) -eq 1 ]; then
echo "NUMA preferred memory allocated in node $Preferred_node is less than expected"
kill -CONT $pid >/dev/null 2>&1
return
fi
cnt=$((cnt+1))
kill -CONT $pid >/dev/null 2>&1
done
done
echo "PASS NUMA preferred node policy"
}
test3()
{
local mem_curr
Exp_incr=$(echo "$MB / $total_nodes" |bc)
numactl --interleave=all ./support_numa alloc_1MB &
pid=$!
for node in $nodes_list; do
mem_curr=$(get_mem_cur $pid $node $MB)
if [ $(echo "$mem_curr < $Exp_incr" |bc ) -eq 1 ]; then
echo "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
echo "PASS NUMA interleave policy"
}
test4()
{
local mem_curr
for node in $nodes_list; do
numactl --cpunodebind=$node --localalloc ./support_numa alloc_1MB &
pid=$!
mem_curr=$(get_mem_cur $pid $node $MB)
if [ $(echo "$mem_curr < $MB" |bc ) -eq 1 ]; then
echo "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
echo "PASS NUMA local node allocation"
}
setup
test1
test2
# test3
# test4
set +m killall support_numa >/dev/null 2>&1
#******************************************************************************#
#* *#
#* File: Makefile *#
#* *#
#* Description: 测试用例编译脚本 *#
#* *#
#* Author: guohui *#
#* *#
#******************************************************************************#
CC := gcc
LDFLAGS := -lnuma -lpthread
SRCS = $(wildcard *.c)
OBJS1 := support_numa.o
OBJS2 := support_numa_case1.o
OBJS3 := tst_getconf.o
TARGET1 := support_numa
TARGET2 := support_numa_case1
TARGET3 := tst_getconf
.PHONY:all
all:$(TARGET1) $(TARGET2) $(TARGET3)
%.o : %.c
$(CC) -c -o $@ $<
$(TARGET1): $(OBJS1)
$(CC) $(LDFLAGS) -o $(TARGET1) $(OBJS1)
$(TARGET2): $(OBJS2)
$(CC) $(LDFLAGS) -o $(TARGET2) $(OBJS2)
$(TARGET3): $(OBJS3)
$(CC) -o $(TARGET3) $(OBJS3)
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET1)
rm -f $(TARGET2)
rm -f $(TARGET3)
©统信软件技术有限公司。访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。除此以外,将本网站任何内容或服务进行转载,须备注:该文档出自【faq.uniontech.com】统信软件知识分享平台。否则统信软件将追究相关版权责任。