diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e592c481f7ae..0f00d57b7c25 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -68,53 +67,74 @@ static int hyperv_report_reg(void) return !sysctl_record_panic_msg || !hv_panic_page; } -static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, +/* + * The panic notifier below is responsible solely for unloading the + * vmbus connection, which is necessary in a panic event. + * + * Notice an intrincate relation of this notifier with Hyper-V + * framebuffer panic notifier exists - we need vmbus connection alive + * there in order to succeed, so we need to order both with each other + * [see hvfb_on_panic()] - this is done using notifiers' priorities. + */ +static int hv_panic_vmbus_unload(struct notifier_block *nb, unsigned long val, void *args) { - struct pt_regs *regs; - vmbus_initiate_unload(true); - - /* - * Hyper-V should be notified only once about a panic. If we will be - * doing hv_kmsg_dump() with kmsg data later, don't do the notification - * here. - */ - if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE - && hyperv_report_reg()) { - regs = current_pt_regs(); - hyperv_report_panic(regs, val, false); - } return NOTIFY_DONE; } +static struct notifier_block hyperv_panic_vmbus_unload_block = { + .notifier_call = hv_panic_vmbus_unload, + .priority = INT_MIN + 1, /* almost the latest one to execute */ +}; -static int hyperv_die_event(struct notifier_block *nb, unsigned long val, - void *args) +static int hv_die_panic_notify_crash(struct notifier_block *self, + unsigned long val, void *args); + +static struct notifier_block hyperv_die_report_block = { + .notifier_call = hv_die_panic_notify_crash, +}; +static struct notifier_block hyperv_panic_report_block = { + .notifier_call = hv_die_panic_notify_crash, +}; + +/* + * The following callback works both as die and panic notifier; its + * goal is to provide panic information to the hypervisor unless the + * kmsg dumper is used [see hv_kmsg_dump()], which provides more + * information but isn't always available. + * + * Notice that both the panic/die report notifiers are registered only + * if we have the capability HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE set. + */ +static int hv_die_panic_notify_crash(struct notifier_block *self, + unsigned long val, void *args) { - struct die_args *die = args; - struct pt_regs *regs = die->regs; + struct pt_regs *regs; + bool is_die; - /* Don't notify Hyper-V if the die event is other than oops */ - if (val != DIE_OOPS) - return NOTIFY_DONE; + /* Don't notify Hyper-V unless we have a die oops event or panic. */ + if (self == &hyperv_panic_report_block) { + is_die = false; + regs = current_pt_regs(); + } else { /* die event */ + if (val != DIE_OOPS) + return NOTIFY_DONE; + + is_die = true; + regs = ((struct die_args *)args)->regs; + } /* - * Hyper-V should be notified only once about a panic. If we will be - * doing hv_kmsg_dump() with kmsg data later, don't do the notification - * here. + * Hyper-V should be notified only once about a panic/die. If we will + * be calling hv_kmsg_dump() later with kmsg data, don't do the + * notification here. */ if (hyperv_report_reg()) - hyperv_report_panic(regs, val, true); + hyperv_report_panic(regs, val, is_die); + return NOTIFY_DONE; } -static struct notifier_block hyperv_die_block = { - .notifier_call = hyperv_die_event, -}; -static struct notifier_block hyperv_panic_block = { - .notifier_call = hyperv_panic_event, -}; - static const char *fb_mmio_name = "fb_range"; static struct resource *fb_mmio; static struct resource *hyperv_mmio; @@ -1538,16 +1558,17 @@ static int vmbus_bus_init(void) if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) hv_kmsg_dump_register(); - register_die_notifier(&hyperv_die_block); + register_die_notifier(&hyperv_die_report_block); + atomic_notifier_chain_register(&panic_notifier_list, + &hyperv_panic_report_block); } /* - * Always register the panic notifier because we need to unload - * the VMbus channel connection to prevent any VMbus - * activity after the VM panics. + * Always register the vmbus unload panic notifier because we + * need to shut the VMbus channel connection on panic. */ atomic_notifier_chain_register(&panic_notifier_list, - &hyperv_panic_block); + &hyperv_panic_vmbus_unload_block); vmbus_request_offers(); @@ -2800,15 +2821,17 @@ static void __exit vmbus_exit(void) if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { kmsg_dump_unregister(&hv_kmsg_dumper); - unregister_die_notifier(&hyperv_die_block); + unregister_die_notifier(&hyperv_die_report_block); + atomic_notifier_chain_unregister(&panic_notifier_list, + &hyperv_panic_report_block); } /* - * The panic notifier is always registered, hence we should + * The vmbus panic notifier is always registered, hence we should * also unconditionally unregister it here as well. */ atomic_notifier_chain_unregister(&panic_notifier_list, - &hyperv_panic_block); + &hyperv_panic_vmbus_unload_block); free_page((unsigned long)hv_panic_page); unregister_sysctl_table(hv_ctl_table_hdr); diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 4ff25dfc865d..3ce746a46179 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -1214,7 +1214,15 @@ static int hvfb_probe(struct hv_device *hdev, par->fb_ready = true; par->synchronous_fb = false; + + /* + * We need to be sure this panic notifier runs _before_ the + * vmbus disconnect, so order it by priority. It must execute + * before the function hv_panic_vmbus_unload() [drivers/hv/vmbus_drv.c], + * which is almost at the end of list, with priority = INT_MIN + 1. + */ par->hvfb_panic_nb.notifier_call = hvfb_on_panic; + par->hvfb_panic_nb.priority = INT_MIN + 10, atomic_notifier_chain_register(&panic_notifier_list, &par->hvfb_panic_nb);