1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright International Business Machines Corp., 2007, 2008 4 * 5 * Test Description: 6 * The test process is affined to a CPU. It then calls getcpu and 7 * checks that the CPU and node (if supported) match the expected 8 * values. 9 */ 10 11 #define _GNU_SOURCE 12 #include <dirent.h> 13 #include <errno.h> 14 #include <sched.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <sys/types.h> 18 #include "lapi/syscalls.h" 19 #include "lapi/cpuset.h" 20 #include "tst_test.h" 21 22 static inline int get_cpu(unsigned *cpu_id, 23 unsigned *node_id LTP_ATTRIBUTE_UNUSED, 24 void *cache_struct LTP_ATTRIBUTE_UNUSED) 25 { 26 #if defined(__i386__) 27 return syscall(__NR_getcpu, cpu_id, node_id, cache_struct); 28 #else 29 *cpu_id = sched_getcpu(); 30 #endif 31 return 0; 32 } 33 34 static unsigned int max_cpuid(size_t size, cpu_set_t * set) 35 { 36 unsigned int index, max = 0; 37 for (index = 0; index < size * 8; index++) 38 if (CPU_ISSET_S(index, size, set)) 39 max = index; 40 return max; 41 } 42 43 /* 44 * This will set the affinity to max cpu on which process can run 45 * and return that cpu id to the calling process 46 */ 47 static unsigned int set_cpu_affinity(void) 48 { 49 unsigned cpu_max; 50 cpu_set_t *set; 51 size_t size; 52 int nrcpus = 1024; 53 54 realloc: 55 set = CPU_ALLOC(nrcpus); 56 if (!set) 57 tst_brk(TBROK | TERRNO, "CPU_ALLOC()"); 58 59 size = CPU_ALLOC_SIZE(nrcpus); 60 CPU_ZERO_S(size, set); 61 if (sched_getaffinity(0, size, set) < 0) { 62 CPU_FREE(set); 63 if (errno == EINVAL && nrcpus < (1024 << 8)) { 64 nrcpus = nrcpus << 2; 65 goto realloc; 66 } 67 tst_brk(TBROK | TERRNO, "sched_getaffinity()"); 68 } 69 cpu_max = max_cpuid(size, set); 70 CPU_ZERO_S(size, set); 71 CPU_SET_S(cpu_max, size, set); 72 if (sched_setaffinity(0, size, set) < 0) { 73 CPU_FREE(set); 74 tst_brk(TBROK | TERRNO, "sched_setaffinity()"); 75 } 76 CPU_FREE(set); 77 return cpu_max; 78 } 79 80 #ifdef __i386__ 81 static unsigned int get_nodeid(unsigned int cpu_id) 82 { 83 DIR *directory_parent, *directory_node; 84 struct dirent *de, *dn; 85 char directory_path[PATH_MAX]; 86 unsigned int cpu; 87 int node_id = 0; 88 89 directory_parent = opendir("/sys/devices/system/node"); 90 if (!directory_parent) { 91 tst_res(TINFO, 92 "/sys not mounted or not a numa system. " 93 "Assuming one node"); 94 tst_res(TINFO, "Error opening: /sys/devices/system/node :%s", 95 strerror(errno)); 96 /* Assume CPU belongs to the only node, node zero. */ 97 return 0; 98 } else { 99 while ((de = readdir(directory_parent)) != NULL) { 100 if (strncmp(de->d_name, "node", 4)) 101 continue; 102 sprintf(directory_path, "/sys/devices/system/node/%s", 103 de->d_name); 104 directory_node = opendir(directory_path); 105 while ((dn = readdir(directory_node)) != NULL) { 106 if (strncmp(dn->d_name, "cpu", 3)) 107 continue; 108 cpu = strtoul(dn->d_name + 3, NULL, 0); 109 if (cpu == cpu_id) { 110 node_id = 111 strtoul(de->d_name + 4, NULL, 0); 112 break; 113 } 114 } 115 closedir(directory_node); 116 } 117 closedir(directory_parent); 118 } 119 return node_id; 120 } 121 #endif 122 123 static void run(void) 124 { 125 unsigned int cpu_id, node_id = 0; 126 unsigned int cpu_set; 127 #ifdef __i386__ 128 unsigned int node_set; 129 #endif 130 131 cpu_set = set_cpu_affinity(); 132 #ifdef __i386__ 133 node_set = get_nodeid(cpu_set); 134 #endif 135 136 TEST(get_cpu(&cpu_id, &node_id, NULL)); 137 if (TST_RET == 0) { 138 if (cpu_id != cpu_set) 139 tst_res(TFAIL, "getcpu() returned wrong value" 140 " expected cpuid:%d, returned value cpuid: %d", 141 cpu_set, cpu_id); 142 #ifdef __i386__ 143 else if (node_id != node_set) 144 tst_res(TFAIL, "getcpu() returned wrong value" 145 " expected node id:%d returned node id:%d", 146 node_set, node_id); 147 #endif 148 else 149 tst_res(TPASS, "getcpu() returned proper" 150 " cpuid:%d, node id:%d", cpu_id, 151 node_id); 152 } else { 153 tst_res(TFAIL, "getcpu() Failed, errno=%d:%s", 154 TST_ERR, strerror(TST_ERR)); 155 } 156 } 157 158 static void setup(void) 159 { 160 if (tst_kvercmp(2, 6, 20) < 0) 161 tst_brk(TCONF, "kernel >= 2.6.20 required"); 162 } 163 164 static struct tst_test test = { 165 .test_all = run, 166 .setup = setup, 167 }; 168