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 <stdio.h>
15#include <stdlib.h>
16#include <sys/types.h>
17#include "tst_test.h"
18#include "lapi/cpuset.h"
19#include "lapi/sched.h"
20
21static unsigned int max_cpuid(size_t size, cpu_set_t * set)
22{
23	unsigned int index, max = 0;
24	for (index = 0; index < size * 8; index++)
25		if (CPU_ISSET_S(index, size, set))
26			max = index;
27	return max;
28}
29
30/*
31 * This will set the affinity to max cpu on which process can run
32 * and return that cpu id to the calling process
33 */
34static unsigned int set_cpu_affinity(void)
35{
36	unsigned cpu_max;
37	cpu_set_t *set;
38	size_t size;
39	int nrcpus = 1024;
40
41realloc:
42	set = CPU_ALLOC(nrcpus);
43	if (!set)
44		tst_brk(TBROK | TERRNO, "CPU_ALLOC()");
45
46	size = CPU_ALLOC_SIZE(nrcpus);
47	CPU_ZERO_S(size, set);
48	if (sched_getaffinity(0, size, set) < 0) {
49		CPU_FREE(set);
50		if (errno == EINVAL && nrcpus < (1024 << 8)) {
51			nrcpus = nrcpus << 2;
52			goto realloc;
53		}
54		tst_brk(TBROK | TERRNO, "sched_getaffinity()");
55	}
56	cpu_max = max_cpuid(size, set);
57	CPU_ZERO_S(size, set);
58	CPU_SET_S(cpu_max, size, set);
59	if (sched_setaffinity(0, size, set) < 0) {
60		CPU_FREE(set);
61		tst_brk(TBROK | TERRNO, "sched_setaffinity()");
62	}
63	CPU_FREE(set);
64	return cpu_max;
65}
66
67static unsigned int get_nodeid(unsigned int cpu_id)
68{
69	DIR *directory_parent, *directory_node;
70	struct dirent *de, *dn;
71	char directory_path[PATH_MAX];
72	char *invalid_number;
73	unsigned int cpu;
74	int node_id = 0;
75
76	directory_parent = opendir("/sys/devices/system/node");
77	if (!directory_parent) {
78		tst_res(TINFO,
79			"/sys not mounted or not a numa system. "
80			"Assuming one node");
81		tst_res(TINFO, "Error opening: /sys/devices/system/node :%s",
82			strerror(errno));
83		/* Assume CPU belongs to the only node, node zero. */
84		return 0;
85	} else {
86		while ((de = readdir(directory_parent)) != NULL) {
87			if (strncmp(de->d_name, "node", 4))
88				continue;
89			sprintf(directory_path, "/sys/devices/system/node/%s",
90				de->d_name);
91			directory_node = opendir(directory_path);
92			while ((dn = readdir(directory_node)) != NULL) {
93				if (strncmp(dn->d_name, "cpu", 3))
94					continue;
95				cpu = strtoul(dn->d_name + 3, &invalid_number, 0);
96				if (strcmp(invalid_number, "\0"))
97					continue;
98				if (cpu == cpu_id) {
99					node_id =
100					    strtoul(de->d_name + 4, NULL, 0);
101					break;
102				}
103			}
104			closedir(directory_node);
105		}
106		closedir(directory_parent);
107	}
108	return node_id;
109}
110
111static void run(void)
112{
113	unsigned int cpu_id, node_id = 0;
114	unsigned int cpu_set;
115	unsigned int node_set;
116
117	cpu_set = set_cpu_affinity();
118	node_set = get_nodeid(cpu_set);
119
120	TEST(getcpu(&cpu_id, &node_id));
121	if (TST_RET == 0) {
122		if (cpu_id != cpu_set)
123			tst_res(TFAIL, "getcpu() returned wrong value"
124				" expected cpuid:%d, returned value cpuid: %d",
125				cpu_set, cpu_id);
126		else if (node_id != node_set)
127			tst_res(TFAIL, "getcpu() returned wrong value"
128				" expected  node id:%d returned  node id:%d",
129				node_set, node_id);
130		else
131			tst_res(TPASS, "getcpu() returned proper"
132				" cpuid:%d, node id:%d", cpu_id,
133				node_id);
134	} else {
135		tst_res(TFAIL, "getcpu() Failed, errno=%d:%s",
136			TST_ERR, strerror(TST_ERR));
137	}
138}
139
140static struct tst_test test = {
141	.test_all = run,
142};
143