Skip to content

Commit 9c318a1

Browse files
longlimsftliuw
authored andcommitted
Drivers: hv: move panic report code from vmbus to hv early init code
The panic reporting code was added in commit 81b18bc ("Drivers: HV: Send one page worth of kmsg dump over Hyper-V during panic") It was added to the vmbus driver. The panic reporting has no dependence on vmbus, and can be enabled at an earlier boot time when Hyper-V is initialized. This patch moves the panic reporting code out of vmbus. There is no functionality changes. During moving, also refactored some cleanup functions into hv_kmsg_dump_unregister(). Signed-off-by: Long Li <longli@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Link: https://lore.kernel.org/r/1682030946-6372-1-git-send-email-longli@linuxonhyperv.com Signed-off-by: Wei Liu <wei.liu@kernel.org>
1 parent 3be1bc2 commit 9c318a1

3 files changed

Lines changed: 231 additions & 235 deletions

File tree

drivers/hv/hv.c

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,6 @@ int hv_init(void)
3838
return 0;
3939
}
4040

41-
/*
42-
* Functions for allocating and freeing memory with size and
43-
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
44-
* the guest page size may not be the same as the Hyper-V page
45-
* size. We depend upon kmalloc() aligning power-of-two size
46-
* allocations to the allocation size boundary, so that the
47-
* allocated memory appears to Hyper-V as a page of the size
48-
* it expects.
49-
*/
50-
51-
void *hv_alloc_hyperv_page(void)
52-
{
53-
BUILD_BUG_ON(PAGE_SIZE < HV_HYP_PAGE_SIZE);
54-
55-
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
56-
return (void *)__get_free_page(GFP_KERNEL);
57-
else
58-
return kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
59-
}
60-
61-
void *hv_alloc_hyperv_zeroed_page(void)
62-
{
63-
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
64-
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
65-
else
66-
return kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
67-
}
68-
69-
void hv_free_hyperv_page(unsigned long addr)
70-
{
71-
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
72-
free_page(addr);
73-
else
74-
kfree((void *)addr);
75-
}
76-
7741
/*
7842
* hv_post_message - Post a message using the hypervisor message IPC.
7943
*

drivers/hv/hv_common.c

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
#include <linux/export.h>
1818
#include <linux/bitfield.h>
1919
#include <linux/cpumask.h>
20+
#include <linux/sched/task_stack.h>
2021
#include <linux/panic_notifier.h>
2122
#include <linux/ptrace.h>
23+
#include <linux/kdebug.h>
24+
#include <linux/kmsg_dump.h>
2225
#include <linux/slab.h>
2326
#include <linux/dma-map-ops.h>
2427
#include <asm/hyperv-tlfs.h>
@@ -54,6 +57,10 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);
5457
void * __percpu *hyperv_pcpu_output_arg;
5558
EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
5659

60+
static void hv_kmsg_dump_unregister(void);
61+
62+
static struct ctl_table_header *hv_ctl_table_hdr;
63+
5764
/*
5865
* Hyper-V specific initialization and shutdown code that is
5966
* common across all architectures. Called from architecture
@@ -62,6 +69,12 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_output_arg);
6269

6370
void __init hv_common_free(void)
6471
{
72+
unregister_sysctl_table(hv_ctl_table_hdr);
73+
hv_ctl_table_hdr = NULL;
74+
75+
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
76+
hv_kmsg_dump_unregister();
77+
6578
kfree(hv_vp_index);
6679
hv_vp_index = NULL;
6780

@@ -72,10 +85,203 @@ void __init hv_common_free(void)
7285
hyperv_pcpu_input_arg = NULL;
7386
}
7487

88+
/*
89+
* Functions for allocating and freeing memory with size and
90+
* alignment HV_HYP_PAGE_SIZE. These functions are needed because
91+
* the guest page size may not be the same as the Hyper-V page
92+
* size. We depend upon kmalloc() aligning power-of-two size
93+
* allocations to the allocation size boundary, so that the
94+
* allocated memory appears to Hyper-V as a page of the size
95+
* it expects.
96+
*/
97+
98+
void *hv_alloc_hyperv_page(void)
99+
{
100+
BUILD_BUG_ON(PAGE_SIZE < HV_HYP_PAGE_SIZE);
101+
102+
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
103+
return (void *)__get_free_page(GFP_KERNEL);
104+
else
105+
return kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
106+
}
107+
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_page);
108+
109+
void *hv_alloc_hyperv_zeroed_page(void)
110+
{
111+
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
112+
return (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
113+
else
114+
return kzalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
115+
}
116+
EXPORT_SYMBOL_GPL(hv_alloc_hyperv_zeroed_page);
117+
118+
void hv_free_hyperv_page(unsigned long addr)
119+
{
120+
if (PAGE_SIZE == HV_HYP_PAGE_SIZE)
121+
free_page(addr);
122+
else
123+
kfree((void *)addr);
124+
}
125+
EXPORT_SYMBOL_GPL(hv_free_hyperv_page);
126+
127+
static void *hv_panic_page;
128+
129+
/*
130+
* Boolean to control whether to report panic messages over Hyper-V.
131+
*
132+
* It can be set via /proc/sys/kernel/hyperv_record_panic_msg
133+
*/
134+
static int sysctl_record_panic_msg = 1;
135+
136+
/*
137+
* sysctl option to allow the user to control whether kmsg data should be
138+
* reported to Hyper-V on panic.
139+
*/
140+
static struct ctl_table hv_ctl_table[] = {
141+
{
142+
.procname = "hyperv_record_panic_msg",
143+
.data = &sysctl_record_panic_msg,
144+
.maxlen = sizeof(int),
145+
.mode = 0644,
146+
.proc_handler = proc_dointvec_minmax,
147+
.extra1 = SYSCTL_ZERO,
148+
.extra2 = SYSCTL_ONE
149+
},
150+
{}
151+
};
152+
153+
static int hv_die_panic_notify_crash(struct notifier_block *self,
154+
unsigned long val, void *args);
155+
156+
static struct notifier_block hyperv_die_report_block = {
157+
.notifier_call = hv_die_panic_notify_crash,
158+
};
159+
160+
static struct notifier_block hyperv_panic_report_block = {
161+
.notifier_call = hv_die_panic_notify_crash,
162+
};
163+
164+
/*
165+
* The following callback works both as die and panic notifier; its
166+
* goal is to provide panic information to the hypervisor unless the
167+
* kmsg dumper is used [see hv_kmsg_dump()], which provides more
168+
* information but isn't always available.
169+
*
170+
* Notice that both the panic/die report notifiers are registered only
171+
* if we have the capability HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE set.
172+
*/
173+
static int hv_die_panic_notify_crash(struct notifier_block *self,
174+
unsigned long val, void *args)
175+
{
176+
struct pt_regs *regs;
177+
bool is_die;
178+
179+
/* Don't notify Hyper-V unless we have a die oops event or panic. */
180+
if (self == &hyperv_panic_report_block) {
181+
is_die = false;
182+
regs = current_pt_regs();
183+
} else { /* die event */
184+
if (val != DIE_OOPS)
185+
return NOTIFY_DONE;
186+
187+
is_die = true;
188+
regs = ((struct die_args *)args)->regs;
189+
}
190+
191+
/*
192+
* Hyper-V should be notified only once about a panic/die. If we will
193+
* be calling hv_kmsg_dump() later with kmsg data, don't do the
194+
* notification here.
195+
*/
196+
if (!sysctl_record_panic_msg || !hv_panic_page)
197+
hyperv_report_panic(regs, val, is_die);
198+
199+
return NOTIFY_DONE;
200+
}
201+
202+
/*
203+
* Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
204+
* buffer and call into Hyper-V to transfer the data.
205+
*/
206+
static void hv_kmsg_dump(struct kmsg_dumper *dumper,
207+
enum kmsg_dump_reason reason)
208+
{
209+
struct kmsg_dump_iter iter;
210+
size_t bytes_written;
211+
212+
/* We are only interested in panics. */
213+
if (reason != KMSG_DUMP_PANIC || !sysctl_record_panic_msg)
214+
return;
215+
216+
/*
217+
* Write dump contents to the page. No need to synchronize; panic should
218+
* be single-threaded.
219+
*/
220+
kmsg_dump_rewind(&iter);
221+
kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
222+
&bytes_written);
223+
if (!bytes_written)
224+
return;
225+
/*
226+
* P3 to contain the physical address of the panic page & P4 to
227+
* contain the size of the panic data in that page. Rest of the
228+
* registers are no-op when the NOTIFY_MSG flag is set.
229+
*/
230+
hv_set_register(HV_REGISTER_CRASH_P0, 0);
231+
hv_set_register(HV_REGISTER_CRASH_P1, 0);
232+
hv_set_register(HV_REGISTER_CRASH_P2, 0);
233+
hv_set_register(HV_REGISTER_CRASH_P3, virt_to_phys(hv_panic_page));
234+
hv_set_register(HV_REGISTER_CRASH_P4, bytes_written);
235+
236+
/*
237+
* Let Hyper-V know there is crash data available along with
238+
* the panic message.
239+
*/
240+
hv_set_register(HV_REGISTER_CRASH_CTL,
241+
(HV_CRASH_CTL_CRASH_NOTIFY |
242+
HV_CRASH_CTL_CRASH_NOTIFY_MSG));
243+
}
244+
245+
static struct kmsg_dumper hv_kmsg_dumper = {
246+
.dump = hv_kmsg_dump,
247+
};
248+
249+
static void hv_kmsg_dump_unregister(void)
250+
{
251+
kmsg_dump_unregister(&hv_kmsg_dumper);
252+
unregister_die_notifier(&hyperv_die_report_block);
253+
atomic_notifier_chain_unregister(&panic_notifier_list,
254+
&hyperv_panic_report_block);
255+
256+
hv_free_hyperv_page((unsigned long)hv_panic_page);
257+
hv_panic_page = NULL;
258+
}
259+
260+
static void hv_kmsg_dump_register(void)
261+
{
262+
int ret;
263+
264+
hv_panic_page = hv_alloc_hyperv_zeroed_page();
265+
if (!hv_panic_page) {
266+
pr_err("Hyper-V: panic message page memory allocation failed\n");
267+
return;
268+
}
269+
270+
ret = kmsg_dump_register(&hv_kmsg_dumper);
271+
if (ret) {
272+
pr_err("Hyper-V: kmsg dump register error 0x%x\n", ret);
273+
hv_free_hyperv_page((unsigned long)hv_panic_page);
274+
hv_panic_page = NULL;
275+
}
276+
}
277+
75278
int __init hv_common_init(void)
76279
{
77280
int i;
78281

282+
if (hv_is_isolation_supported())
283+
sysctl_record_panic_msg = 0;
284+
79285
/*
80286
* Hyper-V expects to get crash register data or kmsg when
81287
* crash enlightment is available and system crashes. Set
@@ -84,8 +290,33 @@ int __init hv_common_init(void)
84290
* kernel.
85291
*/
86292
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
293+
u64 hyperv_crash_ctl;
294+
87295
crash_kexec_post_notifiers = true;
88296
pr_info("Hyper-V: enabling crash_kexec_post_notifiers\n");
297+
298+
/*
299+
* Panic message recording (sysctl_record_panic_msg)
300+
* is enabled by default in non-isolated guests and
301+
* disabled by default in isolated guests; the panic
302+
* message recording won't be available in isolated
303+
* guests should the following registration fail.
304+
*/
305+
hv_ctl_table_hdr = register_sysctl("kernel", hv_ctl_table);
306+
if (!hv_ctl_table_hdr)
307+
pr_err("Hyper-V: sysctl table register error");
308+
309+
/*
310+
* Register for panic kmsg callback only if the right
311+
* capability is supported by the hypervisor.
312+
*/
313+
hyperv_crash_ctl = hv_get_register(HV_REGISTER_CRASH_CTL);
314+
if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG)
315+
hv_kmsg_dump_register();
316+
317+
register_die_notifier(&hyperv_die_report_block);
318+
atomic_notifier_chain_register(&panic_notifier_list,
319+
&hyperv_panic_report_block);
89320
}
90321

91322
/*

0 commit comments

Comments
 (0)