1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: chrisrd <chris@onthe.net.au>
3 Date: Sat, 24 Feb 2018 03:50:06 +1100
4 Subject: [PATCH] Fix free memory calculation on v3.14+
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
9 Provide infrastructure to auto-configure to enum and API changes in the
10 global page stats used for our free memory calculations.
12 arc_free_memory has been broken since an API change in Linux v3.14:
14 2016-07-28 v4.8 599d0c95 mm, vmscan: move LRU lists to node
15 2016-07-28 v4.8 75ef7184 mm, vmstat: add infrastructure for per-node
18 These commits moved some of global_page_state() into
19 global_node_page_state(). The API change was particularly egregious as,
20 instead of breaking the old code, it silently did the wrong thing and we
21 continued using global_page_state() where we should have been using
22 global_node_page_state(), thus indexing into the wrong array via
23 NR_SLAB_RECLAIMABLE et al.
25 There have been further API changes along the way:
27 2017-07-06 v4.13 385386cf mm: vmstat: move slab statistics from zone to
29 2017-09-06 v4.14 c41f012a mm: rename global_page_state to
30 global_zone_page_state
32 ...and various (incomplete, as it turns out) attempts to accomodate
35 2017-08-24 2209e409 Linux 4.8+ compatibility fix for vm stats
36 2017-09-16 787acae0 Linux 3.14 compat: IO acct, global_page_state, etc
37 2017-09-19 661907e6 Linux 4.14 compat: IO acct, global_page_state, etc
39 The config infrastructure provided here resolves these issues going back
40 to the original API change in v3.14 and is robust against further Linux
43 Reviewed-by: Giuseppe Di Natale <dinatale2@llnl.gov>
44 Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
45 Reviewed-by: George Melikov <mail@gmelikov.ru>
46 Signed-off-by: Chris Dunlop <chris@onthe.net.au>
48 (cherry picked from commit 338523dd6ec641cc4d552c3f67e1becfb9e22b0a)
49 Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
51 include/linux/Makefile.am | 3 +-
52 scripts/Makefile.am | 2 +
53 include/linux/page_compat.h | 78 ++++++++++++++++++++++++++
54 module/zfs/arc.c | 23 ++------
55 config/kernel-global_page_state.m4 | 109 +++++++++++++++++++++++++++++++++++++
56 config/kernel-vm_node_stat.m4 | 22 --------
57 config/kernel.m4 | 2 +-
58 scripts/enum-extract.pl | 58 ++++++++++++++++++++
59 8 files changed, 256 insertions(+), 41 deletions(-)
60 create mode 100644 include/linux/page_compat.h
61 create mode 100644 config/kernel-global_page_state.m4
62 delete mode 100644 config/kernel-vm_node_stat.m4
63 create mode 100755 scripts/enum-extract.pl
65 diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am
66 index 9bb0b3493..89c2689f6 100644
67 --- a/include/linux/Makefile.am
68 +++ b/include/linux/Makefile.am
69 @@ -9,7 +9,8 @@ KERNEL_H = \
70 $(top_srcdir)/include/linux/kmap_compat.h \
71 $(top_srcdir)/include/linux/simd_x86.h \
72 $(top_srcdir)/include/linux/simd_aarch64.h \
73 - $(top_srcdir)/include/linux/mod_compat.h
74 + $(top_srcdir)/include/linux/mod_compat.h \
75 + $(top_srcdir)/include/linux/page_compat.h
79 diff --git a/scripts/Makefile.am b/scripts/Makefile.am
80 index 74b8b31a5..5a8abd135 100644
81 --- a/scripts/Makefile.am
82 +++ b/scripts/Makefile.am
83 @@ -5,6 +5,7 @@ EXTRA_DIST = dkms.mkconf dkms.postbuild kmodtool zfs2zol-patch.sed cstyle.pl
84 pkgdatadir = $(datadir)/@PACKAGE@
85 dist_pkgdata_SCRIPTS = \
86 $(top_builddir)/scripts/common.sh \
87 + $(top_srcdir)/scripts/enum-extract.pl \
88 $(top_srcdir)/scripts/zimport.sh \
89 $(top_srcdir)/scripts/zfs.sh \
90 $(top_srcdir)/scripts/zfs-tests.sh \
91 @@ -15,3 +16,4 @@ dist_pkgdata_SCRIPTS = \
92 $(top_srcdir)/scripts/zpios-survey.sh \
93 $(top_srcdir)/scripts/smb.sh \
94 $(top_srcdir)/scripts/zfs-helpers.sh
96 diff --git a/include/linux/page_compat.h b/include/linux/page_compat.h
98 index 000000000..95acb7d53
100 +++ b/include/linux/page_compat.h
102 +#ifndef _ZFS_PAGE_COMPAT_H
103 +#define _ZFS_PAGE_COMPAT_H
106 + * We have various enum members moving between two separate enum types,
107 + * and accessed by different functions at various times. Centralise the
110 + * < v4.8: all enums in zone_stat_item, via global_page_state()
111 + * v4.8: some enums moved to node_stat_item, global_node_page_state() introduced
112 + * v4.13: some enums moved from zone_stat_item to node_state_item
113 + * v4.14: global_page_state() rename to global_zone_page_state()
115 + * The defines used here are created by config/kernel-global_page_state.m4
119 + * Create our own accessor functions to follow the Linux API changes
121 +#if defined(ZFS_GLOBAL_ZONE_PAGE_STATE)
123 +/* global_zone_page_state() introduced */
124 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES)
125 +#define nr_file_pages() global_node_page_state(NR_FILE_PAGES)
127 +#define nr_file_pages() global_zone_page_state(NR_FILE_PAGES)
129 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON)
130 +#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON)
132 +#define nr_inactive_anon_pages() global_zone_page_state(NR_INACTIVE_ANON)
134 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE)
135 +#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE)
137 +#define nr_inactive_file_pages() global_zone_page_state(NR_INACTIVE_FILE)
139 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE)
140 +#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE)
142 +#define nr_slab_reclaimable_pages() global_zone_page_state(NR_SLAB_RECLAIMABLE)
145 +#elif defined(ZFS_GLOBAL_NODE_PAGE_STATE)
147 +/* global_node_page_state() introduced */
148 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_FILE_PAGES)
149 +#define nr_file_pages() global_node_page_state(NR_FILE_PAGES)
151 +#define nr_file_pages() global_page_state(NR_FILE_PAGES)
153 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON)
154 +#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON)
156 +#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON)
158 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE)
159 +#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE)
161 +#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE)
163 +#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE)
164 +#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE)
166 +#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE)
171 +/* global_page_state() only */
172 +#define nr_file_pages() global_page_state(NR_FILE_PAGES)
173 +#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON)
174 +#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE)
175 +#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE)
177 +#endif /* ZFS_GLOBAL_ZONE_PAGE_STATE */
179 +#endif /* _ZFS_PAGE_COMPAT_H */
180 diff --git a/module/zfs/arc.c b/module/zfs/arc.c
181 index 9d1d0db1d..236794672 100644
182 --- a/module/zfs/arc.c
183 +++ b/module/zfs/arc.c
185 #include <sys/fs/swapnode.h>
187 #include <linux/mm_compat.h>
188 +#include <linux/page_compat.h>
190 #include <sys/callb.h>
191 #include <sys/kstat.h>
192 @@ -4016,17 +4017,11 @@ arc_free_memory(void)
194 return (ptob(si.freeram - si.freehigh));
196 -#ifdef ZFS_GLOBAL_NODE_PAGE_STATE
197 return (ptob(nr_free_pages() +
198 - global_node_page_state(NR_INACTIVE_FILE) +
199 - global_node_page_state(NR_INACTIVE_ANON) +
200 - global_node_page_state(NR_SLAB_RECLAIMABLE)));
202 - return (ptob(nr_free_pages() +
203 - global_page_state(NR_INACTIVE_FILE) +
204 - global_page_state(NR_INACTIVE_ANON) +
205 - global_page_state(NR_SLAB_RECLAIMABLE)));
206 -#endif /* ZFS_GLOBAL_NODE_PAGE_STATE */
207 + nr_inactive_file_pages() +
208 + nr_inactive_anon_pages() +
209 + nr_slab_reclaimable_pages()));
211 #endif /* CONFIG_HIGHMEM */
213 return (spa_get_random(arc_all_memory() * 20 / 100));
214 @@ -4437,13 +4432,7 @@ arc_evictable_memory(void)
215 * Scale reported evictable memory in proportion to page cache, cap
216 * at specified min/max.
218 -#ifdef ZFS_GLOBAL_NODE_PAGE_STATE
219 - uint64_t min = (ptob(global_node_page_state(NR_FILE_PAGES)) / 100) *
220 - zfs_arc_pc_percent;
222 - uint64_t min = (ptob(global_page_state(NR_FILE_PAGES)) / 100) *
223 - zfs_arc_pc_percent;
225 + uint64_t min = (ptob(nr_file_pages()) / 100) * zfs_arc_pc_percent;
226 min = MAX(arc_c_min, MIN(arc_c_max, min));
228 if (arc_dirty >= min)
229 diff --git a/config/kernel-global_page_state.m4 b/config/kernel-global_page_state.m4
231 index 000000000..f4a40011f
233 +++ b/config/kernel-global_page_state.m4
236 +dnl # 4.8 API change
238 +dnl # 75ef71840539 mm, vmstat: add infrastructure for per-node vmstats
239 +dnl # 599d0c954f91 mm, vmscan: move LRU lists to node
241 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE], [
242 + AC_MSG_CHECKING([whether global_node_page_state() exists])
243 + ZFS_LINUX_TRY_COMPILE([
244 + #include <linux/mm.h>
245 + #include <linux/vmstat.h>
247 + (void) global_node_page_state(0);
250 + AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, [global_node_page_state() exists])
257 +dnl # 4.14 API change
259 +dnl # c41f012ade0b mm: rename global_page_state to global_zone_page_state
261 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE], [
262 + AC_MSG_CHECKING([whether global_zone_page_state() exists])
263 + ZFS_LINUX_TRY_COMPILE([
264 + #include <linux/mm.h>
265 + #include <linux/vmstat.h>
267 + (void) global_zone_page_state(0);
270 + AC_DEFINE(ZFS_GLOBAL_ZONE_PAGE_STATE, 1, [global_zone_page_state() exists])
277 +dnl # Create a define and autoconf variable for an enum member
279 +AC_DEFUN([ZFS_AC_KERNEL_ENUM_MEMBER], [
280 + AC_MSG_CHECKING([whether enum $2 contains $1])
281 + AS_IF([AC_TRY_COMMAND("${srcdir}/scripts/enum-extract.pl" "$2" "$3" | egrep -qx $1)],[
282 + AC_MSG_RESULT([yes])
283 + AC_DEFINE(m4_join([_], [ZFS_ENUM], m4_toupper($2), $1), 1, [enum $2 contains $1])
284 + m4_join([_], [ZFS_ENUM], m4_toupper($2), $1)=1
286 + AC_MSG_RESULT([no])
291 +dnl # Sanity check helpers
293 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR],[
295 + AC_MSG_RESULT([$1 in either node_stat_item or zone_stat_item: $2])
296 + AC_MSG_RESULT([configure needs updating, see: config/kernel-global_page_state.m4])
297 + AC_MSG_FAILURE([SHUT 'ER DOWN CLANCY, SHE'S PUMPIN' MUD!])
300 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK], [
301 + enum_check_a="m4_join([_], [$ZFS_ENUM_NODE_STAT_ITEM], $1)"
302 + enum_check_b="m4_join([_], [$ZFS_ENUM_ZONE_STAT_ITEM], $1)"
303 + AS_IF([test -n "$enum_check_a" -a -n "$enum_check_b"],[
304 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [DUPLICATE])
306 + AS_IF([test -z "$enum_check_a" -a -z "$enum_check_b"],[
307 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR([$1], [NOT FOUND])
312 +dnl # Ensure the config tests are finding one and only one of each enum of interest
314 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY], [
315 + AC_MSG_CHECKING([global_page_state enums are sane])
317 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_FILE_PAGES])
318 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_ANON])
319 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_INACTIVE_FILE])
320 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_CHECK([NR_SLAB_RECLAIMABLE])
326 +dnl # enum members in which we're interested
328 +AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE], [
329 + ZFS_AC_KERNEL_GLOBAL_NODE_PAGE_STATE
330 + ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE
332 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [node_stat_item], [$LINUX/include/linux/mmzone.h])
333 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [node_stat_item], [$LINUX/include/linux/mmzone.h])
334 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [node_stat_item], [$LINUX/include/linux/mmzone.h])
335 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [node_stat_item], [$LINUX/include/linux/mmzone.h])
337 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_FILE_PAGES], [zone_stat_item], [$LINUX/include/linux/mmzone.h])
338 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_ANON], [zone_stat_item], [$LINUX/include/linux/mmzone.h])
339 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_INACTIVE_FILE], [zone_stat_item], [$LINUX/include/linux/mmzone.h])
340 + ZFS_AC_KERNEL_ENUM_MEMBER([NR_SLAB_RECLAIMABLE], [zone_stat_item], [$LINUX/include/linux/mmzone.h])
342 + ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY
344 diff --git a/config/kernel-vm_node_stat.m4 b/config/kernel-vm_node_stat.m4
345 deleted file mode 100644
346 index 5dcd9d827..000000000
347 --- a/config/kernel-vm_node_stat.m4
351 -dnl # 4.8 API change
352 -dnl # kernel vm counters change
354 -AC_DEFUN([ZFS_AC_KERNEL_VM_NODE_STAT], [
355 - AC_MSG_CHECKING([whether to use vm_node_stat based fn's])
356 - ZFS_LINUX_TRY_COMPILE([
357 - #include <linux/mm.h>
358 - #include <linux/vmstat.h>
360 - int a __attribute__ ((unused)) = NR_VM_NODE_STAT_ITEMS;
361 - long x __attribute__ ((unused)) =
362 - atomic_long_read(&vm_node_stat[0]);
363 - (void) global_node_page_state(0);
366 - AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1,
367 - [using global_node_page_state()])
372 diff --git a/config/kernel.m4 b/config/kernel.m4
373 index 7bb86a96e..3e499e447 100644
374 --- a/config/kernel.m4
375 +++ b/config/kernel.m4
376 @@ -123,7 +123,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
377 ZFS_AC_KERNEL_RENAME_WANTS_FLAGS
378 ZFS_AC_KERNEL_HAVE_GENERIC_SETXATTR
379 ZFS_AC_KERNEL_CURRENT_TIME
380 - ZFS_AC_KERNEL_VM_NODE_STAT
381 + ZFS_AC_KERNEL_GLOBAL_PAGE_STATE
382 ZFS_AC_KERNEL_ACL_HAS_REFCOUNT
384 AS_IF([test "$LINUX_OBJ" != "$LINUX"], [
385 diff --git a/scripts/enum-extract.pl b/scripts/enum-extract.pl
387 index 000000000..5112cc807
389 +++ b/scripts/enum-extract.pl
394 +usage: config-enum enum [file ...]
396 +Returns the elements from an enum declaration.
398 +"Best effort": we're not building an entire C interpreter here!
407 +if (!getopts("", \%opts) || @ARGV < 1) {
421 + last if s/\/\*.*\*\///s;
425 + # preprocessor stuff
429 + $in_enum = 1 if s/^\s*enum\s+${enum}(?:\s|$)//;
430 + next unless $in_enum;
432 + # remove explicit values
435 + # extract each identifier
436 + while (m/\b([a-z_][a-z0-9_]*)\b/ig) {
441 + # don't exit: there may be multiple versions of the same enum, e.g.
442 + # inside different #ifdef blocks. Let's explicitly return all of
443 + # them and let external tooling deal with it.
445 + $in_enum = 0 if m/}\s*;/;