diff --git a/lustre/ChangeLog b/lustre/ChangeLog
index b7581af559a1c65e57c369616460912982f4824f..c10a3e1805fec3439f60343f108ca89f35a3dfea 100644
--- a/lustre/ChangeLog
+++ b/lustre/ChangeLog
@@ -1692,6 +1692,11 @@ Description: Replace RW_LOCK_UNLOCKED() macro with rwlock_init().
 Details    : Replace RW_LOCK_UNLOCKED() with rwlock_init() as the former
 	     doesn't work with lockdep.
 
+Severity   : normal
+Bugzilla   : 16450
+Description: Add rwv.c test program.
+Details    : New testing program exercising readv(2) and writev(2) (Qian).
+
 --------------------------------------------------------------------------------
 
 2007-08-10         Cluster File Systems, Inc. <info@clusterfs.com>
diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am
index 4f59ac43da45e06dded1a41e18534ad3452aa1a0..2a1568601fdf36fbf5bc9acee8dc6ec3d1721f0c 100644
--- a/lustre/tests/Makefile.am
+++ b/lustre/tests/Makefile.am
@@ -41,7 +41,7 @@ noinst_PROGRAMS += small_write multiop sleeptest ll_sparseness_verify cmknod
 noinst_PROGRAMS += ll_sparseness_write mrename ll_dirstripe_verify mkdirmany
 noinst_PROGRAMS += openfilleddirunlink rename_many memhog iopentest1 iopentest2
 noinst_PROGRAMS += mmap_sanity flock_test writemany reads flocks_test
-noinst_PROGRAMS += ll_getstripe_info write_time_limit
+noinst_PROGRAMS += ll_getstripe_info write_time_limit rwv
 if MPITESTS
 noinst_PROGRAMS += parallel_grouplock write_append_truncate createmany_mpi mdsrate
 endif
