diff --git a/.SRCINFO b/.SRCINFO index b96b53a..a46520e 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,7 +1,7 @@ pkgbase = zfs-dkms pkgdesc = Kernel modules for the Zettabyte File System. pkgver = 2.2.2 - pkgrel = 1 + pkgrel = 2 url = https://openzfs.org/ arch = x86_64 groups = archzfs-dkms @@ -19,6 +19,8 @@ pkgbase = zfs-dkms conflicts = spl-headers replaces = spl-dkms source = https://github.com/openzfs/zfs/releases/download/zfs-2.2.2/zfs-2.2.2.tar.gz + source = linux-6.7-compat.patch sha256sums = 76bc0547d9ba31d4b0142e417aaaf9f969072c3cb3c1a5b10c8738f39ed12fc9 + sha256sums = 5afd5ce236dfe0eb96abbe61a61c211623cb4a89c9fbb0be5f9f400a0970719e pkgname = zfs-dkms diff --git a/PKGBUILD b/PKGBUILD index 2dad23b..28d18d0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -9,18 +9,22 @@ pkgname="zfs-dkms" pkgdesc="Kernel modules for the Zettabyte File System." pkgver=2.2.2 -pkgrel=1 +pkgrel=2 makedepends=() arch=("x86_64") url="https://openzfs.org/" -source=("https://github.com/openzfs/zfs/releases/download/zfs-${pkgver}/zfs-${pkgver}.tar.gz") -sha256sums=("76bc0547d9ba31d4b0142e417aaaf9f969072c3cb3c1a5b10c8738f39ed12fc9") +source=("https://github.com/openzfs/zfs/releases/download/zfs-${pkgver}/zfs-${pkgver}.tar.gz" "linux-6.7-compat.patch") +sha256sums=("76bc0547d9ba31d4b0142e417aaaf9f969072c3cb3c1a5b10c8738f39ed12fc9" "5afd5ce236dfe0eb96abbe61a61c211623cb4a89c9fbb0be5f9f400a0970719e") license=("CDDL") depends=("zfs-utils=${pkgver}" "lsb-release" "dkms") provides=("zfs" "zfs-headers" "spl" "spl-headers") groups=("archzfs-dkms") conflicts=("zfs" "zfs-headers" "spl" "spl-headers") replaces=("spl-dkms") +prepare() { + cd "${srcdir}/zfs-${pkgver}" + patch -Np1 -i ${srcdir}/linux-6.7-compat.patch +} build() { cd "${srcdir}/zfs-${pkgver}" diff --git a/linux-6.7-compat.patch b/linux-6.7-compat.patch new file mode 100644 index 0000000..766ca92 --- /dev/null +++ b/linux-6.7-compat.patch @@ -0,0 +1,885 @@ +From 35d4d9df7041b25b966f660de7b5f92b57cc64b7 Mon Sep 17 00:00:00 2001 +From: Rob Norris +Date: Sat, 16 Dec 2023 18:01:45 +1100 +Subject: [PATCH 1/4] linux 6.7 compat: simplify current_time() check + +6.7 changed the names of the time members in struct inode, so we can't +assign back to it because we don't know its name. In practice this +doesn't matter though - if we're missing current_time(), then we must be +on <4.9, and we know our fallback will need to return timespec. + +Signed-off-by: Rob Norris +Sponsored-by: https://github.com/sponsors/robn +--- + config/kernel-current-time.m4 | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/config/kernel-current-time.m4 b/config/kernel-current-time.m4 +index 3ceb5f63efa9..ab7d9c5cedba 100644 +--- a/config/kernel-current-time.m4 ++++ b/config/kernel-current-time.m4 +@@ -2,12 +2,15 @@ dnl # + dnl # 4.9, current_time() added + dnl # 4.18, return type changed from timespec to timespec64 + dnl # ++dnl # Note that we don't care about the return type in this check. If we have ++dnl # to implement a fallback, we'll know we're <4.9, which was timespec. ++dnl # + AC_DEFUN([ZFS_AC_KERNEL_SRC_CURRENT_TIME], [ + ZFS_LINUX_TEST_SRC([current_time], [ + #include + ], [ + struct inode ip __attribute__ ((unused)); +- ip.i_atime = current_time(&ip); ++ (void) current_time(&ip); + ]) + ]) + + +From 82d0e42313aedc17447e4481822c38824a9e0d6c Mon Sep 17 00:00:00 2001 +From: Rob Norris +Date: Sat, 16 Dec 2023 22:31:32 +1100 +Subject: [PATCH 2/4] linux 6.7 compat: use inode atime/mtime accessors + +6.6 made i_ctime inaccessible; 6.7 has done the same for i_atime and +i_mtime. This extends the method used for ctime in b37f29341 to atime +and mtime as well. + +Signed-off-by: Rob Norris +Sponsored-by: https://github.com/sponsors/robn +--- + config/kernel-inode-times.m4 | 78 ++++++++++++++++++++++++++++++ + include/os/linux/zfs/sys/zpl.h | 20 ++++++++ + module/os/linux/zfs/zfs_ctldir.c | 4 +- + module/os/linux/zfs/zfs_vnops_os.c | 33 ++++++++----- + module/os/linux/zfs/zfs_znode.c | 45 +++++++++-------- + module/os/linux/zfs/zpl_inode.c | 3 +- + 6 files changed, 148 insertions(+), 35 deletions(-) + +diff --git a/config/kernel-inode-times.m4 b/config/kernel-inode-times.m4 +index aae95abf1720..4d861596ed0b 100644 +--- a/config/kernel-inode-times.m4 ++++ b/config/kernel-inode-times.m4 +@@ -52,6 +52,48 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_INODE_TIMES], [ + memset(&ip, 0, sizeof(ip)); + inode_set_ctime_to_ts(&ip, ts); + ]) ++ ++ dnl # ++ dnl # 6.7 API change ++ dnl # i_atime/i_mtime no longer directly accessible, must use ++ dnl # inode_get_mtime(ip), inode_set_mtime*(ip) to ++ dnl # read/write. ++ dnl # ++ ZFS_LINUX_TEST_SRC([inode_get_atime], [ ++ #include ++ ],[ ++ struct inode ip; ++ ++ memset(&ip, 0, sizeof(ip)); ++ inode_get_atime(&ip); ++ ]) ++ ZFS_LINUX_TEST_SRC([inode_get_mtime], [ ++ #include ++ ],[ ++ struct inode ip; ++ ++ memset(&ip, 0, sizeof(ip)); ++ inode_get_mtime(&ip); ++ ]) ++ ++ ZFS_LINUX_TEST_SRC([inode_set_atime_to_ts], [ ++ #include ++ ],[ ++ struct inode ip; ++ struct timespec64 ts = {0}; ++ ++ memset(&ip, 0, sizeof(ip)); ++ inode_set_atime_to_ts(&ip, ts); ++ ]) ++ ZFS_LINUX_TEST_SRC([inode_set_mtime_to_ts], [ ++ #include ++ ],[ ++ struct inode ip; ++ struct timespec64 ts = {0}; ++ ++ memset(&ip, 0, sizeof(ip)); ++ inode_set_mtime_to_ts(&ip, ts); ++ ]) + ]) + + AC_DEFUN([ZFS_AC_KERNEL_INODE_TIMES], [ +@@ -90,4 +132,40 @@ AC_DEFUN([ZFS_AC_KERNEL_INODE_TIMES], [ + ],[ + AC_MSG_RESULT(no) + ]) ++ ++ AC_MSG_CHECKING([whether inode_get_atime() exists]) ++ ZFS_LINUX_TEST_RESULT([inode_get_atime], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_INODE_GET_ATIME, 1, ++ [inode_get_atime() exists in linux/fs.h]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++ ++ AC_MSG_CHECKING([whether inode_set_atime_to_ts() exists]) ++ ZFS_LINUX_TEST_RESULT([inode_set_atime_to_ts], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_INODE_SET_ATIME_TO_TS, 1, ++ [inode_set_atime_to_ts() exists in linux/fs.h]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++ ++ AC_MSG_CHECKING([whether inode_get_mtime() exists]) ++ ZFS_LINUX_TEST_RESULT([inode_get_mtime], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_INODE_GET_MTIME, 1, ++ [inode_get_mtime() exists in linux/fs.h]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++ ++ AC_MSG_CHECKING([whether inode_set_mtime_to_ts() exists]) ++ ZFS_LINUX_TEST_RESULT([inode_set_mtime_to_ts], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_INODE_SET_MTIME_TO_TS, 1, ++ [inode_set_mtime_to_ts() exists in linux/fs.h]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) + ]) +diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h +index 9b729be6d74d..91a4751fffb0 100644 +--- a/include/os/linux/zfs/sys/zpl.h ++++ b/include/os/linux/zfs/sys/zpl.h +@@ -273,5 +273,25 @@ extern long zpl_ioctl_fideduperange(struct file *filp, void *arg); + #else + #define zpl_inode_set_ctime_to_ts(ip, ts) (ip->i_ctime = ts) + #endif ++#ifdef HAVE_INODE_GET_ATIME ++#define zpl_inode_get_atime(ip) inode_get_atime(ip) ++#else ++#define zpl_inode_get_atime(ip) (ip->i_atime) ++#endif ++#ifdef HAVE_INODE_SET_ATIME_TO_TS ++#define zpl_inode_set_atime_to_ts(ip, ts) inode_set_atime_to_ts(ip, ts) ++#else ++#define zpl_inode_set_atime_to_ts(ip, ts) (ip->i_atime = ts) ++#endif ++#ifdef HAVE_INODE_GET_MTIME ++#define zpl_inode_get_mtime(ip) inode_get_mtime(ip) ++#else ++#define zpl_inode_get_mtime(ip) (ip->i_mtime) ++#endif ++#ifdef HAVE_INODE_SET_MTIME_TO_TS ++#define zpl_inode_set_mtime_to_ts(ip, ts) inode_set_mtime_to_ts(ip, ts) ++#else ++#define zpl_inode_set_mtime_to_ts(ip, ts) (ip->i_mtime = ts) ++#endif + + #endif /* _SYS_ZPL_H */ +diff --git a/module/os/linux/zfs/zfs_ctldir.c b/module/os/linux/zfs/zfs_ctldir.c +index 94e25fa0ae8f..54ed70d0394f 100644 +--- a/module/os/linux/zfs/zfs_ctldir.c ++++ b/module/os/linux/zfs/zfs_ctldir.c +@@ -520,8 +520,8 @@ zfsctl_inode_alloc(zfsvfs_t *zfsvfs, uint64_t id, + ip->i_uid = SUID_TO_KUID(0); + ip->i_gid = SGID_TO_KGID(0); + ip->i_blkbits = SPA_MINBLOCKSHIFT; +- ip->i_atime = now; +- ip->i_mtime = now; ++ zpl_inode_set_atime_to_ts(ip, now); ++ zpl_inode_set_mtime_to_ts(ip, now); + zpl_inode_set_ctime_to_ts(ip, now); + ip->i_fop = fops; + ip->i_op = ops; +diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c +index e990f7055f8a..10162f62cda2 100644 +--- a/module/os/linux/zfs/zfs_vnops_os.c ++++ b/module/os/linux/zfs/zfs_vnops_os.c +@@ -2438,15 +2438,17 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zidmap_t *mnt_ns) + + if ((mask & ATTR_ATIME) || zp->z_atime_dirty) { + zp->z_atime_dirty = B_FALSE; +- ZFS_TIME_ENCODE(&ip->i_atime, atime); ++ inode_timespec_t tmp_atime; ++ ZFS_TIME_ENCODE(&tmp_atime, atime); ++ zpl_inode_set_atime_to_ts(ZTOI(zp), tmp_atime); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL, + &atime, sizeof (atime)); + } + + if (mask & (ATTR_MTIME | ATTR_SIZE)) { + ZFS_TIME_ENCODE(&vap->va_mtime, mtime); +- ZTOI(zp)->i_mtime = zpl_inode_timestamp_truncate( +- vap->va_mtime, ZTOI(zp)); ++ zpl_inode_set_mtime_to_ts(ZTOI(zp), ++ zpl_inode_timestamp_truncate(vap->va_mtime, ZTOI(zp))); + + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, + mtime, sizeof (mtime)); +@@ -3660,7 +3662,7 @@ zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc, + caddr_t va; + int err = 0; + uint64_t mtime[2], ctime[2]; +- inode_timespec_t tmp_ctime; ++ inode_timespec_t tmp_ts; + sa_bulk_attr_t bulk[3]; + int cnt = 0; + struct address_space *mapping; +@@ -3824,9 +3826,10 @@ zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc, + &zp->z_pflags, 8); + + /* Preserve the mtime and ctime provided by the inode */ +- ZFS_TIME_ENCODE(&ip->i_mtime, mtime); +- tmp_ctime = zpl_inode_get_ctime(ip); +- ZFS_TIME_ENCODE(&tmp_ctime, ctime); ++ tmp_ts = zpl_inode_get_mtime(ip); ++ ZFS_TIME_ENCODE(&tmp_ts, mtime); ++ tmp_ts = zpl_inode_get_ctime(ip); ++ ZFS_TIME_ENCODE(&tmp_ts, ctime); + zp->z_atime_dirty = B_FALSE; + zp->z_seq++; + +@@ -3880,7 +3883,7 @@ zfs_dirty_inode(struct inode *ip, int flags) + zfsvfs_t *zfsvfs = ITOZSB(ip); + dmu_tx_t *tx; + uint64_t mode, atime[2], mtime[2], ctime[2]; +- inode_timespec_t tmp_ctime; ++ inode_timespec_t tmp_ts; + sa_bulk_attr_t bulk[4]; + int error = 0; + int cnt = 0; +@@ -3925,10 +3928,12 @@ zfs_dirty_inode(struct inode *ip, int flags) + SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16); + + /* Preserve the mode, mtime and ctime provided by the inode */ +- ZFS_TIME_ENCODE(&ip->i_atime, atime); +- ZFS_TIME_ENCODE(&ip->i_mtime, mtime); +- tmp_ctime = zpl_inode_get_ctime(ip); +- ZFS_TIME_ENCODE(&tmp_ctime, ctime); ++ tmp_ts = zpl_inode_get_atime(ip); ++ ZFS_TIME_ENCODE(&tmp_ts, atime); ++ tmp_ts = zpl_inode_get_mtime(ip); ++ ZFS_TIME_ENCODE(&tmp_ts, mtime); ++ tmp_ts = zpl_inode_get_ctime(ip); ++ ZFS_TIME_ENCODE(&tmp_ts, ctime); + mode = ip->i_mode; + + zp->z_mode = mode; +@@ -3971,7 +3976,9 @@ zfs_inactive(struct inode *ip) + if (error) { + dmu_tx_abort(tx); + } else { +- ZFS_TIME_ENCODE(&ip->i_atime, atime); ++ inode_timespec_t tmp_atime; ++ tmp_atime = zpl_inode_get_atime(ip); ++ ZFS_TIME_ENCODE(&tmp_atime, atime); + mutex_enter(&zp->z_lock); + (void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs), + (void *)&atime, sizeof (atime), tx); +diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c +index f71026da83cb..b99df188c64b 100644 +--- a/module/os/linux/zfs/zfs_znode.c ++++ b/module/os/linux/zfs/zfs_znode.c +@@ -542,7 +542,7 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, + uint64_t links; + uint64_t z_uid, z_gid; + uint64_t atime[2], mtime[2], ctime[2], btime[2]; +- inode_timespec_t tmp_ctime; ++ inode_timespec_t tmp_ts; + uint64_t projid = ZFS_DEFAULT_PROJID; + sa_bulk_attr_t bulk[12]; + int count = 0; +@@ -614,10 +614,12 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, + if (zp->z_pflags & ZFS_XATTR) + zp->z_xattr_parent = parent; + +- ZFS_TIME_DECODE(&ip->i_atime, atime); +- ZFS_TIME_DECODE(&ip->i_mtime, mtime); +- ZFS_TIME_DECODE(&tmp_ctime, ctime); +- zpl_inode_set_ctime_to_ts(ip, tmp_ctime); ++ ZFS_TIME_DECODE(&tmp_ts, atime); ++ zpl_inode_set_atime_to_ts(ip, tmp_ts); ++ ZFS_TIME_DECODE(&tmp_ts, mtime); ++ zpl_inode_set_mtime_to_ts(ip, tmp_ts); ++ ZFS_TIME_DECODE(&tmp_ts, ctime); ++ zpl_inode_set_ctime_to_ts(ip, tmp_ts); + ZFS_TIME_DECODE(&zp->z_btime, btime); + + ip->i_ino = zp->z_id; +@@ -1197,7 +1199,7 @@ zfs_rezget(znode_t *zp) + uint64_t gen; + uint64_t z_uid, z_gid; + uint64_t atime[2], mtime[2], ctime[2], btime[2]; +- inode_timespec_t tmp_ctime; ++ inode_timespec_t tmp_ts; + uint64_t projid = ZFS_DEFAULT_PROJID; + znode_hold_t *zh; + +@@ -1290,10 +1292,12 @@ zfs_rezget(znode_t *zp) + zfs_uid_write(ZTOI(zp), z_uid); + zfs_gid_write(ZTOI(zp), z_gid); + +- ZFS_TIME_DECODE(&ZTOI(zp)->i_atime, atime); +- ZFS_TIME_DECODE(&ZTOI(zp)->i_mtime, mtime); +- ZFS_TIME_DECODE(&tmp_ctime, ctime); +- zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ctime); ++ ZFS_TIME_DECODE(&tmp_ts, atime); ++ zpl_inode_set_atime_to_ts(ZTOI(zp), tmp_ts); ++ ZFS_TIME_DECODE(&tmp_ts, mtime); ++ zpl_inode_set_mtime_to_ts(ZTOI(zp), tmp_ts); ++ ZFS_TIME_DECODE(&tmp_ts, ctime); ++ zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ts); + ZFS_TIME_DECODE(&zp->z_btime, btime); + + if ((uint32_t)gen != ZTOI(zp)->i_generation) { +@@ -1401,22 +1405,24 @@ zfs_zinactive(znode_t *zp) + boolean_t + zfs_relatime_need_update(const struct inode *ip) + { +- inode_timespec_t now, tmp_ctime; ++ inode_timespec_t now, tmp_atime, tmp_ts; + + gethrestime(&now); ++ tmp_atime = zpl_inode_get_atime(ip); + /* + * In relatime mode, only update the atime if the previous atime + * is earlier than either the ctime or mtime or if at least a day + * has passed since the last update of atime. + */ +- if (zfs_compare_timespec(&ip->i_mtime, &ip->i_atime) >= 0) ++ tmp_ts = zpl_inode_get_mtime(ip); ++ if (zfs_compare_timespec(&tmp_ts, &tmp_atime) >= 0) + return (B_TRUE); + +- tmp_ctime = zpl_inode_get_ctime(ip); +- if (zfs_compare_timespec(&tmp_ctime, &ip->i_atime) >= 0) ++ tmp_ts = zpl_inode_get_ctime(ip); ++ if (zfs_compare_timespec(&tmp_ts, &tmp_atime) >= 0) + return (B_TRUE); + +- if ((hrtime_t)now.tv_sec - (hrtime_t)ip->i_atime.tv_sec >= 24*60*60) ++ if ((hrtime_t)now.tv_sec - (hrtime_t)tmp_atime.tv_sec >= 24*60*60) + return (B_TRUE); + + return (B_FALSE); +@@ -1439,7 +1445,7 @@ void + zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], + uint64_t ctime[2]) + { +- inode_timespec_t now, tmp_ctime; ++ inode_timespec_t now, tmp_ts; + + gethrestime(&now); + +@@ -1447,7 +1453,8 @@ zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], + + if (flag & ATTR_MTIME) { + ZFS_TIME_ENCODE(&now, mtime); +- ZFS_TIME_DECODE(&(ZTOI(zp)->i_mtime), mtime); ++ ZFS_TIME_DECODE(&tmp_ts, mtime); ++ zpl_inode_set_mtime_to_ts(ZTOI(zp), tmp_ts); + if (ZTOZSB(zp)->z_use_fuids) { + zp->z_pflags |= (ZFS_ARCHIVE | + ZFS_AV_MODIFIED); +@@ -1456,8 +1463,8 @@ zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2], + + if (flag & ATTR_CTIME) { + ZFS_TIME_ENCODE(&now, ctime); +- ZFS_TIME_DECODE(&tmp_ctime, ctime); +- zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ctime); ++ ZFS_TIME_DECODE(&tmp_ts, ctime); ++ zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ts); + if (ZTOZSB(zp)->z_use_fuids) + zp->z_pflags |= ZFS_ARCHIVE; + } +diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c +index 96f65b9e94e2..ad1753f7a071 100644 +--- a/module/os/linux/zfs/zpl_inode.c ++++ b/module/os/linux/zfs/zpl_inode.c +@@ -526,7 +526,8 @@ zpl_setattr(struct dentry *dentry, struct iattr *ia) + vap->va_ctime = ia->ia_ctime; + + if (vap->va_mask & ATTR_ATIME) +- ip->i_atime = zpl_inode_timestamp_truncate(ia->ia_atime, ip); ++ zpl_inode_set_atime_to_ts(ip, ++ zpl_inode_timestamp_truncate(ia->ia_atime, ip)); + + cookie = spl_fstrans_mark(); + #ifdef HAVE_USERNS_IOPS_SETATTR + +From d7ebc6a92c45f5a62510f25b5544ad2ef7333c2c Mon Sep 17 00:00:00 2001 +From: Rob Norris +Date: Sat, 16 Dec 2023 17:39:07 +1100 +Subject: [PATCH 3/4] linux 6.7 compat: handle superblock shrinker member + change + +In 6.7 the superblock shrinker member s_shrink has changed from being an +embedded struct to a pointer. Detect this, and don't take a reference if +it already is one. + +Signed-off-by: Rob Norris +Sponsored-by: https://github.com/sponsors/robn +--- + config/kernel-shrink.m4 | 35 +++++++++++++++++++++++++++++++- + module/os/linux/zfs/zfs_vfsops.c | 10 +++++++-- + 2 files changed, 42 insertions(+), 3 deletions(-) + +diff --git a/config/kernel-shrink.m4 b/config/kernel-shrink.m4 +index 0c702153e8c4..1c5f753d411c 100644 +--- a/config/kernel-shrink.m4 ++++ b/config/kernel-shrink.m4 +@@ -19,12 +19,44 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_SUPER_BLOCK_S_SHRINK], [ + ],[]) + ]) + ++dnl # ++dnl # 6.7 API change ++dnl # s_shrink is now a pointer. ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_SRC_SUPER_BLOCK_S_SHRINK_PTR], [ ++ ZFS_LINUX_TEST_SRC([super_block_s_shrink_ptr], [ ++ #include ++ unsigned long shrinker_cb(struct shrinker *shrink, ++ struct shrink_control *sc) { return 0; } ++ static struct shrinker shrinker = { ++ .count_objects = shrinker_cb, ++ .scan_objects = shrinker_cb, ++ .seeks = DEFAULT_SEEKS, ++ }; ++ static const struct super_block ++ sb __attribute__ ((unused)) = { ++ .s_shrink = &shrinker, ++ }; ++ ],[]) ++]) ++ + AC_DEFUN([ZFS_AC_KERNEL_SUPER_BLOCK_S_SHRINK], [ + AC_MSG_CHECKING([whether super_block has s_shrink]) + ZFS_LINUX_TEST_RESULT([super_block_s_shrink], [ + AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_SUPER_BLOCK_S_SHRINK, 1, ++ [have super_block s_shrink]) + ],[ +- ZFS_LINUX_TEST_ERROR([sb->s_shrink()]) ++ AC_MSG_RESULT(no) ++ AC_MSG_CHECKING([whether super_block has s_shrink pointer]) ++ ZFS_LINUX_TEST_RESULT([super_block_s_shrink_ptr], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_SUPER_BLOCK_S_SHRINK_PTR, 1, ++ [have super_block s_shrink pointer]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ZFS_LINUX_TEST_ERROR([sb->s_shrink()]) ++ ]) + ]) + ]) + +@@ -174,6 +206,7 @@ AC_DEFUN([ZFS_AC_KERNEL_SHRINK_CONTROL_STRUCT], [ + + AC_DEFUN([ZFS_AC_KERNEL_SRC_SHRINKER], [ + ZFS_AC_KERNEL_SRC_SUPER_BLOCK_S_SHRINK ++ ZFS_AC_KERNEL_SRC_SUPER_BLOCK_S_SHRINK_PTR + ZFS_AC_KERNEL_SRC_SHRINK_CONTROL_HAS_NID + ZFS_AC_KERNEL_SRC_SHRINKER_CALLBACK + ZFS_AC_KERNEL_SRC_SHRINK_CONTROL_STRUCT +diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c +index 2792bc027213..2015c20d7340 100644 +--- a/module/os/linux/zfs/zfs_vfsops.c ++++ b/module/os/linux/zfs/zfs_vfsops.c +@@ -1240,12 +1240,18 @@ zfs_prune_aliases(zfsvfs_t *zfsvfs, unsigned long nr_to_scan) + * and inode caches. This can occur when the ARC needs to free meta data + * blocks but can't because they are all pinned by entries in these caches. + */ ++#if defined(HAVE_SUPER_BLOCK_S_SHRINK) ++#define S_SHRINK(sb) (&(sb)->s_shrink) ++#elif defined(HAVE_SUPER_BLOCK_S_SHRINK_PTR) ++#define S_SHRINK(sb) ((sb)->s_shrink) ++#endif ++ + int + zfs_prune(struct super_block *sb, unsigned long nr_to_scan, int *objects) + { + zfsvfs_t *zfsvfs = sb->s_fs_info; + int error = 0; +- struct shrinker *shrinker = &sb->s_shrink; ++ struct shrinker *shrinker = S_SHRINK(sb); + struct shrink_control sc = { + .nr_to_scan = nr_to_scan, + .gfp_mask = GFP_KERNEL, +@@ -1257,7 +1263,7 @@ zfs_prune(struct super_block *sb, unsigned long nr_to_scan, int *objects) + #if defined(HAVE_SPLIT_SHRINKER_CALLBACK) && \ + defined(SHRINK_CONTROL_HAS_NID) && \ + defined(SHRINKER_NUMA_AWARE) +- if (sb->s_shrink.flags & SHRINKER_NUMA_AWARE) { ++ if (shrinker->flags & SHRINKER_NUMA_AWARE) { + *objects = 0; + for_each_online_node(sc.nid) { + *objects += (*shrinker->scan_objects)(shrinker, &sc); + +From e0cc696208e903cb00fe23bf7f5dc235c6995122 Mon Sep 17 00:00:00 2001 +From: Rob Norris +Date: Sun, 17 Dec 2023 00:36:21 +1100 +Subject: [PATCH 4/4] linux 6.7 compat: rework shrinker setup for heap + allocations + +6.7 changes the shrinker API such that shrinkers must be allocated +dynamically by the kernel. To accomodate this, this commit reworks +spl_register_shrinker() to do something similar against earlier kernels. + +Signed-off-by: Rob Norris +Sponsored-by: https://github.com/sponsors/robn +--- + config/kernel-shrink.m4 | 52 +++++++++++-- + include/os/linux/spl/sys/shrinker.h | 66 +++++----------- + module/Kbuild.in | 1 + + module/os/linux/spl/spl-shrinker.c | 115 ++++++++++++++++++++++++++++ + module/os/linux/zfs/arc_os.c | 11 ++- + 5 files changed, 189 insertions(+), 56 deletions(-) + create mode 100644 module/os/linux/spl/spl-shrinker.c + +diff --git a/config/kernel-shrink.m4 b/config/kernel-shrink.m4 +index 1c5f753d411c..4a529c43b5b0 100644 +--- a/config/kernel-shrink.m4 ++++ b/config/kernel-shrink.m4 +@@ -128,6 +128,25 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_SHRINKER_CALLBACK], [ + ]) + ]) + ++dnl # ++dnl # 6.7 API change ++dnl # register_shrinker has been replaced by shrinker_register. ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_SRC_SHRINKER_REGISTER], [ ++ ZFS_LINUX_TEST_SRC([shrinker_register], [ ++ #include ++ unsigned long shrinker_cb(struct shrinker *shrink, ++ struct shrink_control *sc) { return 0; } ++ ],[ ++ struct shrinker cache_shrinker = { ++ .count_objects = shrinker_cb, ++ .scan_objects = shrinker_cb, ++ .seeks = DEFAULT_SEEKS, ++ }; ++ shrinker_register(&cache_shrinker); ++ ]) ++]) ++ + AC_DEFUN([ZFS_AC_KERNEL_SHRINKER_CALLBACK],[ + dnl # + dnl # 6.0 API change +@@ -165,14 +184,36 @@ AC_DEFUN([ZFS_AC_KERNEL_SHRINKER_CALLBACK],[ + dnl # cs->shrink() is logically split in to + dnl # cs->count_objects() and cs->scan_objects() + dnl # +- AC_MSG_CHECKING([if cs->count_objects callback exists]) ++ AC_MSG_CHECKING( ++ [whether cs->count_objects callback exists]) + ZFS_LINUX_TEST_RESULT( +- [shrinker_cb_shrink_control_split],[ +- AC_MSG_RESULT(yes) +- AC_DEFINE(HAVE_SPLIT_SHRINKER_CALLBACK, 1, +- [cs->count_objects exists]) ++ [shrinker_cb_shrink_control_split],[ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_SPLIT_SHRINKER_CALLBACK, 1, ++ [cs->count_objects exists]) + ],[ ++ AC_MSG_RESULT(no) ++ ++ AC_MSG_CHECKING( ++ [whether shrinker_register exists]) ++ ZFS_LINUX_TEST_RESULT([shrinker_register], [ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(HAVE_SHRINKER_REGISTER, 1, ++ [shrinker_register exists]) ++ ++ dnl # We assume that the split shrinker ++ dnl # callback exists if ++ dnl # shrinker_register() exists, ++ dnl # because the latter is a much more ++ dnl # recent addition, and the macro ++ dnl # test for shrinker_register() only ++ dnl # works if the callback is split ++ AC_DEFINE(HAVE_SPLIT_SHRINKER_CALLBACK, ++ 1, [cs->count_objects exists]) ++ ],[ ++ AC_MSG_RESULT(no) + ZFS_LINUX_TEST_ERROR([shrinker]) ++ ]) + ]) + ]) + ]) +@@ -211,6 +252,7 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_SHRINKER], [ + ZFS_AC_KERNEL_SRC_SHRINKER_CALLBACK + ZFS_AC_KERNEL_SRC_SHRINK_CONTROL_STRUCT + ZFS_AC_KERNEL_SRC_REGISTER_SHRINKER_VARARG ++ ZFS_AC_KERNEL_SRC_SHRINKER_REGISTER + ]) + + AC_DEFUN([ZFS_AC_KERNEL_SHRINKER], [ +diff --git a/include/os/linux/spl/sys/shrinker.h b/include/os/linux/spl/sys/shrinker.h +index d472754be4f4..bca4c850694a 100644 +--- a/include/os/linux/spl/sys/shrinker.h ++++ b/include/os/linux/spl/sys/shrinker.h +@@ -29,12 +29,13 @@ + + /* + * Due to frequent changes in the shrinker API the following +- * compatibility wrappers should be used. They are as follows: ++ * compatibility wrapper should be used. + * +- * SPL_SHRINKER_DECLARE(varname, countfunc, scanfunc, seek_cost); ++ * shrinker = spl_register_shrinker(name, countfunc, scanfunc, seek_cost); ++ * spl_unregister_shrinker(shrinker); + * +- * SPL_SHRINKER_DECLARE is used to declare a shrinker with the name varname, +- * which is passed to spl_register_shrinker()/spl_unregister_shrinker(). ++ * spl_register_shrinker is used to create and register a shrinker with the ++ * given name. + * The countfunc returns the number of free-able objects. + * The scanfunc returns the number of objects that were freed. + * The callbacks can return SHRINK_STOP if further calls can't make any more +@@ -57,57 +58,28 @@ + * ...scan objects in the cache and reclaim them... + * } + * +- * SPL_SHRINKER_DECLARE(my_shrinker, my_count, my_scan, DEFAULT_SEEKS); ++ * static struct shrinker *my_shrinker; + * + * void my_init_func(void) { +- * spl_register_shrinker(&my_shrinker); ++ * my_shrinker = spl_register_shrinker("my-shrinker", ++ * my_count, my_scan, DEFAULT_SEEKS); ++ * } ++ * ++ * void my_fini_func(void) { ++ * spl_unregister_shrinker(my_shrinker); + * } + */ + +-#ifdef HAVE_REGISTER_SHRINKER_VARARG +-#define spl_register_shrinker(x) register_shrinker(x, "zfs-arc-shrinker") +-#else +-#define spl_register_shrinker(x) register_shrinker(x) +-#endif +-#define spl_unregister_shrinker(x) unregister_shrinker(x) ++typedef unsigned long (*spl_shrinker_cb) ++ (struct shrinker *, struct shrink_control *); + +-/* +- * Linux 3.0 to 3.11 Shrinker API Compatibility. +- */ +-#if defined(HAVE_SINGLE_SHRINKER_CALLBACK) +-#define SPL_SHRINKER_DECLARE(varname, countfunc, scanfunc, seek_cost) \ +-static int \ +-__ ## varname ## _wrapper(struct shrinker *shrink, struct shrink_control *sc)\ +-{ \ +- if (sc->nr_to_scan != 0) { \ +- (void) scanfunc(shrink, sc); \ +- } \ +- return (countfunc(shrink, sc)); \ +-} \ +- \ +-static struct shrinker varname = { \ +- .shrink = __ ## varname ## _wrapper, \ +- .seeks = seek_cost, \ +-} ++struct shrinker *spl_register_shrinker(const char *name, ++ spl_shrinker_cb countfunc, spl_shrinker_cb scanfunc, int seek_cost); ++void spl_unregister_shrinker(struct shrinker *); + ++#ifndef SHRINK_STOP ++/* 3.0-3.11 compatibility */ + #define SHRINK_STOP (-1) +- +-/* +- * Linux 3.12 and later Shrinker API Compatibility. +- */ +-#elif defined(HAVE_SPLIT_SHRINKER_CALLBACK) +-#define SPL_SHRINKER_DECLARE(varname, countfunc, scanfunc, seek_cost) \ +-static struct shrinker varname = { \ +- .count_objects = countfunc, \ +- .scan_objects = scanfunc, \ +- .seeks = seek_cost, \ +-} +- +-#else +-/* +- * Linux 2.x to 2.6.22, or a newer shrinker API has been introduced. +- */ +-#error "Unknown shrinker callback" + #endif + + #endif /* SPL_SHRINKER_H */ +diff --git a/module/Kbuild.in b/module/Kbuild.in +index e34b9fab9efc..fb22bfe733c0 100644 +--- a/module/Kbuild.in ++++ b/module/Kbuild.in +@@ -79,6 +79,7 @@ SPL_OBJS := \ + spl-kstat.o \ + spl-proc.o \ + spl-procfs-list.o \ ++ spl-shrinker.o \ + spl-taskq.o \ + spl-thread.o \ + spl-trace.o \ +diff --git a/module/os/linux/spl/spl-shrinker.c b/module/os/linux/spl/spl-shrinker.c +new file mode 100644 +index 000000000000..d5c8da471cbb +--- /dev/null ++++ b/module/os/linux/spl/spl-shrinker.c +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. ++ * Copyright (C) 2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Brian Behlendorf . ++ * UCRL-CODE-235197 ++ * ++ * This file is part of the SPL, Solaris Porting Layer. ++ * ++ * The SPL is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * The SPL is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with the SPL. If not, see . ++ * ++ * Solaris Porting Layer (SPL) Shrinker Implementation. ++ */ ++ ++#include ++#include ++ ++#ifdef HAVE_SINGLE_SHRINKER_CALLBACK ++/* 3.0-3.11: single shrink() callback, which we wrap to carry both functions */ ++struct spl_shrinker_wrap { ++ struct shrinker shrinker; ++ spl_shrinker_cb countfunc; ++ spl_shrinker_cb scanfunc; ++}; ++ ++static int ++spl_shrinker_single_cb(struct shrinker *shrinker, struct shrink_control *sc) ++{ ++ struct spl_shrinker_wrap *sw = (struct spl_shrinker_wrap *)shrinker; ++ ++ if (sc->nr_to_scan != 0) ++ (void) sw->scanfunc(&sw->shrinker, sc); ++ return (sw->countfunc(&sw->shrinker, sc)); ++} ++#endif ++ ++struct shrinker * ++spl_register_shrinker(const char *name, spl_shrinker_cb countfunc, ++ spl_shrinker_cb scanfunc, int seek_cost) ++{ ++ struct shrinker *shrinker; ++ ++ /* allocate shrinker */ ++#if defined(HAVE_SHRINKER_REGISTER) ++ /* 6.7: kernel will allocate the shrinker for us */ ++ shrinker = shrinker_alloc(0, name); ++#elif defined(HAVE_SPLIT_SHRINKER_CALLBACK) ++ /* 3.12-6.6: we allocate the shrinker */ ++ shrinker = kmem_zalloc(sizeof (struct shrinker), KM_SLEEP); ++#elif defined(HAVE_SINGLE_SHRINKER_CALLBACK) ++ /* 3.0-3.11: allocate a wrapper */ ++ struct spl_shrinker_wrap *sw = ++ kmem_zalloc(sizeof (struct spl_shrinker_wrap), KM_SLEEP); ++ shrinker = &sw->shrinker; ++#else ++ /* 2.x-2.6.22, or a newer shrinker API has been introduced. */ ++#error "Unknown shrinker API" ++#endif ++ ++ if (shrinker == NULL) ++ return (NULL); ++ ++ /* set callbacks */ ++#ifdef HAVE_SINGLE_SHRINKER_CALLBACK ++ sw->countfunc = countfunc; ++ sw->scanfunc = scanfunc; ++ shrinker->shrink = spl_shrinker_single_cb; ++#else ++ shrinker->count_objects = countfunc; ++ shrinker->scan_objects = scanfunc; ++#endif ++ ++ /* set params */ ++ shrinker->seeks = seek_cost; ++ ++ /* register with kernel */ ++#if defined(HAVE_SHRINKER_REGISTER) ++ shrinker_register(shrinker); ++#elif defined(HAVE_REGISTER_SHRINKER_VARARG) ++ register_shrinker(shrinker, name); ++#else ++ register_shrinker(shrinker); ++#endif ++ ++ return (shrinker); ++} ++EXPORT_SYMBOL(spl_register_shrinker); ++ ++void ++spl_unregister_shrinker(struct shrinker *shrinker) ++{ ++#if defined(HAVE_SHRINKER_REGISTER) ++ shrinker_free(shrinker); ++#elif defined(HAVE_SPLIT_SHRINKER_CALLBACK) ++ unregister_shrinker(shrinker); ++ kmem_free(shrinker, sizeof (struct shrinker)); ++#elif defined(HAVE_SINGLE_SHRINKER_CALLBACK) ++ unregister_shrinker(shrinker); ++ kmem_free(shrinker, sizeof (struct spl_shrinker_wrap)); ++#else ++#error "Unknown shrinker API" ++#endif ++} ++EXPORT_SYMBOL(spl_unregister_shrinker); +diff --git a/module/os/linux/zfs/arc_os.c b/module/os/linux/zfs/arc_os.c +index 55cdbba5b5eb..02dd80c06062 100644 +--- a/module/os/linux/zfs/arc_os.c ++++ b/module/os/linux/zfs/arc_os.c +@@ -253,8 +253,7 @@ arc_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc) + return (sc->nr_to_scan); + } + +-SPL_SHRINKER_DECLARE(arc_shrinker, +- arc_shrinker_count, arc_shrinker_scan, DEFAULT_SEEKS); ++static struct shrinker *arc_shrinker = NULL; + + int + arc_memory_throttle(spa_t *spa, uint64_t reserve, uint64_t txg) +@@ -357,14 +356,18 @@ arc_lowmem_init(void) + * reclaim from the arc. This is done to prevent kswapd from + * swapping out pages when it is preferable to shrink the arc. + */ +- spl_register_shrinker(&arc_shrinker); ++ arc_shrinker = spl_register_shrinker("zfs-arc-shrinker", ++ arc_shrinker_count, arc_shrinker_scan, DEFAULT_SEEKS); ++ VERIFY(arc_shrinker); ++ + arc_set_sys_free(allmem); + } + + void + arc_lowmem_fini(void) + { +- spl_unregister_shrinker(&arc_shrinker); ++ spl_unregister_shrinker(arc_shrinker); ++ arc_shrinker = NULL; + } + + int