Whamcloud - gitweb
LU-6260 llite: add support for new iter functionality 28/15028/10
authorJames Simmons <uja.ornl@yahoo.com>
Tue, 21 Jul 2015 19:32:34 +0000 (15:32 -0400)
committerOleg Drokin <oleg.drokin@intel.com>
Thu, 30 Jul 2015 03:23:02 +0000 (03:23 +0000)
For the 3.16+ kernels struct file_operations added new read
and write methods; read_iter and write_iter; to use struct
iov_iter as a parameter since it contains all the iovec
data needed. This avoid having the file system managing
iovec data like transversing the iovec page list. Now we
can use supplied iov_iter helper functions that does this
work for us. In later kernels only this back end is
supported. Backported from the upstream lustre client.

-----------------------------------------------------------------------
Linux-commit: b42b15fdad3ebb790250041d1517acebb9bd56d9

lustre: get rid of messing with iovecs

* switch to ->read_iter/->write_iter
* keep a pointer to iov_iter instead of iov/nr_segs
* do not modify iovecs; use iov_iter_truncate()/iov_iter_advance() and
  a new primitive - iov_iter_reexpand() (expand previously truncated
  iterator) istead.
* (racy) check for lustre VMAs intersecting with iovecs kept for now as
  for_each_iov() loop.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-----------------------------------------------------------------------
Signed-off-by: James Simmons <uja.ornl@yahoo.com>
Change-Id: I71928beaa7b1c87f3e2c689e1dee96052eaef872
Reviewed-on: http://review.whamcloud.com/15028
Tested-by: Jenkins
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Dmitry Eremin <dmitry.eremin@intel.com>
Reviewed-by: John L. Hammond <john.hammond@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/autoconf/lustre-core.m4
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/llite/vvp_internal.h
lustre/llite/vvp_io.c

index f553668..b35cdc5 100644 (file)
@@ -1656,6 +1656,50 @@ direct_io_iter, [
 ]) # LC_DIRECTIO_USE_ITER
 
 #
+# LC_HAVE_IOV_ITER_INIT_DIRECTION
+#
+#
+# 3.16 linux commit 71d8e532b1549a478e6a6a8a44f309d050294d00
+#      changed iov_iter_init api to start accepting a tag
+#      that defines if its a read or write operation
+#
+AC_DEFUN([LC_HAVE_IOV_ITER_INIT_DIRECTION], [
+tmp_flags="$EXTRA_KCFLAGS"
+EXTRA_KCFLAGS="-Werror"
+LB_CHECK_COMPILE([if 'iov_iter_init' takes a tag],
+iter_init, [
+       #include <linux/uio.h>
+       #include <linux/fs.h>
+],[
+       const struct iovec *iov = NULL;
+
+       iov_iter_init(NULL, READ, iov, 1, 0);
+],[
+       AC_DEFINE(HAVE_IOV_ITER_INIT_DIRECTION, 1,
+               [iov_iter_init handles directional tag])
+])
+EXTRA_KCFLAGS="$tmp_flags"
+]) # LC_HAVE_IOV_ITER_INIT_DIRECTION
+
+#
+# LC_HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+#
+# 3.16 introduces [read|write]_iter to struct file_operations
+#
+AC_DEFUN([LC_HAVE_FILE_OPERATIONS_READ_WRITE_ITER], [
+LB_CHECK_COMPILE([if 'file_operations.[read|write]_iter' exist],
+file_function_iter, [
+       #include <linux/fs.h>
+],[
+       ((struct file_operations *)NULL)->read_iter(NULL, NULL);
+       ((struct file_operations *)NULL)->write_iter(NULL, NULL);
+],[
+       AC_DEFINE(HAVE_FILE_OPERATIONS_READ_WRITE_ITER, 1,
+               [file_operations.[read|write]_iter functions exist])
+])
+]) # LC_HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+
+#
 # LC_NFS_FILLDIR_USE_CTX
 #
 # 3.18 kernel moved from void cookie to struct dir_context
