Add support for AIX

Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
diff --git a/Makefile.aix b/Makefile.aix
new file mode 100644
index 0000000..29d6e09
--- /dev/null
+++ b/Makefile.aix
@@ -0,0 +1,73 @@
+CC	= gcc
+DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
+OPTFLAGS= -O2 -g $(EXTFLAGS)
+CFLAGS	= -Wwrite-strings -Wall -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(DEBUGFLAGS) -fno-omit-frame-pointer -D_LARGE_FILES -D__ppc__
+PROGS	= fio
+SCRIPTS = fio_generate_plots
+OBJS = gettime.o fio.o ioengines.o init.o stat.o log.o time.o filesetup.o \
+	eta.o verify.o memory.o io_u.o parse.o mutex.o options.o \
+	rbtree.o fifo.o smalloc.o filehash.o helpers.o \
+	profile.o debug.o getopt_long.o
+
+OBJS += lib/rand.o
+OBJS += lib/getopt_long.o
+
+OBJS += crc/crc7.o
+OBJS += crc/crc16.o
+OBJS += crc/crc32.o
+OBJS += crc/crc32c.o
+OBJS += crc/crc32c-intel.o
+OBJS += crc/crc64.o
+OBJS += crc/sha1.o
+OBJS += crc/sha256.o
+OBJS += crc/sha512.o
+OBJS += crc/md5.o
+
+OBJS += engines/cpu.o
+OBJS += engines/mmap.o
+OBJS += engines/posixaio.o
+OBJS += engines/sync.o
+OBJS += engines/null.o
+OBJS += engines/net.o
+
+OBJS += profiles/tiobench.o
+
+ifneq ($(findstring $(MAKEFLAGS),s),s)
+ifndef V
+	QUIET_CC	= @echo '   ' CC $@;
+	QUIET_DEP	= @echo '   ' DEP $@;
+endif
+endif
+
+INSTALL = installbsd -c
+prefix = /usr/local
+bindir = $(prefix)/bin
+mandir = $(prefix)/man
+
+%.o: %.c
+	$(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
+fio: $(OBJS)
+	$(QUIET_CC)$(CC) $(CFLAGS) -o $@ $(filter %.o,$^) $(EXTLIBS) -lpthread -lm -ldl -lrt
+
+depend:
+	$(QUIET_DEP)$(CC) -MM $(ALL_CFLAGS) *.c engines/*.c crc/*.c 1> .depend
+
+$(PROGS): depend
+
+all: depend $(PROGS) $(SCRIPTS)
+
+clean:
+	-rm -f .depend cscope.out $(OBJS) $(PROGS) core.* core
+
+cscope:
+	@cscope -b
+
+install: $(PROGS) $(SCRIPTS)
+	mkdir -p -m 755 $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1
+	$(INSTALL) $(PROGS) $(SCRIPTS) $(DESTDIR)$(bindir)
+	$(INSTALL) -m 644 fio.1 $(DESTDIR)$(mandir)/man1
+	$(INSTALL) -m 644 fio_generate_plots.1 $(DESTDIR)$(mandir)/man1
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/README b/README
index f2438ba..4115455 100644
--- a/README
+++ b/README
@@ -65,6 +65,10 @@
 
 $ gmake -f Makefile.Freebsd && gmake -f Makefile.FreeBSD install
 
+Same goes for AIX:
+
+$ gmake -f Makefile.aix && gmake -f Makefile.aix install
+
 Likewise with OpenSolaris, use the Makefile.solaris to compile there.
 The OpenSolaris make should work fine. This might change in the
 future if I opt for an autoconf type setup.
@@ -270,7 +274,7 @@
 Platforms
 ---------
 
-Fio works on (at least) Linux, Solaris, and FreeBSD. Some features and/or
+Fio works on (at least) Linux, Solaris, AIX and FreeBSD. Some features and/or
 options may only be available on some of the platforms, typically because
 those features only apply to that platform (like the solarisaio engine, or
 the splice engine on Linux).
@@ -291,6 +295,23 @@
 appreciated. There's a lot of value in having the same test/benchmark tool
 available on all platforms.
 
+Note that POSIX aio is not enabled by default on AIX. If you get messages like:
+
+    Symbol resolution failed for /usr/lib/libc.a(posix_aio.o) because:
+        Symbol _posix_kaio_rdwr (number 2) is not exported from dependent module /unix.
+
+you need to enable POSIX aio. Run the following commands as root:
+
+    # lsdev -C -l posix_aio0
+        posix_aio0 Defined  Posix Asynchronous I/O
+    # cfgmgr -l posix_aio0
+    # lsdev -C -l posix_aio0
+        posix_aio0 Available  Posix Asynchronous I/O
+
+POSIX aio should work now. To make the change permanent:
+
+    # chdev -l posix_aio0 -P -a autoconfig='available'
+        posix_aio0 changed
 
 
 Author
diff --git a/fio.h b/fio.h
index de76e65..5788107 100644
--- a/fio.h
+++ b/fio.h
@@ -11,7 +11,6 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
-#include <getopt.h>
 #include <inttypes.h>
 #include <assert.h>
 
@@ -32,6 +31,7 @@
 #include "options.h"
 #include "profile.h"
 #include "time.h"
+#include "lib/getopt.h"
 
 #ifdef FIO_HAVE_GUASI
 #include <guasi.h>
diff --git a/init.c b/init.c
index 09b9152..c3fe88f 100644
--- a/init.c
+++ b/init.c
@@ -8,7 +8,6 @@
 #include <ctype.h>
 #include <string.h>
 #include <errno.h>
-#include <getopt.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <sys/types.h>
@@ -21,6 +20,8 @@
 #include "verify.h"
 #include "profile.h"
 
+#include "lib/getopt.h"
+
 static char fio_version_string[] = "fio 1.42";
 
 #define FIO_RANDSEED		(0xb1899bedUL)
diff --git a/lib/getopt.h b/lib/getopt.h
new file mode 100644
index 0000000..56fe3bf
--- /dev/null
+++ b/lib/getopt.h
@@ -0,0 +1,26 @@
+#ifndef _AIX
+
+#include <getopt.h>
+
+#else /* _AIX */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+struct option {
+	const char *name;
+	int has_arg;
+	int *flag;
+	int val;
+};
+
+enum {
+	no_argument	  = 0,
+	required_argument = 1,
+	optional_argument = 2,
+};
+
+int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
+
+#endif /* _GETOPT_H */
+#endif /* _AIX */
diff --git a/lib/getopt_long.c b/lib/getopt_long.c
new file mode 100644
index 0000000..3d80627
--- /dev/null
+++ b/lib/getopt_long.c
@@ -0,0 +1,152 @@
+/*
+ * getopt.c
+ *
+ * getopt_long(), or at least a common subset thereof:
+ *
+ * - Option reordering is not supported
+ * - -W foo is not supported
+ * - First optstring character "-" not supported.
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+char *optarg;
+int optind, opterr, optopt;
+static struct getopt_private_state {
+	const char *optptr;
+	const char *last_optstring;
+	char *const *last_argv;
+} pvt;
+
+static inline const char *option_matches(const char *arg_str,
+					 const char *opt_name)
+{
+	while (*arg_str != '\0' && *arg_str != '=') {
+		if (*arg_str++ != *opt_name++)
+			return NULL;
+	}
+
+	if (*opt_name)
+		return NULL;
+
+	return arg_str;
+}
+
+int getopt_long_only(int argc, char *const *argv, const char *optstring,
+		const struct option *longopts, int *longindex)
+{
+	const char *carg;
+	const char *osptr;
+	int opt;
+
+	/* getopt() relies on a number of different global state
+	   variables, which can make this really confusing if there is
+	   more than one use of getopt() in the same program.  This
+	   attempts to detect that situation by detecting if the
+	   "optstring" or "argv" argument have changed since last time
+	   we were called; if so, reinitialize the query state. */
+
+	if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
+	    optind < 1 || optind > argc) {
+		/* optind doesn't match the current query */
+		pvt.last_optstring = optstring;
+		pvt.last_argv = argv;
+		optind = 1;
+		pvt.optptr = NULL;
+	}
+
+	carg = argv[optind];
+
+	/* First, eliminate all non-option cases */
+
+	if (!carg || carg[0] != '-' || !carg[1])
+		return -1;
+
+	if (carg[1] == '-') {
+		const struct option *lo;
+		const char *opt_end = NULL;
+
+		optind++;
+
+		/* Either it's a long option, or it's -- */
+		if (!carg[2]) {
+			/* It's -- */
+			return -1;
+		}
+
+		for (lo = longopts; lo->name; lo++) {
+			if ((opt_end = option_matches(carg+2, lo->name)))
+			    break;
+		}
+		if (!opt_end)
+			return '?';
+
+		if (longindex)
+			*longindex = lo-longopts;
+
+		if (*opt_end == '=') {
+			if (lo->has_arg)
+				optarg = (char *)opt_end+1;
+			else
+				return '?';
+		} else if (lo->has_arg == 1) {
+			if (!(optarg = argv[optind]))
+				return '?';
+			optind++;
+		}
+
+		if (lo->flag) {
+			*lo->flag = lo->val;
+			return 0;
+		} else {
+			return lo->val;
+		}
+	}
+
+	if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
+		/* Someone frobbed optind, change to new opt. */
+		pvt.optptr = carg + 1;
+	}
+
+	opt = *pvt.optptr++;
+
+	if (opt != ':' && (osptr = strchr(optstring, opt))) {
+		if (osptr[1] == ':') {
+			if (*pvt.optptr) {
+				/* Argument-taking option with attached
+				   argument */
+				optarg = (char *)pvt.optptr;
+				optind++;
+			} else {
+				/* Argument-taking option with non-attached
+				   argument */
+				if (argv[optind + 1]) {
+					optarg = (char *)argv[optind+1];
+					optind += 2;
+				} else {
+					/* Missing argument */
+					optind++;
+					return (optstring[0] == ':')
+						? ':' : '?';
+				}
+			}
+			return opt;
+		} else {
+			/* Non-argument-taking option */
+			/* pvt.optptr will remember the exact position to
+			   resume at */
+			if (!*pvt.optptr)
+				optind++;
+			return opt;
+		}
+	} else {
+		/* Unknown option */
+		optopt = opt;
+		if (!*pvt.optptr)
+			optind++;
+		return '?';
+	}
+}
diff --git a/options.c b/options.c
index 6dd74ed..7a9d5d3 100644
--- a/options.c
+++ b/options.c
@@ -3,7 +3,6 @@
 #include <unistd.h>
 #include <ctype.h>
 #include <string.h>
