diff options
author | Brian Behlendorf <[email protected]> | 2017-11-15 17:19:23 -0800 |
---|---|---|
committer | GitHub <[email protected]> | 2017-11-15 17:19:23 -0800 |
commit | ed19bccfb651843fa208232b3a2d3d22a4152bc8 (patch) | |
tree | acfc58ea9f9a8d996cc566e7b43b4c01b2abea5c | |
parent | 35a44fcb8d6e346f51be82dfe57562c2ea0c6a9c (diff) |
Linux 4.14 compat: vfs_read & vfs_write
The kernel_read & kernel_write functions have always wrapped the
vfs_read & vfs_write functions respectively. However, they could
not be used by vn_rdwr() since the offset wasn't passed as a
pointer. This prevented us from being able to properly update
the file offset.
Linux 4.14 unexported vfs_read & vfs_write but also changed the
signature of kernel_read & kernel_write to provide the needed
functionality. Use these updated functions when available.
Reviewed-by: Pritam Baral <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #656
Closes #667
-rw-r--r-- | config/spl-build.m4 | 60 | ||||
-rw-r--r-- | include/linux/file_compat.h | 41 | ||||
-rw-r--r-- | module/spl/spl-vnode.c | 21 |
3 files changed, 105 insertions, 17 deletions
diff --git a/config/spl-build.m4 b/config/spl-build.m4 index 8e9dc99ff..7b66f2c86 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -52,6 +52,8 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_KMEM_CACHE_CREATE_USERCOPY SPL_AC_WAIT_QUEUE_ENTRY_T SPL_AC_WAIT_QUEUE_HEAD_ENTRY + SPL_AC_KERNEL_WRITE + SPL_AC_KERNEL_READ ]) AC_DEFUN([SPL_AC_MODULE_SYMVERS], [ @@ -1594,3 +1596,61 @@ AC_DEFUN([SPL_AC_WAIT_QUEUE_HEAD_ENTRY], [ AC_MSG_RESULT(no) ]) ]) + +dnl # +dnl # 4.14 API change +dnl # kernel_write() which was introduced in 3.9 was updated to take +dnl # the offset as a pointer which is needed by vn_rdwr(). +dnl # +AC_DEFUN([SPL_AC_KERNEL_WRITE], [ + AC_MSG_CHECKING([whether kernel_write() takes loff_t pointer]) + tmp_flags="$EXTRA_KCFLAGS" + EXTRA_KCFLAGS="-Werror" + SPL_LINUX_TRY_COMPILE([ + #include <linux/fs.h> + ],[ + struct file *file = NULL; + const void *buf = NULL; + size_t count = 0; + loff_t *pos = NULL; + ssize_t ret; + + ret = kernel_write(file, buf, count, pos); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_WRITE_PPOS, 1, + [kernel_write() take loff_t pointer]) + ],[ + AC_MSG_RESULT(no) + ]) + EXTRA_KCFLAGS="$tmp_flags" +]) + +dnl # +dnl # 4.14 API change +dnl # kernel_read() which has existed for forever was updated to take +dnl # the offset as a pointer which is needed by vn_rdwr(). +dnl # +AC_DEFUN([SPL_AC_KERNEL_READ], [ + AC_MSG_CHECKING([whether kernel_read() takes loff_t pointer]) + tmp_flags="$EXTRA_KCFLAGS" + EXTRA_KCFLAGS="-Werror" + SPL_LINUX_TRY_COMPILE([ + #include <linux/fs.h> + ],[ + struct file *file = NULL; + void *buf = NULL; + size_t count = 0; + loff_t *pos = NULL; + ssize_t ret; + + ret = kernel_read(file, buf, count, pos); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_READ_PPOS, 1, + [kernel_read() take loff_t pointer]) + ],[ + AC_MSG_RESULT(no) + ]) + EXTRA_KCFLAGS="$tmp_flags" +]) diff --git a/include/linux/file_compat.h b/include/linux/file_compat.h index 7d61ba5f9..55ba2cc76 100644 --- a/include/linux/file_compat.h +++ b/include/linux/file_compat.h @@ -26,6 +26,7 @@ #define _SPL_FILE_COMPAT_H #include <linux/fs.h> +#include <linux/uaccess.h> #ifdef HAVE_FDTABLE_HEADER #include <linux/fdtable.h> #endif @@ -70,6 +71,46 @@ spl_filp_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) return (error); } +static inline ssize_t +spl_kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos) +{ +#if defined(HAVE_KERNEL_WRITE_PPOS) + return (kernel_write(file, buf, count, pos)); +#else + mm_segment_t saved_fs; + ssize_t ret; + + saved_fs = get_fs(); + set_fs(get_ds()); + + ret = vfs_write(file, (__force const char __user *)buf, count, pos); + + set_fs(saved_fs); + + return (ret); +#endif +} + +static inline ssize_t +spl_kernel_read(struct file *file, void *buf, size_t count, loff_t *pos) +{ +#if defined(HAVE_KERNEL_READ_PPOS) + return (kernel_read(file, buf, count, pos)); +#else + mm_segment_t saved_fs; + ssize_t ret; + + saved_fs = get_fs(); + set_fs(get_ds()); + + ret = vfs_read(file, (void __user *)buf, count, pos); + + set_fs(saved_fs); + + return (ret); +#endif +} + #ifdef HAVE_2ARGS_VFS_FSYNC #define spl_filp_fsync(fp, sync) vfs_fsync(fp, sync) #else diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index 0e4c386a3..19b3b76cd 100644 --- a/module/spl/spl-vnode.c +++ b/module/spl/spl-vnode.c @@ -211,35 +211,22 @@ int vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off, uio_seg_t seg, int ioflag, rlim64_t x2, void *x3, ssize_t *residp) { - loff_t offset; - mm_segment_t saved_fs; - struct file *fp; + struct file *fp = vp->v_file; + loff_t offset = off; int rc; ASSERT(uio == UIO_WRITE || uio == UIO_READ); - ASSERT(vp); - ASSERT(vp->v_file); ASSERT(seg == UIO_SYSSPACE); ASSERT((ioflag & ~FAPPEND) == 0); - fp = vp->v_file; - - offset = off; if (ioflag & FAPPEND) offset = fp->f_pos; - /* Writable user data segment must be briefly increased for this - * process so we can use the user space read call paths to write - * in to memory allocated by the kernel. */ - saved_fs = get_fs(); - set_fs(get_ds()); - if (uio & UIO_WRITE) - rc = vfs_write(fp, addr, len, &offset); + rc = spl_kernel_write(fp, addr, len, &offset); else - rc = vfs_read(fp, addr, len, &offset); + rc = spl_kernel_read(fp, addr, len, &offset); - set_fs(saved_fs); fp->f_pos = offset; if (rc < 0) |