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