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 69int sys_support = 1; 70#elif defined(__i386__) 71int sys_support = 1; 72#else 73int sys_support = 0; 74#endif 75#else 76int sys_support = 0; 77#endif 78 79#if !(__GLIBC_PREREQ(2, 7)) 80#define CPU_FREE(ptr) free(ptr) 81#endif 82 83void cleanup(void); 84void setup(void); 85static inline int getcpu(unsigned int *, unsigned int *, void *); 86unsigned int set_cpu_affinity(void); 87unsigned int get_nodeid(unsigned int); 88unsigned int max_cpuid(size_t, cpu_set_t *); 89 90char *TCID = "getcpu01"; 91int TST_TOTAL = 1; 92 93int 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 */ 162static 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 */ 176void 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 */ 188unsigned 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) 195realloc: 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 246unsigned 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 */ 262unsigned 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 */ 306void cleanup(void) 307{ 308 309} 310