Skip to content

Commit cb68dda

Browse files
committed
Merge feature/mm_discard (e0e7ed2)
* msft/feature/mm_discard: Hyper-V: support for free page reporting Hyper-V: Support for querying extended capabilities Hyper-V: arm64: Support for repetitive hypercall
2 parents 69d65f5 + e0e7ed2 commit cb68dda

File tree

8 files changed

+233
-38
lines changed

8 files changed

+233
-38
lines changed

arch/arm64/hyperv/hv_core.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,21 @@ EXPORT_SYMBOL_GPL(hv_vp_index);
4040
u32 hv_max_vp_index;
4141
EXPORT_SYMBOL_GPL(hv_max_vp_index);
4242

43+
void __percpu **hyperv_pcpu_input_arg;
44+
EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);
45+
4346
static int hv_cpu_init(unsigned int cpu)
4447
{
48+
void **input_arg;
49+
struct page *pg;
4550
u64 msr_vp_index;
4651

52+
input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
53+
pg = alloc_page(GFP_KERNEL);
54+
if (unlikely(!pg))
55+
return -ENOMEM;
56+
*input_arg = page_address(pg);
57+
4758
msr_vp_index = hv_get_vpreg(HV_REGISTER_VPINDEX);
4859

4960
hv_vp_index[smp_processor_id()] = msr_vp_index;
@@ -54,6 +65,22 @@ static int hv_cpu_init(unsigned int cpu)
5465
return 0;
5566
}
5667

68+
static int hv_cpu_die(unsigned int cpu)
69+
{
70+
void **input_arg;
71+
void *input_pg;
72+
unsigned long flags;
73+
74+
local_irq_save(flags);
75+
input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
76+
input_pg = *input_arg;
77+
*input_arg = NULL;
78+
local_irq_restore(flags);
79+
free_page((unsigned long)input_pg);
80+
81+
return 0;
82+
}
83+
5784
/*
5885
* Functions for allocating and freeing memory with size and
5986
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
@@ -132,13 +159,25 @@ static int __init hyperv_init(struct acpi_table_header *table)
132159
/* Get the features and hints from Hyper-V */
133160
hv_get_vpreg_128(HV_REGISTER_FEATURES, &result);
134161
ms_hyperv.features = result.as32.a;
162+
ms_hyperv.priv_high = result.as32.b;
135163
ms_hyperv.misc_features = result.as32.c;
136164

137165
hv_get_vpreg_128(HV_REGISTER_ENLIGHTENMENTS, &result);
138166
ms_hyperv.hints = result.as32.a;
139167

140-
pr_info("Hyper-V: Features 0x%x, hints 0x%x, misc 0x%x\n",
141-
ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
168+
pr_info("Hyper-V: Features 0x%x, privilege high: 0x%x, hints 0x%x, misc 0x%x\n",
169+
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
170+
ms_hyperv.misc_features);
171+
172+
/*
173+
* Allocate the per-CPU state for the hypercall input arg.
174+
* If this allocation fails, we will not be able to setup
175+
* (per-CPU) hypercall input page and thus this failure is
176+
* fatal on Hyper-V.
177+
*/
178+
hyperv_pcpu_input_arg = alloc_percpu(void *);
179+
180+
BUG_ON(hyperv_pcpu_input_arg == NULL);
142181

143182
/*
144183
* If Hyper-V has crash notifications, set crash_kexec_post_notifiers
@@ -166,7 +205,7 @@ static int __init hyperv_init(struct acpi_table_header *table)
166205
hv_vp_index[i] = VP_INVAL;
167206

168207
cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
169-
"arm64/hyperv_init:online", hv_cpu_init, NULL);
208+
"arm64/hyperv_init:online", hv_cpu_init, hv_cpu_die);
170209
if (cpuhp < 0)
171210
goto free_vp_index;
172211

arch/arm64/include/asm/mshyperv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ extern void hv_set_vpreg(u32 reg, u64 value);
3535
extern u64 hv_get_vpreg(u32 reg);
3636
extern void hv_get_vpreg_128(u32 reg, struct hv_get_vp_registers_output *result);
3737

38+
extern void __percpu **hyperv_pcpu_input_arg;
39+
3840
/* Access various Hyper-V synthetic registers */
3941
static inline void hv_set_simp(u64 val)
4042
{

arch/x86/include/asm/mshyperv.h

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

189-
/*
190-
* Rep hypercalls. Callers of this functions are supposed to ensure that
191-
* rep_count and varhead_size comply with Hyper-V hypercall definition.
192-
*/
193-
static inline u64 hv_do_rep_hypercall(u16 code, u16 rep_count, u16 varhead_size,
194-
void *input, void *output)
195-
{
196-
u64 control = code;
197-
u64 status;
198-
u16 rep_comp;
199-
200-
control |= (u64)varhead_size << HV_HYPERCALL_VARHEAD_OFFSET;
201-
control |= (u64)rep_count << HV_HYPERCALL_REP_COMP_OFFSET;
202-
203-
do {
204-
status = hv_do_hypercall(control, input, output);
205-
if ((status & HV_HYPERCALL_RESULT_MASK) != HV_STATUS_SUCCESS)
206-
return status;
207-
208-
/* Bits 32-43 of status have 'Reps completed' data. */
209-
rep_comp = (status & HV_HYPERCALL_REP_COMP_MASK) >>
210-
HV_HYPERCALL_REP_COMP_OFFSET;
211-
212-
control &= ~HV_HYPERCALL_REP_START_MASK;
213-
control |= (u64)rep_comp << HV_HYPERCALL_REP_START_OFFSET;
214-
215-
touch_nmi_watchdog();
216-
} while (rep_comp < rep_count);
217-
218-
return status;
219-
}
220-
221189
extern struct hv_vp_assist_page **hv_vp_assist_page;
222190

223191
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
@@ -243,11 +243,13 @@ static void __init ms_hyperv_init_platform(void)
243243
* Extract the features and hints
244244
*/
245245
ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES);
246+
ms_hyperv.priv_high = cpuid_ebx(HYPERV_CPUID_FEATURES);
246247
ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
247248
ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
248249

249-
pr_info("Hyper-V: features 0x%x, hints 0x%x, misc 0x%x\n",
250-
ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
250+
pr_info("Hyper-V: features 0x%x, privilege high: 0x%x, hints 0x%x, misc 0x%x\n",
251+
ms_hyperv.features, ms_hyperv.priv_high, ms_hyperv.hints,
252+
ms_hyperv.misc_features);
251253

252254
ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS);
253255
ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS);

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/hv_balloon.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include <linux/memory.h>
2222
#include <linux/notifier.h>
2323
#include <linux/percpu_counter.h>
24-
24+
#include <linux/page_reporting.h>
2525
#include <linux/hyperv.h>
2626
#include <asm/hyperv-tlfs.h>
2727

