From: Petr Tesarik <ptesarik@suse.cz>
Subject: Add support for hugepage filtering
References: bnc#738503, bnc#741191

This filters out pages that have the compound page flag set.
The precise position of the flag(s) depends on the kernel version
and configuration, so an additional patch is needed for the kernel.

It is still possible to filter out huge pages on unpatched kernels
if the debuginfo is available. The page flag can also be saved
into a generated vmcoreinfo file.

To sum it up:

a. any kernel with the appropriate patch includes the PG_xxx values in
   VMCOERINFO, and these are automatically picked up by makedumpfile
   (planned for a regular SLES11 kernel maint update)
b. for unpatched 2.6.26+ kernels, the info can be pre-generated with
   "makedumpfile -g", or you can pass the "-x" option with the kernel
   debuginfo file when filtering
   NOTE: running "makedumpfile" without either "-g" or "-x" will NOT
   filter out huge pages; a warning message should be issued
c. for pre-2.6.26 kernels, makedumpfile will assume the legacy value of
   14 for PG_compound (this also covers SLES10 kernels)
d. ancient pre-2.6.13 kernels will not be supported; no change here

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 makedumpfile.c |   43 ++++++++++++++++++++++++++++++++++++++++++-
 makedumpfile.h |    8 ++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -163,6 +163,13 @@ is_in_same_page(unsigned long vaddr1, un
 	return FALSE;
 }
 
+/* Test whether flags belong to a huge page */
+static inline int
+isHugePage(unsigned long flags)
+{
+	return flags & info->hugepage_flags;
+}
+
 #define BITMAP_SECT_LEN 4096
 static inline int is_dumpable(struct dump_bitmap *, unsigned long long);
 unsigned long
@@ -973,8 +980,15 @@ get_structure_info(void)
 
 	ENUM_NUMBER_INIT(PG_lru, "PG_lru");
 	ENUM_NUMBER_INIT(PG_private, "PG_private");
+	ENUM_NUMBER_INIT(PG_head, "PG_head");
+	ENUM_NUMBER_INIT(PG_tail, "PG_tail");
+	ENUM_NUMBER_INIT(PG_compound, "PG_compound");
 	ENUM_NUMBER_INIT(PG_swapcache, "PG_swapcache");
 
+	if (NUMBER(PG_head) == NOT_FOUND_NUMBER &&
+	    NUMBER(PG_compound) == NOT_FOUND_NUMBER)
+		/* Pre-2.6.26 kernels did not have pageflags */
+		NUMBER(PG_compound) = PG_compound_ORIGINAL;
 	TYPEDEF_SIZE_INIT(nodemask_t, "nodemask_t");
 
 	SIZE_INIT(percpu_data, "percpu_data");
@@ -1375,6 +1389,12 @@ write_vmcoreinfo_data(void)
 
 	WRITE_NUMBER("PG_lru", PG_lru);
 	WRITE_NUMBER("PG_private", PG_private);
+	if (NUMBER(PG_head) != NOT_FOUND_NUMBER)
+		WRITE_NUMBER("PG_head", PG_head);
+	if (NUMBER(PG_tail) != NOT_FOUND_NUMBER)
+		WRITE_NUMBER("PG_tail", PG_tail);
+	if (NUMBER(PG_compound) != NOT_FOUND_NUMBER)
+		WRITE_NUMBER("PG_compound", PG_compound);
 	WRITE_NUMBER("PG_swapcache", PG_swapcache);
 
 	/*
@@ -1663,6 +1683,9 @@ read_vmcoreinfo(void)
 
 	READ_NUMBER("PG_lru", PG_lru);
 	READ_NUMBER("PG_private", PG_private);
+	READ_NUMBER("PG_head", PG_head);
+	READ_NUMBER("PG_tail", PG_tail);
+	READ_NUMBER("PG_compound", PG_compound);
 	READ_NUMBER("PG_swapcache", PG_swapcache);
 
 	READ_SRCFILE("pud_t", pud_t);
@@ -2645,6 +2668,24 @@ initial(void)
 
 	if (!get_value_for_old_linux())
 		return FALSE;
+
+	/* Get page flags for compound pages */
+	if (NUMBER(PG_head) != NOT_FOUND_NUMBER) {
+		info->hugepage_flags = 1UL << NUMBER(PG_head);
+		if (NUMBER(PG_tail) != NOT_FOUND_NUMBER)
+			info->hugepage_flags |= 1UL << NUMBER(PG_tail);
+		else
+			/* If PG_tail is not explicitly saved, then assume
+			 * that it immediately follows PG_head.
+			 */
+			info->hugepage_flags |= info->hugepage_flags << 1;
+	} else if (NUMBER(PG_compound) != NOT_FOUND_NUMBER) {
+		info->hugepage_flags = 1UL << NUMBER(PG_compound);
+	} else if (info->dump_level & DL_EXCLUDE_USER_DATA) {
+		MSG("Compound page bit could not be determined: ");
+		MSG("huge pages will NOT be filtered.\n");
+	}
+
 out:
 	if (!info->page_size) {
 		/*
@@ -3734,7 +3775,7 @@ __exclude_unnecessary_pages(unsigned lon
 		 * Exclude the data page of the user process.
 		 */
 		else if ((info->dump_level & DL_EXCLUDE_USER_DATA)
-		    && isAnon(mapping)) {
+		    && (isAnon(mapping) || isHugePage(flags))) {
 			clear_bit_on_2nd_bitmap_for_kernel(pfn);
 			pfn_user++;
 		}
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -69,6 +69,7 @@ int get_mem_type(void);
  */
 #define PG_lru_ORIGINAL	 	(5)
 #define PG_private_ORIGINAL	(11)	/* Has something at ->private */
+#define PG_compound_ORIGINAL	(14)	/* Is part of a compound page */
 #define PG_swapcache_ORIGINAL	(15)	/* Swap page: swp_entry_t in private */
 
 #define PAGE_MAPPING_ANON	(1)
@@ -106,6 +107,9 @@ test_bit(int nr, unsigned long addr)
 
 #define isLRU(flags)		test_bit(NUMBER(PG_lru), flags)
 #define isPrivate(flags)	test_bit(NUMBER(PG_private), flags)
+#define isHead(flags)		test_bit(NUMBER(PG_head), flags)
+#define isTail(flags)		test_bit(NUMBER(PG_tail), flags)
+#define isCompound(flags)	test_bit(NUMBER(PG_compound), flags)
 #define isSwapCache(flags)	test_bit(NUMBER(PG_swapcache), flags)
 
 static inline int
@@ -821,6 +825,7 @@ struct DumpInfo {
 	unsigned long   vmalloc_end;
 	unsigned long	vmemmap_start;
 	unsigned long	vmemmap_end;
+	unsigned long	hugepage_flags;	     /* page flags for a compound page */
 
 	/*
 	 * Filter config file containing filter commands to filter out kernel
@@ -1232,6 +1237,9 @@ struct number_table {
 	 */
 	long	PG_lru;
 	long	PG_private;
+	long	PG_head;
+	long	PG_tail;
+	long	PG_compound;
 	long	PG_swapcache;
 };
 
