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