@@ -563,6 +563,9 @@ struct hv_dynmem_device {
563563
* The negotiated version agreed by host.
564564
*/
565565
__u32 version;
566+
#ifdef CONFIG_PAGE_REPORTING
567+
struct page_reporting_dev_info ph_dev_info;
568+
#endif
566569
};
567570

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

15661569
}
15671570

1571+
#ifdef CONFIG_PAGE_REPORTING
1572+
static int hyperv_discard_pages(struct scatterlist **sgs, int nents)
1573+
{
1574+
unsigned long flags;
1575+
struct hv_memory_hint *hint;
1576+
int i;
1577+
struct scatterlist *sg;
1578+
u64 status;
1579+
1580+
WARN_ON(nents > HV_MAX_GPA_PAGE_RANGES);
1581+
local_irq_save(flags);
1582+
hint = *(struct hv_memory_hint **)this_cpu_ptr(hyperv_pcpu_input_arg);
1583+
if (!hint) {
1584+
local_irq_restore(flags);
1585+
return -EINVAL;
1586+
}
1587+
1588+
hint->type = HV_MEMORY_HINT_TYPE_COLD_DISCARD;
1589+
hint->reserved = 0;
1590+
for (i = 0, sg = sgs[0]; sg; sg = sg_next(sg), i++) {
1591+
int order;
1592+
union hv_gpa_page_range *range;
1593+
1594+
order = get_order(sg->length);
1595+
range = &hint->ranges[i];
1596+
range->address_space = 0;
1597+
range->page.largepage = 1;
1598+
range->page.additional_pages = (1ull << (order - 9)) - 1;
1599+
range->base_large_pfn = page_to_pfn(sg_page(sg)) >> 9;
1600+
}
1601+
1602+
WARN_ON(i != nents);
1603+
1604+
status = hv_do_rep_hypercall(HVCALL_MEMORY_HEAT_HINT, nents, 0,
1605+
hint, NULL);
1606+
local_irq_restore(flags);
1607+
status &= HV_HYPERCALL_RESULT_MASK;
1608+
if (status != HV_STATUS_SUCCESS) {
1609+
WARN_ONCE(1,
1610+
"Cold memory discard hypercall failed with status 0x%llx\n",
1611+
status);
1612+
return -EINVAL;
1613+
} else {
1614+
return 0;
1615+
}
1616+
}
1617+
1618+
static int hv_page_hinting(struct page_reporting_dev_info *ph_dev_info,
1619+
struct scatterlist *sg, unsigned int nents)
1620+
{
1621+
return hyperv_discard_pages(&sg, nents);
1622+
}
1623+
1624+
static void enable_page_reporting(void)
1625+
{
1626+
int ret;
1627+
1628+
if (!hyperv_query_ext_cap(HV_CAPABILITY_MEMORY_COLD_DISCARD_HINT)) {
1629+
pr_info("Cold memory discard hint not supported.\n");
1630+
return;
1631+
}
1632+
1633+
BUILD_BUG_ON(PAGE_REPORTING_CAPACITY > HV_MAX_GPA_PAGE_RANGES);
1634+
dm_device.ph_dev_info.report = hv_page_hinting;
1635+
ret = page_reporting_register(&dm_device.ph_dev_info);
1636+
if (ret < 0) {
1637+
pr_err("Failed to enable cold memory discard: %d\n", ret);
1638+
dm_device.ph_dev_info.report = NULL;
1639+
} else {
1640+
pr_info("Cold memory discard hint enabled\n");
1641+
}
1642+
}
1643+
1644+
static void disable_hinting(void)
1645+
{
1646+
if (dm_device.ph_dev_info.report) {
1647+
page_reporting_unregister(&dm_device.ph_dev_info);
1648+
dm_device.ph_dev_info.report = NULL;
1649+
}
1650+
}
1651+
#endif //CONFIG_PAGE_REPORTING
1652+
15681653
static int balloon_connect_vsp(struct hv_device *dev)
15691654
{
15701655
struct dm_version_request version_req;
@@ -1710,6 +1795,10 @@ static int balloon_probe(struct hv_device *dev,
17101795
if (ret != 0)
17111796
return ret;
17121797

1798+
#ifdef CONFIG_PAGE_REPORTING
1799+
enable_page_reporting();
1800+
#endif
1801+
17131802
dm_device.state = DM_INITIALIZED;
17141803

17151804
dm_device.thread =
@@ -1725,6 +1814,9 @@ static int balloon_probe(struct hv_device *dev,
17251814
dm_device.state = DM_INIT_ERROR;
17261815
dm_device.thread = NULL;
17271816
vmbus_close(dev->channel);
1817+
#ifdef CONFIG_PAGE_REPORTING
1818+
disable_hinting();
1819+
#endif
17281820
#ifdef CONFIG_MEMORY_HOTPLUG
17291821
unregister_memory_notifier(&hv_memory_nb);
17301822
restore_online_page_callback(&hv_online_page);
@@ -1747,6 +1839,9 @@ static int balloon_remove(struct hv_device *dev)
17471839

17481840
kthread_stop(dm->thread);
17491841
vmbus_close(dev->channel);
1842+
#ifdef CONFIG_PAGE_REPORTING
1843+
disable_hinting();
1844+
#endif
17501845
#ifdef CONFIG_MEMORY_HOTPLUG
17511846
unregister_memory_notifier(&hv_memory_nb);
17521847
restore_online_page_callback(&hv_online_page);

include/asm-generic/hyperv-tlfs.h

Lines changed: 25 additions & 0 deletions
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
/*
@@ -151,6 +152,8 @@ struct ms_hyperv_tsc_page {
151152
#define HVCALL_RETARGET_INTERRUPT 0x007e
152153
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
153154
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
155+
#define HVCALL_QUERY_CAPABILITIES 0x8001
156+
#define HVCALL_MEMORY_HEAT_HINT 0x8003
154157

155158
#define HV_FLUSH_ALL_PROCESSORS BIT(0)
156159
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
@@ -375,6 +378,12 @@ union hv_gpa_page_range {
375378
u64 largepage:1;
376379
u64 basepfn:52;
377380
} page;
381+
struct {
382+
u64:12;
383+
u64 page_size:1;
384+
u64 reserved:8;
385+
u64 base_large_pfn:43;
386+
};
378387
};
379388

380389
/*
@@ -494,4 +503,20 @@ struct hv_set_vp_registers_input {
494503
} element[];
495504
} __packed;
496505

506+
#ifdef CONFIG_PAGE_REPORTING
507+
#define HV_CAPABILITY_MEMORY_COLD_DISCARD_HINT BIT(8)
508+
509+
// The whole argument should fit in a page to be able to pass to the hypervisor
510+
// in one hypercall.
511+
#define HV_MAX_GPA_PAGE_RANGES ((PAGE_SIZE - 8)/sizeof(union hv_gpa_page_range))
512+
513+
/* HvExtMemoryHeatHint hypercall */
514+
#define HV_MEMORY_HINT_TYPE_COLD_DISCARD BIT(1)
515+
struct hv_memory_hint {
516+
u64 type:2;
517+
u64 reserved:62;
518+
union hv_gpa_page_range ranges[1];
519+
};
520+
521+
#endif //CONFIG_PAGE_REPORTING
497522
#endif

0 commit comments

Comments
 (0)