-#include <getopt.h>
 #include <assert.h>
 #include <libgen.h>
 #include <fcntl.h>
diff --git a/os/os-aix.h b/os/os-aix.h
new file mode 100644
index 0000000..1cc35e8
--- /dev/null
+++ b/os/os-aix.h
@@ -0,0 +1,46 @@
+#ifndef FIO_OS_AIX_H
+#define FIO_OS_AIX_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/devinfo.h>
+#include <sys/ioctl.h>
+
+#define FIO_HAVE_POSIXAIO
+#define FIO_HAVE_FALLOCATE
+#define FIO_HAVE_ODIRECT
+#define FIO_USE_GENERIC_RAND
+
+#define FIO_HAVE_PSHARED_MUTEX
+
+#define OS_MAP_ANON		MAP_ANON
+
+static inline int blockdev_invalidate_cache(int fd)
+{
+	return EINVAL;
+}
+
+static inline int blockdev_size(int fd, unsigned long long *bytes)
+{
+	struct devinfo info;
+
+	if (!ioctl(fd, IOCINFO, &info)) {
+        	*bytes = (unsigned long long)info.un.scdk.numblks *
+				info.un.scdk.blksize;
+		return 0;
+	}
+
+	return errno;
+}
+
+static inline unsigned long long os_phys_mem(void)
+{
+	long mem = sysconf(_SC_AIX_REALMEM);
+
+	if (mem == -1)
+		return 0;
+
+	return (unsigned long long) mem * 1024;
+}
+
+#endif
diff --git a/os/os.h b/os/os.h
index 218766a..e2bb0ba 100644
--- a/os/os.h
+++ b/os/os.h
@@ -14,6 +14,8 @@
 #include "os-solaris.h"
 #elif defined(__APPLE__)
 #include "os-mac.h"
+#elif defined(_AIX)
+#include "os-aix.h"
 #else
 #error "unsupported os"
 #endif