Skip to content

Commit f9e4953

Browse files
committed
Merge feature/mm_discard/5.10 (b1cf932)
* msft/feature/mm_discard/5.10: Hyper-V: support for free page reporting Hyper-V: arm64: Support for repetitive hypercall
2 parents 33afc9d + b1cf932 commit f9e4953

File tree

11 files changed

+278
-39
lines changed

11 files changed

+278
-39
lines changed

arch/arm64/hyperv/mshyperv.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,37 @@ EXPORT_SYMBOL_GPL(hv_vp_index);
3232
u32 hv_max_vp_index;
3333
EXPORT_SYMBOL_GPL(hv_max_vp_index);
3434

35+
void __percpu **hyperv_pcpu_input_arg;
36+
EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);
37+
3538
static int hv_cpu_init(unsigned int cpu)
3639
{
40+
void **input_arg;
41+
42+
input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
43+
*input_arg = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
44+
if (unlikely(!*input_arg))
45+
return -ENOMEM;
3746
hv_vp_index[cpu] = hv_get_vpreg(HV_REGISTER_VP_INDEX);
3847
return 0;
3948
}
4049

50+
static int hv_cpu_die(unsigned int cpu)
51+
{
52+
void **input_arg;
53+
void *input;
54+
unsigned long flags;
55+
56+
local_irq_save(flags);
57+
input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
58+
input = *input_arg;
59+
*input_arg = NULL;
60+
local_irq_restore(flags);
61+
kfree(input);
62+
63+
return 0;
64+
}
65+
4166
void __init hyperv_early_init(void)
4267
{
4368
struct hv_get_vp_registers_output result;
@@ -58,13 +83,15 @@ void __init hyperv_early_init(void)
5883
/* Get the features and hints from Hyper-V */
5984
hv_get_vpreg_128(HV_REGISTER_FEATURES, &result);
6085
ms_hyperv.features = result.as32.a;
86+
ms_hyperv.priv_high = result.as32.b;
6187
ms_hyperv.misc_features = result.as32.c;
6288

6389
hv_get_vpreg_128(HV_REGISTER_ENLIGHTENMENTS, &result);
6490
ms_hyperv.hints = result.as32.a;
6591

66-
pr_info("Hyper-V: Features 0x%x, hints 0x%x, misc 0x%x\n",
67-
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);
6895

6996
/*
7097
* If Hyper-V has crash notifications, set crash_kexec_post_notifiers
@@ -95,26 +122,40 @@ static int __init hyperv_init(void)
95122
if (!hyperv_initialized)
96123
return 0;
97124

125+
/*
126+
* Allocate the per-CPU state for the hypercall input arg.
127+
* If this allocation fails, we will not be able to setup
128+
* (per-CPU) hypercall input page and thus this failure is
129+
* fatal on Hyper-V.
130+
*/
131+
hyperv_pcpu_input_arg = alloc_percpu(void *);
132+
if (unlikely(!hyperv_pcpu_input_arg))
133+
return -ENOMEM;
134+
98135
/* Allocate and initialize percpu VP index array */
99136
hv_max_vp_index = num_possible_cpus();
100137
hv_vp_index = kmalloc_array(hv_max_vp_index, sizeof(*hv_vp_index),
101138
GFP_KERNEL);
102139
if (!hv_vp_index) {
103140
hv_max_vp_index = 0;
141+
free_percpu(hyperv_pcpu_input_arg);
104142
return -ENOMEM;
105143
}
106144

107145
for (i = 0; i < hv_max_vp_index; i++)
108146
hv_vp_index[i] = VP_INVAL;
109147

110148
if (cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "arm64/hyperv_init:online",
111-
hv_cpu_init, NULL) < 0) {
149+
hv_cpu_init, hv_cpu_die) < 0) {
112150
hv_max_vp_index = 0;
113151
kfree(hv_vp_index);
114152
hv_vp_index = NULL;
153+
free_percpu(hyperv_pcpu_input_arg);
115154
return -EINVAL;
116155
}
117156

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

arch/arm64/include/asm/mshyperv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extern u64 hv_do_hvc_fast_get(u64 control, u64 input1, u64 input2, u64 input3,
4141
extern void hv_set_vpreg(u32 reg, u64 value);
4242
extern u64 hv_get_vpreg(u32 reg);
4343
extern void hv_get_vpreg_128(u32 reg, struct hv_get_vp_registers_output *result);
44+
extern void __percpu **hyperv_pcpu_input_arg;
4445

4546
static inline void hv_set_register(unsigned int reg, u64 value)
4647
{

arch/x86/include/asm/mshyperv.h

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -140,38 +140,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
140140
return hv_status;
141141
}
142142

143-
/*
144-
* Rep hypercalls. Callers of this functions are supposed to ensure that
145-
* rep_count and varhead_size comply with Hyper-V hypercall definition.
146-
*/
147-
static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size,
148-
void *input, void *output)
149-
{
150-
u64 control = code;
151-
u64 status;
152-
u16 rep_comp;
153-
154-
control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET;
155-
control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET;
156-
157-
do {
158-
status = hv_do_hypercall(control, input, output);
159-
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS)
160-
return status;
161-
162-
/* Bits 32-43 of status have 'Reps completed' data. */
163-
rep_comp = (status & HV_HYPERCALL_REP_COMP_MASK) >>
164-
HV_HYPERCALL_REP_COMP_OFFSET;
165-
166-
control &= ~HV_HYPERCALL_REP_START_MASK;
167-
control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET;
168-
169-
touch_nmi_watchdog();
170-
} while (rep_comp < rep_count);
171-
172-
return status;
173-
}
174-
175143
extern struct hv_vp_assist_page **hv_vp_assist_page;
176144

177145
static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)

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
@@ -12,3 +12,6 @@ hv_vmbus-y := vmbus_drv.o \
1212
channel_mgmt.o ring_buffer.o hv_trace.o
1313
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
1414
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
15+
16+
# Code that must be built-in
17+
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);

0 commit comments

Comments
 (0)