1 /*
2 * Copyright (C) 2016-2021 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * This code is a complete clean re-write of the stress tool by
19 * Colin Ian King <colin.king@canonical.com> and attempts to be
20 * backwardly compatible with the stress tool by Amos Waterland
21 * <apw@rossby.metr.ou.edu> but has more stress tests and more
22 * functionality.
23 *
24 */
25 #include "stress-ng.h"
26
27 static const stress_help_t help[] = {
28 { NULL, "cpu-online N", "start N workers offlining/onlining the CPUs" },
29 { NULL, "cpu-online-ops N", "stop after N offline/online operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #define STRESS_CPU_ONLINE_MAX_CPUS (65536)
34
35 #if defined(__linux__)
36
37 /*
38 * stress_cpu_online_set()
39 * set a specified CPUs online or offline
40 */
stress_cpu_online_set(const stress_args_t * args,const uint32_t cpu,const int setting)41 static int stress_cpu_online_set(
42 const stress_args_t *args,
43 const uint32_t cpu,
44 const int setting)
45 {
46 char filename[PATH_MAX];
47 char data[3] = { '0' + (char)setting, '\n', 0 };
48 ssize_t ret;
49
50 (void)snprintf(filename, sizeof(filename),
51 "/sys/devices/system/cpu/cpu%" PRIu32 "/online", cpu);
52
53 ret = system_write(filename, data, sizeof data);
54 if ((ret < 0) &&
55 ((ret != -EAGAIN) && (ret != -EINTR) &&
56 (ret != -EBUSY) && (ret != -EOPNOTSUPP))) {
57 pr_fail("%s: write failed, errno=%d (%s)\n",
58 args->name, errno, strerror(errno));
59 return EXIT_FAILURE;
60 }
61 return EXIT_SUCCESS;
62 }
63
64 /*
65 * stress_cpu_online_supported()
66 * check if we can run this as root
67 */
stress_cpu_online_supported(const char * name)68 static int stress_cpu_online_supported(const char *name)
69 {
70 ssize_t ret;
71
72 if (geteuid() != 0) {
73 pr_inf_skip("%s stressor will be skipped, "
74 "need to be running as root for this stressor\n", name);
75 return -1;
76 }
77
78 ret = system_write("/sys/devices/system/cpu/cpu1/online", "1\n", 2);
79 if (ret < 0) {
80 pr_inf_skip("%s stressor will be skipped, "
81 "cannot write to cpu1 online sysfs control file\n", name);
82 return -1;
83 }
84
85 return 0;
86 }
87
88 /*
89 * stress_cpu_online
90 * stress twiddling CPUs online/offline
91 */
stress_cpu_online(const stress_args_t * args)92 static int stress_cpu_online(const stress_args_t *args)
93 {
94 int32_t cpus = stress_get_processors_configured();
95 int32_t i, cpu_online_count = 0;
96 bool *cpu_online;
97 int rc = EXIT_SUCCESS;
98
99 if (geteuid() != 0) {
100 if (args->instance == 0)
101 pr_inf("%s: need root privilege to run "
102 "this stressor\n", args->name);
103 /* Not strictly a test failure */
104 return EXIT_SUCCESS;
105 }
106
107 if (cpus < 1) {
108 pr_fail("%s: too few CPUs (detected %" PRId32 ")\n",
109 args->name, cpus);
110 return EXIT_FAILURE;
111 }
112 if (cpus > STRESS_CPU_ONLINE_MAX_CPUS) {
113 pr_inf("%s: more than %" PRId32 " CPUs detected, "
114 "limiting to %d\n",
115 args->name, cpus, STRESS_CPU_ONLINE_MAX_CPUS);
116 cpus = STRESS_CPU_ONLINE_MAX_CPUS;
117 }
118
119 cpu_online = calloc((size_t)cpus, sizeof(*cpu_online));
120 if (!cpu_online) {
121 pr_err("%s: out of memory\n", args->name);
122 return EXIT_NO_RESOURCE;
123 }
124
125 /*
126 * Determine how many CPUs we can online/offline via
127 * the online sys interface
128 */
129 for (i = 0; i < cpus; i++) {
130 char filename[PATH_MAX];
131 int ret;
132
133 (void)snprintf(filename, sizeof(filename),
134 "/sys/devices/system/cpu/cpu%" PRId32 "/online", i);
135 ret = access(filename, O_RDWR);
136 if (ret == 0) {
137 cpu_online[i] = true;
138 cpu_online_count++;
139 }
140 }
141 if (cpu_online_count == 0) {
142 pr_inf("%s: no CPUs can be set online/offline\n", args->name);
143 free(cpu_online);
144 return EXIT_FAILURE;
145 }
146 if ((args->num_instances > 1) &&
147 (g_opt_flags & OPT_FLAGS_CPU_ONLINE_ALL)) {
148 if (args->instance == 0) {
149 pr_inf("%s: disabling --cpu-online-all option because "
150 "more than 1 %s stressor is being invoked\n",
151 args->name, args->name);
152 }
153 g_opt_flags &= ~OPT_FLAGS_CPU_ONLINE_ALL;
154 }
155
156 if ((args->instance == 0) &&
157 (g_opt_flags & OPT_FLAGS_CPU_ONLINE_ALL)) {
158 pr_inf("%s: exercising all %" PRId32 " cpus\n",
159 args->name, cpu_online_count + 1);
160 }
161
162 stress_set_proc_state(args->name, STRESS_STATE_RUN);
163
164 /*
165 * Now randomly offline/online them all
166 */
167 do {
168 uint32_t cpu = stress_mwc32() % (uint32_t)cpus;
169
170 /*
171 * Only allow CPU 0 to be offlined if OPT_FLAGS_CPU_ONLINE_ALL
172 * --cpu-online-all has been enabled
173 */
174 if ((cpu == 0) && !(g_opt_flags & OPT_FLAGS_CPU_ONLINE_ALL))
175 continue;
176 if (cpu_online[cpu]) {
177 rc = stress_cpu_online_set(args, cpu, 0);
178 if (rc != EXIT_SUCCESS)
179 break;
180 rc = stress_cpu_online_set(args, cpu, 1);
181 if (rc != EXIT_SUCCESS)
182 break;
183 inc_counter(args);
184 }
185 } while (keep_stressing(args));
186
187 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
188
189 /*
190 * Force CPUs all back online
191 */
192 for (i = 0; i < cpus; i++) {
193 if (cpu_online[i])
194 (void)stress_cpu_online_set(args, (uint32_t)i, 1);
195 }
196 free(cpu_online);
197
198 return rc;
199 }
200
201 stressor_info_t stress_cpu_online_info = {
202 .stressor = stress_cpu_online,
203 .supported = stress_cpu_online_supported,
204 .class = CLASS_CPU | CLASS_OS | CLASS_PATHOLOGICAL,
205 .help = help
206 };
207 #else
208 stressor_info_t stress_cpu_online_info = {
209 .stressor = stress_not_implemented,
210 .class = CLASS_CPU | CLASS_OS | CLASS_PATHOLOGICAL,
211 .help = help
212 };
213 #endif
214