Skip to content

Commit 8e24f06

Browse files
committed
Merge feature/page-reporting/5.15 into v5.15.79
* commit 'ad427234defd6cdfdc0c21ca5b64ef589b82a421': hv_balloon: Add support for configurable order free page reporting mm/page_reporting: Add checks for page_reporting_order param
2 parents fed826c + ad42723 commit 8e24f06

File tree

2 files changed

+118
-26
lines changed

2 files changed

+118
-26
lines changed

drivers/hv/hv_balloon.c

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,16 @@ static bool do_hot_add;
468468
* the specified number of seconds.
469469
*/
470470
static uint pressure_report_delay = 45;
471+
extern unsigned int page_reporting_order;
472+
#define HV_MAX_FAILURES 2
471473

472474
/*
473475
* The last time we posted a pressure report to host.
474476
*/
475477
static unsigned long last_post_time;
476478

479+
static int hv_hypercall_multi_failure;
480+
477481
module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
478482
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
479483

@@ -573,6 +577,10 @@ static struct hv_dynmem_device dm_device;
573577

574578
static void post_status(struct hv_dynmem_device *dm);
575579

580+
static void enable_page_reporting(void);
581+
582+
static void disable_page_reporting(void);
583+
576584
#ifdef CONFIG_MEMORY_HOTPLUG
577585
static inline bool has_pfn_is_backed(struct hv_hotadd_state *has,
578586
unsigned long pfn)
@@ -1402,6 +1410,18 @@ static int dm_thread_func(void *dm_dev)
14021410
*/
14031411
reinit_completion(&dm_device.config_event);
14041412
post_status(dm);
1413+
/*
1414+
* disable free page reporting if multiple hypercall
1415+
* failure flag set. It is not done in the page_reporting
1416+
* callback context as that causes a deadlock between
1417+
* page_reporting_process() and page_reporting_unregister()
1418+
*/
1419+
if (hv_hypercall_multi_failure >= HV_MAX_FAILURES) {
1420+
pr_err("Multiple failures in cold memory discard hypercall, disabling page reporting\n");
1421+
disable_page_reporting();
1422+
/* Reset the flag after disabling reporting */
1423+
hv_hypercall_multi_failure = 0;
1424+
}
14051425
}
14061426

14071427
return 0;
@@ -1577,20 +1597,20 @@ static void balloon_onchannelcallback(void *context)
15771597

15781598
}
15791599

