Skip to content

Commit 81b18bc

Browse files
sunilmutgregkh
authored andcommitted
Drivers: HV: Send one page worth of kmsg dump over Hyper-V during panic
In the VM mode on Hyper-V, currently, when the kernel panics, an error code and few register values are populated in an MSR and the Hypervisor notified. This information is collected on the host. The amount of information currently collected is found to be limited and not very actionable. To gather more actionable data, such as stack trace, the proposal is to write one page worth of kmsg data on an allocated page and the Hypervisor notified of the page address through the MSR. - Sysctl option to control the behavior, with ON by default. Cc: K. Y. Srinivasan <[email protected]> Cc: Stephen Hemminger <[email protected]> Signed-off-by: Sunil Muthuswamy <[email protected]> Signed-off-by: K. Y. Srinivasan <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent e464d28 commit 81b18bc

File tree

5 files changed

+152
-2
lines changed

5 files changed

+152
-2
lines changed

Documentation/sysctl/kernel.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ show up in /proc/sys/kernel:
3939
- hung_task_check_count
4040
- hung_task_timeout_secs
4141
- hung_task_warnings
42+
- hyperv_record_panic_msg
4243
- kexec_load_disabled
4344
- kptr_restrict
4445
- l2cr [ PPC only ]
@@ -374,6 +375,16 @@ This file shows up if CONFIG_DETECT_HUNG_TASK is enabled.
374375

375376
==============================================================
376377

378+
hyperv_record_panic_msg:
379+
380+
Controls whether the panic kmsg data should be reported to Hyper-V.
381+
382+
0: do not report panic kmsg data.
383+
384+
1: report the panic kmsg data. This is the default behavior.
385+
386+
==============================================================
387+
377388
kexec_load_disabled:
378389

379390
A toggle indicating if the kexec_load syscall has been disabled. This

arch/x86/hyperv/hv_init.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,33 @@ void hyperv_report_panic(struct pt_regs *regs, long err)
423423
}
424424
EXPORT_SYMBOL_GPL(hyperv_report_panic);
425425

426+
/**
427+
* hyperv_report_panic_msg - report panic message to Hyper-V
428+
* @pa: physical address of the panic page containing the message
429+
* @size: size of the message in the page
430+
*/
431+
void hyperv_report_panic_msg(phys_addr_t pa, size_t size)
432+
{
433+
/*
434+
* P3 to contain the physical address of the panic page & P4 to
435+
* contain the size of the panic data in that page. Rest of the
436+
* registers are no-op when the NOTIFY_MSG flag is set.
437+
*/
438+
wrmsrl(HV_X64_MSR_CRASH_P0, 0);
439+
wrmsrl(HV_X64_MSR_CRASH_P1, 0);
440+
wrmsrl(HV_X64_MSR_CRASH_P2, 0);
441+
wrmsrl(HV_X64_MSR_CRASH_P3, pa);
442+
wrmsrl(HV_X64_MSR_CRASH_P4, size);
443+
444+
/*
445+
* Let Hyper-V know there is crash data available along with
446+
* the panic message.
447+
*/
448+
wrmsrl(HV_X64_MSR_CRASH_CTL,
449+
(HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG));
450+
}
451+
EXPORT_SYMBOL_GPL(hyperv_report_panic_msg);
452+
426453
bool hv_is_hyperv_initialized(void)
427454
{
428455
union hv_x64_msr_hypercall_contents hypercall_msr;

arch/x86/include/asm/hyperv-tlfs.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,10 @@
176176
#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED (1 << 14)
177177

178178
/*
179-
* Crash notification flag.
179+
* Crash notification flags.
180180
*/
181-
#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63)
181+
#define HV_CRASH_CTL_CRASH_NOTIFY_MSG BIT_ULL(62)
182+
#define HV_CRASH_CTL_CRASH_NOTIFY BIT_ULL(63)
182183

183184
/* MSR used to identify the guest OS. */
184185
#define HV_X64_MSR_GUEST_OS_ID 0x40000000

arch/x86/include/asm/mshyperv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset,
299299
void __init hyperv_init(void);
300300
void hyperv_setup_mmu_ops(void);
301301
void hyperv_report_panic(struct pt_regs *regs, long err);
302+
void hyperv_report_panic_msg(phys_addr_t pa, size_t size);
302303
bool hv_is_hyperv_initialized(void);
303304
void hyperv_cleanup(void);
304305

drivers/hv/vmbus_drv.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ static struct completion probe_event;
5656

5757
static int hyperv_cpuhp_online;
5858

59+
static void *hv_panic_page;
60+
5961
static int hyperv_panic_event(struct notifier_block *nb, unsigned long val,
6062
void *args)
6163
{
@@ -1018,6 +1020,75 @@ static void vmbus_isr(void)
10181020
add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0);
10191021
}
10201022