diff --git a/lustre/tests/rwv.c b/lustre/tests/rwv.c
new file mode 100644
index 0000000000000000000000000000000000000000..96856e92befb291679ea88bc1e51134bf641940a
--- /dev/null
+++ b/lustre/tests/rwv.c
@@ -0,0 +1,200 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <liblustre.h>
+#include <lnet/lnetctl.h>
+#include <obd.h>
+#include <lustre_lib.h>
+#include <obd_lov.h>
+#include <lustre/liblustreapi.h>
+
+#define ACT_NONE        0
+#define ACT_READ        1
+#define ACT_WRITE       2
+#define ACT_SEEK        4
+#define ACT_READHOLE    8
+#define ACT_VERIFY      16
+
+void usage()
+{
+	printf("usage: rwv -f filename <-r|-w> [-a] [-z] [-d] [-v]"
+                "[-s offset] -n iovcnt SIZE1 SIZE2 SIZE3...\n");
+        printf("-a  append IO (O_APPEND)\n");
+        printf("-r  file read (O_RDONLY)\n");
+        printf("-w  file write (O_WRONLY)\n");
+        printf("-s  set the start pos of the read/write test\n");
+        printf("-z  test for read hitting hole\n");
+        printf("-d  create flags (O_LOV_DELAY_CREATE)\n");
+        printf("-v  verify the data content of read\n");
+}
+
+int data_verify(struct iovec *iov, int iovcnt, char c)
+{
+        int i;
+
+        for (i = 0; i < iovcnt; i++) {
+                size_t count = iov[i].iov_len;
+                char *s = iov[i].iov_base;
+
+                for (; count > 0; ++s, count--) {
+                        if (*s != c) {
+                                printf("Data mismatch %x: %x\n", *s, c);
+                                return 1;
+                        }
+                }
+        }
+        return 0;
+}
+
+int main(int argc, char** argv)
+{
+        int c;
+        int fd;
+        int rc = 0;
+        int flags = 0;
+        int iovcnt = 0;
+        int act = ACT_NONE;
+        char pad = 0xba;
+        char *end;
+        char *fname = "FILE";
+        unsigned long len = 0;
+        struct iovec *iov;
+        off64_t offset = 0;
+
+        while ((c = getopt(argc, argv, "f:n:s:rwahvdz")) != -1) {
+                switch (c) {
+                case 'f':
+                        fname = optarg;
+                        break;
+                case 'n':
+                        iovcnt = strtoul(optarg, &end, 0);
+                        if (*end) {
+                                printf("Bad iov count: %s\n", optarg);
+                                return 1;
+                        }
+                        if (iovcnt > UIO_MAXIOV || iovcnt <= 0) {
+                                printf("Wrong iov count\n");
+                                return 1;
+                        }
+                        break;
+                case 's':
+                        act |= ACT_SEEK;
+                        offset = strtoull(optarg, &end, 0);
+                        if (*end) {
+                                printf("Bad seek offset: %s\n", optarg);
+                                return 1;
+                        }
+                        break;
+                case 'w':
+                        act |= ACT_WRITE;
+                        break;
+                case 'r':
+                        act |= ACT_READ;
+                        break;
+                case 'a':
+                        flags |= O_APPEND;
+                        break;
+                case 'd':
+                        flags |= O_LOV_DELAY_CREATE;
+                        break;
+                case 'z':
+                        pad = 0;
+                        act |= ACT_READHOLE;
+                        break;
+                case 'v':
+                        act |= ACT_VERIFY;
+                        break;
+                case 'h':
+                        usage();
+                        break;
+                }
+        }
+
+        if (act == ACT_NONE) {
+                usage();
+                return 1;
+        }
+
+        if ((act & ACT_READ) &&  (act & ACT_WRITE)) {
+                printf("Read and write test should be exclusive\n");
+                return 1;
+        }
+
+        if (argc - optind < iovcnt) {
+                printf("Not enough parameters for iov size\n");
+                return 1;
+        }
+
+        iov = (struct iovec *)malloc(iovcnt * sizeof(struct iovec));
+        if (iov == NULL) {
+                printf("No memory %s\n", strerror(errno));
+                return 1;
+        }
+
+        for (c = 0; c < iovcnt; c++) {
+                struct iovec *iv = &iov[c];
+
+                iv->iov_len = strtoul(argv[optind++], &end, 0);
+                if (*end) {
+                        printf("Error iov size\n");
+                        GOTO(out, rc = 1);
+                }
+                iv->iov_base = mmap(NULL, iv->iov_len, PROT_READ | PROT_WRITE,
+                                    MAP_PRIVATE | MAP_ANON, 0, 0);
+                if (iv->iov_base == MAP_FAILED) {
+                        printf("No memory %s\n", strerror(errno));
+                        GOTO(out, rc = 1);
+                }
+                if (act & ACT_WRITE)
+                        memset(iv->iov_base, pad, iv->iov_len);
+                len += iv->iov_len;
+        }
+
+        fd = open(fname, O_LARGEFILE | O_RDWR | O_CREAT | flags, 0644);
+        if (fd == -1) {
+                printf("Cannot open %s:%s\n", fname, strerror(errno));
+                return 1;
+        }
+
+        if ((act & ACT_SEEK) && (lseek64(fd, offset, SEEK_SET) < 0)) {
+                printf("Cannot seek %s\n", strerror(errno));
+                GOTO(out, rc == 1);
+        }
+
+        if (act & ACT_WRITE) {
+                rc = writev(fd, iov, iovcnt);
+                if (rc != len) {
+                        printf("Write error: %s (rc = %d, len = %ld)\n",
+                                strerror(errno), rc, len);
+                        GOTO(out, rc = 1);
+                }
+        } else if (act & ACT_READ) {
+                rc = readv(fd, iov, iovcnt);
+                if (rc != len) {
+                        printf("Read error: %s rc = %d\n", strerror(errno), rc);
+                        GOTO(out, rc = 1);
+                }
+
+                /* It should return zeroed buf if the read hits hole.*/
+                if (((act & ACT_READHOLE) || (act & ACT_VERIFY)) &&
+                    data_verify(iov, iovcnt, pad))
+                        GOTO(out, rc = 1);
+        }
+
+        rc = 0;
+out:
+        if (iov)
+                free(iov);
+	return rc;
+}