1 /*
2 * Migration stress workload
3 *
4 * Copyright (c) 2016 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include <getopt.h>
22 #include <sys/reboot.h>
23 #include <sys/syscall.h>
24 #include <linux/random.h>
25 #include <pthread.h>
26 #include <sys/mount.h>
27
28 const char *argv0;
29
30 #define RAM_PAGE_SIZE 4096
31
32 #ifndef CONFIG_GETTID
gettid(void)33 static int gettid(void)
34 {
35 return syscall(SYS_gettid);
36 }
37 #endif
38
exit_failure(void)39 static __attribute__((noreturn)) void exit_failure(void)
40 {
41 if (getpid() == 1) {
42 sync();
43 reboot(RB_POWER_OFF);
44 fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
45 argv0, gettid(), strerror(errno));
46 abort();
47 } else {
48 exit(1);
49 }
50 }
51
get_command_arg_str(const char * name,char ** val)52 static int get_command_arg_str(const char *name,
53 char **val)
54 {
55 static char line[1024];
56 FILE *fp = fopen("/proc/cmdline", "r");
57 char *start, *end;
58
59 if (fp == NULL) {
60 fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n",
61 argv0, gettid(), strerror(errno));
62 return -1;
63 }
64
65 if (!fgets(line, sizeof line, fp)) {
66 fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n",
67 argv0, gettid(), strerror(errno));
68 fclose(fp);
69 return -1;
70 }
71 fclose(fp);
72
73 start = strstr(line, name);
74 if (!start)
75 return 0;
76
77 start += strlen(name);
78
79 if (*start != '=') {
80 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
81 argv0, gettid(), name);
82 }
83 start++;
84
85 end = strstr(start, " ");
86 if (!end)
87 end = strstr(start, "\n");
88
89 if (end == start) {
90 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
91 argv0, gettid(), name);
92 return -1;
93 }
94
95 if (end)
96 *val = g_strndup(start, end - start);
97 else
98 *val = g_strdup(start);
99 return 1;
100 }
101
102
get_command_arg_ull(const char * name,unsigned long long * val)103 static int get_command_arg_ull(const char *name,
104 unsigned long long *val)
105 {
106 char *valstr;
107 char *end;
108
109 int ret = get_command_arg_str(name, &valstr);
110 if (ret <= 0)
111 return ret;
112
113 errno = 0;
114 *val = strtoll(valstr, &end, 10);
115 if (errno || *end) {
116 fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n",
117 argv0, gettid(), name, valstr);
118 g_free(valstr);
119 return -1;
120 }
121 g_free(valstr);
122 return 0;
123 }
124
125
random_bytes(char * buf,size_t len)126 static int random_bytes(char *buf, size_t len)
127 {
128 int fd;
129
130 fd = open("/dev/urandom", O_RDONLY);
131 if (fd < 0) {
132 fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n",
133 argv0, gettid(), strerror(errno));
134 return -1;
135 }
136
137 if (read(fd, buf, len) != len) {
138 fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n",
139 argv0, gettid(), strerror(errno));
140 close(fd);
141 return -1;
142 }
143
144 close(fd);
145
146 return 0;
147 }
148
149
now(void)150 static unsigned long long now(void)
151 {
152 struct timeval tv;
153
154 gettimeofday(&tv, NULL);
155
156 return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull);
157 }
158
stressone(unsigned long long ramsizeMB)159 static void stressone(unsigned long long ramsizeMB)
160 {
161 size_t pagesPerMB = 1024 * 1024 / RAM_PAGE_SIZE;
162 g_autofree char *ram = g_malloc(ramsizeMB * 1024 * 1024);
163 char *ramptr;
164 size_t i, j, k;
165 g_autofree char *data = g_malloc(RAM_PAGE_SIZE);
166 char *dataptr;
167 size_t nMB = 0;
168 unsigned long long before, after;
169
170 /* We don't care about initial state, but we do want
171 * to fault it all into RAM, otherwise the first iter
172 * of the loop below will be quite slow. We can't use
173 * 0x0 as the byte as gcc optimizes that away into a
174 * calloc instead :-) */
175 memset(ram, 0xfe, ramsizeMB * 1024 * 1024);
176
177 if (random_bytes(data, RAM_PAGE_SIZE) < 0) {
178 return;
179 }
180
181 before = now();
182
183 while (1) {
184
185 ramptr = ram;
186 for (i = 0; i < ramsizeMB; i++, nMB++) {
187 for (j = 0; j < pagesPerMB; j++) {
188 dataptr = data;
189 for (k = 0; k < RAM_PAGE_SIZE; k += sizeof(long long)) {
190 ramptr += sizeof(long long);
191 dataptr += sizeof(long long);
192 *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr;
193 }
194 }
195
196 if (nMB == 1024) {
197 after = now();
198 fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n",
199 argv0, gettid(), after, after - before);
200 before = now();
201 nMB = 0;
202 }
203 }
204 }
205 }
206
207
stressthread(void * arg)208 static void *stressthread(void *arg)
209 {
210 unsigned long long ramsizeMB = *(unsigned long long *)arg;
211
212 stressone(ramsizeMB);
213
214 return NULL;
215 }
216
stress(unsigned long long ramsizeGB,int ncpus)217 static void stress(unsigned long long ramsizeGB, int ncpus)
218 {
219 size_t i;
220 unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus;
221 ncpus--;
222
223 for (i = 0; i < ncpus; i++) {
224 pthread_t thr;
225 pthread_create(&thr, NULL,
226 stressthread, &ramsizeMB);
227 }
228
229 stressone(ramsizeMB);
230 }
231
232
mount_misc(const char * fstype,const char * dir)233 static int mount_misc(const char *fstype, const char *dir)
234 {
235 if (g_mkdir_with_parents(dir, 0755) < 0 && errno != EEXIST) {
236 fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n",
237 argv0, gettid(), dir, strerror(errno));
238 return -1;
239 }
240
241 if (mount("none", dir, fstype, 0, NULL) < 0) {
242 fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n",
243 argv0, gettid(), dir, strerror(errno));
244 return -1;
245 }
246
247 return 0;
248 }
249
mount_all(void)250 static int mount_all(void)
251 {
252 if (mount_misc("proc", "/proc") < 0 ||
253 mount_misc("sysfs", "/sys") < 0 ||
254 mount_misc("tmpfs", "/dev") < 0)
255 return -1;
256
257 mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9));
258 mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8));
259
260 return 0;
261 }
262
main(int argc,char ** argv)263 int main(int argc, char **argv)
264 {
265 unsigned long long ramsizeGB = 1;
266 char *end;
267 int ch;
268 int opt_ind = 0;
269 const char *sopt = "hr:c:";
270 struct option lopt[] = {
271 { "help", no_argument, NULL, 'h' },
272 { "ramsize", required_argument, NULL, 'r' },
273 { "cpus", required_argument, NULL, 'c' },
274 { NULL, 0, NULL, 0 }
275 };
276 int ret;
277 int ncpus = 0;
278
279 argv0 = argv[0];
280
281 while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
282 switch (ch) {
283 case 'r':
284 errno = 0;
285 ramsizeGB = strtoll(optarg, &end, 10);
286 if (errno != 0 || *end) {
287 fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n",
288 argv0, gettid(), optarg);
289 exit_failure();
290 }
291 break;
292
293 case 'c':
294 errno = 0;
295 ncpus = strtoll(optarg, &end, 10);
296 if (errno != 0 || *end) {
297 fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n",
298 argv0, gettid(), optarg);
299 exit_failure();
300 }
301 break;
302
303 case '?':
304 case 'h':
305 fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0);
306 exit_failure();
307 }
308 }
309
310 if (getpid() == 1) {
311 if (mount_all() < 0)
312 exit_failure();
313
314 ret = get_command_arg_ull("ramsize", &ramsizeGB);
315 if (ret < 0)
316 exit_failure();
317 }
318
319 if (ncpus == 0)
320 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
321
322 fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n",
323 argv0, gettid(), ramsizeGB, ncpus);
324
325 stress(ramsizeGB, ncpus);
326
327 exit_failure();
328 }
329