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