From 8f86252a327da29852839bb6546db7bc0ec4ba1f Mon Sep 17 00:00:00 2001 From: Olaf Faaland Date: Wed, 8 Mar 2017 10:14:59 -0800 Subject: [PATCH] Linux 4.11 compat: iops.getattr and friends In torvalds/linux@a528d35, there are several changes to the getattr family of functions, struct kstat, and the interface of inode_operations .getattr. The inode_operations .getattr interface changed to: int (*getattr) (const struct path *, struct dentry *, struct kstat *, u32 request_mask, unsigned int query_flags) The request_mask argument indicates which field(s) the caller intends to use. Fields the caller has not specified via request_mask may be set in he returned struct anyway, but their values may be approximate. The query_flags argument indicates whether the filesystem must update the attributes from the backing store. Currently both fields are ignored. struct kstat includes new fields: u32 result_mask; /* What fields the user got */ u64 attributes; /* See STATX_ATTR_* flags */ struct timespec btime; /* File creation time */ XXX result_mask is set but may be incorrect. attributes and btime are not set by zfs, may need to be. The interface to simple_getattr() has changed to: int simple_getattr(const struct path *, struct kstat *, u32, unsigned int); Requires-spl: refs/pull/608/head Signed-off-by: Olaf Faaland --- config/kernel-inode-getattr.m4 | 67 ++++++++++++++++++++++++++++++++++ config/kernel-struct-kstat.m4 | 21 +++++++++++ config/kernel.m4 | 2 + include/sys/zfs_ctldir.h | 34 +++++++++++++++++ module/zfs/zfs_vnops.c | 9 +++++ module/zfs/zpl_ctldir.c | 46 +++++++++++++++++------ module/zfs/zpl_inode.c | 11 +++++- 7 files changed, 177 insertions(+), 13 deletions(-) create mode 100644 config/kernel-inode-getattr.m4 create mode 100644 config/kernel-struct-kstat.m4 diff --git a/config/kernel-inode-getattr.m4 b/config/kernel-inode-getattr.m4 new file mode 100644 index 000000000000..f10e0b251085 --- /dev/null +++ b/config/kernel-inode-getattr.m4 @@ -0,0 +1,67 @@ +dnl # +dnl # Linux 4.11 API +dnl # See torvalds/linux@a528d35 +dnl # +AC_DEFUN([ZFS_AC_PATH_KERNEL_IOPS_GETATTR], [ + AC_MSG_CHECKING([whether iops->getattr() takes a path]) + ZFS_LINUX_TRY_COMPILE([ + #include + + int test_getattr( + const struct path *p, struct kstat *k, + u32 request_mask, unsigned int query_flags) + { return 0; } + + static const struct inode_operations + iops __attribute__ ((unused)) = { + .getattr = test_getattr, + }; + ],[ + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PATH_IOPS_GETATTR, 1, + [iops->getattr() takes a path]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + + + +dnl # +dnl # Linux 3.9 - 4.10 API +dnl # +AC_DEFUN([ZFS_AC_VFSMOUNT_KERNEL_IOPS_GETATTR], [ + AC_MSG_CHECKING([whether iops->getattr() takes a vfsmount]) + ZFS_LINUX_TRY_COMPILE([ + #include + + int test_getattr( + struct vfsmount *mnt, struct dentry *d, + struct kstat *k) + { return 0; } + + static const struct inode_operations + iops __attribute__ ((unused)) = { + .getattr = test_getattr, + }; + ],[ + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_VFSMOUNT_IOPS_GETATTR, 1, + [iops->getattr() takes a vfsmount]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + + +dnl # +dnl # The interface of the getattr callback from the inode_operations +dnl # structure changed. Also, the interface of the simple_getattr() +dnl # function provided by the kernel changed. +dnl # +AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_GETATTR], [ + ZFS_AC_PATH_KERNEL_IOPS_GETATTR + ZFS_AC_VFSMOUNT_KERNEL_IOPS_GETATTR +]) diff --git a/config/kernel-struct-kstat.m4 b/config/kernel-struct-kstat.m4 new file mode 100644 index 000000000000..199c58e008f5 --- /dev/null +++ b/config/kernel-struct-kstat.m4 @@ -0,0 +1,21 @@ +dnl # +dnl # Linux 4.11 API +dnl # See torvalds/linux@a528d35 +dnl # Two members were added to 'struct kstat': +dnl # results_mask +dnl # query_flags +dnl # +AC_DEFUN([ZFS_AC_KERNEL_STAT_RESULT_MASK], [ + AC_MSG_CHECKING([whether ((struct kstat *) stat)->results_mask exists]) + ZFS_LINUX_TRY_COMPILE([ + #include + ],[ + struct kstat stat = { .result_mask = 0 }; + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_STAT_RESULT_MASK, 1, + [((struct kstat *) stat)->results_mask exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 0b50a54e3fff..db8c265998de 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -59,7 +59,9 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ ZFS_AC_KERNEL_INODE_OPERATIONS_CHECK_ACL_WITH_FLAGS ZFS_AC_KERNEL_INODE_OPERATIONS_GET_ACL ZFS_AC_KERNEL_INODE_OPERATIONS_SET_ACL + ZFS_AC_KERNEL_INODE_OPERATIONS_GETATTR ZFS_AC_KERNEL_INODE_SET_FLAGS + ZFS_AC_KERNEL_STAT_RESULT_MASK ZFS_AC_KERNEL_GET_ACL_HANDLE_CACHE ZFS_AC_KERNEL_SHOW_OPTIONS ZFS_AC_KERNEL_FILE_INODE diff --git a/include/sys/zfs_ctldir.h b/include/sys/zfs_ctldir.h index 51933bc4fe47..2b15c7f7fd2f 100644 --- a/include/sys/zfs_ctldir.h +++ b/include/sys/zfs_ctldir.h @@ -47,6 +47,40 @@ (zfs_has_ctldir(zdp) && \ (ZTOZSB(zdp)->z_show_ctldir)) +/* + * Linux 4.11 compat + * When building for kernel < 4.11, these constants are not defined by the + * kernel. We define them so that we can use the 4.11 interface for + * getattr-related functions. They are set to 0 so that it will create obvious + * failures if they are accidentally used. + */ + +#if !defined(HAVE_PATH_IOPS_GETATTR) +#define STATX_BASIC_STATS 0 +#define AT_STATX_SYNC_AS_STAT 0 +#endif + +#ifdef HAVE_VFSMOUNT_IOPS_GETATTR +#define ZPL_GETATTR_WRAPPER(func) \ +static int \ +func(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) \ +{ \ + struct path path = { .mnt = mnt, .dentry = dentry }; \ + return func##_impl(&path, stat, STATX_BASIC_STATS, \ + AT_STATX_SYNC_AS_STAT); \ +} +#elif defined(HAVE_PATH_IOPS_GETATTR) +#define ZPL_GETATTR_WRAPPER(func) \ +static int \ +func(const struct path *path, struct kstat *stat, u32 request_mask, \ + unsigned int query_flags) \ +{ \ + return func##_impl(path, stat, request_mask, query_flags); \ +} +#else +#error +#endif + extern int zfs_expire_snapshot; /* zfsctl generic functions */ diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index 4afae6c36b1a..0bc4962fd1a3 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -2609,6 +2609,15 @@ zfs_getattr_fast(struct inode *ip, struct kstat *sp) generic_fillattr(ip, sp); +#if defined(HAVE_STAT_RESULT_MASK) + /* + * generic_fillattr() does not set stat->result_mask, but toggles a + * few individual bits. So set it to BASIC_STATS first and then + * result should be correct. + */ + stat->result_mask = STATX_BASIC_STATS; +#endif /* defined(HAVE_STAT_RESULT_MASK) */ + sa_object_size(zp->z_sa_hdl, &blksize, &nblocks); sp->blksize = blksize; sp->blocks = nblocks; diff --git a/module/zfs/zpl_ctldir.c b/module/zfs/zpl_ctldir.c index b6a3b669d1f3..2ee03401710a 100644 --- a/module/zfs/zpl_ctldir.c +++ b/module/zfs/zpl_ctldir.c @@ -95,21 +95,41 @@ zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir) } #endif /* HAVE_VFS_ITERATE */ +/* + * Used by zpl*getattr* functions below. + */ +static int +zpl_simple_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + int error; + +#ifdef HAVE_VFSMOUNT_IOPS_GETATTR + error = simple_getattr(path->mnt, path->dentry, stat); +#elif defined(HAVE_PATH_IOPS_GETATTR) + error = simple_getattr(path, stat, request_mask, query_flags); +#else +#error +#endif + return (error); +} + /* * Get root directory attributes. */ /* ARGSUSED */ static int -zpl_root_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +zpl_root_getattr_impl(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) { int error; - error = simple_getattr(mnt, dentry, stat); + error = zpl_simple_getattr(path, stat, request_mask, query_flags); stat->atime = CURRENT_TIME; return (error); } +ZPL_GETATTR_WRAPPER(zpl_root_getattr); static struct dentry * #ifdef HAVE_LOOKUP_NAMEIDATA @@ -375,14 +395,15 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, zpl_umode_t mode) */ /* ARGSUSED */ static int -zpl_snapdir_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +zpl_snapdir_getattr_impl(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) { - zfsvfs_t *zfsvfs = ITOZSB(dentry->d_inode); + zfsvfs_t *zfsvfs = ITOZSB(path->dentry->d_inode); int error; ZFS_ENTER(zfsvfs); - error = simple_getattr(mnt, dentry, stat); + error = zpl_simple_getattr(path, stat, request_mask, query_flags); + stat->nlink = stat->size = 2; stat->ctime = stat->mtime = dmu_objset_snap_cmtime(zfsvfs->z_os); stat->atime = CURRENT_TIME; @@ -390,6 +411,7 @@ zpl_snapdir_getattr(struct vfsmount *mnt, struct dentry *dentry, return (error); } +ZPL_GETATTR_WRAPPER(zpl_snapdir_getattr); /* * The '.zfs/snapshot' directory file operations. These mainly control @@ -509,10 +531,10 @@ zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir) /* ARGSUSED */ static int -zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +zpl_shares_getattr_impl(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) { - struct inode *ip = dentry->d_inode; + struct inode *ip = path->dentry->d_inode; zfsvfs_t *zfsvfs = ITOZSB(ip); znode_t *dzp; int error; @@ -520,7 +542,8 @@ zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry, ZFS_ENTER(zfsvfs); if (zfsvfs->z_shares_dir == 0) { - error = simple_getattr(mnt, dentry, stat); + error = zpl_simple_getattr(path, stat, request_mask, + query_flags); stat->nlink = stat->size = 2; stat->atime = CURRENT_TIME; ZFS_EXIT(zfsvfs); @@ -538,6 +561,7 @@ zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry, return (error); } +ZPL_GETATTR_WRAPPER(zpl_shares_getattr); /* * The '.zfs/shares' directory file operations. diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c index 2e438eaff8c2..8351ab5a0c62 100644 --- a/module/zfs/zpl_inode.c +++ b/module/zfs/zpl_inode.c @@ -340,18 +340,25 @@ zpl_rmdir(struct inode *dir, struct dentry *dentry) } static int -zpl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +zpl_getattr_impl(const struct path *path, struct kstat *stat, u32 request_mask, + unsigned int query_flags) { int error; fstrans_cookie_t cookie; cookie = spl_fstrans_mark(); - error = -zfs_getattr_fast(dentry->d_inode, stat); + + /* + * XXX request_mask and query_flags currently ignored. + */ + + error = -zfs_getattr_fast(path->dentry->d_inode, stat); spl_fstrans_unmark(cookie); ASSERT3S(error, <=, 0); return (error); } +ZPL_GETATTR_WRAPPER(zpl_getattr); static int zpl_setattr(struct dentry *dentry, struct iattr *ia)