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