1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright © International Business Machines  Corp., 2007, 2008
4  *
5  * Test Description:
6  *  The test process is affined to a CPU. It then calls getcpu and
7  *  checks that the CPU and node (if supported) match the expected
8  *  values.
9  */
10 
11 #define _GNU_SOURCE
12 #include <dirent.h>
13 #include <errno.h>
14 #include <sched.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/types.h>
18 #include "lapi/syscalls.h"
19 #include "lapi/cpuset.h"
20 #include "tst_test.h"
21 #include "config.h"
22 
get_cpu(unsigned * cpu_id,unsigned * node_id LTP_ATTRIBUTE_UNUSED,void * cache_struct LTP_ATTRIBUTE_UNUSED)23 static inline int get_cpu(unsigned *cpu_id,
24 			  unsigned *node_id LTP_ATTRIBUTE_UNUSED,
25 			  void *cache_struct LTP_ATTRIBUTE_UNUSED)
26 {
27 #ifndef HAVE_SCHED_GETCPU
28 	return tst_syscall(__NR_getcpu, cpu_id, node_id, cache_struct);
29 #else
30 	*cpu_id = sched_getcpu();
31 #endif
32 	return 0;
33 }
34 
max_cpuid(size_t size,cpu_set_t * set)35 static unsigned int max_cpuid(size_t size, cpu_set_t * set)
36 {
37 	unsigned int index, max = 0;
38 	for (index = 0; index < size * 8; index++)
39 		if (CPU_ISSET_S(index, size, set))
40 			max = index;
41 	return max;
42 }
43 
44 /*
45  * This will set the affinity to max cpu on which process can run
46  * and return that cpu id to the calling process
47  */
set_cpu_affinity(void)48 static unsigned int set_cpu_affinity(void)
49 {
50 	unsigned cpu_max;
51 	cpu_set_t *set;
52 	size_t size;
53 	int nrcpus = 1024;
54 
55 realloc:
56 	set = CPU_ALLOC(nrcpus);
57 	if (!set)
58 		tst_brk(TBROK | TERRNO, "CPU_ALLOC()");
59 
60 	size = CPU_ALLOC_SIZE(nrcpus);
61 	CPU_ZERO_S(size, set);
62 	if (sched_getaffinity(0, size, set) < 0) {
63 		CPU_FREE(set);
64 		if (errno == EINVAL && nrcpus < (1024 << 8)) {
65 			nrcpus = nrcpus << 2;
66 			goto realloc;
67 		}
68 		tst_brk(TBROK | TERRNO, "sched_getaffinity()");
69 	}
70 	cpu_max = max_cpuid(size, set);
71 	CPU_ZERO_S(size, set);
72 	CPU_SET_S(cpu_max, size, set);
73 	if (sched_setaffinity(0, size, set) < 0) {
74 		CPU_FREE(set);
75 		tst_brk(TBROK | TERRNO, "sched_setaffinity()");
76 	}
77 	CPU_FREE(set);
78 	return cpu_max;
79 }
80 
81 #ifdef __i386__
get_nodeid(unsigned int cpu_id)82 static unsigned int get_nodeid(unsigned int cpu_id)
83 {
84 	DIR *directory_parent, *directory_node;
85 	struct dirent *de, *dn;
86 	char directory_path[PATH_MAX];
87 	unsigned int cpu;
88 	int node_id = 0;
89 
90 	directory_parent = opendir("/sys/devices/system/node");
91 	if (!directory_parent) {
92 		tst_res(TINFO,
93 			"/sys not mounted or not a numa system. "
94 			"Assuming one node");
95 		tst_res(TINFO, "Error opening: /sys/devices/system/node :%s",
96 			strerror(errno));
97 		/* Assume CPU belongs to the only node, node zero. */
98 		return 0;
99 	} else {
100 		while ((de = readdir(directory_parent)) != NULL) {
101 			if (strncmp(de->d_name, "node", 4))
102 				continue;
103 			sprintf(directory_path, "/sys/devices/system/node/%s",
104 				de->d_name);
105 			directory_node = opendir(directory_path);
106 			while ((dn = readdir(directory_node)) != NULL) {
107 				if (strncmp(dn->d_name, "cpu", 3))
108 					continue;
109 				cpu = strtoul(dn->d_name + 3, NULL, 0);
110 				if (cpu == cpu_id) {
111 					node_id =
112 					    strtoul(de->d_name + 4, NULL, 0);
113 					break;
114 				}
115 			}
116 			closedir(directory_node);
117 		}
118 		closedir(directory_parent);
119 	}
120 	return node_id;
121 }
122 #endif
123 
run(void)124 static void run(void)
125 {
126 	unsigned int cpu_id, node_id = 0;
127 	unsigned int cpu_set;
128 #ifdef __i386__
129 	unsigned int node_set;
130 #endif
131 
132 	cpu_set = set_cpu_affinity();
133 #ifdef __i386__
134 	node_set = get_nodeid(cpu_set);
135 #endif
136 
137 	TEST(get_cpu(&cpu_id, &node_id, NULL));
138 	if (TST_RET == 0) {
139 		if (cpu_id != cpu_set)
140 			tst_res(TFAIL, "getcpu() returned wrong value"
141 				" expected cpuid:%d, returned value cpuid: %d",
142 				cpu_set, cpu_id);
143 #ifdef __i386__
144 		else if (node_id != node_set)
145 			tst_res(TFAIL, "getcpu() returned wrong value"
146 				" expected  node id:%d returned  node id:%d",
147 				node_set, node_id);
148 #endif
149 		else
150 			tst_res(TPASS, "getcpu() returned proper"
151 				" cpuid:%d, node id:%d", cpu_id,
152 				node_id);
153 	} else {
154 		tst_res(TFAIL, "getcpu() Failed, errno=%d:%s",
155 			TST_ERR, strerror(TST_ERR));
156 	}
157 }
158 
setup(void)159 static void setup(void)
160 {
161 	if (tst_kvercmp(2, 6, 20) < 0)
162 		tst_brk(TCONF, "kernel >= 2.6.20 required");
163 }
164 
165 static struct tst_test test = {
166 	.test_all = run,
167 	.setup = setup,
168 };
169