Skip to content

Commit 03e2fb9

Browse files
vittyvkgregkh
authored andcommitted
Drivers: hv: balloon: account for gaps in hot add regions
commit cb7a5724c7e1bfb5766ad1c3beba14cc715991cf upstream. I'm observing the following hot add requests from the WS2012 host: hot_add_req: start_pfn = 0x108200 count = 330752 hot_add_req: start_pfn = 0x158e00 count = 193536 hot_add_req: start_pfn = 0x188400 count = 239616 As the host doesn't specify hot add regions we're trying to create 128Mb-aligned region covering the first request, we create the 0x108000 - 0x160000 region and we add 0x108000 - 0x158e00 memory. The second request passes the pfn_covered() check, we enlarge the region to 0x108000 - 0x190000 and add 0x158e00 - 0x188200 memory. The problem emerges with the third request as it starts at 0x188400 so there is a 0x200 gap which is not covered. As the end of our region is 0x190000 now it again passes the pfn_covered() check were we just adjust the covered_end_pfn and make it 0x188400 instead of 0x188200 which means that we'll try to online 0x188200-0x188400 pages but these pages were never assigned to us and we crash. We can't react to such requests by creating new hot add regions as it may happen that the whole suggested range falls into the previously identified 128Mb-aligned area so we'll end up adding nothing or create intersecting regions and our current logic doesn't allow that. Instead, create a list of such 'gaps' and check for them in the page online callback. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8e7a6db commit 03e2fb9

1 file changed

Lines changed: 94 additions & 37 deletions

File tree

drivers/hv/hv_balloon.c

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,16 @@ struct hv_hotadd_state {
441441
unsigned long covered_end_pfn;
442442
unsigned long ha_end_pfn;
443443
unsigned long end_pfn;
444+
/*
445+
* A list of gaps.
446+
*/
447+
struct list_head gap_list;
448+
};
449+
450+
struct hv_hotadd_gap {
451+
struct list_head list;
452+
unsigned long start_pfn;
453+
unsigned long end_pfn;
444454
};
445455

446456
struct balloon_state {
@@ -596,18 +606,46 @@ static struct notifier_block hv_memory_nb = {
596606
.priority = 0
597607
};
598608

609+
/* Check if the particular page is backed and can be onlined and online it. */
610+
static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
611+
{
612+
unsigned long cur_start_pgp;
613+
unsigned long cur_end_pgp;
614+
struct hv_hotadd_gap *gap;
615+
616+
cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
617+
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
599618

600-
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
619+
/* The page is not backed. */
620+
if (((unsigned long)pg < cur_start_pgp) ||
621+
((unsigned long)pg >= cur_end_pgp))
622+
return;
623+
624+
/* Check for gaps. */
625+
list_for_each_entry(gap, &has->gap_list, list) {
626+
cur_start_pgp = (unsigned long)
627+
pfn_to_page(gap->start_pfn);
628+
cur_end_pgp = (unsigned long)
629+
pfn_to_page(gap->end_pfn);
630+
if (((unsigned long)pg >= cur_start_pgp) &&
631+
((unsigned long)pg < cur_end_pgp)) {
632+
return;
633+
}
634+
}
635+
636+
/* This frame is currently backed; online the page. */
637+
__online_page_set_limits(pg);
638+
__online_page_increment_counters(pg);
639+
__online_page_free(pg);
640+
}
641+
642+
static void hv_bring_pgs_online(struct hv_hotadd_state *has,
643+
unsigned long start_pfn, unsigned long size)
601644
{
602645
int i;
603646

604-
for (i = 0; i < size; i++) {
605-
struct page *pg;
606-
pg = pfn_to_page(start_pfn + i);
607-
__online_page_set_limits(pg);
608-
__online_page_increment_counters(pg);
609-
__online_page_free(pg);
610-
}
647+
for (i = 0; i < size; i++)
648+
hv_page_online_one(has, pfn_to_page(start_pfn + i));
611649
}
612650

613651
static void hv_mem_hot_add(unsigned long start, unsigned long size,
@@ -684,26 +722,24 @@ static void hv_online_page(struct page *pg)
684722
list_for_each(cur, &dm_device.ha_region_list) {
685723
has = list_entry(cur, struct hv_hotadd_state, list);
686724
cur_start_pgp = (unsigned long)
687-
pfn_to_page(has->covered_start_pfn);
688-
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
725+
pfn_to_page(has->start_pfn);
726+
cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
689727

690-
if (((unsigned long)pg >= cur_start_pgp) &&
691-
((unsigned long)pg < cur_end_pgp)) {
692-
/*
693-
* This frame is currently backed; online the
694-
* page.
695-
*/
696-
__online_page_set_limits(pg);
697-
__online_page_increment_counters(pg);
698-
__online_page_free(pg);
699-
}
728+
/* The page belongs to a different HAS. */
729+
if (((unsigned long)pg < cur_start_pgp) ||
730+
((unsigned long)pg >= cur_end_pgp))
731+
continue;
732+
733+
hv_page_online_one(has, pg);
734+
break;
700735
}
701736
}
702737

703-
static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
738+
static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
704739
{
705740
struct list_head *cur;
706741
struct hv_hotadd_state *has;
742+
struct hv_hotadd_gap *gap;
707743
unsigned long residual, new_inc;
708744

709745
if (list_empty(&dm_device.ha_region_list))
@@ -718,6 +754,24 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
718754
*/
719755
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
720756
continue;
757+
758+
/*
759+
* If the current start pfn is not where the covered_end
760+
* is, create a gap and update covered_end_pfn.
761+
*/
762+
if (has->covered_end_pfn != start_pfn) {
763+
gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC);
764+
if (!gap)
765+
return -ENOMEM;
766+
767+
INIT_LIST_HEAD(&gap->list);
768+
gap->start_pfn = has->covered_end_pfn;
769+
gap->end_pfn = start_pfn;
770+
list_add_tail(&gap->list, &has->gap_list);
771+
772+
has->covered_end_pfn = start_pfn;
773+
}
774+
721775
/*
722776
* If the current hot add-request extends beyond
723777
* our current limit; extend it.
@@ -734,19 +788,10 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
734788
has->end_pfn += new_inc;
735789
}
736790

737-
/*
738-
* If the current start pfn is not where the covered_end
739-
* is, update it.
740-
*/
741-
742-
if (has->covered_end_pfn != start_pfn)
743-
has->covered_end_pfn = start_pfn;
744-
745-
return true;
746-
791+
return 1;
747792
}
748793