1023+
/*
1024+
* Boolean to control whether to report panic messages over Hyper-V.
1025+
*
1026+
* It can be set via /proc/sys/kernel/hyperv/record_panic_msg
1027+
*/
1028+
static int sysctl_record_panic_msg = 1;
1029+
1030+
/*
1031+
* Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
1032+
* buffer and call into Hyper-V to transfer the data.
1033+
*/
1034+
static void hv_kmsg_dump(struct kmsg_dumper *dumper,
1035+
enum kmsg_dump_reason reason)
1036+
{
1037+
size_t bytes_written;
1038+
phys_addr_t panic_pa;
1039+
1040+
/* We are only interested in panics. */
1041+
if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg))
1042+
return;
1043+
1044+
panic_pa = virt_to_phys(hv_panic_page);
1045+
1046+
/*
1047+
* Write dump contents to the page. No need to synchronize; panic should
1048+
* be single-threaded.
1049+
*/
1050+
if (!kmsg_dump_get_buffer(dumper, true, hv_panic_page,
1051+
PAGE_SIZE, &bytes_written)) {
1052+
pr_err("Hyper-V: Unable to get kmsg data for panic\n");
1053+
return;
1054+
}
1055+
1056+
hyperv_report_panic_msg(panic_pa, bytes_written);
1057+
}
1058+
1059+
static struct kmsg_dumper hv_kmsg_dumper = {
1060+
.dump = hv_kmsg_dump,
1061+
};
1062+
1063+
static struct ctl_table_header *hv_ctl_table_hdr;
1064+
static int zero;
1065+
static int one = 1;
1066+
1067+
/*
1068+
* sysctl option to allow the user to control whether kmsg data should be
1069+
* reported to Hyper-V on panic.
1070+
*/
1071+
static struct ctl_table hv_ctl_table[] = {
1072+
{
1073+
.procname = "hyperv_record_panic_msg",
1074+
.data = &sysctl_record_panic_msg,
1075+
.maxlen = sizeof(int),
1076+
.mode = 0644,
1077+
.proc_handler = proc_dointvec_minmax,
1078+
.extra1 = &zero,
1079+
.extra2 = &one
1080+
},
1081+
{}
1082+
};
1083+
1084+
static struct ctl_table hv_root_table[] = {
1085+
{
1086+
.procname = "kernel",
1087+
.mode = 0555,
1088+
.child = hv_ctl_table
1089+
},
1090+
{}
1091+
};
10211092

10221093
/*
10231094
* vmbus_bus_init -Main vmbus driver initialization routine.
@@ -1065,6 +1136,32 @@ static int vmbus_bus_init(void)
10651136
* Only register if the crash MSRs are available
10661137
*/
10671138
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
1139+
u64 hyperv_crash_ctl;
1140+
/*
1141+
* Sysctl registration is not fatal, since by default
1142+
* reporting is enabled.
1143+
*/
1144+
hv_ctl_table_hdr = register_sysctl_table(hv_root_table);
1145+
if (!hv_ctl_table_hdr)
1146+
pr_err("Hyper-V: sysctl table register error");
1147+
1148+
/*
1149+
* Register for panic kmsg callback only if the right
1150+
* capability is supported by the hypervisor.
1151+
*/
1152+
rdmsrl(HV_X64_MSR_CRASH_CTL, hyperv_crash_ctl);
1153+
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) {
1154+
hv_panic_page = (void *)get_zeroed_page(GFP_KERNEL);
1155+
if (hv_panic_page) {
1156+
ret = kmsg_dump_register(&hv_kmsg_dumper);
1157+
if (ret)
1158+
pr_err("Hyper-V: kmsg dump register "
1159+
"error 0x%x\n", ret);
1160+
} else
1161+
pr_err("Hyper-V: panic message page memory "
1162+
"allocation failed");
1163+
}
1164+
10681165
register_die_notifier(&hyperv_die_block);
10691166
atomic_notifier_chain_register(&panic_notifier_list,
10701167
&hyperv_panic_block);
@@ -1081,6 +1178,11 @@ static int vmbus_bus_init(void)
10811178
hv_remove_vmbus_irq();
10821179

10831180
bus_unregister(&hv_bus);
1181+
free_page((unsigned long)hv_panic_page);
1182+
if (!hv_ctl_table_hdr) {
1183+
unregister_sysctl_table(hv_ctl_table_hdr);
1184+
hv_ctl_table_hdr = NULL;
1185+
}
10841186

10851187
return ret;
10861188
}
@@ -1785,10 +1887,18 @@ static void __exit vmbus_exit(void)
17851887
vmbus_free_channels();
17861888

17871889
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
1890+
kmsg_dump_unregister(&hv_kmsg_dumper);
17881891
unregister_die_notifier(&hyperv_die_block);
17891892
atomic_notifier_chain_unregister(&panic_notifier_list,
17901893
&hyperv_panic_block);
17911894
}
1895+
1896+
free_page((unsigned long)hv_panic_page);
1897+
if (!hv_ctl_table_hdr) {
1898+
unregister_sysctl_table(hv_ctl_table_hdr);
1899+
hv_ctl_table_hdr = NULL;
1900+
}
1901+
17921902
bus_unregister(&hv_bus);
17931903

17941904
cpuhp_remove_state(hyperv_cpuhp_online);

0 commit comments

Comments
 (0)