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