Skip to content

Commit b1cf932

Browse files
sunilmuttyhicks
authored andcommitted
Hyper-V: support for free page reporting
Linux has support for free page reporting now (36e66c5) for virtualized environment. On Hyper-V when virtually backed VMs are configured, Hyper-V will advertise cold memory discard capability, when supported. This patch adds the support to hook into the free page reporting infrastructure and leverage the Hyper-V cold memory discard hint hypercall to report/free these pages back to the host. Signed-off-by: Sunil Muthuswamy <[email protected]>
1 parent 7db6f28 commit b1cf932

File tree

9 files changed

+206
-6
lines changed

9 files changed

+206
-6
lines changed

arch/arm64/hyperv/mshyperv.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,15 @@ void __init hyperv_early_init(void)
8383
/* Get the features and hints from Hyper-V */
8484
hv_get_vpreg_128(HV_REGISTER_FEATURES, &result);
8585
ms_hyperv.features = result.as32.a;
86+
ms_hyperv.priv_high = result.as32.b;
8687
ms_hyperv.misc_features = result.as32.c;
8788

8889
hv_get_vpreg_128(HV_REGISTER_ENLIGHTENMENTS, &result);
8990
ms_hyperv.hints = result.as32.a;
9091

91-
pr_info("Hyper-V: Features 0x%x, hints 0x%x, misc 0x%x\n",
92-
ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
92+
pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
93+
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
94+
ms_hyperv.misc_features);
9395

9496
/*
9597
* If Hyper-V has crash notifications, set crash_kexec_post_notifiers
@@ -152,6 +154,8 @@ static int __init hyperv_init(void)
152154
return -EINVAL;
153155
}
154156

157+
/* Query the VMs extended capability once, so that it can be cached. */
158+
hv_query_ext_cap(0);
155159
return 0;
156160
}
157161

arch/x86/kernel/cpu/mshyperv.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,13 @@ static void __init ms_hyperv_init_platform(void)
234234
* Extract the features and hints
235235
*/
236236
ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
237+
ms_hyperv.priv_high = cpuid_ebx(HYPERV_CPUID_FEATURES);
237238
ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
238239
ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
239240

240-
pr_info("Hyper-V: features 0x%x, hints 0x%x, misc 0x%x\n",
241-
ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
241+
pr_info("Hyper-V: privilege flags low 0x%x, high 0x%x, hints 0x%x, misc 0x%x\n",
242+
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
243+
ms_hyperv.misc_features);
242244

243245
ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS);
244246
ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS);

drivers/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ obj-$(CONFIG_SOUNDWIRE) += soundwire/
160160

161161
# Virtualization drivers
162162
obj-$(CONFIG_VIRT_DRIVERS) += virt/
163-
obj-$(CONFIG_HYPERV) += hv/
163+
obj-$(subst m,y,$(CONFIG_HYPERV)) += hv/
164164

165165
obj-$(CONFIG_PM_DEVFREQ) += devfreq/
166166
obj-$(CONFIG_EXTCON) += extcon/

drivers/hv/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ config HYPERV_UTILS
2424
config HYPERV_BALLOON
2525
tristate "Microsoft Hyper-V Balloon driver"
2626
depends on HYPERV
27+
select PAGE_REPORTING
2728
help
2829
Select this option to enable Hyper-V Balloon driver.
2930

drivers/hv/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ hv_vmbus-y := vmbus_drv.o \
1111
channel_mgmt.o ring_buffer.o hv_trace.o
1212
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
1313
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
14+
15+
# Code that must be built-in
16+
obj-$(subst m,y,$(CONFIG_HYPERV)) += hv_common.o

drivers/hv/hv_balloon.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/memory.h>
2222
#include <linux/notifier.h>
2323
#include <linux/percpu_counter.h>
24+
#include <linux/page_reporting.h>
2425

