2 test program to heavily stress a TSM/HSM system
4 Andrew Tridgell January 2008
8 gcc -Wall -g -DWITH_GPFS=1 -o tsm_torture{,.c} -lgpfs_gpl -lrt
10 If you want to use the -L or -S switches then you must symlink tsm_torture to smbd as
11 otherwise it won't have permission to set share modes or leases
13 ln -s tsm_torture smbd
17 ./smbd /gpfs/data/tsmtest
19 where /gpfs/data/tsmtest is the directory to test on
23 #define _XOPEN_SOURCE 500
46 /* The signal we'll use to signify aio done. */
48 #define RT_SIGNAL_AIO (SIGRTMIN+3)
59 const char *migrate_cmd;
62 bool skip_file_creation;
65 .use_sharemode = false,
70 .migrate_cmd = "dsmmigrate",
72 .skip_file_creation = false,
75 static pid_t parent_pid;
77 enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_MIGRATE, OP_GETOFFLINE, OP_ENDOFLIST};
80 unsigned offline_count;
81 unsigned online_count;
82 unsigned migrate_fail_count;
83 unsigned io_fail_count;
84 unsigned migrate_ok_count;
87 struct timeval tv_start;
88 double latencies[OP_ENDOFLIST];
89 double worst_latencies[OP_ENDOFLIST];
94 static struct timeval tv_start;
95 static struct child *children;
97 static unsigned char *buf;
99 /* return a pointer to a /dev/zero shared memory segment of size "size"
100 which will persist across fork() but will disappear when all processes
103 The memory is zeroed automatically
105 This relies on /dev/zero being shared mmap capable, which it is
106 only under some OSes (Linux 2.1 _not_ included)
108 void *shm_setup(int size)
113 fd = open("/dev/zero", O_RDWR);
117 ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
119 /* note that we don't need to keep the file open */
121 if (ret == (void *)-1) return NULL;
126 static struct timeval timeval_current(void)
129 gettimeofday(&tv, NULL);
133 static double timeval_elapsed(struct timeval *tv)
135 struct timeval tv2 = timeval_current();
136 return (tv2.tv_sec - tv->tv_sec) +
137 (tv2.tv_usec - tv->tv_usec)*1.0e-6;
141 file name given a number
143 static char *filename(int i)
146 asprintf(&s, "%s/file%u.dat", options.dir, (unsigned)i);
150 static void sigio_handler(int sig)
152 printf("\nGot SIGIO\n");
155 static volatile bool signal_received;
157 static void signal_handler(int sig)
159 signal_received = true;
162 /* simulate pread using aio */
163 static ssize_t pread_aio(int fd, void *buf, size_t count, off_t offset)
168 memset(&acb, 0, sizeof(acb));
172 acb.aio_nbytes = count;
173 acb.aio_offset = offset;
174 acb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
175 acb.aio_sigevent.sigev_signo = RT_SIGNAL_AIO;
176 acb.aio_sigevent.sigev_value.sival_int = 1;
178 signal(RT_SIGNAL_AIO, signal_handler);
181 if (options.io_uid) {
182 if (seteuid(options.io_uid) != 0) {
183 printf("\nFailed to become uid %u\n", options.io_uid);
187 if (aio_read(&acb) != 0) {
190 if (options.io_uid) {
191 if (seteuid(0) != 0) {
192 printf("\nFailed to become root\n");
197 while (signal_received == 0) {
201 ret = aio_error(&acb);
203 printf("\naio operation failed - %s\n", strerror(ret));
207 return aio_return(&acb);
211 /* simulate pwrite using aio */
212 static ssize_t pwrite_aio(int fd, void *buf, size_t count, off_t offset)
217 memset(&acb, 0, sizeof(acb));
221 acb.aio_nbytes = count;
222 acb.aio_offset = offset;
223 acb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
224 acb.aio_sigevent.sigev_signo = RT_SIGNAL_AIO;
225 acb.aio_sigevent.sigev_value.sival_int = 1;
227 signal(RT_SIGNAL_AIO, signal_handler);
230 if (options.io_uid) {
231 if (seteuid(options.io_uid) != 0) {
232 printf("\nFailed to become uid %u\n", options.io_uid);
236 if (aio_write(&acb) != 0) {
239 if (options.io_uid) {
240 if (seteuid(0) != 0) {
241 printf("\nFailed to become root\n");
246 while (signal_received == 0) {
250 ret = aio_error(&acb);
252 printf("\naio operation failed - %s\n", strerror(ret));
256 return aio_return(&acb);
262 static void child_loadfile(struct child *child, const char *fname, unsigned fnumber)
267 signal(SIGIO, sigio_handler);
269 fd = open(fname, O_RDONLY);
276 if (options.use_lease && gpfs_set_lease(fd, GPFS_LEASE_READ) != 0) {
277 printf("\ngpfs_set_lease on '%s' - %s\n", fname, strerror(errno));
281 if (options.use_sharemode && gpfs_set_share(fd, 1, 2) != 0) {
282 printf("\ngpfs_set_share on '%s' - %s\n", fname, strerror(errno));
288 if (options.use_aio) {
289 ret = pread_aio(fd, buf, options.fsize, 0);
291 ret = pread(fd, buf, options.fsize, 0);
293 if (ret != options.fsize) {
294 if (child->io_fail_count == 0) {
295 printf("\npread failed on '%s' - %s\n", fname, strerror(errno));
297 child->io_fail_count++;
302 for (i=0;i<options.fsize;i++) {
303 if (buf[i] != fnumber % 256) {
304 printf("\nBad data %u - expected %u for '%s'\n",
305 buf[i], fnumber%256, fname);
317 static void child_savefile(struct child *child, const char *fname, unsigned fnumber)
322 signal(SIGIO, sigio_handler);
324 fd = open(fname, O_WRONLY);
331 if (options.use_lease && gpfs_set_lease(fd, GPFS_LEASE_WRITE) != 0) {
332 printf("\ngpfs_set_lease on '%s' - %s\n", fname, strerror(errno));
336 if (options.use_sharemode && gpfs_set_share(fd, 1, 2) != 0) {
337 printf("\ngpfs_set_share on '%s' - %s\n", fname, strerror(errno));
343 memset(buf, fnumber%256, options.fsize);
345 if (options.use_aio) {
346 ret = pwrite_aio(fd, buf, options.fsize, 0);
348 ret = pwrite(fd, buf, options.fsize, 0);
350 if (ret != options.fsize) {
351 if (child->io_fail_count == 0) {
352 printf("\npwrite failed on '%s' - %s\n", fname, strerror(errno));
354 child->io_fail_count++;
364 get file offline status
366 static void child_getoffline(struct child *child, const char *fname)
369 if (stat(fname, &st) != 0) {
370 printf("\nFailed to stat '%s' - %s\n", fname, strerror(errno));
373 if (st.st_size != options.fsize) {
374 printf("\nWrong file size for '%s' - %u\n", fname, (unsigned)st.st_size);
377 if (st.st_blocks == 0) {
378 child->offline_count++;
379 if (strcmp(options.migrate_cmd, "/bin/true") == 0) {
380 printf("\nFile '%s' is offline with no migration command\n", fname);
383 child->online_count++;
391 static void child_migrate(struct child *child, const char *fname)
398 if (stat(fname, &st) != 0) {
399 printf("\nFailed to stat '%s' - %s\n", fname, strerror(errno));
402 if (st.st_size != options.fsize) {
403 printf("\nWrong file size for '%s' - %u\n", fname, (unsigned)st.st_size);
406 if (st.st_blocks == 0) {
407 /* already offline */
411 /* make the file a bit older so migation works */
413 t.modtime = time(NULL) - 60*60;
416 asprintf(&cmd, "%s %s > /dev/null 2>&1", options.migrate_cmd, fname);
419 ret = WEXITSTATUS(ret);
422 children->migrate_fail_count++;
424 children->migrate_ok_count++;
432 static void run_child(struct child *child)
434 srandom(time(NULL) ^ getpid());
438 unsigned fnumber = random() % options.nfiles;
439 char *fname = filename(fnumber);
441 if (kill(parent_pid, 0) != 0) {
442 /* parent has exited */
446 child->tv_start = timeval_current();
448 child->op = random() % OP_ENDOFLIST;
451 child_loadfile(child, fname, fnumber);
454 child_savefile(child, fname, fnumber);
457 child_migrate(child, fname);
460 child_getoffline(child, fname);
466 latency = timeval_elapsed(&child->tv_start);
467 if (latency > child->latencies[child->op]) {
468 child->latencies[child->op] = latency;
470 if (latency > child->worst_latencies[child->op]) {
471 child->worst_latencies[child->op] = latency;
479 static void sig_alarm(int sig)
482 unsigned total=0, total_offline=0, total_online=0,
483 total_migrate_failures=0, total_migrate_ok=0,
485 double latencies[OP_ENDOFLIST];
486 double worst_latencies[OP_ENDOFLIST];
488 if (timeval_elapsed(&tv_start) >= options.timelimit) {
489 printf("\ntimelimit reached - killing children\n");
490 for (i=0;i<options.nprocesses;i++) {
491 kill(children[i].pid, SIGTERM);
495 for (op=0;op<OP_ENDOFLIST;op++) {
497 worst_latencies[op] = 0;
500 for (i=0;i<options.nprocesses;i++) {
501 total += children[i].count - children[i].lastcount;
502 children[i].lastcount = children[i].count;
503 total_online += children[i].online_count;
504 total_offline += children[i].offline_count;
505 total_migrate_failures += children[i].migrate_fail_count;
506 total_io_failures += children[i].io_fail_count;
507 total_migrate_ok += children[i].migrate_ok_count;
508 for (op=0;op<OP_ENDOFLIST;op++) {
509 if (children[i].latencies[op] > latencies[op]) {
510 latencies[op] = children[i].latencies[op];
512 children[i].latencies[op] = 0;
514 if (timeval_elapsed(&children[i].tv_start) > latencies[children[i].op]) {
516 lat = timeval_elapsed(&children[i].tv_start);
517 latencies[children[i].op] = lat;
518 if (lat > worst_latencies[children[i].op]) {
519 worst_latencies[children[i].op] = lat;
522 for (op=0;op<OP_ENDOFLIST;op++) {
523 double lat = children[i].worst_latencies[op];
524 if (lat > worst_latencies[op]) {
525 worst_latencies[op] = lat;
530 printf("ops/s=%4u offline=%u/%u failures: mig=%u io=%u latencies: mig=%4.1f/%4.1f stat=%4.1f/%4.1f write=%4.1f/%4.1f read=%4.1f/%4.1f\n",
531 total, total_offline, total_online+total_offline,
532 total_migrate_failures,
534 latencies[OP_MIGRATE], worst_latencies[OP_MIGRATE],
535 latencies[OP_GETOFFLINE], worst_latencies[OP_GETOFFLINE],
536 latencies[OP_SAVEFILE], worst_latencies[OP_SAVEFILE],
537 latencies[OP_LOADFILE], worst_latencies[OP_LOADFILE]);
539 signal(SIGALRM, sig_alarm);
543 static void usage(void)
545 printf("Usage: (note, must run as 'smbd' to use leases or share modes)\n");
546 printf("ln -sf tsm_torture smbd\n");
547 printf("./smbd [options] <directory>\n");
548 printf("Options:\n");
549 printf(" -N <nprocs> number of child processes\n");
550 printf(" -F <nfiles> number of files\n");
551 printf(" -t <time> runtime (seconds)\n");
552 printf(" -s <fsize> file size (bytes)\n");
553 printf(" -M <migrate> set file migrate command\n");
554 printf(" -U <uid> do IO as the specified uid\n");
555 printf(" -L use gpfs leases\n");
556 printf(" -S use gpfs sharemodes\n");
557 printf(" -A use Posix async IO\n");
558 printf(" -C skip file creation\n");
562 int main(int argc, char * const argv[])
565 const char *progname = argv[0];
568 /* parse command-line options */
569 while ((opt = getopt(argc, argv, "LSN:F:t:s:M:U:AhC")) != -1) {
572 options.use_lease = true;
575 options.use_sharemode = true;
578 options.use_aio = true;
581 options.skip_file_creation = true;
584 options.nprocesses = atoi(optarg);
587 options.nfiles = atoi(optarg);
590 options.migrate_cmd = strdup(optarg);
593 options.fsize = atoi(optarg);
596 options.io_uid = atoi(optarg);
599 options.timelimit = atoi(optarg);
607 if ((options.use_lease || options.use_sharemode) && strstr(progname, "smbd") == NULL) {
608 printf("ERROR: you must invoke as smbd to use leases or share modes - use a symlink\n");
620 options.dir = argv[0];
622 if (stat(options.dir, &st) != 0 || !S_ISDIR(st.st_mode)) {
623 printf("'%s' must exist and be a directory\n", options.dir);
627 children = shm_setup(sizeof(*children) * options.nprocesses);
630 buf = malloc(options.fsize);
632 if (!options.skip_file_creation) {
633 printf("Creating %u files of size %u in '%s'\n",
634 options.nfiles, options.fsize, options.dir);
636 for (i=0;i<options.nfiles;i++) {
638 char *fname = filename(i);
639 fd = open(fname, O_CREAT|O_RDWR, 0600);
644 ftruncate(fd, options.fsize);
645 memset(buf, i%256, options.fsize);
646 if (write(fd, buf, options.fsize) != options.fsize) {
647 printf("Failed to write '%s'\n", fname);
656 parent_pid = getpid();
658 printf("Starting %u child processes for %u seconds\n",
659 options.nprocesses, options.timelimit);
660 printf("Results shown as: offline=numoffline/total latencies: current/worst\n");
662 for (i=0;i<options.nprocesses;i++) {
665 children[i].pid = getpid();
666 run_child(&children[i]);
668 children[i].pid = pid;
672 /* show status once a second */
673 signal(SIGALRM, sig_alarm);
674 tv_start = timeval_current();
677 /* wait for the children to finish */
678 for (i=0;i<options.nprocesses;i++) {
679 while (waitpid(children[i].pid, 0, 0) != 0 && errno != ECHILD) ;