getcpu01.c revision c89909da8c877039df9011ead1b627cc9da48839
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 * RESTRICTIONS
51 *	none
52 */
53
54
55#define _GNU_SOURCE
56#include <sched.h>
57#include <errno.h>
58#include "test.h"
59#include "usctest.h"
60#include <sys/types.h>
61#include <dirent.h>
62
63#if defined(__i386__) || defined(__x86_64__)
64	#if __GLIBC_PREREQ(2,6)
65	#if defined(__x86_64__)
66		#include <utmpx.h>
67	#endif
68	int sys_support = 1;
69	#elif defined(__i386__)
70	int sys_support = 1;
71	#else
72	int sys_support = 0;
73	#endif
74#else
75int sys_support = 0;
76#endif
77
78void cleanup(void);
79void setup(void);
80static inline int getcpu(unsigned int *, unsigned int *, void *);
81unsigned int set_cpu_affinity();
82unsigned int get_nodeid(unsigned int);
83unsigned int max_cpuid(cpu_set_t *);
84
85char *TCID= "getcpu01";
86int TST_TOTAL = 1;
87extern int Tst_count;
88
89
90int main(int ac, char **av)
91{
92	int lc;				/* loop counter */
93	char *msg;			/* message returned from parse_opts */
94	unsigned int cpu_id, node_id = 0 ;
95	unsigned int cpu_set;
96	#ifdef __i386__
97	unsigned int node_set;
98	#endif
99
100	/* Check For Kernel Version */
101 	if(((tst_kvercmp(2,6,20)) < 0) || !(sys_support))
102          {
103             tst_resm(TCONF, "This test can only run on kernels that are ");
104             tst_resm(TCONF, "2.6.20 and higher and glibc version 2.6 and above");
105             tst_resm(TCONF, "Currently the test case has been");
106             tst_resm(TCONF, "developed only for i386 and x86_64");
107             exit(0);
108          }
109
110	/* parse standard options */
111	if ((msg = parse_opts(ac, av, (option_t *)NULL, NULL)) != (char *)NULL)
112		tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
113
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,node_id);
147		} else {
148			tst_resm(TFAIL, "getcpu() Failed, errno=%d:%s",
149				TEST_ERRNO,strerror(TEST_ERRNO));
150				tst_exit();
151		}
152	}
153
154	cleanup();
155
156	/*NOTREACHED*/
157	return(0);
158}
159
160/*
161 * getcpu() - calls the system call
162 */
163static inline int getcpu(unsigned *cpu_id, unsigned *node_id, 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/*
175 * setup() - performs all the ONE TIME setup for this test.
176 */
177void
178setup(void)
179{
180	/* capture signals */
181	/* ?? */
182	/* Pause if that option was specified */
183	TEST_PAUSE;
184}
185
186/*
187 * This will set the affinity to max cpu on which process can run
188 * and return that cpu id to the calling process
189 */
190unsigned int
191set_cpu_affinity()
192{
193	unsigned cpu_max;
194	cpu_set_t set;
195	if ( sched_getaffinity(0, sizeof(cpu_set_t), &set) < 0 ){
196			tst_resm(TFAIL,"sched_getaffinity:errno:%d",errno);
197			tst_exit();
198	}
199	cpu_max = max_cpuid(&set);
200	CPU_ZERO(&set);
201	CPU_SET(cpu_max,&set);
202	if ( sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0 ){
203			tst_resm(TFAIL,"sched_setaffinity:errno:%d",errno);
204			tst_exit();
205	}
206	return cpu_max;
207}
208
209/*
210 * Return the maximum cpu id
211 */
212#define BITS_PER_BYTE 8
213unsigned int
214max_cpuid(cpu_set_t *set)
215{
216	unsigned int index, max = 0;
217	for ( index = 0; index < sizeof(cpu_set_t) * BITS_PER_BYTE; index++)
218		if(CPU_ISSET(index,set)) max = index;
219	return max;
220}
221
222
223/*
224 * get_nodeid(cpuid) - This will return the node to which selected cpu belongs
225 */
226unsigned int
227get_nodeid(unsigned int cpu_id)
228{
229 	DIR *directory_parent, *directory_node;
230        struct dirent *de,*dn;
231	char directory_path[255];
232	unsigned int cpu;
233        int node_id = 0;
234
235        directory_parent = opendir("/sys/devices/system/node");
236        if (!directory_parent)  {
237                tst_resm(TWARN,
238                   "/sys not mounted or not a numa system. Assuming one node: %s",
239                        strerror(errno));
240               	return 0; //By Default assume it to belong to node Zero
241        } else {
242                while ((de = readdir(directory_parent)) != NULL) {
243                        if (strncmp(de->d_name, "node", 4))
244 				continue;
245			sprintf(directory_path,"/sys/devices/system/node/%s",de->d_name);
246        		directory_node = opendir(directory_path);
247			while ((dn = readdir(directory_node)) != NULL) {
248			if (strncmp(dn->d_name, "cpu", 3))
249				continue;
250			cpu = strtoul(dn->d_name+3,NULL,0);
251			if ( cpu == cpu_id ){
252	                        node_id = 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
268cleanup(void)
269{
270	/*
271	 * print timing stats if that option was specified.
272	 * print errno log if that option was specified.
273	 */
274	TEST_CLEANUP;
275	/* exit with return code appropriate for results */
276	tst_exit();
277}
278
279