--- /dev/null
+/*
+ * Copyright (c) 2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <attr/attributes.h>
+
+#define MAXNAMELEN 256
+#ifndef __sgi__
+typedef unsigned int uint_t;
+#endif
+
+/*
+ * Loop over directory sizes:
+ * make m directories
+ * touch n files in each directory
+ * write y bytes to all files in each directory
+ * set DMF attribute on x files in each directory
+ * Change directory sizes by multiplication or addition.
+ * Allow control of starting & stopping sizes, name length, target directory.
+ * Print size and wallclock time (ms per file).
+ * Output can be used to make graphs (gnuplot)
+ */
+
+static uint_t addval;
+static uint_t dirchars;
+static char *directory;
+static uint_t firstsize;
+static uint_t lastsize;
+static uint_t minchars;
+static double mulval;
+static uint_t nchars;
+static uint_t ndirs;
+static uint_t pfxchars;
+static off64_t fsize;
+static char *buffer;
+static size_t bsize;
+
+static int mkfile(char *, char *);
+static void filename(int, int, char *);
+static int hexchars(uint_t);
+static uint_t nextsize(uint_t);
+static double now(void);
+static void usage(void);
+
+/*
+ * Maximum size allowed, this is pretty nuts.
+ * The largest one we've ever built has been about 2 million.
+ */
+#define MAX_DIR_SIZE (16 * 1024 * 1024)
+#define DFL_FIRST_SIZE 1
+#define DFL_LAST_SIZE (1024 * 1024)
+#define MAX_DIR_COUNT 1024
+#define MIN_DIR_COUNT 1
+
+#define DMFATTRLEN 22
+#define DMFATTRNAME "SGI_DMI_DMFATTR"
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ uint_t cursize;
+ int i;
+ int j;
+ char name[MAXNAMELEN];
+ char attr[DMFATTRLEN];
+ double stime;
+
+ while ((c = getopt(argc, argv, "a:b:c:d:f:l:m:n:s:")) != -1) {
+ switch (c) {
+ case 'a':
+ addval = (uint_t)atoi(optarg);
+ break;
+ case 'b':
+ bsize = (size_t)atol(optarg);
+ break;
+ case 'c':
+ nchars = (uint_t)atoi(optarg);
+ break;
+ case 'd':
+ directory = optarg;
+ break;
+ case 'f':
+ firstsize = (uint_t)atoi(optarg);
+ break;
+ case 'l':
+ lastsize = (uint_t)atoi(optarg);
+ break;
+ case 'm':
+ mulval = atof(optarg);
+ break;
+ case 'n':
+ ndirs = (uint_t)atoi(optarg);
+ break;
+ case 's':
+ fsize = (off64_t)atol(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ if (!addval && !mulval)
+ mulval = 2.0;
+ else if ((addval && mulval) || mulval < 0.0) {
+ usage();
+ exit(1);
+ }
+ if (!bsize)
+ bsize = 1024 * 1024;
+ buffer = memalign(getpagesize(), bsize);
+ memset(buffer, 0xfeed, bsize);
+ memset(attr, 0xaaaaaaa, sizeof(attr));
+
+ if (!directory)
+ directory = ".";
+ else {
+ if (mkdir(directory, 0777) < 0 && errno != EEXIST) {
+ perror(directory);
+ exit(1);
+ }
+ if (chdir(directory) < 0) {
+ perror(directory);
+ exit(1);
+ }
+ }
+ if (firstsize == 0)
+ firstsize = DFL_FIRST_SIZE;
+ else if (firstsize > MAX_DIR_SIZE)
+ firstsize = MAX_DIR_SIZE;
+ if (lastsize == 0)
+ lastsize = DFL_LAST_SIZE;
+ else if (lastsize > MAX_DIR_SIZE)
+ lastsize = MAX_DIR_SIZE;
+ if (lastsize < firstsize)
+ lastsize = firstsize;
+ minchars = hexchars(lastsize - 1);
+ if (nchars < minchars)
+ nchars = minchars;
+ else if (nchars >= MAXNAMELEN)
+ nchars = MAXNAMELEN - 1;
+ if (ndirs > MAX_DIR_COUNT)
+ ndirs = MAX_DIR_COUNT;
+ if (ndirs < MIN_DIR_COUNT)
+ ndirs = MIN_DIR_COUNT;
+ dirchars = hexchars(ndirs);
+ pfxchars = nchars - minchars;
+ if (pfxchars)
+ memset(&name[dirchars + 1], 'a', pfxchars);
+
+ cursize = firstsize;
+ for (j = 0; j < ndirs; j++) {
+ filename(0, j, name);
+ name[dirchars] = '\0';
+ mkdir(name, 0777);
+ stime = now();
+ for (i = 0; i < cursize; i++) {
+ filename((i + j) % cursize, j, name);
+ close(mkfile(name, attr));
+ }
+ printf("%d %.3f\n", cursize,
+ (now() - stime) * 1.0e3 / (cursize * ndirs));
+ cursize = nextsize(cursize);
+ }
+ return 0;
+}
+
+static int
+mkfile(char *name, char *attr)
+{
+ int fd;
+ ssize_t wrote, wsize;
+ off64_t bytes = fsize;
+
+ if ((fd = open(name, O_WRONLY | O_CREAT | O_EXCL | O_DIRECT)) < 0) {
+ perror("open");
+ exit(1);
+ }
+ if (attr_setf(fd, DMFATTRNAME, attr, DMFATTRLEN, ATTR_ROOT) < 0) {
+ perror("attr_setf");
+ exit(1);
+ }
+ while (bytes > 0) {
+ wsize = (bsize < bytes) ? bsize : bytes;
+ if ((wrote = write(fd, buffer, wsize)) < 0) {
+ perror("write");
+ exit(1);
+ }
+ bytes -= wrote;
+ }
+ return fd;
+}
+
+static void
+filename(int idx, int dir, char *name)
+{
+ static char hexc[16] = "0123456789abcdef";
+ int i;
+
+ for (i = dirchars - 1; i >= 0; i--)
+ *name++ = hexc[(dir >> (4 * i)) & 0xf];
+ *name++ = '/';
+ name += pfxchars; /* skip pfx a's */
+ for (i = minchars - 1; i >= 0; i--)
+ *name++ = hexc[(idx >> (4 * i)) & 0xf];
+ *name = '\0';
+}
+
+static int
+hexchars(uint_t maxval)
+{
+ if (maxval < 16)
+ return 1;
+ if (maxval < 16 * 16)
+ return 2;
+ if (maxval < 16 * 16 * 16)
+ return 3;
+ if (maxval < 16 * 16 * 16 * 16)
+ return 4;
+ if (maxval < 16 * 16 * 16 * 16 * 16)
+ return 5;
+ if (maxval < 16 * 16 * 16 * 16 * 16 * 16)
+ return 6;
+ if (maxval < 16 * 16 * 16 * 16 * 16 * 16 * 16)
+ return 7;
+ return 8;
+}
+
+static uint_t
+nextsize(uint_t cursize)
+{
+ double n;
+
+ n = cursize;
+ if (addval)
+ n += addval;
+ else
+ n *= mulval;
+ if (n > (double)lastsize + 0.5)
+ return lastsize + 1; /* i.e. out of bounds */
+ else if ((uint_t)n == cursize)
+ return cursize + 1;
+ else
+ return (uint_t)n;
+}
+
+static double
+now(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dirperf [-d dir] [-a addstep | -m mulstep] [-f first] "
+ "[-l last] [-c nchars] [-n ndirs] [-s size]\n");
+}