Skip to content

Commit

Permalink
Linux 4.14 compat: vfs_read & vfs_write
Browse files Browse the repository at this point in the history
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 <pritam@pritambaral.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes openzfs#656 
Closes openzfs#667
  • Loading branch information
behlendorf committed Nov 16, 2017
1 parent 35a44fc commit ed19bcc
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 17 deletions.
60 changes: 60 additions & 0 deletions config/spl-build.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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], [
Expand Down Expand Up @@ -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"
])
41 changes: 41 additions & 0 deletions include/linux/file_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 4 additions & 17 deletions module/spl/spl-vnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit ed19bcc

Please sign in to comment.