Linux 5.10 compat: use iov_iter in uio structure
As of the 5.10 kernel the generic splice compatibility code has been removed. All filesystems are now responsible for registering a ->splice_read and ->splice_write callback to support this operation. The good news is the VFS provided generic_file_splice_read() and iter_file_splice_write() callbacks can be used provided the ->iter_read and ->iter_write callback support pipes. However, this is currently not the case and only iovecs and bvecs (not pipes) are ever attached to the uio structure. This commit changes that by allowing full iov_iter structures to be attached to uios. Ever since the 4.9 kernel the iov_iter structure has supported iovecs, kvecs, bvevs, and pipes so it's desirable to pass the entire thing when possible. In conjunction with this the uio helper functions (i.e uiomove(), uiocopy(), etc) have been updated to understand the new UIO_ITER type. Note that using the kernel provided uio_iter interfaces allowed the existing Linux specific uio handling code to be simplified. When there's no longer a need to support kernel's older than 4.9, then it will be possible to remove the iovec and bvec members from the uio structure and always use a uio_iter. Until then we need to maintain all of the existing types for older kernels. Some additional refactoring and cleanup was included in this change: - Added checks to configure to detect available iov_iter interfaces. Some are available all the way back to the 3.10 kernel and are used when available. In particular, uio_prefaultpages() now always uses iov_iter_fault_in_readable() which is available for all supported kernels. - The unused UIO_USERISPACE type has been removed. It is no longer needed now that the uio_seg enum is platform specific. - Moved zfs_uio.c from the zcommon.ko module to the Linux specific platform code for the zfs.ko module. This gets it out of libzfs where it was never needed and keeps this Linux specific code out of the common sources. - Removed unnecessary O_APPEND handling from zfs_iter_write(), this is redundant and O_APPEND is already handled in zfs_write(); Reviewed-by: Colin Ian King <colin.king@canonical.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #11351
This commit is contained in:
parent
2844ad60d4
commit
1c2358c12a
|
@ -0,0 +1,206 @@
|
||||||
|
dnl #
|
||||||
|
dnl # Check for available iov_iter functionality.
|
||||||
|
dnl #
|
||||||
|
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_IOV_ITER], [
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_types], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
int type __attribute__ ((unused)) =
|
||||||
|
ITER_IOVEC | ITER_KVEC | ITER_BVEC | ITER_PIPE;
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_init], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
struct iovec iov;
|
||||||
|
unsigned long nr_segs = 1;
|
||||||
|
size_t count = 1024;
|
||||||
|
|
||||||
|
iov_iter_init(&iter, WRITE, &iov, nr_segs, count);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_init_legacy], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
struct iovec iov;
|
||||||
|
unsigned long nr_segs = 1;
|
||||||
|
size_t count = 1024;
|
||||||
|
size_t written = 0;
|
||||||
|
|
||||||
|
iov_iter_init(&iter, &iov, nr_segs, count, written);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_advance], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
size_t advance = 512;
|
||||||
|
|
||||||
|
iov_iter_advance(&iter, advance);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_revert], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
size_t revert = 512;
|
||||||
|
|
||||||
|
iov_iter_revert(&iter, revert);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_fault_in_readable], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
size_t size = 512;
|
||||||
|
int error __attribute__ ((unused));
|
||||||
|
|
||||||
|
error = iov_iter_fault_in_readable(&iter, size);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([iov_iter_count], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
size_t bytes __attribute__ ((unused));
|
||||||
|
|
||||||
|
bytes = iov_iter_count(&iter);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([copy_to_iter], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
char buf[512] = { 0 };
|
||||||
|
size_t size = 512;
|
||||||
|
size_t bytes __attribute__ ((unused));
|
||||||
|
|
||||||
|
bytes = copy_to_iter((const void *)&buf, size, &iter);
|
||||||
|
])
|
||||||
|
|
||||||
|
ZFS_LINUX_TEST_SRC([copy_from_iter], [
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/uio.h>
|
||||||
|
],[
|
||||||
|
struct iov_iter iter = { 0 };
|
||||||
|
char buf[512] = { 0 };
|
||||||
|
size_t size = 512;
|
||||||
|
size_t bytes __attribute__ ((unused));
|
||||||
|
|
||||||
|
bytes = copy_from_iter((void *)&buf, size, &iter);
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([ZFS_AC_KERNEL_VFS_IOV_ITER], [
|
||||||
|
enable_vfs_iov_iter="yes"
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether iov_iter types are available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_types], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_TYPES, 1,
|
||||||
|
[iov_iter types are available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl #
|
||||||
|
dnl # 'iov_iter_init' available in Linux 3.16 and newer.
|
||||||
|
dnl # 'iov_iter_init_legacy' available in Linux 3.15 and older.
|
||||||
|
dnl #
|
||||||
|
AC_MSG_CHECKING([whether iov_iter_init() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_init], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_INIT, 1,
|
||||||
|
[iov_iter_init() is available])
|
||||||
|
],[
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_init_legacy], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_INIT_LEGACY, 1,
|
||||||
|
[iov_iter_init() is available])
|
||||||
|
],[
|
||||||
|
ZFS_LINUX_TEST_ERROR([iov_iter_init()])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether iov_iter_advance() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_advance], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_ADVANCE, 1,
|
||||||
|
[iov_iter_advance() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether iov_iter_revert() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_revert], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_REVERT, 1,
|
||||||
|
[iov_iter_revert() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether iov_iter_fault_in_readable() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_fault_in_readable], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_FAULT_IN_READABLE, 1,
|
||||||
|
[iov_iter_fault_in_readable() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether iov_iter_count() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([iov_iter_count], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_IOV_ITER_COUNT, 1,
|
||||||
|
[iov_iter_count() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether copy_to_iter() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([copy_to_iter], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_COPY_TO_ITER, 1,
|
||||||
|
[copy_to_iter() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether copy_from_iter() is available])
|
||||||
|
ZFS_LINUX_TEST_RESULT([copy_from_iter], [
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE(HAVE_COPY_FROM_ITER, 1,
|
||||||
|
[copy_from_iter() is available])
|
||||||
|
],[
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
enable_vfs_iov_iter="no"
|
||||||
|
])
|
||||||
|
|
||||||
|
dnl #
|
||||||
|
dnl # As of the 4.9 kernel support is provided for iovecs, kvecs,
|
||||||
|
dnl # bvecs and pipes in the iov_iter structure. As long as the
|
||||||
|
dnl # other support interfaces are all available the iov_iter can
|
||||||
|
dnl # be correctly used in the uio structure.
|
||||||
|
dnl #
|
||||||
|
AS_IF([test "x$enable_vfs_iov_iter" = "xyes"], [
|
||||||
|
AC_DEFINE(HAVE_VFS_IOV_ITER, 1,
|
||||||
|
[All required iov_iter interfaces are available])
|
||||||
|
])
|
||||||
|
])
|
|
@ -106,6 +106,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
|
||||||
ZFS_AC_KERNEL_SRC_VFS_DIRECT_IO
|
ZFS_AC_KERNEL_SRC_VFS_DIRECT_IO
|
||||||
ZFS_AC_KERNEL_SRC_VFS_RW_ITERATE
|
ZFS_AC_KERNEL_SRC_VFS_RW_ITERATE
|
||||||
ZFS_AC_KERNEL_SRC_VFS_GENERIC_WRITE_CHECKS
|
ZFS_AC_KERNEL_SRC_VFS_GENERIC_WRITE_CHECKS
|
||||||
|
ZFS_AC_KERNEL_SRC_VFS_IOV_ITER
|
||||||
ZFS_AC_KERNEL_SRC_KMAP_ATOMIC_ARGS
|
ZFS_AC_KERNEL_SRC_KMAP_ATOMIC_ARGS
|
||||||
ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE
|
ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE
|
||||||
ZFS_AC_KERNEL_SRC_MAKE_REQUEST_FN
|
ZFS_AC_KERNEL_SRC_MAKE_REQUEST_FN
|
||||||
|
@ -204,6 +205,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
|
||||||
ZFS_AC_KERNEL_VFS_DIRECT_IO
|
ZFS_AC_KERNEL_VFS_DIRECT_IO
|
||||||
ZFS_AC_KERNEL_VFS_RW_ITERATE
|
ZFS_AC_KERNEL_VFS_RW_ITERATE
|
||||||
ZFS_AC_KERNEL_VFS_GENERIC_WRITE_CHECKS
|
ZFS_AC_KERNEL_VFS_GENERIC_WRITE_CHECKS
|
||||||
|
ZFS_AC_KERNEL_VFS_IOV_ITER
|
||||||
ZFS_AC_KERNEL_KMAP_ATOMIC_ARGS
|
ZFS_AC_KERNEL_KMAP_ATOMIC_ARGS
|
||||||
ZFS_AC_KERNEL_FOLLOW_DOWN_ONE
|
ZFS_AC_KERNEL_FOLLOW_DOWN_ONE
|
||||||
ZFS_AC_KERNEL_MAKE_REQUEST_FN
|
ZFS_AC_KERNEL_MAKE_REQUEST_FN
|
||||||
|
|
|
@ -44,14 +44,19 @@ typedef enum uio_rw {
|
||||||
typedef enum uio_seg {
|
typedef enum uio_seg {
|
||||||
UIO_USERSPACE = 0,
|
UIO_USERSPACE = 0,
|
||||||
UIO_SYSSPACE = 1,
|
UIO_SYSSPACE = 1,
|
||||||
UIO_USERISPACE = 2,
|
UIO_BVEC = 2,
|
||||||
UIO_BVEC = 3,
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
UIO_ITER = 3,
|
||||||
|
#endif
|
||||||
} uio_seg_t;
|
} uio_seg_t;
|
||||||
|
|
||||||
typedef struct uio {
|
typedef struct uio {
|
||||||
union {
|
union {
|
||||||
const struct iovec *uio_iov;
|
const struct iovec *uio_iov;
|
||||||
const struct bio_vec *uio_bvec;
|
const struct bio_vec *uio_bvec;
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
struct iov_iter *uio_iter;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
int uio_iovcnt;
|
int uio_iovcnt;
|
||||||
offset_t uio_loffset;
|
offset_t uio_loffset;
|
||||||
|
@ -97,4 +102,65 @@ uio_index_at_offset(uio_t *uio, offset_t off, uint_t *vec_idx)
|
||||||
return (off);
|
return (off);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
iov_iter_init_compat(struct iov_iter *iter, unsigned int dir,
|
||||||
|
const struct iovec *iov, unsigned long nr_segs, size_t count)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_IOV_ITER_INIT)
|
||||||
|
iov_iter_init(iter, dir, iov, nr_segs, count);
|
||||||
|
#elif defined(HAVE_IOV_ITER_INIT_LEGACY)
|
||||||
|
iov_iter_init(iter, iov, nr_segs, count, 0);
|
||||||
|
#else
|
||||||
|
#error "Unsupported kernel"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
uio_iovec_init(uio_t *uio, const struct iovec *iov, unsigned long nr_segs,
|
||||||
|
offset_t offset, uio_seg_t seg, ssize_t resid, size_t skip)
|
||||||
|
{
|
||||||
|
ASSERT(seg == UIO_USERSPACE || seg == UIO_SYSSPACE);
|
||||||
|
|
||||||
|
uio->uio_iov = iov;
|
||||||
|
uio->uio_iovcnt = nr_segs;
|
||||||
|
uio->uio_loffset = offset;
|
||||||
|
uio->uio_segflg = seg;
|
||||||
|
uio->uio_fault_disable = B_FALSE;
|
||||||
|
uio->uio_fmode = 0;
|
||||||
|
uio->uio_extflg = 0;
|
||||||
|
uio->uio_resid = resid;
|
||||||
|
uio->uio_skip = skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
uio_bvec_init(uio_t *uio, struct bio *bio)
|
||||||
|
{
|
||||||
|
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
||||||
|
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
||||||
|
uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
|
||||||
|
uio->uio_segflg = UIO_BVEC;
|
||||||
|
uio->uio_fault_disable = B_FALSE;
|
||||||
|
uio->uio_fmode = 0;
|
||||||
|
uio->uio_extflg = 0;
|
||||||
|
uio->uio_resid = BIO_BI_SIZE(bio);
|
||||||
|
uio->uio_skip = BIO_BI_SKIP(bio);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
static inline void
|
||||||
|
uio_iov_iter_init(uio_t *uio, struct iov_iter *iter, offset_t offset,
|
||||||
|
ssize_t resid, size_t skip)
|
||||||
|
{
|
||||||
|
uio->uio_iter = iter;
|
||||||
|
uio->uio_iovcnt = iter->nr_segs;
|
||||||
|
uio->uio_loffset = offset;
|
||||||
|
uio->uio_segflg = UIO_ITER;
|
||||||
|
uio->uio_fault_disable = B_FALSE;
|
||||||
|
uio->uio_fmode = 0;
|
||||||
|
uio->uio_extflg = 0;
|
||||||
|
uio->uio_resid = resid;
|
||||||
|
uio->uio_skip = skip;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* SPL_UIO_H */
|
#endif /* SPL_UIO_H */
|
||||||
|
|
|
@ -46,15 +46,6 @@ extern const struct inode_operations zpl_dir_inode_operations;
|
||||||
extern const struct inode_operations zpl_symlink_inode_operations;
|
extern const struct inode_operations zpl_symlink_inode_operations;
|
||||||
extern const struct inode_operations zpl_special_inode_operations;
|
extern const struct inode_operations zpl_special_inode_operations;
|
||||||
extern dentry_operations_t zpl_dentry_operations;
|
extern dentry_operations_t zpl_dentry_operations;
|
||||||
|
|
||||||
/* zpl_file.c */
|
|
||||||
extern ssize_t zpl_read_common(struct inode *ip, const char *buf,
|
|
||||||
size_t len, loff_t *ppos, uio_seg_t segment, int flags,
|
|
||||||
cred_t *cr);
|
|
||||||
extern ssize_t zpl_write_common(struct inode *ip, const char *buf,
|
|
||||||
size_t len, loff_t *ppos, uio_seg_t segment, int flags,
|
|
||||||
cred_t *cr);
|
|
||||||
|
|
||||||
extern const struct address_space_operations zpl_address_space_operations;
|
extern const struct address_space_operations zpl_address_space_operations;
|
||||||
extern const struct file_operations zpl_file_operations;
|
extern const struct file_operations zpl_file_operations;
|
||||||
extern const struct file_operations zpl_dir_file_operations;
|
extern const struct file_operations zpl_dir_file_operations;
|
||||||
|
|
|
@ -59,7 +59,6 @@ typedef enum uio_rw {
|
||||||
typedef enum uio_seg {
|
typedef enum uio_seg {
|
||||||
UIO_USERSPACE = 0,
|
UIO_USERSPACE = 0,
|
||||||
UIO_SYSSPACE = 1,
|
UIO_SYSSPACE = 1,
|
||||||
UIO_USERISPACE = 2,
|
|
||||||
} uio_seg_t;
|
} uio_seg_t;
|
||||||
|
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
|
|
|
@ -61,7 +61,6 @@ KERNEL_C = \
|
||||||
zfs_fletcher_superscalar4.c \
|
zfs_fletcher_superscalar4.c \
|
||||||
zfs_namecheck.c \
|
zfs_namecheck.c \
|
||||||
zfs_prop.c \
|
zfs_prop.c \
|
||||||
zfs_uio.c \
|
|
||||||
zpool_prop.c \
|
zpool_prop.c \
|
||||||
zprop_common.c
|
zprop_common.c
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ KERNEL_C = \
|
||||||
zfs_fletcher_superscalar4.c \
|
zfs_fletcher_superscalar4.c \
|
||||||
zfs_namecheck.c \
|
zfs_namecheck.c \
|
||||||
zfs_prop.c \
|
zfs_prop.c \
|
||||||
zfs_uio.c \
|
|
||||||
zpool_prop.c \
|
zpool_prop.c \
|
||||||
zprop_common.c \
|
zprop_common.c \
|
||||||
abd.c \
|
abd.c \
|
||||||
|
|
|
@ -23,6 +23,7 @@ $(MODULE)-objs += ../os/linux/zfs/zfs_dir.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_file_os.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_file_os.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_ioctl_os.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_ioctl_os.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_sysfs.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_sysfs.o
|
||||||
|
$(MODULE)-objs += ../os/linux/zfs/zfs_uio.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_vfsops.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_vfsops.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_vnops_os.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_vnops_os.o
|
||||||
$(MODULE)-objs += ../os/linux/zfs/zfs_znode.o
|
$(MODULE)-objs += ../os/linux/zfs/zfs_znode.o
|
||||||
|
|
|
@ -39,12 +39,6 @@
|
||||||
* Copyright (c) 2015 by Chunwei Chen. All rights reserved.
|
* Copyright (c) 2015 by Chunwei Chen. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* The uio support from OpenSolaris has been added as a short term
|
|
||||||
* work around. The hope is to adopt native Linux type and drop the
|
|
||||||
* use of uio's entirely. Under Linux they only add overhead and
|
|
||||||
* when possible we want to use native APIs for the ZPL layer.
|
|
||||||
*/
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -71,7 +65,6 @@ uiomove_iov(void *p, size_t n, enum uio_rw rw, struct uio *uio)
|
||||||
cnt = MIN(iov->iov_len - skip, n);
|
cnt = MIN(iov->iov_len - skip, n);
|
||||||
switch (uio->uio_segflg) {
|
switch (uio->uio_segflg) {
|
||||||
case UIO_USERSPACE:
|
case UIO_USERSPACE:
|
||||||
case UIO_USERISPACE:
|
|
||||||
/*
|
/*
|
||||||
* p = kernel data pointer
|
* p = kernel data pointer
|
||||||
* iov->iov_base = user data pointer
|
* iov->iov_base = user data pointer
|
||||||
|
@ -165,81 +158,82 @@ uiomove_bvec(void *p, size_t n, enum uio_rw rw, struct uio *uio)
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
static int
|
||||||
|
uiomove_iter(void *p, size_t n, enum uio_rw rw, struct uio *uio,
|
||||||
|
boolean_t revert)
|
||||||
|
{
|
||||||
|
size_t cnt = MIN(n, uio->uio_resid);
|
||||||
|
|
||||||
|
if (uio->uio_skip)
|
||||||
|
iov_iter_advance(uio->uio_iter, uio->uio_skip);
|
||||||
|
|
||||||
|
if (rw == UIO_READ)
|
||||||
|
cnt = copy_to_iter(p, cnt, uio->uio_iter);
|
||||||
|
else
|
||||||
|
cnt = copy_from_iter(p, cnt, uio->uio_iter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When operating on a full pipe no bytes are processed.
|
||||||
|
* In which case return EFAULT which is converted to EAGAIN
|
||||||
|
* by the kernel's generic_file_splice_read() function.
|
||||||
|
*/
|
||||||
|
if (cnt == 0)
|
||||||
|
return (EFAULT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Revert advancing the uio_iter. This is set by uiocopy()
|
||||||
|
* to avoid consuming the uio and its iov_iter structure.
|
||||||
|
*/
|
||||||
|
if (revert)
|
||||||
|
iov_iter_revert(uio->uio_iter, cnt);
|
||||||
|
|
||||||
|
uio->uio_resid -= cnt;
|
||||||
|
uio->uio_loffset += cnt;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
|
uiomove(void *p, size_t n, enum uio_rw rw, struct uio *uio)
|
||||||
{
|
{
|
||||||
if (uio->uio_segflg != UIO_BVEC)
|
if (uio->uio_segflg == UIO_BVEC)
|
||||||
return (uiomove_iov(p, n, rw, uio));
|
|
||||||
else
|
|
||||||
return (uiomove_bvec(p, n, rw, uio));
|
return (uiomove_bvec(p, n, rw, uio));
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
else if (uio->uio_segflg == UIO_ITER)
|
||||||
|
return (uiomove_iter(p, n, rw, uio, B_FALSE));
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
return (uiomove_iov(p, n, rw, uio));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(uiomove);
|
EXPORT_SYMBOL(uiomove);
|
||||||
|
|
||||||
#define fuword8(uptr, vptr) get_user((*vptr), (uptr))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fault in the pages of the first n bytes specified by the uio structure.
|
|
||||||
* 1 byte in each page is touched and the uio struct is unmodified. Any
|
|
||||||
* error will terminate the process as this is only a best attempt to get
|
|
||||||
* the pages resident.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
uio_prefaultpages(ssize_t n, struct uio *uio)
|
uio_prefaultpages(ssize_t n, struct uio *uio)
|
||||||
{
|
{
|
||||||
const struct iovec *iov;
|
struct iov_iter iter, *iterp = NULL;
|
||||||
ulong_t cnt, incr;
|
|
||||||
caddr_t p;
|
|
||||||
uint8_t tmp;
|
|
||||||
int iovcnt;
|
|
||||||
size_t skip;
|
|
||||||
|
|
||||||
/* no need to fault in kernel pages */
|
#if defined(HAVE_IOV_ITER_FAULT_IN_READABLE)
|
||||||
switch (uio->uio_segflg) {
|
if (uio->uio_segflg == UIO_USERSPACE) {
|
||||||
case UIO_SYSSPACE:
|
iterp = &iter;
|
||||||
case UIO_BVEC:
|
iov_iter_init_compat(iterp, READ, uio->uio_iov,
|
||||||
return (0);
|
uio->uio_iovcnt, uio->uio_resid);
|
||||||
case UIO_USERSPACE:
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
case UIO_USERISPACE:
|
} else if (uio->uio_segflg == UIO_ITER) {
|
||||||
break;
|
iterp = uio->uio_iter;
|
||||||
default:
|
#endif
|
||||||
ASSERT(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iov = uio->uio_iov;
|
if (iterp && iov_iter_fault_in_readable(iterp, n))
|
||||||
iovcnt = uio->uio_iovcnt;
|
|
||||||
skip = uio->uio_skip;
|
|
||||||
|
|
||||||
for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
|
|
||||||
cnt = MIN(iov->iov_len - skip, n);
|
|
||||||
/* empty iov */
|
|
||||||
if (cnt == 0)
|
|
||||||
continue;
|
|
||||||
n -= cnt;
|
|
||||||
/*
|
|
||||||
* touch each page in this segment.
|
|
||||||
*/
|
|
||||||
p = iov->iov_base + skip;
|
|
||||||
while (cnt) {
|
|
||||||
if (fuword8((uint8_t *)p, &tmp))
|
|
||||||
return (EFAULT);
|
return (EFAULT);
|
||||||
incr = MIN(cnt, PAGESIZE);
|
#endif
|
||||||
p += incr;
|
|
||||||
cnt -= incr;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* touch the last byte in case it straddles a page.
|
|
||||||
*/
|
|
||||||
p--;
|
|
||||||
if (fuword8((uint8_t *)p, &tmp))
|
|
||||||
return (EFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(uio_prefaultpages);
|
EXPORT_SYMBOL(uio_prefaultpages);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* same as uiomove() but doesn't modify uio structure.
|
* The same as uiomove() but doesn't modify uio structure.
|
||||||
* return in cbytes how many bytes were copied.
|
* return in cbytes how many bytes were copied.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
@ -249,39 +243,54 @@ uiocopy(void *p, size_t n, enum uio_rw rw, struct uio *uio, size_t *cbytes)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
bcopy(uio, &uio_copy, sizeof (struct uio));
|
bcopy(uio, &uio_copy, sizeof (struct uio));
|
||||||
ret = uiomove(p, n, rw, &uio_copy);
|
|
||||||
|
if (uio->uio_segflg == UIO_BVEC)
|
||||||
|
ret = uiomove_bvec(p, n, rw, &uio_copy);
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
else if (uio->uio_segflg == UIO_ITER)
|
||||||
|
ret = uiomove_iter(p, n, rw, &uio_copy, B_TRUE);
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
ret = uiomove_iov(p, n, rw, &uio_copy);
|
||||||
|
|
||||||
*cbytes = uio->uio_resid - uio_copy.uio_resid;
|
*cbytes = uio->uio_resid - uio_copy.uio_resid;
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(uiocopy);
|
EXPORT_SYMBOL(uiocopy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drop the next n chars out of *uiop.
|
* Drop the next n chars out of *uio.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
uioskip(uio_t *uiop, size_t n)
|
uioskip(uio_t *uio, size_t n)
|
||||||
{
|
{
|
||||||
if (n > uiop->uio_resid)
|
if (n > uio->uio_resid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uiop->uio_skip += n;
|
if (uio->uio_segflg == UIO_BVEC) {
|
||||||
if (uiop->uio_segflg != UIO_BVEC) {
|
uio->uio_skip += n;
|
||||||
while (uiop->uio_iovcnt &&
|
while (uio->uio_iovcnt &&
|
||||||
uiop->uio_skip >= uiop->uio_iov->iov_len) {
|
uio->uio_skip >= uio->uio_bvec->bv_len) {
|
||||||
uiop->uio_skip -= uiop->uio_iov->iov_len;
|
uio->uio_skip -= uio->uio_bvec->bv_len;
|
||||||
uiop->uio_iov++;
|
uio->uio_bvec++;
|
||||||
uiop->uio_iovcnt--;
|
uio->uio_iovcnt--;
|
||||||
}
|
}
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
} else if (uio->uio_segflg == UIO_ITER) {
|
||||||
|
iov_iter_advance(uio->uio_iter, n);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
while (uiop->uio_iovcnt &&
|
uio->uio_skip += n;
|
||||||
uiop->uio_skip >= uiop->uio_bvec->bv_len) {
|
while (uio->uio_iovcnt &&
|
||||||
uiop->uio_skip -= uiop->uio_bvec->bv_len;
|
uio->uio_skip >= uio->uio_iov->iov_len) {
|
||||||
uiop->uio_bvec++;
|
uio->uio_skip -= uio->uio_iov->iov_len;
|
||||||
uiop->uio_iovcnt--;
|
uio->uio_iov++;
|
||||||
|
uio->uio_iovcnt--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uiop->uio_loffset += n;
|
uio->uio_loffset += n;
|
||||||
uiop->uio_resid -= n;
|
uio->uio_resid -= n;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(uioskip);
|
EXPORT_SYMBOL(uioskip);
|
||||||
#endif /* _KERNEL */
|
#endif /* _KERNEL */
|
|
@ -355,28 +355,37 @@ unsigned long zfs_delete_blocks = DMU_MAX_DELETEBLKCNT;
|
||||||
* OUT: resid - remaining bytes to write
|
* OUT: resid - remaining bytes to write
|
||||||
*
|
*
|
||||||
* RETURN: 0 if success
|
* RETURN: 0 if success
|
||||||
* positive error code if failure
|
* positive error code if failure. EIO is returned
|
||||||
|
* for a short write when residp isn't provided.
|
||||||
*
|
*
|
||||||
* Timestamps:
|
* Timestamps:
|
||||||
* zp - ctime|mtime updated if byte count > 0
|
* zp - ctime|mtime updated if byte count > 0
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
zfs_write_simple(znode_t *zp, const void *data, size_t len,
|
zfs_write_simple(znode_t *zp, const void *data, size_t len,
|
||||||
loff_t pos, size_t *resid)
|
loff_t pos, size_t *residp)
|
||||||
{
|
{
|
||||||
ssize_t written;
|
fstrans_cookie_t cookie;
|
||||||
int error = 0;
|
int error;
|
||||||
|
|
||||||
written = zpl_write_common(ZTOI(zp), data, len, &pos,
|
struct iovec iov;
|
||||||
UIO_SYSSPACE, 0, kcred);
|
iov.iov_base = (void *)data;
|
||||||
if (written < 0) {
|
iov.iov_len = len;
|
||||||
error = -written;
|
|
||||||
} else if (resid == NULL) {
|
uio_t uio;
|
||||||
if (written < len)
|
uio_iovec_init(&uio, &iov, 1, pos, UIO_SYSSPACE, len, 0);
|
||||||
error = SET_ERROR(EIO); /* short write */
|
|
||||||
} else {
|
cookie = spl_fstrans_mark();
|
||||||
*resid = len - written;
|
error = zfs_write(zp, &uio, 0, kcred);
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
if (residp != NULL)
|
||||||
|
*residp = uio_resid(&uio);
|
||||||
|
else if (uio_resid(&uio) != 0)
|
||||||
|
error = SET_ERROR(EIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,242 +212,224 @@ zfs_io_flags(struct kiocb *kiocb)
|
||||||
return (flags);
|
return (flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
zpl_read_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count,
|
|
||||||
unsigned long nr_segs, loff_t *ppos, uio_seg_t segment, int flags,
|
|
||||||
cred_t *cr, size_t skip)
|
|
||||||
{
|
|
||||||
ssize_t read;
|
|
||||||
uio_t uio = { { 0 }, 0 };
|
|
||||||
int error;
|
|
||||||
fstrans_cookie_t cookie;
|
|
||||||
|
|
||||||
uio.uio_iov = iovp;
|
|
||||||
uio.uio_iovcnt = nr_segs;
|
|
||||||
uio.uio_loffset = *ppos;
|
|
||||||
uio.uio_segflg = segment;
|
|
||||||
uio.uio_resid = count;
|
|
||||||
uio.uio_skip = skip;
|
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
|
||||||
error = -zfs_read(ITOZ(ip), &uio, flags, cr);
|
|
||||||
spl_fstrans_unmark(cookie);
|
|
||||||
if (error < 0)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
read = count - uio.uio_resid;
|
|
||||||
*ppos += read;
|
|
||||||
|
|
||||||
return (read);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ssize_t
|
|
||||||
zpl_read_common(struct inode *ip, const char *buf, size_t len, loff_t *ppos,
|
|
||||||
uio_seg_t segment, int flags, cred_t *cr)
|
|
||||||
{
|
|
||||||
struct iovec iov;
|
|
||||||
|
|
||||||
iov.iov_base = (void *)buf;
|
|
||||||
iov.iov_len = len;
|
|
||||||
|
|
||||||
return (zpl_read_common_iovec(ip, &iov, len, 1, ppos, segment,
|
|
||||||
flags, cr, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
zpl_iter_read_common(struct kiocb *kiocb, const struct iovec *iovp,
|
|
||||||
unsigned long nr_segs, size_t count, uio_seg_t seg, size_t skip)
|
|
||||||
{
|
|
||||||
cred_t *cr = CRED();
|
|
||||||
struct file *filp = kiocb->ki_filp;
|
|
||||||
struct inode *ip = filp->f_mapping->host;
|
|
||||||
zfsvfs_t *zfsvfs = ZTOZSB(ITOZ(ip));
|
|
||||||
ssize_t read;
|
|
||||||
unsigned int f_flags = filp->f_flags;
|
|
||||||
|
|
||||||
f_flags |= zfs_io_flags(kiocb);
|
|
||||||
crhold(cr);
|
|
||||||
read = zpl_read_common_iovec(filp->f_mapping->host, iovp, count,
|
|
||||||
nr_segs, &kiocb->ki_pos, seg, f_flags, cr, skip);
|
|
||||||
crfree(cr);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If relatime is enabled, call file_accessed() only if
|
* If relatime is enabled, call file_accessed() if zfs_relatime_need_update()
|
||||||
* zfs_relatime_need_update() is true. This is needed since datasets
|
* is true. This is needed since datasets with inherited "relatime" property
|
||||||
* with inherited "relatime" property aren't necessarily mounted with
|
* aren't necessarily mounted with the MNT_RELATIME flag (e.g. after
|
||||||
* MNT_RELATIME flag (e.g. after `zfs set relatime=...`), which is what
|
* `zfs set relatime=...`), which is what relatime test in VFS by
|
||||||
* relatime test in VFS by relatime_need_update() is based on.
|
* relatime_need_update() is based on.
|
||||||
*/
|
*/
|
||||||
if (!IS_NOATIME(ip) && zfsvfs->z_relatime) {
|
static inline void
|
||||||
|
zpl_file_accessed(struct file *filp)
|
||||||
|
{
|
||||||
|
struct inode *ip = filp->f_mapping->host;
|
||||||
|
|
||||||
|
if (!IS_NOATIME(ip) && ITOZSB(ip)->z_relatime) {
|
||||||
if (zfs_relatime_need_update(ip))
|
if (zfs_relatime_need_update(ip))
|
||||||
file_accessed(filp);
|
file_accessed(filp);
|
||||||
} else {
|
} else {
|
||||||
file_accessed(filp);
|
file_accessed(filp);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_VFS_RW_ITERATE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When HAVE_VFS_IOV_ITER is defined the iov_iter structure supports
|
||||||
|
* iovecs, kvevs, bvecs and pipes, plus all the required interfaces to
|
||||||
|
* manipulate the iov_iter are available. In which case the full iov_iter
|
||||||
|
* can be attached to the uio and correctly handled in the lower layers.
|
||||||
|
* Otherwise, for older kernels extract the iovec and pass it instead.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
zpl_uio_init(uio_t *uio, struct kiocb *kiocb, struct iov_iter *to,
|
||||||
|
loff_t pos, ssize_t count, size_t skip)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_VFS_IOV_ITER)
|
||||||
|
uio_iov_iter_init(uio, to, pos, count, skip);
|
||||||
|
#else
|
||||||
|
uio_iovec_init(uio, to->iov, to->nr_segs, pos,
|
||||||
|
to->type & ITER_KVEC ? UIO_SYSSPACE : UIO_USERSPACE,
|
||||||
|
count, skip);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
zpl_iter_read(struct kiocb *kiocb, struct iov_iter *to)
|
||||||
|
{
|
||||||
|
cred_t *cr = CRED();
|
||||||
|
fstrans_cookie_t cookie;
|
||||||
|
struct file *filp = kiocb->ki_filp;
|
||||||
|
ssize_t count = iov_iter_count(to);
|
||||||
|
uio_t uio;
|
||||||
|
|
||||||
|
zpl_uio_init(&uio, kiocb, to, kiocb->ki_pos, count, 0);
|
||||||
|
|
||||||
|
crhold(cr);
|
||||||
|
cookie = spl_fstrans_mark();
|
||||||
|
|
||||||
|
int error = -zfs_read(ITOZ(filp->f_mapping->host), &uio,
|
||||||
|
filp->f_flags | zfs_io_flags(kiocb), cr);
|
||||||
|
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
crfree(cr);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
ssize_t read = count - uio.uio_resid;
|
||||||
|
kiocb->ki_pos += read;
|
||||||
|
|
||||||
|
zpl_file_accessed(filp);
|
||||||
|
|
||||||
|
if (read > 0)
|
||||||
|
iov_iter_advance(to, read);
|
||||||
|
|
||||||
return (read);
|
return (read);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_VFS_RW_ITERATE)
|
static inline ssize_t
|
||||||
static ssize_t
|
zpl_generic_write_checks(struct kiocb *kiocb, struct iov_iter *from,
|
||||||
zpl_iter_read(struct kiocb *kiocb, struct iov_iter *to)
|
size_t *countp)
|
||||||
{
|
{
|
||||||
ssize_t ret;
|
#ifdef HAVE_GENERIC_WRITE_CHECKS_KIOCB
|
||||||
uio_seg_t seg = UIO_USERSPACE;
|
ssize_t ret = generic_write_checks(kiocb, from);
|
||||||
if (to->type & ITER_KVEC)
|
if (ret <= 0)
|
||||||
seg = UIO_SYSSPACE;
|
|
||||||
if (to->type & ITER_BVEC)
|
|
||||||
seg = UIO_BVEC;
|
|
||||||
ret = zpl_iter_read_common(kiocb, to->iov, to->nr_segs,
|
|
||||||
iov_iter_count(to), seg, to->iov_offset);
|
|
||||||
if (ret > 0)
|
|
||||||
iov_iter_advance(to, ret);
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
|
||||||
#else
|
|
||||||
static ssize_t
|
|
||||||
zpl_aio_read(struct kiocb *kiocb, const struct iovec *iovp,
|
|
||||||
unsigned long nr_segs, loff_t pos)
|
|
||||||
{
|
|
||||||
ssize_t ret;
|
|
||||||
size_t count;
|
|
||||||
|
|
||||||
ret = generic_segment_checks(iovp, &nr_segs, &count, VERIFY_WRITE);
|
*countp = ret;
|
||||||
|
#else
|
||||||
|
struct file *file = kiocb->ki_filp;
|
||||||
|
struct address_space *mapping = file->f_mapping;
|
||||||
|
struct inode *ip = mapping->host;
|
||||||
|
int isblk = S_ISBLK(ip->i_mode);
|
||||||
|
|
||||||
|
*countp = iov_iter_count(from);
|
||||||
|
ssize_t ret = generic_write_checks(file, &kiocb->ki_pos, countp, isblk);
|
||||||
if (ret)
|
if (ret)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
#endif
|
||||||
|
|
||||||
return (zpl_iter_read_common(kiocb, iovp, nr_segs, count,
|
return (0);
|
||||||
UIO_USERSPACE, 0));
|
|
||||||
}
|
|
||||||
#endif /* HAVE_VFS_RW_ITERATE */
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
zpl_write_common_iovec(struct inode *ip, const struct iovec *iovp, size_t count,
|
|
||||||
unsigned long nr_segs, loff_t *ppos, uio_seg_t segment, int flags,
|
|
||||||
cred_t *cr, size_t skip)
|
|
||||||
{
|
|
||||||
ssize_t wrote;
|
|
||||||
uio_t uio = { { 0 }, 0 };
|
|
||||||
int error;
|
|
||||||
fstrans_cookie_t cookie;
|
|
||||||
|
|
||||||
if (flags & O_APPEND)
|
|
||||||
*ppos = i_size_read(ip);
|
|
||||||
|
|
||||||
uio.uio_iov = iovp;
|
|
||||||
uio.uio_iovcnt = nr_segs;
|
|
||||||
uio.uio_loffset = *ppos;
|
|
||||||
uio.uio_segflg = segment;
|
|
||||||
uio.uio_resid = count;
|
|
||||||
uio.uio_skip = skip;
|
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
|
||||||
error = -zfs_write(ITOZ(ip), &uio, flags, cr);
|
|
||||||
spl_fstrans_unmark(cookie);
|
|
||||||
if (error < 0)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
wrote = count - uio.uio_resid;
|
|
||||||
*ppos += wrote;
|
|
||||||
|
|
||||||
return (wrote);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ssize_t
|
|
||||||
zpl_write_common(struct inode *ip, const char *buf, size_t len, loff_t *ppos,
|
|
||||||
uio_seg_t segment, int flags, cred_t *cr)
|
|
||||||
{
|
|
||||||
struct iovec iov;
|
|
||||||
|
|
||||||
iov.iov_base = (void *)buf;
|
|
||||||
iov.iov_len = len;
|
|
||||||
|
|
||||||
return (zpl_write_common_iovec(ip, &iov, len, 1, ppos, segment,
|
|
||||||
flags, cr, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
zpl_iter_write_common(struct kiocb *kiocb, const struct iovec *iovp,
|
|
||||||
unsigned long nr_segs, size_t count, uio_seg_t seg, size_t skip)
|
|
||||||
{
|
|
||||||
cred_t *cr = CRED();
|
|
||||||
struct file *filp = kiocb->ki_filp;
|
|
||||||
ssize_t wrote;
|
|
||||||
unsigned int f_flags = filp->f_flags;
|
|
||||||
|
|
||||||
f_flags |= zfs_io_flags(kiocb);
|
|
||||||
crhold(cr);
|
|
||||||
wrote = zpl_write_common_iovec(filp->f_mapping->host, iovp, count,
|
|
||||||
nr_segs, &kiocb->ki_pos, seg, f_flags, cr, skip);
|
|
||||||
crfree(cr);
|
|
||||||
|
|
||||||
return (wrote);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(HAVE_VFS_RW_ITERATE)
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
zpl_iter_write(struct kiocb *kiocb, struct iov_iter *from)
|
zpl_iter_write(struct kiocb *kiocb, struct iov_iter *from)
|
||||||
{
|
{
|
||||||
|
cred_t *cr = CRED();
|
||||||
|
fstrans_cookie_t cookie;
|
||||||
|
struct file *filp = kiocb->ki_filp;
|
||||||
|
struct inode *ip = filp->f_mapping->host;
|
||||||
|
uio_t uio;
|
||||||
size_t count;
|
size_t count;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
uio_seg_t seg = UIO_USERSPACE;
|
|
||||||
|
|
||||||
#ifndef HAVE_GENERIC_WRITE_CHECKS_KIOCB
|
ret = zpl_generic_write_checks(kiocb, from, &count);
|
||||||
struct file *file = kiocb->ki_filp;
|
|
||||||
struct address_space *mapping = file->f_mapping;
|
|
||||||
struct inode *ip = mapping->host;
|
|
||||||
int isblk = S_ISBLK(ip->i_mode);
|
|
||||||
|
|
||||||
count = iov_iter_count(from);
|
|
||||||
ret = generic_write_checks(file, &kiocb->ki_pos, &count, isblk);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return (ret);
|
return (ret);
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* XXX - ideally this check should be in the same lock region with
|
|
||||||
* write operations, so that there's no TOCTTOU race when doing
|
|
||||||
* append and someone else grow the file.
|
|
||||||
*/
|
|
||||||
ret = generic_write_checks(kiocb, from);
|
|
||||||
if (ret <= 0)
|
|
||||||
return (ret);
|
|
||||||
count = ret;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (from->type & ITER_KVEC)
|
zpl_uio_init(&uio, kiocb, from, kiocb->ki_pos, count, from->iov_offset);
|
||||||
seg = UIO_SYSSPACE;
|
|
||||||
if (from->type & ITER_BVEC)
|
|
||||||
seg = UIO_BVEC;
|
|
||||||
|
|
||||||
ret = zpl_iter_write_common(kiocb, from->iov, from->nr_segs,
|
crhold(cr);
|
||||||
count, seg, from->iov_offset);
|
cookie = spl_fstrans_mark();
|
||||||
if (ret > 0)
|
|
||||||
iov_iter_advance(from, ret);
|
|
||||||
|
|
||||||
return (ret);
|
int error = -zfs_write(ITOZ(ip), &uio,
|
||||||
|
filp->f_flags | zfs_io_flags(kiocb), cr);
|
||||||
|
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
crfree(cr);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
ssize_t wrote = count - uio.uio_resid;
|
||||||
|
kiocb->ki_pos += wrote;
|
||||||
|
|
||||||
|
if (wrote > 0)
|
||||||
|
iov_iter_advance(from, wrote);
|
||||||
|
|
||||||
|
return (wrote);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
|
#else /* !HAVE_VFS_RW_ITERATE */
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
zpl_aio_write(struct kiocb *kiocb, const struct iovec *iovp,
|
zpl_aio_read(struct kiocb *kiocb, const struct iovec *iov,
|
||||||
unsigned long nr_segs, loff_t pos)
|
unsigned long nr_segs, loff_t pos)
|
||||||
{
|
{
|
||||||
struct file *file = kiocb->ki_filp;
|
cred_t *cr = CRED();
|
||||||
struct address_space *mapping = file->f_mapping;
|
fstrans_cookie_t cookie;
|
||||||
struct inode *ip = mapping->host;
|
struct file *filp = kiocb->ki_filp;
|
||||||
int isblk = S_ISBLK(ip->i_mode);
|
|
||||||
size_t count;
|
size_t count;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = generic_segment_checks(iovp, &nr_segs, &count, VERIFY_READ);
|
ret = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
|
||||||
if (ret)
|
if (ret)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
ret = generic_write_checks(file, &pos, &count, isblk);
|
uio_t uio;
|
||||||
|
uio_iovec_init(&uio, iov, nr_segs, kiocb->ki_pos, UIO_USERSPACE,
|
||||||
|
count, 0);
|
||||||
|
|
||||||
|
crhold(cr);
|
||||||
|
cookie = spl_fstrans_mark();
|
||||||
|
|
||||||
|
int error = -zfs_read(ITOZ(filp->f_mapping->host), &uio,
|
||||||
|
filp->f_flags | zfs_io_flags(kiocb), cr);
|
||||||
|
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
crfree(cr);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
ssize_t read = count - uio.uio_resid;
|
||||||
|
kiocb->ki_pos += read;
|
||||||
|
|
||||||
|
zpl_file_accessed(filp);
|
||||||
|
|
||||||
|
return (read);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
zpl_aio_write(struct kiocb *kiocb, const struct iovec *iov,
|
||||||
|
unsigned long nr_segs, loff_t pos)
|
||||||
|
{
|
||||||
|
cred_t *cr = CRED();
|
||||||
|
fstrans_cookie_t cookie;
|
||||||
|
struct file *filp = kiocb->ki_filp;
|
||||||
|
struct inode *ip = filp->f_mapping->host;
|
||||||
|
size_t count;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
ret = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
|
||||||
if (ret)
|
if (ret)
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
return (zpl_iter_write_common(kiocb, iovp, nr_segs, count,
|
ret = generic_write_checks(filp, &pos, &count, S_ISBLK(ip->i_mode));
|
||||||
UIO_USERSPACE, 0));
|
if (ret)
|
||||||
|
return (ret);
|
||||||
|
|
||||||
|
uio_t uio;
|
||||||
|
uio_iovec_init(&uio, iov, nr_segs, kiocb->ki_pos, UIO_USERSPACE,
|
||||||
|
count, 0);
|
||||||
|
|
||||||
|
crhold(cr);
|
||||||
|
cookie = spl_fstrans_mark();
|
||||||
|
|
||||||
|
int error = -zfs_write(ITOZ(ip), &uio,
|
||||||
|
filp->f_flags | zfs_io_flags(kiocb), cr);
|
||||||
|
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
crfree(cr);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
ssize_t wrote = count - uio.uio_resid;
|
||||||
|
kiocb->ki_pos += wrote;
|
||||||
|
|
||||||
|
return (wrote);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_VFS_RW_ITERATE */
|
#endif /* HAVE_VFS_RW_ITERATE */
|
||||||
|
|
||||||
|
@ -488,13 +470,13 @@ zpl_direct_IO(int rw, struct kiocb *kiocb, struct iov_iter *iter, loff_t pos)
|
||||||
|
|
||||||
#if defined(HAVE_VFS_DIRECT_IO_IOVEC)
|
#if defined(HAVE_VFS_DIRECT_IO_IOVEC)
|
||||||
static ssize_t
|
static ssize_t
|
||||||
zpl_direct_IO(int rw, struct kiocb *kiocb, const struct iovec *iovp,
|
zpl_direct_IO(int rw, struct kiocb *kiocb, const struct iovec *iov,
|
||||||
loff_t pos, unsigned long nr_segs)
|
loff_t pos, unsigned long nr_segs)
|
||||||
{
|
{
|
||||||
if (rw == WRITE)
|
if (rw == WRITE)
|
||||||
return (zpl_aio_write(kiocb, iovp, nr_segs, pos));
|
return (zpl_aio_write(kiocb, iov, nr_segs, pos));
|
||||||
else
|
else
|
||||||
return (zpl_aio_read(kiocb, iovp, nr_segs, pos));
|
return (zpl_aio_read(kiocb, iov, nr_segs, pos));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#error "Unknown direct IO interface"
|
#error "Unknown direct IO interface"
|
||||||
|
@ -601,10 +583,6 @@ zpl_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||||
* Populate a page with data for the Linux page cache. This function is
|
* Populate a page with data for the Linux page cache. This function is
|
||||||
* only used to support mmap(2). There will be an identical copy of the
|
* only used to support mmap(2). There will be an identical copy of the
|
||||||
* data in the ARC which is kept up to date via .write() and .writepage().
|
* data in the ARC which is kept up to date via .write() and .writepage().
|
||||||
*
|
|
||||||
* Current this function relies on zpl_read_common() and the O_DIRECT
|
|
||||||
* flag to read in a page. This works but the more correct way is to
|
|
||||||
* update zfs_fillpage() to be Linux friendly and use that interface.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
zpl_readpage(struct file *filp, struct page *pp)
|
zpl_readpage(struct file *filp, struct page *pp)
|
||||||
|
@ -1035,6 +1013,10 @@ const struct file_operations zpl_file_operations = {
|
||||||
#endif
|
#endif
|
||||||
.read_iter = zpl_iter_read,
|
.read_iter = zpl_iter_read,
|
||||||
.write_iter = zpl_iter_write,
|
.write_iter = zpl_iter_write,
|
||||||
|
#ifdef HAVE_VFS_IOV_ITER
|
||||||
|
.splice_read = generic_file_splice_read,
|
||||||
|
.splice_write = iter_file_splice_write,
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
.read = do_sync_read,
|
.read = do_sync_read,
|
||||||
.write = do_sync_write,
|
.write = do_sync_write,
|
||||||
|
|
|
@ -490,19 +490,17 @@ zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link)
|
||||||
{
|
{
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
cred_t *cr = CRED();
|
cred_t *cr = CRED();
|
||||||
struct iovec iov;
|
|
||||||
uio_t uio = { { 0 }, 0 };
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
*link = NULL;
|
*link = NULL;
|
||||||
|
|
||||||
|
struct iovec iov;
|
||||||
iov.iov_len = MAXPATHLEN;
|
iov.iov_len = MAXPATHLEN;
|
||||||
iov.iov_base = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
|
iov.iov_base = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
|
||||||
|
|
||||||
uio.uio_iov = &iov;
|
uio_t uio;
|
||||||
uio.uio_iovcnt = 1;
|
uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, MAXPATHLEN - 1, 0);
|
||||||
uio.uio_segflg = UIO_SYSSPACE;
|
|
||||||
uio.uio_resid = (MAXPATHLEN - 1);
|
|
||||||
|
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
error = -zfs_readlink(ip, &uio, cr);
|
error = -zfs_readlink(ip, &uio, cr);
|
||||||
|
|
|
@ -274,10 +274,10 @@ static int
|
||||||
zpl_xattr_get_dir(struct inode *ip, const char *name, void *value,
|
zpl_xattr_get_dir(struct inode *ip, const char *name, void *value,
|
||||||
size_t size, cred_t *cr)
|
size_t size, cred_t *cr)
|
||||||
{
|
{
|
||||||
|
fstrans_cookie_t cookie;
|
||||||
struct inode *xip = NULL;
|
struct inode *xip = NULL;
|
||||||
znode_t *dxzp = NULL;
|
znode_t *dxzp = NULL;
|
||||||
znode_t *xzp = NULL;
|
znode_t *xzp = NULL;
|
||||||
loff_t pos = 0;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
/* Lookup the xattr directory */
|
/* Lookup the xattr directory */
|
||||||
|
@ -302,7 +302,19 @@ zpl_xattr_get_dir(struct inode *ip, const char *name, void *value,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = zpl_read_common(xip, value, size, &pos, UIO_SYSSPACE, 0, cr);
|
struct iovec iov;
|
||||||
|
iov.iov_base = (void *)value;
|
||||||
|
iov.iov_len = size;
|
||||||
|
|
||||||
|
uio_t uio;
|
||||||
|
uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, size, 0);
|
||||||
|
|
||||||
|
cookie = spl_fstrans_mark();
|
||||||
|
error = -zfs_read(ITOZ(xip), &uio, 0, cr);
|
||||||
|
spl_fstrans_unmark(cookie);
|
||||||
|
|
||||||
|
if (error == 0)
|
||||||
|
error = size - uio_resid(&uio);
|
||||||
out:
|
out:
|
||||||
if (xzp)
|
if (xzp)
|
||||||
zrele(xzp);
|
zrele(xzp);
|
||||||
|
@ -441,7 +453,6 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
|
||||||
znode_t *dxzp = NULL;
|
znode_t *dxzp = NULL;
|
||||||
znode_t *xzp = NULL;
|
znode_t *xzp = NULL;
|
||||||
vattr_t *vap = NULL;
|
vattr_t *vap = NULL;
|
||||||
ssize_t wrote;
|
|
||||||
int lookup_flags, error;
|
int lookup_flags, error;
|
||||||
const int xattr_mode = S_IFREG | 0644;
|
const int xattr_mode = S_IFREG | 0644;
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
@ -496,13 +507,8 @@ zpl_xattr_set_dir(struct inode *ip, const char *name, const void *value,
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
wrote = zpl_write_common(ZTOI(xzp), value, size, &pos,
|
error = -zfs_write_simple(xzp, value, size, pos, NULL);
|
||||||
UIO_SYSSPACE, 0, cr);
|
|
||||||
if (wrote < 0)
|
|
||||||
error = wrote;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
ip->i_ctime = current_time(ip);
|
ip->i_ctime = current_time(ip);
|
||||||
zfs_mark_inode_dirty(ip);
|
zfs_mark_inode_dirty(ip);
|
||||||
|
|
|
@ -84,26 +84,15 @@ zvol_is_zvol_impl(const char *device)
|
||||||
return (B_FALSE);
|
return (B_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
uio_from_bio(uio_t *uio, struct bio *bio)
|
|
||||||
{
|
|
||||||
uio->uio_bvec = &bio->bi_io_vec[BIO_BI_IDX(bio)];
|
|
||||||
uio->uio_iovcnt = bio->bi_vcnt - BIO_BI_IDX(bio);
|
|
||||||
uio->uio_loffset = BIO_BI_SECTOR(bio) << 9;
|
|
||||||
uio->uio_segflg = UIO_BVEC;
|
|
||||||
uio->uio_resid = BIO_BI_SIZE(bio);
|
|
||||||
uio->uio_skip = BIO_BI_SKIP(bio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
zvol_write(void *arg)
|
zvol_write(void *arg)
|
||||||
{
|
{
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
zv_request_t *zvr = arg;
|
zv_request_t *zvr = arg;
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
uio_t uio = { { 0 }, 0 };
|
int error = 0;
|
||||||
uio_from_bio(&uio, bio);
|
uio_t uio;
|
||||||
|
|
||||||
|
uio_bvec_init(&uio, bio);
|
||||||
|
|
||||||
zvol_state_t *zv = zvr->zv;
|
zvol_state_t *zv = zvr->zv;
|
||||||
ASSERT3P(zv, !=, NULL);
|
ASSERT3P(zv, !=, NULL);
|
||||||
|
@ -249,12 +238,12 @@ unlock:
|
||||||
static void
|
static void
|
||||||
zvol_read(void *arg)
|
zvol_read(void *arg)
|
||||||
{
|
{
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
zv_request_t *zvr = arg;
|
zv_request_t *zvr = arg;
|
||||||
struct bio *bio = zvr->bio;
|
struct bio *bio = zvr->bio;
|
||||||
uio_t uio = { { 0 }, 0 };
|
int error = 0;
|
||||||
uio_from_bio(&uio, bio);
|
uio_t uio;
|
||||||
|
|
||||||
|
uio_bvec_init(&uio, bio);
|
||||||
|
|
||||||
zvol_state_t *zv = zvr->zv;
|
zvol_state_t *zv = zvr->zv;
|
||||||
ASSERT3P(zv, !=, NULL);
|
ASSERT3P(zv, !=, NULL);
|
||||||
|
|
|
@ -19,7 +19,6 @@ $(MODULE)-objs += zfs_fletcher_superscalar.o
|
||||||
$(MODULE)-objs += zfs_fletcher_superscalar4.o
|
$(MODULE)-objs += zfs_fletcher_superscalar4.o
|
||||||
$(MODULE)-objs += zfs_namecheck.o
|
$(MODULE)-objs += zfs_namecheck.o
|
||||||
$(MODULE)-objs += zfs_prop.o
|
$(MODULE)-objs += zfs_prop.o
|
||||||
$(MODULE)-objs += zfs_uio.o
|
|
||||||
$(MODULE)-objs += zpool_prop.o
|
$(MODULE)-objs += zpool_prop.o
|
||||||
$(MODULE)-objs += zprop_common.o
|
$(MODULE)-objs += zprop_common.o
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ zfs_write(znode_t *zp, uio_t *uio, int ioflag, cred_t *cr)
|
||||||
dmu_return_arcbuf(abuf);
|
dmu_return_arcbuf(abuf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ASSERT(cbytes == max_blksz);
|
ASSERT3S(cbytes, ==, max_blksz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -652,7 +652,7 @@ zfs_write(znode_t *zp, uio_t *uio, int ioflag, cred_t *cr)
|
||||||
|
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
break;
|
break;
|
||||||
ASSERT(tx_bytes == nbytes);
|
ASSERT3S(tx_bytes, ==, nbytes);
|
||||||
n -= nbytes;
|
n -= nbytes;
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
|
|
Loading…
Reference in New Issue