@@ -1872,6 +1916,8 @@ AC_DEFUN([LC_PROG_LINUX], [
 
        # 3.16
        LC_DIRECTIO_USE_ITER
+       LC_HAVE_IOV_ITER_INIT_DIRECTION
+       LC_HAVE_FILE_OPERATIONS_READ_WRITE_ITER
 
        # 3.18
        LC_NFS_FILLDIR_USE_CTX
index 97f0754..b10ef2e 100644 (file)
@@ -1080,9 +1080,10 @@ restart:
 
                switch (vio->vui_io_subtype) {
                case IO_NORMAL:
-                       vio->vui_iov = args->u.normal.via_iov;
-                       vio->vui_nrsegs = args->u.normal.via_nrsegs;
-                       vio->vui_tot_nrsegs = vio->vui_nrsegs;
+                       vio->vui_iter = args->u.normal.via_iter;
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+                       vio->vui_tot_nrsegs = vio->vui_iter->nr_segs;
+#endif /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
                        vio->vui_iocb = args->u.normal.via_iocb;
                        /* Direct IO reads must also take range lock,
                         * or multiple reads will try to work on the same pages
@@ -1129,8 +1130,10 @@ restart:
 
                /* prepare IO restart */
                if (count > 0 && args->via_io_subtype == IO_NORMAL) {
-                       args->u.normal.via_iov = vio->vui_iov;
-                       args->u.normal.via_nrsegs = vio->vui_tot_nrsegs;
+                       args->u.normal.via_iter = vio->vui_iter;
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+                       args->u.normal.via_iter->nr_segs = vio->vui_tot_nrsegs;
+#endif /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
                }
        }
        GOTO(out, rc);
@@ -1166,78 +1169,133 @@ out:
 }
 
 /*
+ * Read from a file (through the page cache).
+ */
+static ssize_t ll_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       struct vvp_io_args *args;
+       struct lu_env *env;
+       ssize_t result;
+       int refcheck;
+
+       env = cl_env_get(&refcheck);
+       if (IS_ERR(env))
+               return PTR_ERR(env);
+
+       args = ll_env_args(env, IO_NORMAL);
+       args->u.normal.via_iter = to;
+       args->u.normal.via_iocb = iocb;
+
+       result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_READ,
+                                   &iocb->ki_pos, iov_iter_count(to));
+       cl_env_put(env, &refcheck);
+       return result;
+}
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t ll_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+       struct vvp_io_args *args;
+       struct lu_env *env;
+       ssize_t result;
+       int refcheck;
+
+       env = cl_env_get(&refcheck);
+       if (IS_ERR(env))
+               return PTR_ERR(env);
+
+       args = ll_env_args(env, IO_NORMAL);
+       args->u.normal.via_iter = from;
+       args->u.normal.via_iocb = iocb;
+
+       result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_WRITE,
+                                   &iocb->ki_pos, iov_iter_count(from));
+       cl_env_put(env, &refcheck);
+       return result;
+}
+
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+/*
  * XXX: exact copy from kernel code (__generic_file_aio_write_nolock)
  */
 static int ll_file_get_iov_count(const struct iovec *iov,
-                                 unsigned long *nr_segs, size_t *count)
+                                unsigned long *nr_segs, size_t *count)
 {
-        size_t cnt = 0;
-        unsigned long seg;
+       size_t cnt = 0;
+       unsigned long seg;
 
-        for (seg = 0; seg < *nr_segs; seg++) {
-                const struct iovec *iv = &iov[seg];
+       for (seg = 0; seg < *nr_segs; seg++) {
+               const struct iovec *iv = &iov[seg];
 
-                /*
-                 * If any segment has a negative length, or the cumulative
-                 * length ever wraps negative then return -EINVAL.
-                 */
-                cnt += iv->iov_len;
-                if (unlikely((ssize_t)(cnt|iv->iov_len) < 0))
-                        return -EINVAL;
-                if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
-                        continue;
-                if (seg == 0)
-                        return -EFAULT;
-                *nr_segs = seg;
-                cnt -= iv->iov_len;   /* This segment is no good */
-                break;
-        }
-        *count = cnt;
-        return 0;
+               /*
+                * If any segment has a negative length, or the cumulative
+                * length ever wraps negative then return -EINVAL.
+                */
+               cnt += iv->iov_len;
+               if (unlikely((ssize_t)(cnt|iv->iov_len) < 0))
+                       return -EINVAL;
+               if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
+                       continue;
+               if (seg == 0)
+                       return -EFAULT;
+               *nr_segs = seg;
+               cnt -= iv->iov_len;     /* This segment is no good */
+               break;
+       }
+       *count = cnt;
+       return 0;
 }
 
 static ssize_t ll_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
                                unsigned long nr_segs, loff_t pos)
 {
-       struct lu_env      *env;
-       struct vvp_io_args *args;
-       struct iovec       *local_iov;
-        size_t              count;
-        ssize_t             result;
-        int                 refcheck;
-        ENTRY;
+       struct iovec *local_iov;
+       struct iov_iter *to;
+       size_t iov_count;
+       ssize_t result;
+       ENTRY;
 
-        result = ll_file_get_iov_count(iov, &nr_segs, &count);
-        if (result)
-                RETURN(result);
-
-        env = cl_env_get(&refcheck);
-        if (IS_ERR(env))
-                RETURN(PTR_ERR(env));
+       result = ll_file_get_iov_count(iov, &nr_segs, &iov_count);
+       if (result)
+               RETURN(result);
 
        if (nr_segs == 1) {
+               struct lu_env *env;
+               int refcheck;
+
+               env = cl_env_get(&refcheck);
+               if (IS_ERR(env))
+                       RETURN(PTR_ERR(env));
+
                local_iov = &ll_env_info(env)->lti_local_iov;
                *local_iov = *iov;
+
+               cl_env_put(env, &refcheck);
        } else {
                OBD_ALLOC(local_iov, sizeof(*iov) * nr_segs);
-               if (local_iov == NULL) {
-                       cl_env_put(env, &refcheck);
+               if (local_iov == NULL)
                        RETURN(-ENOMEM);
-               }
 
                memcpy(local_iov, iov, sizeof(*iov) * nr_segs);
        }
 
-       args = ll_env_args(env, IO_NORMAL);
-       args->u.normal.via_iov = local_iov;
-       args->u.normal.via_nrsegs = nr_segs;
-       args->u.normal.via_iocb = iocb;
-
-       result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_READ,
-                                   &iocb->ki_pos, count);
+       OBD_ALLOC_PTR(to);
+       if (to == NULL) {
+               result = -ENOMEM;
+               goto out;
+       }
+# ifdef HAVE_IOV_ITER_INIT_DIRECTION
+       iov_iter_init(to, READ, local_iov, nr_segs, iov_count);
+# else /* !HAVE_IOV_ITER_INIT_DIRECTION */
+       iov_iter_init(to, local_iov, nr_segs, iov_count, 0);
+# endif /* HAVE_IOV_ITER_INIT_DIRECTION */
 