749-
return false;
794+
return 0;
750795
}
751796

752797
static unsigned long handle_pg_range(unsigned long pg_start,
@@ -785,6 +830,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
785830
if (pgs_ol > pfn_cnt)
786831
pgs_ol = pfn_cnt;
787832

833+
has->covered_end_pfn += pgs_ol;
834+
pfn_cnt -= pgs_ol;
788835
/*
789836
* Check if the corresponding memory block is already
790837
* online by checking its last previously backed page.
@@ -793,10 +840,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
793840
*/
794841
if (start_pfn > has->start_pfn &&
795842
!PageReserved(pfn_to_page(start_pfn - 1)))
796-
hv_bring_pgs_online(start_pfn, pgs_ol);
843+
hv_bring_pgs_online(has, start_pfn, pgs_ol);
797844

798-
has->covered_end_pfn += pgs_ol;
799-
pfn_cnt -= pgs_ol;
800845
}
801846

802847
if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
@@ -834,13 +879,19 @@ static unsigned long process_hot_add(unsigned long pg_start,
834879
unsigned long rg_size)
835880
{
836881
struct hv_hotadd_state *ha_region = NULL;
882+
int covered;
837883

838884
if (pfn_cnt == 0)
839885
return 0;
840886

841-
if (!dm_device.host_specified_ha_region)
842-
if (pfn_covered(pg_start, pfn_cnt))
887+
if (!dm_device.host_specified_ha_region) {
888+
covered = pfn_covered(pg_start, pfn_cnt);
889+
if (covered < 0)
890+
return 0;
891+
892+
if (covered)
843893
goto do_pg_range;
894+
}
844895

845896
/*
846897
* If the host has specified a hot-add range; deal with it first.
@@ -852,6 +903,7 @@ static unsigned long process_hot_add(unsigned long pg_start,
852903
return 0;
853904

854905
INIT_LIST_HEAD(&ha_region->list);
906+
INIT_LIST_HEAD(&ha_region->gap_list);
855907

856908
list_add_tail(&ha_region->list, &dm_device.ha_region_list);
857909
ha_region->start_pfn = rg_start;
@@ -1584,6 +1636,7 @@ static int balloon_remove(struct hv_device *dev)
15841636
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
15851637
struct list_head *cur, *tmp;
15861638
struct hv_hotadd_state *has;
1639+
struct hv_hotadd_gap *gap, *tmp_gap;
15871640

15881641
if (dm->num_pages_ballooned != 0)
15891642
pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
@@ -1600,6 +1653,10 @@ static int balloon_remove(struct hv_device *dev)
16001653
#endif
16011654
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
16021655
has = list_entry(cur, struct hv_hotadd_state, list);
1656+
list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) {
1657+
list_del(&gap->list);
1658+
kfree(gap);
1659+
}
16031660
list_del(&has->list);
16041661
kfree(has);
16051662
}

0 commit comments

Comments
 (0)