1 /*
2  *
3  *   Copyright © International Business Machines  Corp., 2007, 2008
4  *
5  *   This program is free software;  you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  *   the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program;  if not, write to the Free Software
17  *   Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
18  */
19 
20 /*
21  * NAME
22  *	getcpu1.c
23  *
24  * DESCRIPTION
25  *	getcpu01 - call getcpu() and make sure it succeeds
26  *
27  * ALGORITHM
28  *	set cpu affinity of the process
29  *	If setaffinity() fails exit from the test suite
30  *	Store the node ID of the cpu which has been set in previous step
31  *	Make a call to getcpu() system call
32  *	Verify the returned valued with the set value
33  *	if they match
34  *	  test is considered PASS
35  *	else
36  *	  test is considered FAIL
37  *
38  * USAGE:  <for command-line>
39  *  getcpu [-c n] [-f] [-i n] [-I x] [-P x] [-t]
40  *     where,  -c n : Run n copies concurrently.
41  *             -f   : Turn off functionality Testing.
42  *	       -i n : Execute test n times.
43  *	       -I x : Execute test for x seconds.
44  *	       -P x : Pause for x seconds between iterations.
45  *	       -t   : Turn on syscall timing.
46  *
47  * HISTORY
48  *	06/2008 written by Sharyathi Nagesh <sharyathi@in.ibm.com>
49  *
50  *      05/2009         Suzuki K P <suzuki@in.ibm.com>
51  *                      Use TCONF instead of TWARN for non-NUMA machines
52  *
53  * RESTRICTIONS
54  *	none
55  */
56 
57 #define _GNU_SOURCE
58 #include <sched.h>
59 #include <errno.h>
60 #include "test.h"
61 #include <sys/types.h>
62 #include <dirent.h>
63 
64 #if defined(__i386__) || defined(__x86_64__)
65 #if __GLIBC_PREREQ(2,6)
66 #if defined(__x86_64__)
67 #include <utmpx.h>
68 #endif
69 int sys_support = 1;
70 #elif defined(__i386__)
71 int sys_support = 1;
72 #else
73 int sys_support = 0;
74 #endif
75 #else
76 int sys_support = 0;
77 #endif
78 
79 #if !(__GLIBC_PREREQ(2, 7))
80 #define CPU_FREE(ptr) free(ptr)
81 #endif
82 
83 void cleanup(void);
84 void setup(void);
85 static inline int getcpu(unsigned int *, unsigned int *, void *);
86 unsigned int set_cpu_affinity(void);
87 unsigned int get_nodeid(unsigned int);
88 unsigned int max_cpuid(size_t, cpu_set_t *);
89 
90 char *TCID = "getcpu01";
91 int TST_TOTAL = 1;
92 
main(int ac,char ** av)93 int main(int ac, char **av)
94 {
95 	int lc;
96 	unsigned int cpu_id, node_id = 0;
97 	unsigned int cpu_set;
98 #ifdef __i386__
99 	unsigned int node_set;
100 #endif
101 
102 	/* Check For Kernel Version */
103 	if (((tst_kvercmp(2, 6, 20)) < 0) || !(sys_support)) {
104 		tst_resm(TCONF, "This test can only run on kernels that are ");
105 		tst_resm(TCONF,
106 			 "2.6.20 and higher and glibc version 2.6 and above");
107 		tst_resm(TCONF, "Currently the test case has been");
108 		tst_resm(TCONF, "developed only for i386 and x86_64");
109 		exit(0);
110 	}
111 
112 	tst_parse_opts(ac, av, NULL, NULL);
113 
114 	setup();		/* global setup */
115 
116 	/* The following loop checks looping state if -i option given */
117 
118 	for (lc = 0; TEST_LOOPING(lc); lc++) {
119 		/* reset tst_count in case we are looping */
120 		tst_count = 0;
121 
122 		/* call the system call with the TEST() macro */
123 		cpu_set = set_cpu_affinity();
124 #ifdef __i386__
125 		node_set = get_nodeid(cpu_set);
126 #endif
127 		TEST(getcpu(&cpu_id, &node_id, NULL));
128 		if (TEST_RETURN == 0) {
129 			if (cpu_id != cpu_set) {
130 				tst_resm(TFAIL, "getcpu() returned wrong value"
131 					 " expected cpuid:%d, returned value cpuid: %d",
132 					 cpu_set, cpu_id);
133 
134 			}
135 #ifdef __i386__
136 			else if (node_id != node_set) {
137 				tst_resm(TFAIL, "getcpu() returned wrong value"
138 					 " expected  node id:%d returned  node id:%d",
139 					 node_set, node_id);
140 
141 			}
142 #endif
143 			else
144 				tst_resm(TPASS, "getcpu() returned proper"
145 					 " cpuid:%d, node id:%d", cpu_id,
146 					 node_id);
147 		} else {
148 			tst_resm(TFAIL, "getcpu() Failed, errno=%d:%s",
149 				 TEST_ERRNO, strerror(TEST_ERRNO));
150 
151 		}
152 	}
153 
154 	cleanup();
155 
156 	tst_exit();
157 }
158 
159 /*
160  * getcpu() - calls the system call
161  */
getcpu(unsigned * cpu_id,unsigned * node_id,void * cache_struct)162 static inline int getcpu(unsigned *cpu_id, unsigned *node_id,
163 			 void *cache_struct)
164 {
165 #if defined(__i386__)
166 	return syscall(318, cpu_id, node_id, cache_struct);
167 #elif __GLIBC_PREREQ(2,6)
168 	*cpu_id = sched_getcpu();
169 #endif
170 	return 0;
171 }
172 
173 /*
174  * setup() - performs all the ONE TIME setup for this test.
175  */
setup(void)176 void setup(void)
177 {
178 
179 	/* ?? */
180 
181 	TEST_PAUSE;
182 }
183 
184 /*
185  * This will set the affinity to max cpu on which process can run
186  * and return that cpu id to the calling process
187  */
set_cpu_affinity(void)188 unsigned int set_cpu_affinity(void)
189 {
190 	unsigned cpu_max;
191 	cpu_set_t *set;
192 	size_t size;
193 	int nrcpus = 1024;
194 #if __GLIBC_PREREQ(2, 7)
195 realloc:
196 	set = CPU_ALLOC(nrcpus);
197 #else
198 	set = malloc(sizeof(cpu_set_t));
199 #endif
200 	if (set == NULL) {
201 		tst_brkm(TFAIL, NULL, "CPU_ALLOC:errno:%d", errno);
202 	}
203 #if __GLIBC_PREREQ(2, 7)
204 	size = CPU_ALLOC_SIZE(nrcpus);
205 	CPU_ZERO_S(size, set);
206 #else
207 	size = sizeof(cpu_set_t);
208 	CPU_ZERO(set);
209 #endif
210 	if (sched_getaffinity(0, size, set) < 0) {
211 		CPU_FREE(set);
212 #if __GLIBC_PREREQ(2, 7)
213 		if (errno == EINVAL && nrcpus < (1024 << 8)) {
214 			nrcpus = nrcpus << 2;
215 			goto realloc;
216 		}
217 #else
218 		if (errno == EINVAL)
219 			tst_resm(TFAIL,
220 				 "NR_CPUS of the kernel is more than 1024, so we'd better use a newer glibc(>= 2.7)");
221 		else
222 #endif
223 			tst_resm(TFAIL, "sched_getaffinity:errno:%d", errno);
224 		tst_exit();
225 	}
226 	cpu_max = max_cpuid(size, set);
227 #if __GLIBC_PREREQ(2, 7)
228 	CPU_ZERO_S(size, set);
229 	CPU_SET_S(cpu_max, size, set);
230 #else
231 	CPU_ZERO(set);
232 	CPU_SET(cpu_max, set);
233 #endif
234 	if (sched_setaffinity(0, size, set) < 0) {
235 		CPU_FREE(set);
236 		tst_brkm(TFAIL, NULL, "sched_setaffinity:errno:%d", errno);
237 	}
238 	CPU_FREE(set);
239 	return cpu_max;
240 }
241 
242 /*
243  * Return the maximum cpu id
244  */
245 #define BITS_PER_BYTE 8
max_cpuid(size_t size,cpu_set_t * set)246 unsigned int max_cpuid(size_t size, cpu_set_t * set)
247 {
248 	unsigned int index, max = 0;
249 	for (index = 0; index < size * BITS_PER_BYTE; index++)
250 #if __GLIBC_PREREQ(2, 7)
251 		if (CPU_ISSET_S(index, size, set))
252 #else
253 		if (CPU_ISSET(index, set))
254 #endif
255 			max = index;
256 	return max;
257 }
258 
259 /*
260  * get_nodeid(cpuid) - This will return the node to which selected cpu belongs
261  */
get_nodeid(unsigned int cpu_id)262 unsigned int get_nodeid(unsigned int cpu_id)
263 {
264 	DIR *directory_parent, *directory_node;
265 	struct dirent *de, *dn;
266 	char directory_path[255];
267 	unsigned int cpu;
268 	int node_id = 0;
269 
270 	directory_parent = opendir("/sys/devices/system/node");
271 	if (!directory_parent) {
272 		tst_resm(TCONF,
273 			 "/sys not mounted or not a numa system. Assuming one node");
274 		tst_resm(TCONF,
275 			 "Error opening: /sys/devices/system/node :%s",
276 			 strerror(errno));
277 		return 0;	//By Default assume it to belong to node Zero
278 	} else {
279 		while ((de = readdir(directory_parent)) != NULL) {
280 			if (strncmp(de->d_name, "node", 4))
281 				continue;
282 			sprintf(directory_path, "/sys/devices/system/node/%s",
283 				de->d_name);
284 			directory_node = opendir(directory_path);
285 			while ((dn = readdir(directory_node)) != NULL) {
286 				if (strncmp(dn->d_name, "cpu", 3))
287 					continue;
288 				cpu = strtoul(dn->d_name + 3, NULL, 0);
289 				if (cpu == cpu_id) {
290 					node_id =
291 					    strtoul(de->d_name + 4, NULL, 0);
292 					break;
293 				}
294 			}
295 			closedir(directory_node);
296 		}
297 		closedir(directory_parent);
298 	}
299 	return node_id;
300 }
301 
302 /*
303  * cleanup() - performs all the ONE TIME cleanup for this test at completion
304  * 	       or premature exit.
305  */
cleanup(void)306 void cleanup(void)
307 {
308 
309 }
310