-       cl_env_put(env, &refcheck);
+       result = ll_file_read_iter(iocb, to);
 
+       OBD_FREE_PTR(to);
+out:
        if (nr_segs > 1)
                OBD_FREE(local_iov, sizeof(*iov) * nr_segs);
 
@@ -1281,44 +1339,51 @@ static ssize_t ll_file_read(struct file *file, char __user *buf, size_t count,
 static ssize_t ll_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                                 unsigned long nr_segs, loff_t pos)
 {
-       struct lu_env      *env;
-       struct vvp_io_args *args;
-       struct iovec       *local_iov;
-        size_t              count;
-        ssize_t             result;
-        int                 refcheck;
-        ENTRY;
-
-        result = ll_file_get_iov_count(iov, &nr_segs, &count);
-        if (result)
-                RETURN(result);
+       struct iovec *local_iov;
+       struct iov_iter *from;
+       size_t iov_count;
+       ssize_t result;
+       ENTRY;
 
-        env = cl_env_get(&refcheck);
-        if (IS_ERR(env))
-                RETURN(PTR_ERR(env));
+       result = ll_file_get_iov_count(iov, &nr_segs, &iov_count);
+       if (result)
+               RETURN(result);
 
        if (nr_segs == 1) {
+               struct lu_env *env;
+               int refcheck;
+
+               env = cl_env_get(&refcheck);
+               if (IS_ERR(env))
+                       RETURN(PTR_ERR(env));
+
                local_iov = &ll_env_info(env)->lti_local_iov;
                *local_iov = *iov;
+
+               cl_env_put(env, &refcheck);
        } else {
                OBD_ALLOC(local_iov, sizeof(*iov) * nr_segs);
-               if (local_iov == NULL) {
-                       cl_env_put(env, &refcheck);
+               if (local_iov == NULL)
                        RETURN(-ENOMEM);
-               }
 
                memcpy(local_iov, iov, sizeof(*iov) * nr_segs);
        }
 
-       args = ll_env_args(env, IO_NORMAL);
-       args->u.normal.via_iov = local_iov;
-       args->u.normal.via_nrsegs = nr_segs;
-       args->u.normal.via_iocb = iocb;
+       OBD_ALLOC_PTR(from);
+       if (from == NULL) {
+               result = -ENOMEM;
+               goto out;
+       }
+# ifdef HAVE_IOV_ITER_INIT_DIRECTION
+       iov_iter_init(from, WRITE, local_iov, nr_segs, iov_count);
+# else /* !HAVE_IOV_ITER_INIT_DIRECTION */
+       iov_iter_init(from, local_iov, nr_segs, iov_count, 0);
+# endif /* HAVE_IOV_ITER_INIT_DIRECTION */
 
-       result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_WRITE,
-                                 &iocb->ki_pos, count);
-       cl_env_put(env, &refcheck);
+       result = ll_file_write_iter(iocb, from);
 