2526
#include <linux/hyperv.h>
2627
#include <asm/hyperv-tlfs.h>
@@ -563,6 +564,8 @@ struct hv_dynmem_device {
563564
* The negotiated version agreed by host.
564565
*/
565566
__u32 version;
567+
568+
struct page_reporting_dev_info pr_dev_info;
566569
};
567570

568571
static struct hv_dynmem_device dm_device;
@@ -1565,6 +1568,89 @@ static void balloon_onchannelcallback(void *context)
15651568

15661569
}
15671570

1571+
/* Hyper-V only supports reporting 2MB pages or higher */
1572+
#define HV_MIN_PAGE_REPORTING_ORDER 9
1573+
#define HV_MIN_PAGE_REPORTING_LEN (HV_HYP_PAGE_SIZE << HV_MIN_PAGE_REPORTING_ORDER)
1574+
static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info,
1575+
struct scatterlist *sgl, unsigned int nents)
1576+
{
1577+
unsigned long flags;
1578+
struct hv_memory_hint *hint;
1579+
int i;
1580+
u64 status;
1581+
struct scatterlist *sg;
1582+
1583+
WARN_ON_ONCE(nents > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
1584+
WARN_ON_ONCE(sgl->length < HV_MIN_PAGE_REPORTING_LEN);
1585+
local_irq_save(flags);
1586+
hint = *(struct hv_memory_hint **)this_cpu_ptr(hyperv_pcpu_input_arg);
1587+
if (!hint) {
1588+
local_irq_restore(flags);
1589+
return -ENOSPC;
1590+
}
1591+
1592+
hint->type = HV_EXT_MEMORY_HEAT_HINT_TYPE_COLD_DISCARD;
1593+
hint->reserved = 0;
1594+
for_each_sg(sgl, sg, nents, i) {
1595+
union hv_gpa_page_range *range;
1596+
1597+
range = &hint->ranges[i];
1598+
range->address_space = 0;
1599+
/* page reporting only reports 2MB pages or higher */
1600+
range->page.largepage = 1;
1601+
range->page.additional_pages =
1602+
(sg->length / HV_MIN_PAGE_REPORTING_LEN) - 1;
1603+
range->page_size = HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB;
1604+
range->base_large_pfn =
1605+
page_to_hvpfn(sg_page(sg)) >> HV_MIN_PAGE_REPORTING_ORDER;
1606+
}
1607+
1608+
status = hv_do_rep_hypercall(HV_EXT_CALL_MEMORY_HEAT_HINT, nents, 0,
1609+
hint, NULL);
1610+
local_irq_restore(flags);
1611+
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS) {
1612+
pr_err("Cold memory discard hypercall failed with status %llx\n",
1613+
status);
1614+
return -EINVAL;
1615+
}
1616+
1617+
return 0;
1618+
}
1619+
1620+
static void enable_page_reporting(void)
1621+
{
1622+
int ret;
1623+
1624+
/* Essentially, validating 'PAGE_REPORTING_MIN_ORDER' is big enough. */
1625+
if (pageblock_order < HV_MIN_PAGE_REPORTING_ORDER) {
1626+
pr_debug("Cold memory discard is only supported on 2MB pages and above\n");
1627+
return;
1628+
}
1629+
1630+
if (!hv_query_ext_cap(HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT)) {
1631+
pr_debug("Cold memory discard hint not supported by Hyper-V\n");
1632+
return;
1633+
}
1634+
1635+
BUILD_BUG_ON(PAGE_REPORTING_CAPACITY > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES);
1636+
dm_device.pr_dev_info.report = hv_free_page_report;
1637+
ret = page_reporting_register(&dm_device.pr_dev_info);
1638+
if (ret < 0) {
1639+
dm_device.pr_dev_info.report = NULL;
1640+
pr_err("Failed to enable cold memory discard: %d\n", ret);
1641+
} else {
1642+
pr_info("Cold memory discard hint enabled\n");
1643+
}
1644+
}
1645+
1646+
static void disable_page_reporting(void)
1647+
{
1648+
if (dm_device.pr_dev_info.report) {
1649+
page_reporting_unregister(&dm_device.pr_dev_info);
1650+
dm_device.pr_dev_info.report = NULL;
1651+
}
1652+
}
1653+
15681654
static int balloon_connect_vsp(struct hv_device *dev)
15691655
{
15701656
struct dm_version_request version_req;
@@ -1710,6 +1796,7 @@ static int balloon_probe(struct hv_device *dev,
17101796
if (ret != 0)
17111797
return ret;
17121798

1799+
enable_page_reporting();
17131800
dm_device.state = DM_INITIALIZED;
17141801

17151802
dm_device.thread =
@@ -1724,6 +1811,7 @@ static int balloon_probe(struct hv_device *dev,
17241811
probe_error:
17251812
dm_device.state = DM_INIT_ERROR;
17261813
dm_device.thread = NULL;
1814+
disable_page_reporting();
17271815
vmbus_close(dev->channel);
17281816
#ifdef CONFIG_MEMORY_HOTPLUG
17291817
unregister_memory_notifier(&hv_memory_nb);
@@ -1746,6 +1834,7 @@ static int balloon_remove(struct hv_device *dev)
17461834
cancel_work_sync(&dm->ha_wrk.wrk);
17471835

17481836
kthread_stop(dm->thread);
1837+
disable_page_reporting();
17491838
vmbus_close(dev->channel);
17501839
#ifdef CONFIG_MEMORY_HOTPLUG
17511840
unregister_memory_notifier(&hv_memory_nb);

drivers/hv/hv_common.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Architecture neutral utility routines for interacting with
5+
* Hyper-V. This file is specifically for code that must be
6+
* built-in to the kernel image when CONFIG_HYPERV is set
7+
* (vs. being in a module) because it is called from architecture
8+
* specific code under arch/.
9+
*
10+
* Copyright (C) 2021, Microsoft, Inc.
11+
*
12+
* Author : Michael Kelley <[email protected]>
13+
*/
14+
15+
#include <linux/types.h>
16+
#include <linux/export.h>
17+
#include <linux/bitfield.h>
18+
#include <asm/hyperv-tlfs.h>
19+
#include <asm/mshyperv.h>
20+
21+
22+
/* Bit mask of the extended capability to query: see HV_EXT_CAPABILITY_xxx */
23+
bool hv_query_ext_cap(u64 cap_query)
24+
{
25+
/*
26+
* The address of the 'hv_extended_cap' variable will be used as an
27+
* output parameter to the hypercall below and so it should be
28+
* compatible with 'virt_to_phys'. Which means, it's address should be
29+
* directly mapped. Use 'static' to keep it compatible; stack variables
30+
* can be virtually mapped, making them imcompatible with
31+
* 'virt_to_phys'.
32+
* Hypercall input/output addresses should also be 8-byte aligned.
33+
*/
34+
static u64 hv_extended_cap __aligned(8);
35+
static bool hv_extended_cap_queried;
36+
u64 status;
37+
38+
/*
39+
* Querying extended capabilities is an extended hypercall. Check if the
40+
* partition supports extended hypercall, first.
41+
*/
42+
if (!(ms_hyperv.priv_high & HV_ENABLE_EXTENDED_HYPERCALLS))
43+
return false;
44+
45+
/* Extended capabilities do not change at runtime. */
46+
if (hv_extended_cap_queried)
47+
return hv_extended_cap & cap_query;
48+
49+
status = hv_do_hypercall(HV_EXT_CALL_QUERY_CAPABILITIES, NULL,
50+
&hv_extended_cap);
51+
52+
/*
53+
* The query extended capabilities hypercall should not fail under
54+
* any normal circumstances. Avoid repeatedly making the hypercall, on
55+
* error.
56+
*/
57+
hv_extended_cap_queried = true;
58+
status &= HV_HYPERCALL_RESULT_MASK;
59+
if (status != HV_STATUS_SUCCESS) {
60+
pr_err("Hyper-V: Extended query capabilities hypercall failed 0x%llx\n",
61+
status);
62+
return false;
63+
}
64+
65+
return hv_extended_cap & cap_query;
66+
}
67+
EXPORT_SYMBOL_GPL(hv_query_ext_cap);

include/asm-generic/hyperv-tlfs.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#define HV_ACCESS_STATS BIT(8)
9090
#define HV_DEBUGGING BIT(11)
9191
#define HV_CPU_POWER_MANAGEMENT BIT(12)
92+
#define HV_ENABLE_EXTENDED_HYPERCALLS BIT(20)
9293

9394

9495
/*
@@ -152,11 +153,18 @@ struct ms_hyperv_tsc_page {
152153
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
153154
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
154155

156+
/* Extended hypercalls */
157+
#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
158+
#define HV_EXT_CALL_MEMORY_HEAT_HINT 0x8003
159+
155160
#define HV_FLUSH_ALL_PROCESSORS BIT(0)
156161
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
157162
#define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2)
158163
#define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3)
159164

165+
/* Extended capability bits */
166+
#define HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT BIT(8)
167+
160168
enum HV_GENERIC_SET_FORMAT {
161169
HV_GENERIC_SET_SPARSE_4K,
162170
HV_GENERIC_SET_ALL,
@@ -401,15 +409,23 @@ struct hv_guest_mapping_flush {
401409
* by the bitwidth of "additional_pages" in union hv_gpa_page_range.
402410
*/
403411
#define HV_MAX_FLUSH_PAGES (2048)
412+
#define HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB 0
413+
#define HV_GPA_PAGE_RANGE_PAGE_SIZE_1GB 1
404414

405-
/* HvFlushGuestPhysicalAddressList hypercall */
415+
/* HvFlushGuestPhysicalAddressList, HvExtCallMemoryHeatHint hypercall */
406416
union hv_gpa_page_range {
407417
u64 address_space;
408418
struct {
409419
u64 additional_pages:11;
410420
u64 largepage:1;
411421
u64 basepfn:52;
412422
} page;
423+
struct {
424+
u64 reserved:12;
425+
u64 page_size:1;
426+
u64 reserved1:8;
427+
u64 base_large_pfn:43;
428+
};
413429
};
414430

415431
/*
@@ -529,4 +545,20 @@ struct hv_set_vp_registers_input {
529545
} element[];
530546
} __packed;
531547

548+
/*
549+
* The whole argument should fit in a page to be able to pass to the hypervisor
550+
* in one hypercall.
551+
*/
552+
#define HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES \
553+
((HV_HYP_PAGE_SIZE - sizeof(struct hv_memory_hint)) / \
554+
sizeof(union hv_gpa_page_range))
555+
556+
/* HvExtCallMemoryHeatHint hypercall */
557+
#define HV_EXT_MEMORY_HEAT_HINT_TYPE_COLD_DISCARD 2
558+
struct hv_memory_hint {
559+
u64 type:2;
560+
u64 reserved:62;
561+
union hv_gpa_page_range ranges[];
562+
} __packed;
563+
532564
#endif

include/asm-generic/mshyperv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
struct ms_hyperv_info {
3030
u32 features;
31+
u32 priv_high;
3132
u32 misc_features;
3233
u32 hints;
3334
u32 nested_features;
@@ -205,6 +206,7 @@ void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
205206
bool hv_is_hyperv_initialized(void);
206207
bool hv_is_hibernation_supported(void);
207208
void hyperv_cleanup(void);
209+
bool hv_query_ext_cap(u64 cap_query);
208210
#else /* CONFIG_HYPERV */
209211
static inline bool hv_is_hyperv_initialized(void) { return false; }
210212
static inline bool hv_is_hibernation_supported(void) { return false; }

0 commit comments

Comments
 (0)