| /* |
| * |
| * Copyright © International Business Machines Corp., 2007, 2008 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
| */ |
| |
| /* |
| * NAME |
| * getcpu1.c |
| * |
| * DESCRIPTION |
| * getcpu01 - call getcpu() and make sure it succeeds |
| * |
| * ALGORITHM |
| * set cpu affinity of the process |
| * If setaffinity() fails exit from the test suite |
| * Store the node ID of the cpu which has been set in previous step |
| * Make a call to getcpu() system call |
| * Verify the returned valued with the set value |
| * if they match |
| * test is considered PASS |
| * else |
| * test is considered FAIL |
| * |
| * USAGE: <for command-line> |
| * getcpu [-c n] [-f] [-i n] [-I x] [-P x] [-t] |
| * where, -c n : Run n copies concurrently. |
| * -f : Turn off functionality Testing. |
| * -i n : Execute test n times. |
| * -I x : Execute test for x seconds. |
| * -P x : Pause for x seconds between iterations. |
| * -t : Turn on syscall timing. |
| * |
| * HISTORY |
| * 06/2008 written by Sharyathi Nagesh <sharyathi@in.ibm.com> |
| * |
| * 05/2009 Suzuki K P <suzuki@in.ibm.com> |
| * Use TCONF instead of TWARN for non-NUMA machines |
| * |
| * RESTRICTIONS |
| * none |
| */ |
| |
| #define _GNU_SOURCE |
| #include <sched.h> |
| #include <errno.h> |
| #include "test.h" |
| #include <sys/types.h> |
| #include <dirent.h> |
| |
| #if defined(__i386__) || defined(__x86_64__) |
| #if __GLIBC_PREREQ(2,6) |
| #if defined(__x86_64__) |
| #include <utmpx.h> |
| #endif |
| int sys_support = 1; |
| #elif defined(__i386__) |
| int sys_support = 1; |
| #else |
| int sys_support = 0; |
| #endif |
| #else |
| int sys_support = 0; |
| #endif |
| |
| #if !(__GLIBC_PREREQ(2, 7)) |
| #define CPU_FREE(ptr) free(ptr) |
| #endif |
| |
| void cleanup(void); |
| void setup(void); |
| static inline int getcpu(unsigned int *, unsigned int *, void *); |
| unsigned int set_cpu_affinity(void); |
| unsigned int get_nodeid(unsigned int); |
| unsigned int max_cpuid(size_t, cpu_set_t *); |
| |
| char *TCID = "getcpu01"; |
| int TST_TOTAL = 1; |
| |
| int main(int ac, char **av) |
| { |
| int lc; |
| const char *msg; |
| unsigned int cpu_id, node_id = 0; |
| unsigned int cpu_set; |
| #ifdef __i386__ |
| unsigned int node_set; |
| #endif |
| |
| /* Check For Kernel Version */ |
| if (((tst_kvercmp(2, 6, 20)) < 0) || !(sys_support)) { |
| tst_resm(TCONF, "This test can only run on kernels that are "); |
| tst_resm(TCONF, |
| "2.6.20 and higher and glibc version 2.6 and above"); |
| tst_resm(TCONF, "Currently the test case has been"); |
| tst_resm(TCONF, "developed only for i386 and x86_64"); |
| exit(0); |
| } |
| |
| if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| setup(); /* global setup */ |
| |
| /* The following loop checks looping state if -i option given */ |
| |
| for (lc = 0; TEST_LOOPING(lc); lc++) { |
| /* reset tst_count in case we are looping */ |
| tst_count = 0; |
| |
| /* call the system call with the TEST() macro */ |
| cpu_set = set_cpu_affinity(); |
| #ifdef __i386__ |
| node_set = get_nodeid(cpu_set); |
| #endif |
| TEST(getcpu(&cpu_id, &node_id, NULL)); |
| if (TEST_RETURN == 0) { |
| if (cpu_id != cpu_set) { |
| tst_resm(TFAIL, "getcpu() returned wrong value" |
| " expected cpuid:%d, returned value cpuid: %d", |
| cpu_set, cpu_id); |
| |
| } |
| #ifdef __i386__ |
| else if (node_id != node_set) { |
| tst_resm(TFAIL, "getcpu() returned wrong value" |
| " expected node id:%d returned node id:%d", |
| node_set, node_id); |
| |
| } |
| #endif |
| else |
| tst_resm(TPASS, "getcpu() returned proper" |
| " cpuid:%d, node id:%d", cpu_id, |
| node_id); |
| } else { |
| tst_resm(TFAIL, "getcpu() Failed, errno=%d:%s", |
| TEST_ERRNO, strerror(TEST_ERRNO)); |
| |
| } |
| } |
| |
| cleanup(); |
| |
| tst_exit(); |
| } |
| |
| /* |
| * getcpu() - calls the system call |
| */ |
| static inline int getcpu(unsigned *cpu_id, unsigned *node_id, |
| void *cache_struct) |
| { |
| #if defined(__i386__) |
| return syscall(318, cpu_id, node_id, cache_struct); |
| #elif __GLIBC_PREREQ(2,6) |
| *cpu_id = sched_getcpu(); |
| #endif |
| return 0; |
| } |
| |
| /* |
| * setup() - performs all the ONE TIME setup for this test. |
| */ |
| void setup(void) |
| { |
| |
| /* ?? */ |
| |
| TEST_PAUSE; |
| } |
| |
| /* |
| * This will set the affinity to max cpu on which process can run |
| * and return that cpu id to the calling process |
| */ |
| unsigned int set_cpu_affinity(void) |
| { |
| unsigned cpu_max; |
| cpu_set_t *set; |
| size_t size; |
| int nrcpus = 1024; |
| #if __GLIBC_PREREQ(2, 7) |
| realloc: |
| set = CPU_ALLOC(nrcpus); |
| #else |
| set = malloc(sizeof(cpu_set_t)); |
| #endif |
| if (set == NULL) { |
| tst_brkm(TFAIL, NULL, "CPU_ALLOC:errno:%d", errno); |
| } |
| #if __GLIBC_PREREQ(2, 7) |
| size = CPU_ALLOC_SIZE(nrcpus); |
| CPU_ZERO_S(size, set); |
| #else |
| size = sizeof(cpu_set_t); |
| CPU_ZERO(set); |
| #endif |
| if (sched_getaffinity(0, size, set) < 0) { |
| CPU_FREE(set); |
| #if __GLIBC_PREREQ(2, 7) |
| if (errno == EINVAL && nrcpus < (1024 << 8)) { |
| nrcpus = nrcpus << 2; |
| goto realloc; |
| } |
| #else |
| if (errno == EINVAL) |
| tst_resm(TFAIL, |
| "NR_CPUS of the kernel is more than 1024, so we'd better use a newer glibc(>= 2.7)"); |
| else |
| #endif |
| tst_resm(TFAIL, "sched_getaffinity:errno:%d", errno); |
| tst_exit(); |
| } |
| cpu_max = max_cpuid(size, set); |
| #if __GLIBC_PREREQ(2, 7) |
| CPU_ZERO_S(size, set); |
| CPU_SET_S(cpu_max, size, set); |
| #else |
| CPU_ZERO(set); |
| CPU_SET(cpu_max, set); |
| #endif |
| if (sched_setaffinity(0, size, set) < 0) { |
| CPU_FREE(set); |
| tst_brkm(TFAIL, NULL, "sched_setaffinity:errno:%d", errno); |
| } |
| CPU_FREE(set); |
| return cpu_max; |
| } |
| |
| /* |
| * Return the maximum cpu id |
| */ |
| #define BITS_PER_BYTE 8 |
| unsigned int max_cpuid(size_t size, cpu_set_t * set) |
| { |
| unsigned int index, max = 0; |
| for (index = 0; index < size * BITS_PER_BYTE; index++) |
| #if __GLIBC_PREREQ(2, 7) |
| if (CPU_ISSET_S(index, size, set)) |
| #else |
| if (CPU_ISSET(index, set)) |
| #endif |
| max = index; |
| return max; |
| } |
| |
| /* |
| * get_nodeid(cpuid) - This will return the node to which selected cpu belongs |
| */ |
| unsigned int get_nodeid(unsigned int cpu_id) |
| { |
| DIR *directory_parent, *directory_node; |
| struct dirent *de, *dn; |
| char directory_path[255]; |
| unsigned int cpu; |
| int node_id = 0; |
| |
| directory_parent = opendir("/sys/devices/system/node"); |
| if (!directory_parent) { |
| tst_resm(TCONF, |
| "/sys not mounted or not a numa system. Assuming one node"); |
| tst_resm(TCONF, |
| "Error opening: /sys/devices/system/node :%s", |
| strerror(errno)); |
| return 0; //By Default assume it to belong to node Zero |
| } else { |
| while ((de = readdir(directory_parent)) != NULL) { |
| if (strncmp(de->d_name, "node", 4)) |
| continue; |
| sprintf(directory_path, "/sys/devices/system/node/%s", |
| de->d_name); |
| directory_node = opendir(directory_path); |
| while ((dn = readdir(directory_node)) != NULL) { |
| if (strncmp(dn->d_name, "cpu", 3)) |
| continue; |
| cpu = strtoul(dn->d_name + 3, NULL, 0); |
| if (cpu == cpu_id) { |
| node_id = |
| strtoul(de->d_name + 4, NULL, 0); |
| break; |
| } |
| } |
| closedir(directory_node); |
| } |
| closedir(directory_parent); |
| } |
| return node_id; |
| } |
| |
| /* |
| * cleanup() - performs all the ONE TIME cleanup for this test at completion |
| * or premature exit. |
| */ |
| void cleanup(void) |
| { |
| |
| } |