hv_balloon: avoid touching uninitialized struct page during tail onlining

Hyper-V memory hotplug protocol has 2M granularity and in Linux x86 we use
128M. To deal with it we implement partial section onlining by registering
custom page onlining callback (hv_online_page()). Later, when more memory
arrives we try to online the 'tail' (see hv_bring_pgs_online()).

It was found that in some cases this 'tail' onlining causes issues:

 BUG: Bad page state in process kworker/0:2  pfn:109e3a
 page:ffffe08344278e80 count:0 mapcount:1 mapping:0000000000000000 index:0x0
 flags: 0xfffff80000000()
 raw: 000fffff80000000 dead000000000100 dead000000000200 0000000000000000
 raw: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
 page dumped because: nonzero mapcount
 ...
 Workqueue: events hot_add_req [hv_balloon]
 Call Trace:
  dump_stack+0x5c/0x80
  bad_page.cold.112+0x7f/0xb2
  free_pcppages_bulk+0x4b8/0x690
  free_unref_page+0x54/0x70
  hv_page_online_one+0x5c/0x80 [hv_balloon]
  hot_add_req.cold.24+0x182/0x835 [hv_balloon]
  ...

Turns out that we now have deferred struct page initialization for memory
hotplug so e.g. memory_block_action() in drivers/base/memory.c does
pages_correctly_probed() check and in that check it avoids inspecting
struct pages and checks sections instead. But in Hyper-V balloon driver we
do PageReserved(pfn_to_page()) check and this is now wrong.

Switch to checking online_section_nr() instead.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: stable@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Vitaly Kuznetsov 2019-01-04 15:19:42 +01:00 committed by Sasha Levin
parent ba50bf1ce9
commit da8ced360c

View File

@ -888,12 +888,14 @@ static unsigned long handle_pg_range(unsigned long pg_start,
pfn_cnt -= pgs_ol; pfn_cnt -= pgs_ol;
/* /*
* Check if the corresponding memory block is already * Check if the corresponding memory block is already
* online by checking its last previously backed page. * online. It is possible to observe struct pages still
* In case it is we need to bring rest (which was not * being uninitialized here so check section instead.
* backed previously) online too. * In case the section is online we need to bring the
* rest of pfns (which were not backed previously)
* online too.
*/ */
if (start_pfn > has->start_pfn && if (start_pfn > has->start_pfn &&
!PageReserved(pfn_to_page(start_pfn - 1))) online_section_nr(pfn_to_section_nr(start_pfn)))
hv_bring_pgs_online(has, start_pfn, pgs_ol); hv_bring_pgs_online(has, start_pfn, pgs_ol);
} }