--- /dev/null
+From a96dfddbcc04336bbed50dc2b24823e45e09e80c Mon Sep 17 00:00:00 2001
+From: Toshi Kani <toshi.kani@hpe.com>
+Date: Fri, 3 Feb 2017 13:13:23 -0800
+Subject: base/memory, hotplug: fix a kernel oops in show_valid_zones()
+
+From: Toshi Kani <toshi.kani@hpe.com>
+
+commit a96dfddbcc04336bbed50dc2b24823e45e09e80c upstream.
+
+Reading a sysfs "memoryN/valid_zones" file leads to the following oops
+when the first page of a range is not backed by struct page.
+show_valid_zones() assumes that 'start_pfn' is always valid for
+page_zone().
+
+ BUG: unable to handle kernel paging request at ffffea017a000000
+ IP: show_valid_zones+0x6f/0x160
+
+This issue may happen on x86-64 systems with 64GiB or more memory since
+their memory block size is bumped up to 2GiB. [1] An example of such
+systems is desribed below. 0x3240000000 is only aligned by 1GiB and
+this memory block starts from 0x3200000000, which is not backed by
+struct page.
+
+ BIOS-e820: [mem 0x0000003240000000-0x000000603fffffff] usable
+
+Since test_pages_in_a_zone() already checks holes, fix this issue by
+extending this function to return 'valid_start' and 'valid_end' for a
+given range. show_valid_zones() then proceeds with the valid range.
+
+[1] 'Commit bdee237c0343 ("x86: mm: Use 2GB memory block size on
+ large-memory x86-64 systems")'
+
+Link: http://lkml.kernel.org/r/20170127222149.30893-3-toshi.kani@hpe.com
+Signed-off-by: Toshi Kani <toshi.kani@hpe.com>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Zhang Zhen <zhenzhang.zhang@huawei.com>
+Cc: Reza Arbab <arbab@linux.vnet.ibm.com>
+Cc: David Rientjes <rientjes@google.com>
+Cc: Dan Williams <dan.j.williams@intel.com>
+Cc: <stable@vger.kernel.org> [4.4+]
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/base/memory.c | 11 +++++------
+ include/linux/memory_hotplug.h | 3 ++-
+ mm/memory_hotplug.c | 20 +++++++++++++++-----
+ 3 files changed, 22 insertions(+), 12 deletions(-)
+
+--- a/drivers/base/memory.c
++++ b/drivers/base/memory.c
+@@ -388,30 +388,29 @@ static ssize_t show_valid_zones(struct d
+ {
+ struct memory_block *mem = to_memory_block(dev);
+ unsigned long start_pfn, end_pfn;
++ unsigned long valid_start, valid_end;
+ unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
+- struct page *first_page;
+ struct zone *zone;
+
+ start_pfn = section_nr_to_pfn(mem->start_section_nr);
+ end_pfn = start_pfn + nr_pages;
+- first_page = pfn_to_page(start_pfn);
+
+ /* The block contains more than one zone can not be offlined. */
+- if (!test_pages_in_a_zone(start_pfn, end_pfn))
++ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
+ return sprintf(buf, "none\n");
+
+- zone = page_zone(first_page);
++ zone = page_zone(pfn_to_page(valid_start));
+
+ if (zone_idx(zone) == ZONE_MOVABLE - 1) {
+ /*The mem block is the last memoryblock of this zone.*/
+- if (end_pfn == zone_end_pfn(zone))
++ if (valid_end == zone_end_pfn(zone))
+ return sprintf(buf, "%s %s\n",
+ zone->name, (zone + 1)->name);
+ }
+
+ if (zone_idx(zone) == ZONE_MOVABLE) {
+ /*The mem block is the first memoryblock of ZONE_MOVABLE.*/
+- if (start_pfn == zone->zone_start_pfn)
++ if (valid_start == zone->zone_start_pfn)
+ return sprintf(buf, "%s %s\n",
+ zone->name, (zone - 1)->name);
+ }
+--- a/include/linux/memory_hotplug.h
++++ b/include/linux/memory_hotplug.h
+@@ -85,7 +85,8 @@ extern int zone_grow_waitqueues(struct z
+ extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
+ /* VM interface that may be used by firmware interface */
+ extern int online_pages(unsigned long, unsigned long, int);
+-extern int test_pages_in_a_zone(unsigned long, unsigned long);
++extern int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
++ unsigned long *valid_start, unsigned long *valid_end);
+ extern void __offline_isolated_pages(unsigned long, unsigned long);
+
+ typedef void (*online_page_callback_t)(struct page *page);
+--- a/mm/memory_hotplug.c
++++ b/mm/memory_hotplug.c
+@@ -1372,10 +1372,13 @@ int is_mem_section_removable(unsigned lo
+
+ /*
+ * Confirm all pages in a range [start, end) belong to the same zone.
++ * When true, return its valid [start, end).
+ */
+-int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
++int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
++ unsigned long *valid_start, unsigned long *valid_end)
+ {
+ unsigned long pfn, sec_end_pfn;
++ unsigned long start, end;
+ struct zone *zone = NULL;
+ struct page *page;
+ int i;
+@@ -1397,14 +1400,20 @@ int test_pages_in_a_zone(unsigned long s
+ page = pfn_to_page(pfn + i);
+ if (zone && page_zone(page) != zone)
+ return 0;
++ if (!zone)
++ start = pfn + i;
+ zone = page_zone(page);
++ end = pfn + MAX_ORDER_NR_PAGES;
+ }
+ }
+
+- if (zone)
++ if (zone) {
++ *valid_start = start;
++ *valid_end = end;
+ return 1;
+- else
++ } else {
+ return 0;
++ }
+ }
+
+ /*
+@@ -1722,6 +1731,7 @@ static int __ref __offline_pages(unsigne
+ long offlined_pages;
+ int ret, drain, retry_max, node;
+ unsigned long flags;
++ unsigned long valid_start, valid_end;
+ struct zone *zone;
+ struct memory_notify arg;
+
+@@ -1732,10 +1742,10 @@ static int __ref __offline_pages(unsigne
+ return -EINVAL;
+ /* This makes hotplug much easier...and readable.
+ we assume this for now. .*/
+- if (!test_pages_in_a_zone(start_pfn, end_pfn))
++ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
+ return -EINVAL;
+
+- zone = page_zone(pfn_to_page(start_pfn));
++ zone = page_zone(pfn_to_page(valid_start));
+ node = zone_to_nid(zone);
+ nr_pages = end_pfn - start_pfn;
+
+++ /dev/null
-From 2dae99558e86894e9e5dbf097477baaa5eb70134 Mon Sep 17 00:00:00 2001
-From: Damien Le Moal <damien.lemoal@wdc.com>
-Date: Mon, 19 Dec 2016 10:17:40 +0900
-Subject: libata: Fix ATA request sense
-
-From: Damien Le Moal <damien.lemoal@wdc.com>
-
-commit 2dae99558e86894e9e5dbf097477baaa5eb70134 upstream.
-
-For an ATA device supporting the sense data reporting feature set, a
-failed command will trigger the execution of ata_eh_request_sense if
-the result task file of the failed command has the ATA_SENSE bit set
-(sense data available bit). ata_eh_request_sense executes the REQUEST
-SENSE DATA EXT command to retrieve the sense data of the failed
-command. On success of REQUEST SENSE DATA EXT, the ATA_SENSE bit will
-NOT be set (the command succeeded) but ata_eh_request_sense
-nevertheless tests the availability of sense data by testing that bit
-presence in the result tf of the REQUEST SENSE DATA EXT command. This
-leads us to falsely assume that request sense data failed and to the
-warning message:
-
-atax.xx: request sense failed stat 50 emask 0
-
-Upon success of REQUEST SENSE DATA EXT, set the ATA_SENSE bit in the
-result task file command so that sense data can be returned by
-ata_eh_request_sense.
-
-Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
-Signed-off-by: Tejun Heo <tj@kernel.org>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-
----
- drivers/ata/libata-core.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/ata/libata-core.c
-+++ b/drivers/ata/libata-core.c
-@@ -1692,6 +1692,8 @@ unsigned ata_exec_internal_sg(struct ata
-
- if (qc->err_mask & ~AC_ERR_OTHER)
- qc->err_mask &= ~AC_ERR_OTHER;
-+ } else if (qc->tf.command == ATA_CMD_REQ_SENSE_DATA) {
-+ qc->result_tf.command |= ATA_SENSE;
- }
-
- /* finish up */