xref: /openbmc/qemu/tests/migration/stress.c (revision dc5bd18f)
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 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 PAGE_SIZE 4096
31 
32 static int gettid(void)
33 {
34     return syscall(SYS_gettid);
35 }
36 
37 static __attribute__((noreturn)) void exit_failure(void)
38 {
39     if (getpid() == 1) {
40         sync();
41         reboot(RB_POWER_OFF);
42         fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
43                 argv0, gettid(), strerror(errno));
44         abort();
45     } else {
46         exit(1);
47     }
48 }
49 
50 static __attribute__((noreturn)) void exit_success(void)
51 {
52     if (getpid() == 1) {
53         sync();
54         reboot(RB_POWER_OFF);
55         fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
56                 argv0, gettid(), strerror(errno));
57         abort();
58     } else {
59         exit(0);
60     }
61 }
62 
63 static int get_command_arg_str(const char *name,
64                                char **val)
65 {
66     static char line[1024];
67     FILE *fp = fopen("/proc/cmdline", "r");
68     char *start, *end;
69 
70     if (fp == NULL) {
71         fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n",
72                 argv0, gettid(), strerror(errno));
73         return -1;
74     }
75 
76     if (!fgets(line, sizeof line, fp)) {
77         fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n",
78                 argv0, gettid(), strerror(errno));
79         fclose(fp);
80         return -1;
81     }
82     fclose(fp);
83 
84     start = strstr(line, name);
85     if (!start)
86         return 0;
87 
88     start += strlen(name);
89 
90     if (*start != '=') {
91         fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
92                 argv0, gettid(), name);
93     }
94     start++;
95 
96     end = strstr(start, " ");
97     if (!end)
98         end = strstr(start, "\n");
99 
100     if (end == start) {
101         fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
102                 argv0, gettid(), name);
103         return -1;
104     }
105 
106     if (end)
107         *val = strndup(start, end - start);
108     else
109         *val = strdup(start);
110     return 1;
111 }
112 
113 
114 static int get_command_arg_ull(const char *name,
115                                unsigned long long *val)
116 {
117     char *valstr;
118     char *end;
119 
120     int ret = get_command_arg_str(name, &valstr);
121     if (ret <= 0)
122         return ret;
123 
124     errno = 0;
125     *val = strtoll(valstr, &end, 10);
126     if (errno || *end) {
127         fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n",
128                 argv0, gettid(), name, valstr);
129         free(valstr);
130         return -1;
131     }
132     free(valstr);
133     return 0;
134 }
135 
136 
137 static int random_bytes(char *buf, size_t len)
138 {
139     int fd;
140 
141     fd = open("/dev/urandom", O_RDONLY);
142     if (fd < 0) {
143         fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n",
144                 argv0, gettid(), strerror(errno));
145         return -1;
146     }
147 
148     if (read(fd, buf, len) != len) {
149         fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n",
150                 argv0, gettid(), strerror(errno));
151         close(fd);
152         return -1;
153     }
154 
155     close(fd);
156 
157     return 0;
158 }
159 
160 
161 static unsigned long long now(void)
162 {
163     struct timeval tv;
164 
165     gettimeofday(&tv, NULL);
166 
167     return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull);
168 }
169 
170 static int stressone(unsigned long long ramsizeMB)
171 {
172     size_t pagesPerMB = 1024 * 1024 / PAGE_SIZE;
173     char *ram = malloc(ramsizeMB * 1024 * 1024);
174     char *ramptr;
175     size_t i, j, k;
176     char *data = malloc(PAGE_SIZE);
177     char *dataptr;
178     size_t nMB = 0;
179     unsigned long long before, after;
180 
181     if (!ram) {
182         fprintf(stderr, "%s (%05d): ERROR: cannot allocate %llu MB of RAM: %s\n",
183                 argv0, gettid(), ramsizeMB, strerror(errno));
184         return -1;
185     }
186     if (!data) {
187         fprintf(stderr, "%s (%d): ERROR: cannot allocate %d bytes of RAM: %s\n",
188                 argv0, gettid(), PAGE_SIZE, strerror(errno));
189         free(ram);
190         return -1;
191     }
192 
193     /* We don't care about initial state, but we do want
194      * to fault it all into RAM, otherwise the first iter
195      * of the loop below will be quite slow. We cna't use
196      * 0x0 as the byte as gcc optimizes that away into a
197      * calloc instead :-) */
198     memset(ram, 0xfe, ramsizeMB * 1024 * 1024);
199 
200     if (random_bytes(data, PAGE_SIZE) < 0) {
201         free(ram);
202         free(data);
203         return -1;
204     }
205 
206     before = now();
207 
208     while (1) {
209 
210         ramptr = ram;
211         for (i = 0; i < ramsizeMB; i++, nMB++) {
212             for (j = 0; j < pagesPerMB; j++) {
213                 dataptr = data;
214                 for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) {
215                     ramptr += sizeof(long long);
216                     dataptr += sizeof(long long);
217                     *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr;
218                 }
219             }
220 
221             if (nMB == 1024) {
222                 after = now();
223                 fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n",
224                         argv0, gettid(), after, after - before);
225                 before = now();
226                 nMB = 0;
227             }
228         }
229     }
230 
231     free(data);
232     free(ram);
233 }
234 
235 
236 static void *stressthread(void *arg)
237 {
238     unsigned long long ramsizeMB = *(unsigned long long *)arg;
239 
240     stressone(ramsizeMB);
241 
242     return NULL;
243 }
244 
245 static int stress(unsigned long long ramsizeGB, int ncpus)
246 {
247     size_t i;
248     unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus;
249     ncpus--;
250 
251     for (i = 0; i < ncpus; i++) {
252         pthread_t thr;
253         pthread_create(&thr, NULL,
254                        stressthread,   &ramsizeMB);
255     }
256 
257     stressone(ramsizeMB);
258 
259     return 0;
260 }
261 
262 
263 static int mount_misc(const char *fstype, const char *dir)
264 {
265     if (mkdir(dir, 0755) < 0 && errno != EEXIST) {
266         fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n",
267                 argv0, gettid(), dir, strerror(errno));
268         return -1;
269     }
270 
271     if (mount("none", dir, fstype, 0, NULL) < 0) {
272         fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n",
273                 argv0, gettid(), dir, strerror(errno));
274         return -1;
275     }
276 
277     return 0;
278 }
279 
280 static int mount_all(void)
281 {
282     if (mount_misc("proc", "/proc") < 0 ||
283         mount_misc("sysfs", "/sys") < 0 ||
284         mount_misc("tmpfs", "/dev") < 0)
285         return -1;
286 
287     mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9));
288     mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8));
289 
290     return 0;
291 }
292 
293 int main(int argc, char **argv)
294 {
295     unsigned long long ramsizeGB = 1;
296     char *end;
297     int ch;
298     int opt_ind = 0;
299     const char *sopt = "hr:c:";
300     struct option lopt[] = {
301         { "help", no_argument, NULL, 'h' },
302         { "ramsize", required_argument, NULL, 'r' },
303         { "cpus", required_argument, NULL, 'c' },
304         { NULL, 0, NULL, 0 }
305     };
306     int ret;
307     int ncpus = 0;
308 
309     argv0 = argv[0];
310 
311     while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
312         switch (ch) {
313         case 'r':
314             errno = 0;
315             ramsizeGB = strtoll(optarg, &end, 10);
316             if (errno != 0 || *end) {
317                 fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n",
318                         argv0, gettid(), optarg);
319                 exit_failure();
320             }
321             break;
322 
323         case 'c':
324             errno = 0;
325             ncpus = strtoll(optarg, &end, 10);
326             if (errno != 0 || *end) {
327                 fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n",
328                         argv0, gettid(), optarg);
329                 exit_failure();
330             }
331             break;
332 
333         case '?':
334         case 'h':
335             fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0);
336             exit_failure();
337         }
338     }
339 
340     if (getpid() == 1) {
341         if (mount_all() < 0)
342             exit_failure();
343 
344         ret = get_command_arg_ull("ramsize", &ramsizeGB);
345         if (ret < 0)
346             exit_failure();
347     }
348 
349     if (ncpus == 0)
350         ncpus = sysconf(_SC_NPROCESSORS_ONLN);
351 
352     fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n",
353             argv0, gettid(), ramsizeGB, ncpus);
354 
355     if (stress(ramsizeGB, ncpus) < 0)
356         exit_failure();
357 
358     exit_success();
359 }
360