|
@@ -56,6 +56,8 @@ static struct completion probe_event;
|
|
|
|
|
|
static int hyperv_cpuhp_online;
|
|
|
|
|
|
+static void *hv_panic_page;
|
|
|
+
|
|
|
static int hyperv_panic_event(struct notifier_block *nb, unsigned long val,
|
|
|
void *args)
|
|
|
{
|
|
@@ -1018,6 +1020,75 @@ static void vmbus_isr(void)
|
|
|
add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Boolean to control whether to report panic messages over Hyper-V.
|
|
|
+ *
|
|
|
+ * It can be set via /proc/sys/kernel/hyperv/record_panic_msg
|
|
|
+ */
|
|
|
+static int sysctl_record_panic_msg = 1;
|
|
|
+
|
|
|
+/*
|
|
|
+ * Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
|
|
|
+ * buffer and call into Hyper-V to transfer the data.
|
|
|
+ */
|
|
|
+static void hv_kmsg_dump(struct kmsg_dumper *dumper,
|
|
|
+ enum kmsg_dump_reason reason)
|
|
|
+{
|
|
|
+ size_t bytes_written;
|
|
|
+ phys_addr_t panic_pa;
|
|
|
+
|
|
|
+ /* We are only interested in panics. */
|
|
|
+ if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg))
|
|
|
+ return;
|
|
|
+
|
|
|
+ panic_pa = virt_to_phys(hv_panic_page);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Write dump contents to the page. No need to synchronize; panic should
|
|
|
+ * be single-threaded.
|
|
|
+ */
|
|
|
+ if (!kmsg_dump_get_buffer(dumper, true, hv_panic_page,
|
|
|
+ PAGE_SIZE, &bytes_written)) {
|
|
|
+ pr_err("Hyper-V: Unable to get kmsg data for panic\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ hyperv_report_panic_msg(panic_pa, bytes_written);
|
|
|
+}
|
|
|
+
|
|
|
+static struct kmsg_dumper hv_kmsg_dumper = {
|
|
|
+ .dump = hv_kmsg_dump,
|
|
|
+};
|
|
|
+
|
|
|
+static struct ctl_table_header *hv_ctl_table_hdr;
|
|
|
+static int zero;
|
|
|
+static int one = 1;
|
|
|
+
|
|
|
+/*
|
|
|
+ * sysctl option to allow the user to control whether kmsg data should be
|
|
|
+ * reported to Hyper-V on panic.
|
|
|
+ */
|
|
|
+static struct ctl_table hv_ctl_table[] = {
|
|
|
+ {
|
|
|
+ .procname = "hyperv_record_panic_msg",
|
|
|
+ .data = &sysctl_record_panic_msg,
|
|
|
+ .maxlen = sizeof(int),
|
|
|
+ .mode = 0644,
|
|
|
+ .proc_handler = proc_dointvec_minmax,
|
|
|
+ .extra1 = &zero,
|
|
|
+ .extra2 = &one
|
|
|
+ },
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
+static struct ctl_table hv_root_table[] = {
|
|
|
+ {
|
|
|
+ .procname = "kernel",
|
|
|
+ .mode = 0555,
|
|
|
+ .child = hv_ctl_table
|
|
|
+ },
|
|
|
+ {}
|
|
|
+};
|
|
|
|
|
|
/*
|
|
|
* vmbus_bus_init -Main vmbus driver initialization routine.
|
|
@@ -1065,6 +1136,32 @@ static int vmbus_bus_init(void)
|
|
|
* Only register if the crash MSRs are available
|
|
|
*/
|
|
|
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
|
|
+ u64 hyperv_crash_ctl;
|
|
|
+ /*
|
|
|
+ * Sysctl registration is not fatal, since by default
|
|
|
+ * reporting is enabled.
|
|
|
+ */
|
|
|
+ hv_ctl_table_hdr = register_sysctl_table(hv_root_table);
|
|
|
+ if (!hv_ctl_table_hdr)
|
|
|
+ pr_err("Hyper-V: sysctl table register error");
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Register for panic kmsg callback only if the right
|
|
|
+ * capability is supported by the hypervisor.
|
|
|
+ */
|
|
|
+ rdmsrl(HV_X64_MSR_CRASH_CTL, hyperv_crash_ctl);
|
|
|
+ if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) {
|
|
|
+ hv_panic_page = (void *)get_zeroed_page(GFP_KERNEL);
|
|
|
+ if (hv_panic_page) {
|
|
|
+ ret = kmsg_dump_register(&hv_kmsg_dumper);
|
|
|
+ if (ret)
|
|
|
+ pr_err("Hyper-V: kmsg dump register "
|
|
|
+ "error 0x%x\n", ret);
|
|
|
+ } else
|
|
|
+ pr_err("Hyper-V: panic message page memory "
|
|
|
+ "allocation failed");
|
|
|
+ }
|
|
|
+
|
|
|
register_die_notifier(&hyperv_die_block);
|
|
|
atomic_notifier_chain_register(&panic_notifier_list,
|
|
|
&hyperv_panic_block);
|
|
@@ -1081,6 +1178,11 @@ err_alloc:
|
|
|
hv_remove_vmbus_irq();
|
|
|
|
|
|
bus_unregister(&hv_bus);
|
|
|
+ free_page((unsigned long)hv_panic_page);
|
|
|
+ if (!hv_ctl_table_hdr) {
|
|
|
+ unregister_sysctl_table(hv_ctl_table_hdr);
|
|
|
+ hv_ctl_table_hdr = NULL;
|
|
|
+ }
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -1785,10 +1887,18 @@ static void __exit vmbus_exit(void)
|
|
|
vmbus_free_channels();
|
|
|
|
|
|
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
|
|
|
+ kmsg_dump_unregister(&hv_kmsg_dumper);
|
|
|
unregister_die_notifier(&hyperv_die_block);
|
|
|
atomic_notifier_chain_unregister(&panic_notifier_list,
|
|
|
&hyperv_panic_block);
|
|
|
}
|
|
|
+
|
|
|
+ free_page((unsigned long)hv_panic_page);
|
|
|
+ if (!hv_ctl_table_hdr) {
|
|
|
+ unregister_sysctl_table(hv_ctl_table_hdr);
|
|
|
+ hv_ctl_table_hdr = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
bus_unregister(&hv_bus);
|
|
|
|
|
|
cpuhp_remove_state(hyperv_cpuhp_online);
|