Package: zfs-linux Version: 0.7.5-1 Severity: normal --- Please enter the report below this line. --- A longstanding incorrect calculation of free memory, since Linux 3.14, has been fixed in upstream master.
I have submitted a PR to upstream that re-bases the patch onto the 0.7.6 stable release [1]. Additionally, the fix introduces a new file needed during module build, scripts/enum-extract.pl, which must therefore be packaged with the dkms source. All of this has included in the attached patch, and on [2] in pulls/proposed-0.7.6. That branch also includes a decompression out of bounds check, as well as the 0.7.6 release import. [1] https://github.com/zfsonlinux/zfs/pull/7238 [2] https://github.com/aerusso/zfs
commit 87d8977aa53192a647c8bdc99e14a6ab6dfd5560 Author: Antonio Russo <antonio.e.ru...@gmail.com> Date: Sun Feb 25 21:18:10 2018 -0500 Linux 3.14 compat: fix free memory calculation diff --git a/debian/patches/00004-compat-fix_free_memory_calculation.patch b/debian/patches/00004-compat-fix_free_memory_calculation.patch new file mode 100644 index 00000000..4cadf854 --- /dev/null +++ b/debian/patches/00004-compat-fix_free_memory_calculation.patch @@ -0,0 +1,425 @@ +commit 697dd9746d93740d81c4f5a722b03aa0d9344b0b +Author: chrisrd <ch...@onthe.net.au> +Date: Sat Feb 24 03:50:06 2018 +1100 + + Fix free memory calculation on v3.14+ + + Provide infrastructure to auto-configure to enum and API changes in the + global page stats used for our free memory calculations. + + arc_free_memory has been broken since an API change in Linux v3.14: + + 2016-07-28 v4.8 599d0c95 mm, vmscan: move LRU lists to node + 2016-07-28 v4.8 75ef7184 mm, vmstat: add infrastructure for per-node + vmstats + + These commits moved some of global_page_state() into + global_node_page_state(). The API change was particularly egregious as, + instead of breaking the old code, it silently did the wrong thing and we + continued using global_page_state() where we should have been using + global_node_page_state(), thus indexing into the wrong array via + NR_SLAB_RECLAIMABLE et al. + + There have been further API changes along the way: + + 2017-07-06 v4.13 385386cf mm: vmstat: move slab statistics from zone to + node counters + 2017-09-06 v4.14 c41f012a mm: rename global_page_state to + global_zone_page_state + + ...and various (incomplete, as it turns out) attempts to accomodate + these changes in ZoL: + + 2017-08-24 2209e409 Linux 4.8+ compatibility fix for vm stats + 2017-09-16 787acae0 Linux 3.14 compat: IO acct, global_page_state, etc + 2017-09-19 661907e6 Linux 4.14 compat: IO acct, global_page_state, etc + + The config infrastructure provided here resolves these issues going back + to the original API change in v3.14 and is robust against further Linux + changes in this area. + + Reviewed-by: Giuseppe Di Natale <dinata...@llnl.gov> + Reviewed-by: Brian Behlendorf <behlendo...@llnl.gov> + Reviewed-by: George Melikov <m...@gmelikov.ru> + Signed-off-by: Chris Dunlop <ch...@onthe.net.au> + Closes #7170 + +diff --git a/config/kernel-global_page_state.m4 b/config/kernel-global_page_state.m4 +new file mode 100644 +index 000000000..f4a40011f +--- /dev/null ++++ b/config/kernel-global_page_state.m4 +@@ -0,0 +1,109 @@ ++dnl # ++dnl # 4.8 API change ++dnl # ++dnl # 75ef71840539 mm, vmstat: add infrastructure for per-node vmstats ++dnl # 599d0c954f91 mm, vmscan: move LRU lists to node ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE], [ ++ AC_MSG_CHECKING([whether global_node_page_state() exists]) ++ ZFS_LINUX_TRY_COMPILE([ ++ #include <linux/mm.h> ++ #include <linux/vmstat.h> ++ ],[ ++ (void) global_node_page_state(0); ++ ],[ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, [global_node_page_state() exists]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++]) ++ ++dnl # ++dnl # 4.14 API change ++dnl # ++dnl # c41f012ade0b mm: rename global_page_state to global_zone_page_state ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE], [ ++ AC_MSG_CHECKING([whether global_zone_page_state() exists]) ++ ZFS_LINUX_TRY_COMPILE([ ++ #include <linux/mm.h> ++ #include <linux/vmstat.h> ++ ],[ ++ (void) global_zone_page_state(0); ++ ],[ ++ AC_MSG_RESULT(yes) ++ AC_DEFINE(ZFS_GLOBAL_ZONE_PAGE_STATE, 1, [global_zone_page_state() exists]) ++ ],[ ++ AC_MSG_RESULT(no) ++ ]) ++]) ++ ++dnl # ++dnl # Create a define and autoconf variable for an enum member ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_ENUM_MEMBER], [ ++ AC_MSG_CHECKING([whether enum $2 contains $1]) ++ AS_IF([AC_TRY_COMMAND("${srcdir}/scripts/enum-extract.pl" "$2" "$3" | egrep -qx $1)],[ ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE(m4_join([_], [ZFS_ENUM], m4_toupper($2), $1), 1, [enum $2 contains $1]) ++ m4_join([_], [ZFS_ENUM], m4_toupper($2), $1)=1 ++ ],[ ++ AC_MSG_RESULT([no]) ++ ]) ++]) ++ ++dnl # ++dnl # Sanity check helpers ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR],[ ++ AC_MSG_RESULT(no) ++ AC_MSG_RESULT([$1 in either node_stat_item or zone_stat_item: $2]) ++ AC_MSG_RESULT([configure needs updating, see: config/kernel-global_page_state.m4]) ++ AC_MSG_FAILURE([SHUT 'ER DOWN CLANCY, SHE'S PUMPIN' MUD!]) ++]) ++ ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK], [ ++ enum_check_a="m4_join([_], [$ZFS_ENUM_NODE_STAT_ITEM], $1)" ++ enum_check_b="m4_join([_], [$ZFS_ENUM_ZONE_STAT_ITEM], $1)" ++ AS_IF([test -n "$enum_check_a" -a -n "$enum_check_b"],[ ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [DUPLICATE]) ++ ]) ++ AS_IF([test -z "$enum_check_a" -a -z "$enum_check_b"],[ ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [NOT FOUND]) ++ ]) ++]) ++ ++dnl # ++dnl # Ensure the config tests are finding one and only one of each enum of interest ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY], [ ++ AC_MSG_CHECKING([global_page_state enums are sane]) ++ ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_FILE_PAGES]) ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_ANON]) ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_FILE]) ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_SLAB_RECLAIMABLE]) ++ ++ AC_MSG_RESULT(yes) ++]) ++ ++dnl # ++dnl # enum members in which we're interested ++dnl # ++AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE], [ ++ ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE ++ ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE ++ ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [node_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [node_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [node_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [node_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [zone_stat_item], [$LINUX/include/linux/mmzone.h]) ++ ++ ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY ++]) +diff --git a/config/kernel-vm_node_stat.m4 b/config/kernel-vm_node_stat.m4 +deleted file mode 100644 +index e1c42f884..000000000 +--- a/config/kernel-vm_node_stat.m4 ++++ /dev/null +@@ -1,22 +0,0 @@ +-dnl # +-dnl # 4.8 API change +-dnl # kernel vm counters change +-dnl # +-AC_DEFUN([ZFS_AC_KERNEL_VM_NODE_STAT], [ +- AC_MSG_CHECKING([whether to use vm_node_stat based fn's]) +- ZFS_LINUX_TRY_COMPILE([ +- #include <linux/mm.h> +- #include <linux/vmstat.h> +- ],[ +- int a __attribute__ ((unused)) = NR_VM_NODE_STAT_ITEMS; +- long x __attribute__ ((unused)) = +- atomic_long_read(&vm_node_stat[0]); +- (void) global_node_page_state(0); +- ],[ +- AC_MSG_RESULT(yes) +- AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, +- [using global_node_page_state()]) +- ],[ +- AC_MSG_RESULT(no) +- ]) +-]) +diff --git a/config/kernel.m4 b/config/kernel.m4 +index b759ccd39..c1d2dbaab 100644 +--- a/config/kernel.m4 ++++ b/config/kernel.m4 +@@ -122,7 +122,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ + ZFS_AC_KERNEL_RENAME_WANTS_FLAGS + ZFS_AC_KERNEL_HAVE_GENERIC_SETXATTR + ZFS_AC_KERNEL_CURRENT_TIME +- ZFS_AC_KERNEL_VM_NODE_STAT ++ ZFS_AC_KERNEL_GLOBAL_PAGE_STATE + + AS_IF([test "$LINUX_OBJ" != "$LINUX"], [ + KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$LINUX_OBJ" +diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am +index 9bb0b3493..89c2689f6 100644 +--- a/include/linux/Makefile.am ++++ b/include/linux/Makefile.am +@@ -9,7 +9,8 @@ KERNEL_H = \ + $(top_srcdir)/include/linux/kmap_compat.h \ + $(top_srcdir)/include/linux/simd_x86.h \ + $(top_srcdir)/include/linux/simd_aarch64.h \ +- $(top_srcdir)/include/linux/mod_compat.h ++ $(top_srcdir)/include/linux/mod_compat.h \ ++ $(top_srcdir)/include/linux/page_compat.h + + USER_H = + +diff --git a/include/linux/page_compat.h b/include/linux/page_compat.h +new file mode 100644 +index 000000000..95acb7d53 +--- /dev/null ++++ b/include/linux/page_compat.h +@@ -0,0 +1,78 @@ ++#ifndef _ZFS_PAGE_COMPAT_H ++#define _ZFS_PAGE_COMPAT_H ++ ++/* ++ * We have various enum members moving between two separate enum types, ++ * and accessed by different functions at various times. Centralise the ++ * insanity. ++ * ++ * < v4.8: all enums in zone_stat_item, via global_page_state() ++ * v4.8: some enums moved to node_stat_item, global_node_page_state() introduced ++ * v4.13: some enums moved from zone_stat_item to node_state_item ++ * v4.14: global_page_state() rename to global_zone_page_state() ++ * ++ * The defines used here are created by config/kernel-global_page_state.m4 ++ */ ++ ++/* ++ * Create our own accessor functions to follow the Linux API changes ++ */ ++#if defined(ZFS_GLOBAL_ZONE_PAGE_STATE) ++ ++/* global_zone_page_state() introduced */ ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES) ++#define nr_file_pages() global_node_page_state(NR_FILE_PAGES) ++#else ++#define nr_file_pages() global_zone_page_state(NR_FILE_PAGES) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON) ++#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON) ++#else ++#define nr_inactive_anon_pages() global_zone_page_state(NR_INACTIVE_ANON) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE) ++#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE) ++#else ++#define nr_inactive_file_pages() global_zone_page_state(NR_INACTIVE_FILE) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE) ++#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE) ++#else ++#define nr_slab_reclaimable_pages() global_zone_page_state(NR_SLAB_RECLAIMABLE) ++#endif ++ ++#elif defined(ZFS_GLOBAL_NODE_PAGE_STATE) ++ ++/* global_node_page_state() introduced */ ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES) ++#define nr_file_pages() global_node_page_state(NR_FILE_PAGES) ++#else ++#define nr_file_pages() global_page_state(NR_FILE_PAGES) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON) ++#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON) ++#else ++#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE) ++#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE) ++#else ++#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE) ++#endif ++#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE) ++#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE) ++#else ++#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE) ++#endif ++ ++#else ++ ++/* global_page_state() only */ ++#define nr_file_pages() global_page_state(NR_FILE_PAGES) ++#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON) ++#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE) ++#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE) ++ ++#endif /* ZFS_GLOBAL_ZONE_PAGE_STATE */ ++ ++#endif /* _ZFS_PAGE_COMPAT_H */ +diff --git a/module/zfs/arc.c b/module/zfs/arc.c +index 264e67735..bb39f9c80 100644 +--- a/module/zfs/arc.c ++++ b/module/zfs/arc.c +@@ -280,6 +280,7 @@ + #include <sys/fs/swapnode.h> + #include <sys/zpl.h> + #include <linux/mm_compat.h> ++#include <linux/page_compat.h> + #endif + #include <sys/callb.h> + #include <sys/kstat.h> +@@ -4017,17 +4018,11 @@ arc_free_memory(void) + si_meminfo(&si); + return (ptob(si.freeram - si.freehigh)); + #else +-#ifdef ZFS_GLOBAL_NODE_PAGE_STATE + return (ptob(nr_free_pages() + +- global_node_page_state(NR_INACTIVE_FILE) + +- global_node_page_state(NR_INACTIVE_ANON) + +- global_node_page_state(NR_SLAB_RECLAIMABLE))); +-#else +- return (ptob(nr_free_pages() + +- global_page_state(NR_INACTIVE_FILE) + +- global_page_state(NR_INACTIVE_ANON) + +- global_page_state(NR_SLAB_RECLAIMABLE))); +-#endif /* ZFS_GLOBAL_NODE_PAGE_STATE */ ++ nr_inactive_file_pages() + ++ nr_inactive_anon_pages() + ++ nr_slab_reclaimable_pages())); ++ + #endif /* CONFIG_HIGHMEM */ + #else + return (spa_get_random(arc_all_memory() * 20 / 100)); +@@ -4438,13 +4433,7 @@ arc_evictable_memory(void) + * Scale reported evictable memory in proportion to page cache, cap + * at specified min/max. + */ +-#ifdef ZFS_GLOBAL_NODE_PAGE_STATE +- uint64_t min = (ptob(global_node_page_state(NR_FILE_PAGES)) / 100) * +- zfs_arc_pc_percent; +-#else +- uint64_t min = (ptob(global_page_state(NR_FILE_PAGES)) / 100) * +- zfs_arc_pc_percent; +-#endif ++ uint64_t min = (ptob(nr_file_pages()) / 100) * zfs_arc_pc_percent; + min = MAX(arc_c_min, MIN(arc_c_max, min)); + + if (arc_dirty >= min) +diff --git a/scripts/Makefile.am b/scripts/Makefile.am +index 74b8b31a5..e083fae0f 100644 +--- a/scripts/Makefile.am ++++ b/scripts/Makefile.am +@@ -1,6 +1,6 @@ + SUBDIRS = zpool-config zpios-test zpios-profile + +-EXTRA_DIST = dkms.mkconf dkms.postbuild kmodtool zfs2zol-patch.sed cstyle.pl ++EXTRA_DIST = dkms.mkconf dkms.postbuild enum-extract.pl kmodtool zfs2zol-patch.sed cstyle.pl + + pkgdatadir = $(datadir)/@PACKAGE@ + dist_pkgdata_SCRIPTS = \ +diff --git a/scripts/enum-extract.pl b/scripts/enum-extract.pl +new file mode 100755 +index 000000000..5112cc807 +--- /dev/null ++++ b/scripts/enum-extract.pl +@@ -0,0 +1,58 @@ ++#!/usr/bin/perl -w ++ ++my $usage = <<EOT; ++usage: config-enum enum [file ...] ++ ++Returns the elements from an enum declaration. ++ ++"Best effort": we're not building an entire C interpreter here! ++EOT ++ ++use warnings; ++use strict; ++use Getopt::Std; ++ ++my %opts; ++ ++if (!getopts("", \%opts) || @ARGV < 1) { ++ print $usage; ++ exit 2; ++} ++ ++my $enum = shift; ++ ++my $in_enum = 0; ++ ++while (<>) { ++ # comments ++ s/\/\*.*\*\///; ++ if (m/\/\*/) { ++ while ($_ .= <>) { ++ last if s/\/\*.*\*\///s; ++ } ++ } ++ ++ # preprocessor stuff ++ next if /^#/; ++ ++ # find our enum ++ $in_enum = 1 if s/^\s*enum\s+${enum}(?:\s|$)//; ++ next unless $in_enum; ++ ++ # remove explicit values ++ s/\s*=[^,]+,/,/g; ++ ++ # extract each identifier ++ while (m/\b([a-z_][a-z0-9_]*)\b/ig) { ++ print $1, "\n"; ++ } ++ ++ # ++ # don't exit: there may be multiple versions of the same enum, e.g. ++ # inside different #ifdef blocks. Let's explicitly return all of ++ # them and let external tooling deal with it. ++ # ++ $in_enum = 0 if m/}\s*;/; ++} ++ ++exit 0; diff --git a/debian/patches/series b/debian/patches/series index 2cb08360..8f8b19a2 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,6 +1,7 @@ 00001-compat-4.16-inode_set_iversion.patch 00002-compat-4.11-refcount.patch 00003-fix_lze_out_of_bounds.patch +00004-compat-fix_free_memory_calculation.patch 0001-Prevent-manual-builds-in-the-DKMS-source.patch 0002-Check-for-META-and-DCH-consistency-in-autoconf.patch 0003-Add-libuutil-to-LIBADD-for-libzfs-and-libzfs_core.patch diff --git a/debian/rules b/debian/rules index f445b58c..1e9082ac 100755 --- a/debian/rules +++ b/debian/rules @@ -80,7 +80,8 @@ override_dh_auto_install: @# Install the DKMS source. @# We only want the files needed to build the modules - mkdir -p '$(CURDIR)/debian/tmp/usr/src/$(NAME)-$(DEB_VERSION_UPSTREAM)' + mkdir -p '$(CURDIR)/debian/tmp/usr/src/$(NAME)-$(DEB_VERSION_UPSTREAM)/scripts' + cp '$(CURDIR)/scripts/enum-extract.pl' '$(CURDIR)/debian/tmp/usr/src/$(NAME)-$(DEB_VERSION_UPSTREAM)/scripts' $(foreach file,$(DKMSFILES),mv '$(CURDIR)/$(NAME)-$(DEB_VERSION_UPSTREAM)/$(file)' '$(CURDIR)/debian/tmp/usr/src/$(NAME)-$(DEB_VERSION_UPSTREAM)' || exit 1;) @# Hellish awk line: @# * Deletes from configure.ac the parts not needed for building the kernel module