getcpu01.c revision 60fa8014af7534eaefa901200c8df4b74ce422e6
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 "usctest.h"
62#include <sys/types.h>
63#include <dirent.h>
64
65#if defined(__i386__) || defined(__x86_64__)
66#if __GLIBC_PREREQ(2,6)
67#if defined(__x86_64__)
68#include <utmpx.h>
69#endif
70int sys_support = 1;
71#elif defined(__i386__)
72int sys_support = 1;
73#else
74int sys_support = 0;
75#endif
76#else
77int sys_support = 0;
78#endif
79
80#if !(__GLIBC_PREREQ(2, 7))
81#define CPU_FREE(ptr) free(ptr)
82#endif
83
84void cleanup(void);
85void setup(void);
86static inline int getcpu(unsigned int *, unsigned int *, void *);
87unsigned int set_cpu_affinity();
88unsigned int get_nodeid(unsigned int);
89unsigned int max_cpuid(size_t, cpu_set_t *);
90
91char *TCID = "getcpu01";
92int TST_TOTAL = 1;
93extern int Tst_count;
94
95int main(int ac, char **av)
96{
97	int lc;			/* loop counter */
98	char *msg;		/* message returned from parse_opts */
99	unsigned int cpu_id, node_id = 0;
100	unsigned int cpu_set;
101#ifdef __i386__
102	unsigned int node_set;
103#endif
104
105	/* Check For Kernel Version */
106	if (((tst_kvercmp(2, 6, 20)) < 0) || !(sys_support)) {
107		tst_resm(TCONF, "This test can only run on kernels that are ");
108		tst_resm(TCONF,
109			 "2.6.20 and higher and glibc version 2.6 and above");
110		tst_resm(TCONF, "Currently the test case has been");
111		tst_resm(TCONF, "developed only for i386 and x86_64");
112		exit(0);
113	}
114
115	/* parse standard options */
116	if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL)
117		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
118
119	setup();		/* global setup */
120
121	/* The following loop checks looping state if -i option given */
122
123	for (lc = 0; TEST_LOOPING(lc); lc++) {
124		/* reset Tst_count in case we are looping */
125		Tst_count = 0;
126
127		/* call the system call with the TEST() macro */
128		cpu_set = set_cpu_affinity();
129#ifdef __i386__
130		node_set = get_nodeid(cpu_set);
131#endif
132		TEST(getcpu(&cpu_id, &node_id, NULL));
133		if (TEST_RETURN == 0) {
134			if (cpu_id != cpu_set) {
135				tst_resm(TFAIL, "getcpu() returned wrong value"
136					 " expected cpuid:%d, returned value cpuid: %d",
137					 cpu_set, cpu_id);
138				tst_exit();
139			}
140#ifdef __i386__
141			else if (node_id != node_set) {
142				tst_resm(TFAIL, "getcpu() returned wrong value"
143					 " expected  node id:%d returned  node id:%d",
144					 node_set, node_id);
145				tst_exit();
146			}
147#endif
148			else
149				tst_resm(TPASS, "getcpu() returned proper"
150					 " cpuid:%d, node id:%d", cpu_id,
151					 node_id);
152		} else {
153			tst_resm(TFAIL, "getcpu() Failed, errno=%d:%s",
154				 TEST_ERRNO, strerror(TEST_ERRNO));
155			tst_exit();
156		}
157	}
158
159	cleanup();
160
161	 /*NOTREACHED*/ return 0;
162}
163
164/*
165 * getcpu() - calls the system call
166 */
167static inline int getcpu(unsigned *cpu_id, unsigned *node_id,
168			 void *cache_struct)
169{
170#if defined(__i386__)
171	return syscall(318, cpu_id, node_id, cache_struct);
172#elif __GLIBC_PREREQ(2,6)
173	*cpu_id = sched_getcpu();
174#endif
175	return 0;
176}
177
178/*
179 * setup() - performs all the ONE TIME setup for this test.
180 */
181void setup(void)
182{
183	/* capture signals */
184	/* ?? */
185	/* Pause if that option was specified */
186	TEST_PAUSE;
187}
188
189/*
190 * This will set the affinity to max cpu on which process can run
191 * and return that cpu id to the calling process
192 */
193unsigned int set_cpu_affinity()
194{
195	unsigned cpu_max;
196	cpu_set_t *set;
197	size_t size;
198	int nrcpus = 1024;
199#if __GLIBC_PREREQ(2, 7)
200realloc:
201	set = CPU_ALLOC(nrcpus);
202#else
203	set = malloc(sizeof(cpu_set_t));
204#endif
205	if (set == NULL) {
206		tst_resm(TFAIL, "CPU_ALLOC:errno:%d", errno);
207		tst_exit();
208	}
209
210#if __GLIBC_PREREQ(2, 7)
211	size = CPU_ALLOC_SIZE(nrcpus);
212	CPU_ZERO_S(size, set);
213#else
214	size = sizeof(cpu_set_t);
215	CPU_ZERO(set);
216#endif
217	if (sched_getaffinity(0, size, set) < 0) {
218		CPU_FREE(set);
219#if __GLIBC_PREREQ(2, 7)
220		if (errno == EINVAL && nrcpus < (1024 << 8)) {
221			nrcpus = nrcpus << 2;
222			goto realloc;
223		}
224#else
225		if (errno == EINVAL)
226			tst_resm(TFAIL, "NR_CPUS of the kernel is more than 1024, so we'd better use a newer glibc(>= 2.7)");
227		else
228#endif
229			tst_resm(TFAIL, "sched_getaffinity:errno:%d", errno);
230		tst_exit();
231	}
232	cpu_max = max_cpuid(size, set);
233#if __GLIBC_PREREQ(2, 7)
234	CPU_ZERO_S(size, set);
235	CPU_SET_S(cpu_max, size, set);
236#else
237	CPU_ZERO(set);
238	CPU_SET(cpu_max, set);
239#endif
240	if (sched_setaffinity(0, size, set) < 0) {
241		CPU_FREE(set);
242		tst_resm(TFAIL, "sched_setaffinity:errno:%d", errno);
243		tst_exit();
244	}
245	CPU_FREE(set);
246	return cpu_max;
247}
248
249/*
250 * Return the maximum cpu id
251 */
252#define BITS_PER_BYTE 8
253unsigned int max_cpuid(size_t size, cpu_set_t * set)
254{
255	unsigned int index, max = 0;
256	for (index = 0; index < size * BITS_PER_BYTE; index++)
257#if __GLIBC_PREREQ(2, 7)
258		if (CPU_ISSET_S(index, size, set))
259#else
260		if (CPU_ISSET(index, set))
261#endif
262			max = index;
263	return max;
264}
265
266/*
267 * get_nodeid(cpuid) - This will return the node to which selected cpu belongs
268 */
269unsigned int get_nodeid(unsigned int cpu_id)
270{
271	DIR *directory_parent, *directory_node;
272	struct dirent *de, *dn;
273	char directory_path[255];
274	unsigned int cpu;
275	int node_id = 0;
276
277	directory_parent = opendir("/sys/devices/system/node");
278	if (!directory_parent) {
279                tst_resm(TCONF,
280                    "/sys not mounted or not a numa system. Assuming one node");
281                tst_resm(TCONF,
282                    "Error opening: /sys/devices/system/node :%s",
283			 strerror(errno));
284		return 0;	//By Default assume it to belong to node Zero
285	} else {
286		while ((de = readdir(directory_parent)) != NULL) {
287			if (strncmp(de->d_name, "node", 4))
288				continue;
289			sprintf(directory_path, "/sys/devices/system/node/%s",
290				de->d_name);
291			directory_node = opendir(directory_path);
292			while ((dn = readdir(directory_node)) != NULL) {
293				if (strncmp(dn->d_name, "cpu", 3))
294					continue;
295				cpu = strtoul(dn->d_name + 3, NULL, 0);
296				if (cpu == cpu_id) {
297					node_id =
298					    strtoul(de->d_name + 4, NULL, 0);
299					break;
300				}
301			}
302			closedir(directory_node);
303		}
304		closedir(directory_parent);
305	}
306	return node_id;
307}
308
309/*
310 * cleanup() - performs all the ONE TIME cleanup for this test at completion
311 * 	       or premature exit.
312 */
313void cleanup(void)
314{
315	/*
316	 * print timing stats if that option was specified.
317	 * print errno log if that option was specified.
318	 */
319	TEST_CLEANUP;
320	/* exit with return code appropriate for results */
321	tst_exit();
322}
323