xref: /openbmc/qemu/tests/migration/stress.c (revision 6f9ff551)
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 #ifndef CONFIG_GETTID
33 static int gettid(void)
34 {
35     return syscall(SYS_gettid);
36 }
37 #endif
38 
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 
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 
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 
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 
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 
159 static void stressone(unsigned long long ramsizeMB)
160 {
161     size_t pagesPerMB = 1024 * 1024 / 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(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, 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 < 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 
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 
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 
233 static int mount_misc(const char *fstype, const char *dir)
234 {
235     if (mkdir(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 
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 
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