| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2000-2003 Silicon Graphics, Inc. | |
| * All Rights Reserved. | |
| */ | |
| #include <sys/param.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <dirent.h> | |
| #include <linux/param.h> | |
| typedef void *(*fpi_t)(void); | |
| typedef void (*fpt_t)(int, void *); | |
| typedef void (*fpd_t)(void *); | |
| typedef struct tdesc | |
| { | |
| char *name; | |
| fpi_t init; | |
| fpt_t test; | |
| fpd_t done; | |
| } tdesc_t; | |
| static void d_readdir(void *); | |
| static void *i_readdir(void); | |
| static void t_readdir(int, void *); | |
| static void crfiles(char **, int, char *); | |
| static void d_chown(void *); | |
| static void d_create(void *); | |
| static void d_linkun(void *); | |
| static void d_open(void *); | |
| static void d_rename(void *); | |
| static void d_stat(void *); | |
| static void delflist(char **); | |
| static void dotest(tdesc_t *); | |
| static void *i_chown(void); | |
| static void *i_create(void); | |
| static void *i_linkun(void); | |
| static void *i_open(void); | |
| static void *i_rename(void); | |
| static void *i_stat(void); | |
| static char **mkflist(int, int, char); | |
| static double now(void); | |
| static void prtime(char *, int, double); | |
| static void rmfiles(char **); | |
| static void t_chown(int, void *); | |
| static void t_create(int, void *); | |
| static void t_crunlink(int, void *); | |
| static void t_linkun(int, void *); | |
| static void t_open(int, void *); | |
| static void t_rename(int, void *); | |
| static void t_stat(int, void *); | |
| static void usage(void); | |
| tdesc_t tests[] = { | |
| { "chown", i_chown, t_chown, d_chown }, | |
| { "create", i_create, t_create, d_create }, | |
| { "crunlink", (fpi_t)0, t_crunlink, (fpd_t)0 }, | |
| { "readdir", i_readdir, t_readdir, d_readdir }, | |
| { "linkun", i_linkun, t_linkun, d_linkun }, | |
| { "open", i_open, t_open, d_open }, | |
| { "rename", i_rename, t_rename, d_rename }, | |
| { "stat", i_stat, t_stat, d_stat }, | |
| { NULL } | |
| }; | |
| char *buffer; | |
| int compact = 0; | |
| int files_bg = 0; | |
| int files_op = 1; | |
| char **flist_bg; | |
| char **flist_op; | |
| int fnlen_bg = 5; | |
| int fnlen_op = 5; | |
| int fsize = 0; | |
| int iters = 0; | |
| double time_end; | |
| double time_start; | |
| int totsec = 0; | |
| int verbose = 0; | |
| int | |
| main(int argc, char **argv) | |
| { | |
| int c; | |
| char *testdir; | |
| tdesc_t *tp; | |
| testdir = getenv("TMPDIR"); | |
| if (testdir == NULL) | |
| testdir = "."; | |
| while ((c = getopt(argc, argv, "cd:i:l:L:n:N:s:t:v")) != -1) { | |
| switch (c) { | |
| case 'c': | |
| compact = 1; | |
| break; | |
| case 'd': | |
| testdir = optarg; | |
| break; | |
| case 'i': | |
| iters = atoi(optarg); | |
| break; | |
| case 'l': | |
| fnlen_op = atoi(optarg); | |
| break; | |
| case 'L': | |
| fnlen_bg = atoi(optarg); | |
| break; | |
| case 'n': | |
| files_op = atoi(optarg); | |
| break; | |
| case 'N': | |
| files_bg = atoi(optarg); | |
| break; | |
| case 's': | |
| fsize = atoi(optarg); | |
| break; | |
| case 't': | |
| totsec = atoi(optarg); | |
| break; | |
| case 'v': | |
| verbose = 1; | |
| break; | |
| case '?': | |
| fprintf(stderr, "bad option\n"); | |
| usage(); | |
| } | |
| } | |
| if (!iters && !totsec) | |
| iters = 1; | |
| if (chdir(testdir) < 0) { | |
| perror(testdir); | |
| return 1; | |
| } | |
| if (mkdir("metaperf", 0777) < 0 || chdir("metaperf") < 0) { | |
| perror("metaperf"); | |
| return 1; | |
| } | |
| for (; optind < argc; optind++) { | |
| for (tp = tests; tp->name; tp++) { | |
| if (strcmp(argv[optind], tp->name) == 0) { | |
| dotest(tp); | |
| break; | |
| } | |
| } | |
| } | |
| chdir(".."); | |
| rmdir("metaperf"); | |
| return 0; | |
| } | |
| static void | |
| crfiles(char **flist, int fsize, char *buf) | |
| { | |
| int fd; | |
| char **fnp; | |
| for (fnp = flist; *fnp; fnp++) { | |
| fd = creat(*fnp, 0666); | |
| if (fsize) | |
| write(fd, buf, fsize); | |
| close(fd); | |
| } | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| d_chown(void *v) | |
| { | |
| rmfiles(flist_op); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| d_create(void *v) | |
| { | |
| rmfiles(flist_op); | |
| } | |
| static void | |
| d_readdir(void *v) | |
| { | |
| rmfiles(flist_op); | |
| closedir((DIR *)v); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| d_linkun(void *v) | |
| { | |
| unlink("a"); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| d_open(void *v) | |
| { | |
| rmfiles(flist_op); | |
| } | |
| static void | |
| d_rename(void *v) | |
| { | |
| rmfiles(flist_op); | |
| rmfiles((char **)v); | |
| delflist((char **)v); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| d_stat(void *v) | |
| { | |
| rmfiles(flist_op); | |
| } | |
| static void | |
| delflist(char **flist) | |
| { | |
| char **fnp; | |
| for (fnp = flist; *fnp; fnp++) | |
| free(*fnp); | |
| free(flist); | |
| } | |
| static void | |
| dotest(tdesc_t *tp) | |
| { | |
| double dn; | |
| double gotsec; | |
| int n; | |
| void *v; | |
| flist_bg = mkflist(files_bg, fnlen_bg, 'b'); | |
| flist_op = mkflist(files_op, fnlen_op, 'o'); | |
| if (fsize) | |
| buffer = calloc(fsize, 1); | |
| else | |
| buffer = NULL; | |
| crfiles(flist_bg, 0, (char *)0); | |
| n = iters ? iters : 1; | |
| v = (void *)0; | |
| for (;;) { | |
| if (tp->init) | |
| v = (tp->init)(); | |
| sync(); | |
| sleep(1); | |
| time_start = now(); | |
| (tp->test)(n, v); | |
| time_end = now(); | |
| if (tp->done) | |
| (tp->done)(v); | |
| gotsec = time_end - time_start; | |
| if (!totsec || gotsec >= 0.9 * totsec) | |
| break; | |
| if (verbose) | |
| prtime(tp->name, n, gotsec); | |
| if (!gotsec) | |
| gotsec = 1.0 / (2 * HZ); | |
| if (gotsec < 0.001 * totsec) | |
| dn = n * (0.01 * totsec / gotsec); | |
| else if (gotsec < 0.01 * totsec) | |
| dn = n * (0.1 * totsec / gotsec); | |
| else | |
| dn = n * (totsec / gotsec); | |
| if ((int)dn <= n) | |
| n++; | |
| else | |
| n = (int)dn; | |
| } | |
| prtime(tp->name, n, gotsec); | |
| rmfiles(flist_bg); | |
| delflist(flist_bg); | |
| delflist(flist_op); | |
| if (fsize) | |
| free(buffer); | |
| } | |
| static void * | |
| i_chown(void) | |
| { | |
| char **fnp; | |
| crfiles(flist_op, 0, (char *)0); | |
| for (fnp = flist_op; *fnp; fnp++) | |
| chown(*fnp, 1, -1); | |
| return (void *)0; | |
| } | |
| static void * | |
| i_create(void) | |
| { | |
| crfiles(flist_op, fsize, buffer); | |
| return (void *)0; | |
| } | |
| static void * | |
| i_readdir(void) | |
| { | |
| crfiles(flist_op, 0, (char *)0); | |
| return opendir("."); | |
| } | |
| static void * | |
| i_linkun(void) | |
| { | |
| close(creat("a", 0666)); | |
| return (void *)0; | |
| } | |
| static void * | |
| i_open(void) | |
| { | |
| crfiles(flist_op, 0, (char *)0); | |
| return (void *)0; | |
| } | |
| static void * | |
| i_rename(void) | |
| { | |
| crfiles(flist_op, 0, (char *)0); | |
| return (void *)mkflist(files_op, fnlen_op, 'r'); | |
| } | |
| static void * | |
| i_stat(void) | |
| { | |
| crfiles(flist_op, 0, (char *)0); | |
| return (void *)0; | |
| } | |
| static char ** | |
| mkflist(int files, int fnlen, char start) | |
| { | |
| int i; | |
| char **rval; | |
| rval = calloc(files + 1, sizeof(char *)); | |
| for (i = 0; i < files; i++) { | |
| rval[i] = malloc(fnlen + 1); | |
| sprintf(rval[i], "%0*d%c", fnlen - 1, i, start); | |
| } | |
| return rval; | |
| } | |
| static double | |
| now(void) | |
| { | |
| struct timeval t; | |
| gettimeofday(&t, (void *)0); | |
| return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec; | |
| } | |
| static void | |
| prtime(char *name, int n, double t) | |
| { | |
| double ops_per_sec; | |
| double usec_per_op; | |
| ops_per_sec = (double)n * (double)files_op / t; | |
| usec_per_op = t * 1.0e6 / ((double)n * (double)files_op); | |
| if (compact) | |
| printf("%s %d %d %d %d %d %d %f %f %f\n", | |
| name, n, files_op, fnlen_op, fsize, files_bg, fnlen_bg, | |
| t, ops_per_sec, usec_per_op); | |
| else { | |
| printf("%s: %d times, %d file(s) namelen %d", | |
| name, n, files_op, fnlen_op); | |
| if (fsize) | |
| printf(" size %d", fsize); | |
| if (files_bg) | |
| printf(", bg %d file(s) namelen %d", | |
| files_bg, fnlen_bg); | |
| printf(", time = %f sec, ops/sec=%f, usec/op = %f\n", | |
| t, ops_per_sec, usec_per_op); | |
| } | |
| } | |
| static void | |
| rmfiles(char **flist) | |
| { | |
| char **fnp; | |
| for (fnp = flist; *fnp; fnp++) | |
| unlink(*fnp); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_chown(int n, void *v) | |
| { | |
| char **fnp; | |
| int i; | |
| for (i = 0; i < n; i++) { | |
| for (fnp = flist_op; *fnp; fnp++) { | |
| if ((i & 1) == 0) | |
| chown(*fnp, 2, -1); | |
| else | |
| chown(*fnp, 1, -1); | |
| } | |
| } | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_create(int n, void *v) | |
| { | |
| int i; | |
| for (i = 0; i < n; i++) | |
| crfiles(flist_op, fsize, buffer); | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_crunlink(int n, void *v) | |
| { | |
| int i; | |
| for (i = 0; i < n; i++) { | |
| crfiles(flist_op, fsize, buffer); | |
| rmfiles(flist_op); | |
| } | |
| } | |
| static void | |
| t_readdir(int n, void *v) | |
| { | |
| DIR *dir; | |
| int i; | |
| for (dir = (DIR *)v, i = 0; i < n; i++) { | |
| rewinddir(dir); | |
| while ((readdir(dir)) != NULL); | |
| } | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_linkun(int n, void *v) | |
| { | |
| char **fnp; | |
| int i; | |
| for (i = 0; i < n; i++) { | |
| for (fnp = flist_op; *fnp; fnp++) | |
| link("a", *fnp); | |
| rmfiles(flist_op); | |
| } | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_open(int n, void *v) | |
| { | |
| char **fnp; | |
| int i; | |
| for (i = 0; i < n; i++) { | |
| for (fnp = flist_op; *fnp; fnp++) | |
| close(open(*fnp, O_RDWR)); | |
| } | |
| } | |
| static void | |
| t_rename(int n, void *v) | |
| { | |
| char **fnp; | |
| int i; | |
| char **rflist; | |
| char **rfp; | |
| for (rflist = (char **)v, i = 0; i < n; i++) { | |
| for (fnp = flist_op, rfp = rflist; *fnp; fnp++, rfp++) { | |
| if ((i & 1) == 0) | |
| rename(*fnp, *rfp); | |
| else | |
| rename(*rfp, *fnp); | |
| } | |
| } | |
| } | |
| /* ARGSUSED */ | |
| static void | |
| t_stat(int n, void *v) | |
| { | |
| char **fnp; | |
| int i; | |
| struct stat stb; | |
| for (i = 0; i < n; i++) { | |
| for (fnp = flist_op; *fnp; fnp++) | |
| stat(*fnp, &stb); | |
| } | |
| } | |
| static void | |
| usage(void) | |
| { | |
| fprintf(stderr, | |
| "Usage: metaperf [-d dname] [-i iters|-t seconds] [-s fsize]\n" | |
| "\t[-l opfnamelen] [-L bgfnamelen]\n" | |
| "\t[-n opfcount] [-N bgfcount] test...\n"); | |
| fprintf(stderr, | |
| "Tests: chown create crunlink linkun open rename stat readdir\n"); | |
| exit(1); | |
| } |