1580-
/* Hyper-V only supports reporting 2MB pages or higher */
1581-
#define HV_MIN_PAGE_REPORTING_ORDER 9
1582-
#define HV_MIN_PAGE_REPORTING_LEN (HV_HYP_PAGE_SIZE << HV_MIN_PAGE_REPORTING_ORDER)
1600+
#define HV_LARGE_REPORTING_ORDER 9
1601+
#define HV_LARGE_REPORTING_LEN (HV_HYP_PAGE_SIZE << \
1602+
HV_LARGE_REPORTING_ORDER)
15831603
static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info,
15841604
struct scatterlist *sgl, unsigned int nents)
15851605
{
15861606
unsigned long flags;
15871607
struct hv_memory_hint *hint;
1588-
int i;
1608+
int i, order;
15891609
u64 status;
15901610
struct scatterlist *sg;
15911611

15921612
WARN_ON_ONCE(nents > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
1593-
WARN_ON_ONCE(sgl->length < HV_MIN_PAGE_REPORTING_LEN);
1613+
WARN_ON_ONCE(sgl->length < (HV_HYP_PAGE_SIZE << page_reporting_order));
15941614
local_irq_save(flags);
15951615
hint = *(struct hv_memory_hint **)this_cpu_ptr(hyperv_pcpu_input_arg);
15961616
if (!hint) {
@@ -1605,21 +1625,53 @@ static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info,
16051625

16061626
range = &hint->ranges[i];
16071627
range->address_space = 0;
1608-
/* page reporting only reports 2MB pages or higher */
1609-
range->page.largepage = 1;
1610-
range->page.additional_pages =
1611-
(sg->length / HV_MIN_PAGE_REPORTING_LEN) - 1;
1612-
range->page_size = HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB;
1613-
range->base_large_pfn =
1614-
page_to_hvpfn(sg_page(sg)) >> HV_MIN_PAGE_REPORTING_ORDER;
1628+
order = get_order(sg->length);
1629+
/*
1630+
* Hyper-V expects the additional_pages field in the units
1631+
* of one of these 3 sizes, 4Kbytes, 2Mbytes or 1Gbytes.
1632+
* This is dictated by the values of the fields page.largesize
1633+
* and page_size.
1634+
* This code however, only uses 4Kbytes and 2Mbytes units
1635+
* and not 1Gbytes unit.
1636+
*/
1637+
1638+
/* page reporting for pages 2MB or higher */
1639+
if (order >= HV_LARGE_REPORTING_ORDER ) {
1640+
range->page.largepage = 1;
1641+
range->page_size = HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB;
1642+
range->base_large_pfn = page_to_hvpfn(
1643+
sg_page(sg)) >> HV_LARGE_REPORTING_ORDER;
1644+
range->page.additional_pages =
1645+
(sg->length / HV_LARGE_REPORTING_LEN) - 1;
1646+
} else {
1647+
/* Page reporting for pages below 2MB */
1648+
range->page.basepfn = page_to_hvpfn(sg_page(sg));
1649+
range->page.largepage = false;
1650+
range->page.additional_pages =
1651+
(sg->length / HV_HYP_PAGE_SIZE) - 1;
1652+
}
1653+
16151654
}
16161655

16171656
status = hv_do_rep_hypercall(HV_EXT_CALL_MEMORY_HEAT_HINT, nents, 0,
16181657
hint, NULL);
16191658
local_irq_restore(flags);
1620-
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) {
1659+
if (!hv_result_success(status)) {
1660+
16211661
pr_err("Cold memory discard hypercall failed with status %llx\n",
1622-
status);
1662+
status);
1663+
if (hv_hypercall_multi_failure > 0)
1664+
hv_hypercall_multi_failure++;
1665+
1666+
if (hv_result(status) == HV_STATUS_INVALID_PARAMETER) {
1667+
pr_err("Underlying Hyper-V does not support order less than 9. Hypercall failed\n");
1668+
pr_err("Defaulting to page_reporting_order %d\n",
1669+
pageblock_order);
1670+
page_reporting_order = pageblock_order;
1671+
hv_hypercall_multi_failure++;
1672+
return -EINVAL;
1673+
}
1674+
16231675
return -EINVAL;
16241676
}
16251677

@@ -1630,25 +1682,25 @@ static void enable_page_reporting(void)
16301682
{
16311683
int ret;
16321684

1633-
/* Essentially, validating 'PAGE_REPORTING_MIN_ORDER' is big enough. */
1634-
if (pageblock_order < HV_MIN_PAGE_REPORTING_ORDER) {
1635-
pr_debug("Cold memory discard is only supported on 2MB pages and above\n");
1636-
return;
1637-
}
1638-
16391685
if (!hv_query_ext_cap(HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT)) {
16401686
pr_debug("Cold memory discard hint not supported by Hyper-V\n");
16411687
return;
16421688
}
16431689

16441690
BUILD_BUG_ON(PAGE_REPORTING_CAPACITY > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
16451691
dm_device.pr_dev_info.report = hv_free_page_report;
1692+
/*
1693+
* We let the page_reporting_order parameter decide the order
1694+
* in the page_reporting code
1695+
*/
1696+
dm_device.pr_dev_info.order = 0;
16461697
ret = page_reporting_register(&dm_device.pr_dev_info);
16471698
if (ret < 0) {
16481699
dm_device.pr_dev_info.report = NULL;
16491700
pr_err("Failed to enable cold memory discard: %d\n", ret);
16501701
} else {
1651-
pr_info("Cold memory discard hint enabled\n");
1702+
pr_info("Cold memory discard hint enabled with order %d\n",
1703+
page_reporting_order);
16521704
}
16531705
}
16541706

mm/page_reporting.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,42 @@
1111
#include "page_reporting.h"
1212
#include "internal.h"
1313

14-
unsigned int page_reporting_order = MAX_ORDER;
15-
module_param(page_reporting_order, uint, 0644);
14+
/* Initialize to an unsupported value */
15+
unsigned int page_reporting_order = -1;
16+
17+
static int page_order_update_notify(const char *val, const struct kernel_param *kp)
18+
{
19+
/*
20+
* If param is set beyond this limit, order is set to default
21+
* pageblock_order value
22+
*/
23+
return param_set_uint_minmax(val, kp, 0, MAX_ORDER-1);
24+
}
25+
26+
static const struct kernel_param_ops page_reporting_param_ops = {
27+
.set = &page_order_update_notify,
28+
/*
29+
* For the get op, use param_get_int instead of param_get_uint.
30+
* This is to make sure that when unset the initialized value of
31+
* -1 is shown correctly
32+
*/
33+
.get = &param_get_int,
34+
};
35+
36+
module_param_cb(page_reporting_order, &page_reporting_param_ops,
37+
&page_reporting_order, 0644);
1638
MODULE_PARM_DESC(page_reporting_order, "Set page reporting order");
1739

40+
/*
41+
* This symbol is also a kernel parameter. Export the page_reporting_order
42+
* symbol so that other drivers can access it to control order values without
43+
* having to introduce another configurable parameter. Only one driver can
44+
* register with the page_reporting driver for the service, so we have just
45+
* one control parameter for the use case(which can be accessed in both
46+
* drivers)
47+
*/
48+
EXPORT_SYMBOL_GPL(page_reporting_order);
49+
1850
#define PAGE_REPORTING_DELAY (2 * HZ)
1951
static struct page_reporting_dev_info __rcu *pr_dev_info __read_mostly;
2052

@@ -330,10 +362,18 @@ int page_reporting_register(struct page_reporting_dev_info *prdev)
330362
}
331363

332364
/*
333-
* Update the page reporting order if it's specified by driver.
334-
* Otherwise, it falls back to @pageblock_order.
365+
* If the page_reporting_order value is not set, we check if
366+
* an order is provided from the driver that is performing the
367+
* registration. If that is not provided either, we default to
368+
* pageblock_order.
335369
*/
336-
page_reporting_order = prdev->order ? : pageblock_order;
370+
371+
if (page_reporting_order == -1) {
372+
if (prdev->order > 0 && prdev->order <= MAX_ORDER)
373+
page_reporting_order = prdev->order;
374+
else
375+
page_reporting_order = pageblock_order;
376+
}
337377

338378
/* initialize state and work structures */
339379
atomic_set(&prdev->state, PAGE_REPORTING_IDLE);

0 commit comments

Comments
 (0)