]> git.proxmox.com Git - zfsonlinux.git/blame - zfs-patches/0041-Fix-free-memory-calculation-on-v3.14.patch
bump version to 0.7.7-pve1~bpo9
[zfsonlinux.git] / zfs-patches / 0041-Fix-free-memory-calculation-on-v3.14.patch
CommitLineData
75b07eca
FG
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: chrisrd <chris@onthe.net.au>
3Date: Sat, 24 Feb 2018 03:50:06 +1100
4Subject: [PATCH] Fix free memory calculation on v3.14+
5MIME-Version: 1.0
6Content-Type: text/plain; charset=UTF-8
7Content-Transfer-Encoding: 8bit
8
9Provide infrastructure to auto-configure to enum and API changes in the
10global page stats used for our free memory calculations.
11
12arc_free_memory has been broken since an API change in Linux v3.14:
13
142016-07-28 v4.8 599d0c95 mm, vmscan: move LRU lists to node
152016-07-28 v4.8 75ef7184 mm, vmstat: add infrastructure for per-node
16 vmstats
17
18These commits moved some of global_page_state() into
19global_node_page_state(). The API change was particularly egregious as,
20instead of breaking the old code, it silently did the wrong thing and we
21continued using global_page_state() where we should have been using
22global_node_page_state(), thus indexing into the wrong array via
23NR_SLAB_RECLAIMABLE et al.
24
25There have been further API changes along the way:
26
272017-07-06 v4.13 385386cf mm: vmstat: move slab statistics from zone to
28 node counters
292017-09-06 v4.14 c41f012a mm: rename global_page_state to
30 global_zone_page_state
31
32...and various (incomplete, as it turns out) attempts to accomodate
33these changes in ZoL:
34
352017-08-24 2209e409 Linux 4.8+ compatibility fix for vm stats
362017-09-16 787acae0 Linux 3.14 compat: IO acct, global_page_state, etc
372017-09-19 661907e6 Linux 4.14 compat: IO acct, global_page_state, etc
38
39The config infrastructure provided here resolves these issues going back
40to the original API change in v3.14 and is robust against further Linux
41changes in this area.
42
43Reviewed-by: Giuseppe Di Natale <dinatale2@llnl.gov>
44Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
45Reviewed-by: George Melikov <mail@gmelikov.ru>
46Signed-off-by: Chris Dunlop <chris@onthe.net.au>
47Closes #7170
48(cherry picked from commit 338523dd6ec641cc4d552c3f67e1becfb9e22b0a)
49Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
50---
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
64
65diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am
66index 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
76
77 USER_H =
78
79diff --git a/scripts/Makefile.am b/scripts/Makefile.am
80index 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
95+
96diff --git a/include/linux/page_compat.h b/include/linux/page_compat.h
97new file mode 100644
98index 000000000..95acb7d53
99--- /dev/null
100+++ b/include/linux/page_compat.h
101@@ -0,0 +1,78 @@
102+#ifndef _ZFS_PAGE_COMPAT_H
103+#define _ZFS_PAGE_COMPAT_H
104+
105+/*
106+ * We have various enum members moving between two separate enum types,
107+ * and accessed by different functions at various times. Centralise the
108+ * insanity.
109+ *
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()
114+ *
115+ * The defines used here are created by config/kernel-global_page_state.m4
116+ */
117+
118+/*
119+ * Create our own accessor functions to follow the Linux API changes
120+ */
121+#if defined(ZFS_GLOBAL_ZONE_PAGE_STATE)
122+
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)
126+#else
127+#define nr_file_pages() global_zone_page_state(NR_FILE_PAGES)
128+#endif
129+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON)
130+#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON)
131+#else
132+#define nr_inactive_anon_pages() global_zone_page_state(NR_INACTIVE_ANON)
133+#endif
134+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE)
135+#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE)
136+#else
137+#define nr_inactive_file_pages() global_zone_page_state(NR_INACTIVE_FILE)
138+#endif
139+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE)
140+#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE)
141+#else
142+#define nr_slab_reclaimable_pages() global_zone_page_state(NR_SLAB_RECLAIMABLE)
143+#endif
144+
145+#elif defined(ZFS_GLOBAL_NODE_PAGE_STATE)
146+
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)
150+#else
151+#define nr_file_pages() global_page_state(NR_FILE_PAGES)
152+#endif
153+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_ANON)
154+#define nr_inactive_anon_pages() global_node_page_state(NR_INACTIVE_ANON)
155+#else
156+#define nr_inactive_anon_pages() global_page_state(NR_INACTIVE_ANON)
157+#endif
158+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_INACTIVE_FILE)
159+#define nr_inactive_file_pages() global_node_page_state(NR_INACTIVE_FILE)
160+#else
161+#define nr_inactive_file_pages() global_page_state(NR_INACTIVE_FILE)
162+#endif
163+#if defined(ZFS_ENUM_NODE_STAT_ITEM_NR_SLAB_RECLAIMABLE)
164+#define nr_slab_reclaimable_pages() global_node_page_state(NR_SLAB_RECLAIMABLE)
165+#else
166+#define nr_slab_reclaimable_pages() global_page_state(NR_SLAB_RECLAIMABLE)
167+#endif
168+
169+#else
170+
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)
176+
177+#endif /* ZFS_GLOBAL_ZONE_PAGE_STATE */
178+
179+#endif /* _ZFS_PAGE_COMPAT_H */
180diff --git a/module/zfs/arc.c b/module/zfs/arc.c
181index 9d1d0db1d..236794672 100644
182--- a/module/zfs/arc.c
183+++ b/module/zfs/arc.c
184@@ -280,6 +280,7 @@
185 #include <sys/fs/swapnode.h>
186 #include <sys/zpl.h>
187 #include <linux/mm_compat.h>
188+#include <linux/page_compat.h>
189 #endif
190 #include <sys/callb.h>
191 #include <sys/kstat.h>
192@@ -4016,17 +4017,11 @@ arc_free_memory(void)
193 si_meminfo(&si);
194 return (ptob(si.freeram - si.freehigh));
195 #else
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)));
201-#else
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()));
210+
211 #endif /* CONFIG_HIGHMEM */
212 #else
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.
217 */
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;
221-#else
222- uint64_t min = (ptob(global_page_state(NR_FILE_PAGES)) / 100) *
223- zfs_arc_pc_percent;
224-#endif
225+ uint64_t min = (ptob(nr_file_pages()) / 100) * zfs_arc_pc_percent;
226 min = MAX(arc_c_min, MIN(arc_c_max, min));
227
228 if (arc_dirty >= min)
229diff --git a/config/kernel-global_page_state.m4 b/config/kernel-global_page_state.m4
230new file mode 100644
231index 000000000..f4a40011f
232--- /dev/null
233+++ b/config/kernel-global_page_state.m4
234@@ -0,0 +1,109 @@
235+dnl #
236+dnl # 4.8 API change
237+dnl #
238+dnl # 75ef71840539 mm, vmstat: add infrastructure for per-node vmstats
239+dnl # 599d0c954f91 mm, vmscan: move LRU lists to node
240+dnl #
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>
246+ ],[
247+ (void) global_node_page_state(0);
248+ ],[
249+ AC_MSG_RESULT(yes)
250+ AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1, [global_node_page_state() exists])
251+ ],[
252+ AC_MSG_RESULT(no)
253+ ])
254+])
255+
256+dnl #
257+dnl # 4.14 API change
258+dnl #
259+dnl # c41f012ade0b mm: rename global_page_state to global_zone_page_state
260+dnl #
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>
266+ ],[
267+ (void) global_zone_page_state(0);
268+ ],[
269+ AC_MSG_RESULT(yes)
270+ AC_DEFINE(ZFS_GLOBAL_ZONE_PAGE_STATE, 1, [global_zone_page_state() exists])
271+ ],[
272+ AC_MSG_RESULT(no)
273+ ])
274+])
275+
276+dnl #
277+dnl # Create a define and autoconf variable for an enum member
278+dnl #
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
285+ ],[
286+ AC_MSG_RESULT([no])
287+ ])
288+])
289+
290+dnl #
291+dnl # Sanity check helpers
292+dnl #
293+AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_PAGE_STATE_ENUM_ERROR],[
294+ AC_MSG_RESULT(no)
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!])
298+])
299+
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])
305+ ])
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])
308+ ])
309+])
310+
311+dnl #
312+dnl # Ensure the config tests are finding one and only one of each enum of interest
313+dnl #
314+AC_DEFUN([ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY], [
315+ AC_MSG_CHECKING([global_page_state enums are sane])
316+
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])
321+
322+ AC_MSG_RESULT(yes)
323+])
324+
325+dnl #
326+dnl # enum members in which we're interested
327+dnl #
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
331+
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])
336+
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])
341+
342+ ZFS_AC_KERNEL_GLOBAL_ZONE_PAGE_STATE_SANITY
343+])
344diff --git a/config/kernel-vm_node_stat.m4 b/config/kernel-vm_node_stat.m4
345deleted file mode 100644
346index 5dcd9d827..000000000
347--- a/config/kernel-vm_node_stat.m4
348+++ /dev/null
349@@ -1,22 +0,0 @@
350-dnl #
351-dnl # 4.8 API change
352-dnl # kernel vm counters change
353-dnl #
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>
359- ],[
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);
364- ],[
365- AC_MSG_RESULT(yes)
366- AC_DEFINE(ZFS_GLOBAL_NODE_PAGE_STATE, 1,
367- [using global_node_page_state()])
368- ],[
369- AC_MSG_RESULT(no)
370- ])
371-])
372diff --git a/config/kernel.m4 b/config/kernel.m4
373index 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
383
384 AS_IF([test "$LINUX_OBJ" != "$LINUX"], [
385diff --git a/scripts/enum-extract.pl b/scripts/enum-extract.pl
386new file mode 100755
387index 000000000..5112cc807
388--- /dev/null
389+++ b/scripts/enum-extract.pl
390@@ -0,0 +1,58 @@
391+#!/usr/bin/perl -w
392+
393+my $usage = <<EOT;
394+usage: config-enum enum [file ...]
395+
396+Returns the elements from an enum declaration.
397+
398+"Best effort": we're not building an entire C interpreter here!
399+EOT
400+
401+use warnings;
402+use strict;
403+use Getopt::Std;
404+
405+my %opts;
406+
407+if (!getopts("", \%opts) || @ARGV < 1) {
408+ print $usage;
409+ exit 2;
410+}
411+
412+my $enum = shift;
413+
414+my $in_enum = 0;
415+
416+while (<>) {
417+ # comments
418+ s/\/\*.*\*\///;
419+ if (m/\/\*/) {
420+ while ($_ .= <>) {
421+ last if s/\/\*.*\*\///s;
422+ }
423+ }
424+
425+ # preprocessor stuff
426+ next if /^#/;
427+
428+ # find our enum
429+ $in_enum = 1 if s/^\s*enum\s+${enum}(?:\s|$)//;
430+ next unless $in_enum;
431+
432+ # remove explicit values
433+ s/\s*=[^,]+,/,/g;
434+
435+ # extract each identifier
436+ while (m/\b([a-z_][a-z0-9_]*)\b/ig) {
437+ print $1, "\n";
438+ }
439+
440+ #
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.
444+ #
445+ $in_enum = 0 if m/}\s*;/;
446+}
447+
448+exit 0;
449--
4502.14.2
451