1 // SPDX-License-Identifier: GPL-2.0-only
3 // VCPU stall detector.
4 // Copyright (C) Google, 2022
7 #include <linux/init.h>
9 #include <linux/kernel.h>
11 #include <linux/device.h>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/nmi.h>
16 #include <linux/param.h>
17 #include <linux/percpu.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
21 #define VCPU_STALL_REG_STATUS (0x00)
22 #define VCPU_STALL_REG_LOAD_CNT (0x04)
23 #define VCPU_STALL_REG_CURRENT_CNT (0x08)
24 #define VCPU_STALL_REG_CLOCK_FREQ_HZ (0x0C)
25 #define VCPU_STALL_REG_LEN (0x10)
27 #define VCPU_STALL_DEFAULT_CLOCK_HZ (10)
28 #define VCPU_STALL_MAX_CLOCK_HZ (100)
29 #define VCPU_STALL_DEFAULT_TIMEOUT_SEC (8)
30 #define VCPU_STALL_MAX_TIMEOUT_SEC (600)
32 struct vcpu_stall_detect_config
{
34 u32 stall_timeout_sec
;
37 void __iomem
*membase
;
38 struct platform_device
*dev
;
39 enum cpuhp_state hp_online
;
42 struct vcpu_stall_priv
{
43 struct hrtimer vcpu_hrtimer
;
47 /* The vcpu stall configuration structure which applies to all the CPUs */
48 static struct vcpu_stall_detect_config vcpu_stall_config
;
50 #define vcpu_stall_reg_write(vcpu, reg, value) \
51 writel_relaxed((value), \
52 (void __iomem *)(vcpu_stall_config.membase + \
53 (vcpu) * VCPU_STALL_REG_LEN + (reg)))
56 static struct vcpu_stall_priv __percpu
*vcpu_stall_detectors
;
58 static enum hrtimer_restart
59 vcpu_stall_detect_timer_fn(struct hrtimer
*hrtimer
)
61 u32 ticks
, ping_timeout_ms
;
63 /* Reload the stall detector counter register every
64 * `ping_timeout_ms` to prevent the virtual device
65 * from decrementing it to 0. The virtual device decrements this
66 * register at 'clock_freq_hz' frequency.
68 ticks
= vcpu_stall_config
.clock_freq_hz
*
69 vcpu_stall_config
.stall_timeout_sec
;
70 vcpu_stall_reg_write(smp_processor_id(),
71 VCPU_STALL_REG_LOAD_CNT
, ticks
);
73 ping_timeout_ms
= vcpu_stall_config
.stall_timeout_sec
*
75 hrtimer_forward_now(hrtimer
,
76 ms_to_ktime(ping_timeout_ms
));
78 return HRTIMER_RESTART
;
81 static irqreturn_t
vcpu_stall_detector_irq(int irq
, void *dev
)
83 panic("vCPU stall detector");
87 static int start_stall_detector_cpu(unsigned int cpu
)
89 u32 ticks
, ping_timeout_ms
;
90 struct vcpu_stall_priv
*vcpu_stall_detector
=
91 this_cpu_ptr(vcpu_stall_detectors
);
92 struct hrtimer
*vcpu_hrtimer
= &vcpu_stall_detector
->vcpu_hrtimer
;
94 vcpu_stall_reg_write(cpu
, VCPU_STALL_REG_CLOCK_FREQ_HZ
,
95 vcpu_stall_config
.clock_freq_hz
);
97 /* Compute the number of ticks required for the stall detector
98 * counter register based on the internal clock frequency and the
99 * timeout value given from the device tree.
101 ticks
= vcpu_stall_config
.clock_freq_hz
*
102 vcpu_stall_config
.stall_timeout_sec
;
103 vcpu_stall_reg_write(cpu
, VCPU_STALL_REG_LOAD_CNT
, ticks
);
105 /* Enable the internal clock and start the stall detector */
106 vcpu_stall_reg_write(cpu
, VCPU_STALL_REG_STATUS
, 1);
108 /* Pet the stall detector at half of its expiration timeout
109 * to prevent spurious resets.
111 ping_timeout_ms
= vcpu_stall_config
.stall_timeout_sec
*
114 hrtimer_init(vcpu_hrtimer
, CLOCK_MONOTONIC
, HRTIMER_MODE_REL
);
115 vcpu_hrtimer
->function
= vcpu_stall_detect_timer_fn
;
116 vcpu_stall_detector
->is_initialized
= true;
118 hrtimer_start(vcpu_hrtimer
, ms_to_ktime(ping_timeout_ms
),
119 HRTIMER_MODE_REL_PINNED
);
124 static int stop_stall_detector_cpu(unsigned int cpu
)
126 struct vcpu_stall_priv
*vcpu_stall_detector
=
127 per_cpu_ptr(vcpu_stall_detectors
, cpu
);
129 if (!vcpu_stall_detector
->is_initialized
)
132 /* Disable the stall detector for the current CPU */
133 hrtimer_cancel(&vcpu_stall_detector
->vcpu_hrtimer
);
134 vcpu_stall_reg_write(cpu
, VCPU_STALL_REG_STATUS
, 0);
135 vcpu_stall_detector
->is_initialized
= false;
140 static int vcpu_stall_detect_probe(struct platform_device
*pdev
)
144 void __iomem
*membase
;
145 u32 clock_freq_hz
= VCPU_STALL_DEFAULT_CLOCK_HZ
;
146 u32 stall_timeout_sec
= VCPU_STALL_DEFAULT_TIMEOUT_SEC
;
147 struct device_node
*np
= pdev
->dev
.of_node
;
149 vcpu_stall_detectors
= devm_alloc_percpu(&pdev
->dev
,
150 typeof(struct vcpu_stall_priv
));
151 if (!vcpu_stall_detectors
)
154 membase
= devm_platform_get_and_ioremap_resource(pdev
, 0, &r
);
155 if (IS_ERR(membase
)) {
156 dev_err(&pdev
->dev
, "Failed to get memory resource\n");
157 return PTR_ERR(membase
);
160 if (!of_property_read_u32(np
, "clock-frequency", &clock_freq_hz
)) {
161 if (!(clock_freq_hz
> 0 &&
162 clock_freq_hz
< VCPU_STALL_MAX_CLOCK_HZ
)) {
163 dev_warn(&pdev
->dev
, "clk out of range\n");
164 clock_freq_hz
= VCPU_STALL_DEFAULT_CLOCK_HZ
;
168 if (!of_property_read_u32(np
, "timeout-sec", &stall_timeout_sec
)) {
169 if (!(stall_timeout_sec
> 0 &&
170 stall_timeout_sec
< VCPU_STALL_MAX_TIMEOUT_SEC
)) {
171 dev_warn(&pdev
->dev
, "stall timeout out of range\n");
172 stall_timeout_sec
= VCPU_STALL_DEFAULT_TIMEOUT_SEC
;
176 vcpu_stall_config
= (struct vcpu_stall_detect_config
) {
178 .clock_freq_hz
= clock_freq_hz
,
179 .stall_timeout_sec
= stall_timeout_sec
,
183 irq
= platform_get_irq_optional(pdev
, 0);
185 ret
= request_percpu_irq(irq
,
186 vcpu_stall_detector_irq
,
187 "vcpu_stall_detector",
188 vcpu_stall_detectors
);
192 vcpu_stall_config
.ppi_irq
= irq
;
195 ret
= cpuhp_setup_state(CPUHP_AP_ONLINE_DYN
,
196 "virt/vcpu_stall_detector:online",
197 start_stall_detector_cpu
,
198 stop_stall_detector_cpu
);
200 dev_err(&pdev
->dev
, "failed to install cpu hotplug");
204 vcpu_stall_config
.hp_online
= ret
;
207 if (vcpu_stall_config
.ppi_irq
> 0)
208 free_percpu_irq(vcpu_stall_config
.ppi_irq
,
209 vcpu_stall_detectors
);
213 static void vcpu_stall_detect_remove(struct platform_device
*pdev
)
217 cpuhp_remove_state(vcpu_stall_config
.hp_online
);
219 if (vcpu_stall_config
.ppi_irq
> 0)
220 free_percpu_irq(vcpu_stall_config
.ppi_irq
,
221 vcpu_stall_detectors
);
223 for_each_possible_cpu(cpu
)
224 stop_stall_detector_cpu(cpu
);
227 static const struct of_device_id vcpu_stall_detect_of_match
[] = {
228 { .compatible
= "qemu,vcpu-stall-detector", },
232 MODULE_DEVICE_TABLE(of
, vcpu_stall_detect_of_match
);
234 static struct platform_driver vcpu_stall_detect_driver
= {
235 .probe
= vcpu_stall_detect_probe
,
236 .remove_new
= vcpu_stall_detect_remove
,
238 .name
= KBUILD_MODNAME
,
239 .of_match_table
= vcpu_stall_detect_of_match
,
243 module_platform_driver(vcpu_stall_detect_driver
);
245 MODULE_LICENSE("GPL");
246 MODULE_AUTHOR("Sebastian Ene <sebastianene@google.com>");
247 MODULE_DESCRIPTION("VCPU stall detector");