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