getcpu01.c revision 45e285d46ab47b0ff76c88acb5ba97b0bd5f753d
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, cleanup, "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