+       OBD_FREE_PTR(from);
+out:
        if (nr_segs > 1)
                OBD_FREE(local_iov, sizeof(*iov) * nr_segs);
 
@@ -1355,6 +1420,7 @@ static ssize_t ll_file_write(struct file *file, const char __user *buf,
        cl_env_put(env, &refcheck);
        RETURN(result);
 }
+#endif /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
 
 /*
  * Send file content (through pagecache) somewhere with helper
@@ -3393,53 +3459,74 @@ int ll_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
 
 /* -o localflock - only provides locally consistent flock locks */
 struct file_operations ll_file_operations = {
-        .read           = ll_file_read,
-       .aio_read    = ll_file_aio_read,
-        .write          = ll_file_write,
-       .aio_write   = ll_file_aio_write,
-        .unlocked_ioctl = ll_file_ioctl,
-        .open           = ll_file_open,
-        .release        = ll_file_release,
-        .mmap           = ll_file_mmap,
-        .llseek         = ll_file_seek,
-        .splice_read    = ll_file_splice_read,
-        .fsync          = ll_fsync,
-        .flush          = ll_flush
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       .read           = new_sync_read,
+       .read_iter      = ll_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = ll_file_write_iter,
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .read           = ll_file_read,
+       .aio_read       = ll_file_aio_read,
+       .write          = ll_file_write,
+       .aio_write      = ll_file_aio_write,
+#endif /* HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .unlocked_ioctl = ll_file_ioctl,
+       .open           = ll_file_open,
+       .release        = ll_file_release,
+       .mmap           = ll_file_mmap,
+       .llseek         = ll_file_seek,
+       .splice_read    = ll_file_splice_read,
+       .fsync          = ll_fsync,
+       .flush          = ll_flush
 };
 
 struct file_operations ll_file_operations_flock = {
-        .read           = ll_file_read,
-       .aio_read    = ll_file_aio_read,
-        .write          = ll_file_write,
-       .aio_write   = ll_file_aio_write,
-        .unlocked_ioctl = ll_file_ioctl,
-        .open           = ll_file_open,
-        .release        = ll_file_release,
-        .mmap           = ll_file_mmap,
-        .llseek         = ll_file_seek,
-        .splice_read    = ll_file_splice_read,
-        .fsync          = ll_fsync,
-        .flush          = ll_flush,
-        .flock          = ll_file_flock,
-        .lock           = ll_file_flock
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       .read           = new_sync_read,
+       .read_iter      = ll_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = ll_file_write_iter,
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .read           = ll_file_read,
+       .aio_read       = ll_file_aio_read,
+       .write          = ll_file_write,
+       .aio_write      = ll_file_aio_write,
+#endif /* HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .unlocked_ioctl = ll_file_ioctl,
+       .open           = ll_file_open,
+       .release        = ll_file_release,
+       .mmap           = ll_file_mmap,
+       .llseek         = ll_file_seek,
+       .splice_read    = ll_file_splice_read,
+       .fsync          = ll_fsync,
+       .flush          = ll_flush,
+       .flock          = ll_file_flock,
+       .lock           = ll_file_flock
 };
 
 /* These are for -o noflock - to return ENOSYS on flock calls */
 struct file_operations ll_file_operations_noflock = {
-        .read           = ll_file_read,
-       .aio_read    = ll_file_aio_read,
-        .write          = ll_file_write,
-       .aio_write   = ll_file_aio_write,
-        .unlocked_ioctl = ll_file_ioctl,
-        .open           = ll_file_open,
-        .release        = ll_file_release,
-        .mmap           = ll_file_mmap,
-        .llseek         = ll_file_seek,
-        .splice_read    = ll_file_splice_read,
-        .fsync          = ll_fsync,
-        .flush          = ll_flush,
-        .flock          = ll_file_noflock,
-        .lock           = ll_file_noflock
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       .read           = new_sync_read,
+       .read_iter      = ll_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = ll_file_write_iter,
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .read           = ll_file_read,
+       .aio_read       = ll_file_aio_read,
+       .write          = ll_file_write,
+       .aio_write      = ll_file_aio_write,
+#endif /* HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       .unlocked_ioctl = ll_file_ioctl,
+       .open           = ll_file_open,
+       .release        = ll_file_release,
+       .mmap           = ll_file_mmap,
+       .llseek         = ll_file_seek,
+       .splice_read    = ll_file_splice_read,
+       .fsync          = ll_fsync,
+       .flush          = ll_flush,
+       .flock          = ll_file_noflock,
+       .lock           = ll_file_noflock
 };
 
 struct inode_operations ll_file_inode_operations = {
index c393a38..c37c102 100644 (file)
@@ -987,8 +987,7 @@ struct vvp_io_args {
         union {
                 struct {
                         struct kiocb      *via_iocb;
-                        struct iovec      *via_iov;
-                        unsigned long      via_nrsegs;
+                       struct iov_iter   *via_iter;
                 } normal;
                 struct {
                         struct pipe_inode_info  *via_pipe;
index d92dd1f..31aff08 100644 (file)
@@ -68,8 +68,8 @@ struct vvp_io {
        /**
         * I/O vector information to or from which read/write is going.
         */
-       struct iovec *vui_iov;
-       unsigned long vui_nrsegs;
+       struct iov_iter *vui_iter;
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
        /**
         * Total iov count for left IO.
         */
@@ -78,6 +78,7 @@ struct vvp_io {
         * Old length for iov that was truncated partially.
         */
        size_t vui_iov_olen;
+#endif /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
        /**
         * Total size for the left IO.
         */
index 9871e80..02afd2a 100644 (file)
@@ -393,29 +393,37 @@ static int vvp_mmap_locks(const struct lu_env *env,
         struct vm_area_struct  *vma;
        struct cl_lock_descr   *descr = &vti->vti_descr;
         ldlm_policy_data_t      policy;
-        unsigned long           addr;
-        unsigned long           seg;
-        ssize_t                 count;
-       int                     result = 0;
-        ENTRY;
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       struct iovec iov;
+       struct iov_iter i;
+#else
+       unsigned long seg;
+#endif
+       int result = 0;
+       ENTRY;
 
         LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
 
         if (!cl_is_normalio(env, io))
                 RETURN(0);
 
-       if (vio->vui_iov == NULL) /* nfs or loop back device write */
+       /* nfs or loop back device write */
+       if (vio->vui_iter == NULL)
                RETURN(0);
 
        /* No MM (e.g. NFS)? No vmas too. */
        if (mm == NULL)
                RETURN(0);
 
-       for (seg = 0; seg < vio->vui_nrsegs; seg++) {
-               const struct iovec *iv = &vio->vui_iov[seg];
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       iov_for_each(iov, i, *(vio->vui_iter)) {
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       for (seg = 0; seg < vio->vui_iter->nr_segs; seg++) {
+               const struct iovec iov = vio->vui_iter->iov[seg];
+#endif /* HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+               unsigned long addr = (unsigned long)iov.iov_base;
+               size_t count = iov.iov_len;
 
-                addr = (unsigned long)iv->iov_base;
-                count = iv->iov_len;
                 if (count == 0)
                         continue;
 
@@ -478,24 +486,39 @@ static void vvp_io_advance(const struct lu_env *env,
        struct vvp_io    *vio = cl2vvp_io(env, ios);
        struct cl_io     *io  = ios->cis_io;
        struct cl_object *obj = ios->cis_io->ci_obj;
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
        struct iovec     *iov;
-
+#endif
        CLOBINVRNT(env, obj, vvp_object_invariant(obj));
 
        if (!cl_is_normalio(env, io))
                return;
 
-       LASSERT(vio->vui_tot_nrsegs >= vio->vui_nrsegs);
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       vio->vui_tot_count -= nob;
+       iov_iter_reexpand(vio->vui_iter, vio->vui_tot_count);
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       LASSERT(vio->vui_tot_nrsegs >= vio->vui_iter->nr_segs);
        LASSERT(vio->vui_tot_count  >= nob);
 
        /* Restore the iov changed in vvp_io_update_iov() */
        if (vio->vui_iov_olen > 0) {
-               vio->vui_iov[vio->vui_nrsegs - 1].iov_len = vio->vui_iov_olen;
+               unsigned long idx = vio->vui_iter->nr_segs - 1;
+
+               /* In the latest kernels iov is const so that
+                * changes are done using iter helpers. In older
+                * kernels those helpers don't exist we lustre
+                * has to do some of the management of the iter
+                * itself. */
+               iov = (struct iovec *)&vio->vui_iter->iov[idx];
+               iov->iov_len = vio->vui_iov_olen;
                vio->vui_iov_olen = 0;
        }
 
-       /* advance iov */
-       iov = vio->vui_iov;
+       /* In the latest kernels special helpers exist to help
+        * advance the iov but we don't have that in older kernels
+        * so we need to do the book keeping ourselves. */
+       iov = (struct iovec *)vio->vui_iter->iov;
        while (nob > 0) {
                if (iov->iov_len > nob) {
                        iov->iov_len -= nob;
@@ -508,22 +531,29 @@ static void vvp_io_advance(const struct lu_env *env,
                vio->vui_tot_nrsegs--;
        }
 
-       vio->vui_iov = iov;
+       vio->vui_iter->iov = iov;
        vio->vui_tot_count -= nob;
+#endif /* HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
 }
 
 static void vvp_io_update_iov(const struct lu_env *env,
                              struct vvp_io *vio, struct cl_io *io)
 {
-       int i;
        size_t size = io->u.ci_rw.crw_count;
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+       if (!cl_is_normalio(env, io) || vio->vui_iter == NULL)
+               return;
+
+       iov_iter_truncate(vio->vui_iter, size);
+#else /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
+       unsigned long i;
 
        vio->vui_iov_olen = 0;
        if (!cl_is_normalio(env, io) || vio->vui_tot_nrsegs == 0)
                return;
 
        for (i = 0; i < vio->vui_tot_nrsegs; i++) {
-               struct iovec *iv = &vio->vui_iov[i];
+               struct iovec *iv = (struct iovec *) &vio->vui_iter->iov[i];
 
                if (iv->iov_len < size) {
                        size -= iv->iov_len;
@@ -536,10 +566,11 @@ static void vvp_io_update_iov(const struct lu_env *env,
                }
        }
 
-       vio->vui_nrsegs = i + 1;
-       LASSERTF(vio->vui_tot_nrsegs >= vio->vui_nrsegs,
+       vio->vui_iter->nr_segs = i + 1;
+       LASSERTF(vio->vui_tot_nrsegs >= vio->vui_iter->nr_segs,
                 "tot_nrsegs: %lu, nrsegs: %lu\n",
-                vio->vui_tot_nrsegs, vio->vui_nrsegs);
+                vio->vui_tot_nrsegs, vio->vui_iter->nr_segs);
+#endif /* !HAVE_FILE_OPERATIONS_READ_WRITE_ITER */
 }
 
 static int vvp_io_rw_lock(const struct lu_env *env, struct cl_io *io,
@@ -799,9 +830,14 @@ static int vvp_io_read_start(const struct lu_env *env,
        switch (vio->vui_io_subtype) {
        case IO_NORMAL:
                LASSERT(vio->vui_iocb->ki_pos == pos);
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+               result = generic_file_read_iter(vio->vui_iocb, vio->vui_iter);
+#else
                result = generic_file_aio_read(vio->vui_iocb,
-                                              vio->vui_iov, vio->vui_nrsegs,
+                                              vio->vui_iter->iov,
+                                              vio->vui_iter->nr_segs,
                                               vio->vui_iocb->ki_pos);
+#endif
                break;
        case IO_SPLICE:
                result = generic_file_splice_read(file, &pos,
@@ -1054,7 +1090,7 @@ static int vvp_io_write_start(const struct lu_env *env,
                RETURN(-EFBIG);
        }
 
-       if (vio->vui_iov == NULL) {
+       if (vio->vui_iter == NULL) {
                /* from a temp io in ll_cl_init(). */
                result = 0;
        } else {
@@ -1066,9 +1102,14 @@ static int vvp_io_write_start(const struct lu_env *env,
                 * consistency, proper locking to protect against writes,
                 * trucates, etc. is handled in the higher layers of lustre.
                 */
+#ifdef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
+               result = generic_file_write_iter(vio->vui_iocb, vio->vui_iter);
+#else
                result = __generic_file_aio_write(vio->vui_iocb,
-                                                 vio->vui_iov, vio->vui_nrsegs,
+                                                 vio->vui_iter->iov,
+                                                 vio->vui_iter->nr_segs,
                                                  &vio->vui_iocb->ki_pos);
+#endif
                if (result > 0 || result == -EIOCBQUEUED) {
                        ssize_t err;
 
@@ -1437,7 +1478,9 @@ int vvp_io_init(const struct lu_env *env, struct cl_object *obj,
                         result = 1;
                else {
                        vio->vui_tot_count = count;
+#ifndef HAVE_FILE_OPERATIONS_READ_WRITE_ITER
                        vio->vui_tot_nrsegs = 0;
+#endif
                }
 
                /* for read/write, we store the jobid in the inode, and