248 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// VCPU stall detector.4// Copyright (C) Google, 20225 6#include <linux/cpu.h>7#include <linux/init.h>8#include <linux/io.h>9#include <linux/kernel.h>10 11#include <linux/device.h>12#include <linux/interrupt.h>13#include <linux/module.h>14#include <linux/nmi.h>15#include <linux/of.h>16#include <linux/param.h>17#include <linux/percpu.h>18#include <linux/platform_device.h>19#include <linux/slab.h>20 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)26 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)31 32struct vcpu_stall_detect_config {33 u32 clock_freq_hz;34 u32 stall_timeout_sec;35 int ppi_irq;36 37 void __iomem *membase;38 struct platform_device *dev;39 enum cpuhp_state hp_online;40};41 42struct vcpu_stall_priv {43 struct hrtimer vcpu_hrtimer;44 bool is_initialized;45};46 47/* The vcpu stall configuration structure which applies to all the CPUs */48static struct vcpu_stall_detect_config vcpu_stall_config;49 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)))54 55 56static struct vcpu_stall_priv __percpu *vcpu_stall_detectors;57 58static enum hrtimer_restart59vcpu_stall_detect_timer_fn(struct hrtimer *hrtimer)60{61 u32 ticks, ping_timeout_ms;62 63 /* Reload the stall detector counter register every64 * `ping_timeout_ms` to prevent the virtual device65 * from decrementing it to 0. The virtual device decrements this66 * register at 'clock_freq_hz' frequency.67 */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);72 73 ping_timeout_ms = vcpu_stall_config.stall_timeout_sec *74 MSEC_PER_SEC / 2;75 hrtimer_forward_now(hrtimer,76 ms_to_ktime(ping_timeout_ms));77 78 return HRTIMER_RESTART;79}80 81static irqreturn_t vcpu_stall_detector_irq(int irq, void *dev)82{83 panic("vCPU stall detector");84 return IRQ_HANDLED;85}86 87static int start_stall_detector_cpu(unsigned int cpu)88{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;93 94 vcpu_stall_reg_write(cpu, VCPU_STALL_REG_CLOCK_FREQ_HZ,95 vcpu_stall_config.clock_freq_hz);96 97 /* Compute the number of ticks required for the stall detector98 * counter register based on the internal clock frequency and the99 * timeout value given from the device tree.100 */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);104 105 /* Enable the internal clock and start the stall detector */106 vcpu_stall_reg_write(cpu, VCPU_STALL_REG_STATUS, 1);107 108 /* Pet the stall detector at half of its expiration timeout109 * to prevent spurious resets.110 */111 ping_timeout_ms = vcpu_stall_config.stall_timeout_sec *112 MSEC_PER_SEC / 2;113 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;117 118 hrtimer_start(vcpu_hrtimer, ms_to_ktime(ping_timeout_ms),119 HRTIMER_MODE_REL_PINNED);120 121 return 0;122}123 124static int stop_stall_detector_cpu(unsigned int cpu)125{126 struct vcpu_stall_priv *vcpu_stall_detector =127 per_cpu_ptr(vcpu_stall_detectors, cpu);128 129 if (!vcpu_stall_detector->is_initialized)130 return 0;131 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;136 137 return 0;138}139 140static int vcpu_stall_detect_probe(struct platform_device *pdev)141{142 int ret, irq;143 struct resource *r;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;148 149 vcpu_stall_detectors = devm_alloc_percpu(&pdev->dev,150 typeof(struct vcpu_stall_priv));151 if (!vcpu_stall_detectors)152 return -ENOMEM;153 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);158 }159 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;165 }166 }167 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;173 }174 }175 176 vcpu_stall_config = (struct vcpu_stall_detect_config) {177 .membase = membase,178 .clock_freq_hz = clock_freq_hz,179 .stall_timeout_sec = stall_timeout_sec,180 .ppi_irq = -1,181 };182 183 irq = platform_get_irq_optional(pdev, 0);184 if (irq > 0) {185 ret = request_percpu_irq(irq,186 vcpu_stall_detector_irq,187 "vcpu_stall_detector",188 vcpu_stall_detectors);189 if (ret)190 goto err;191 192 vcpu_stall_config.ppi_irq = irq;193 }194 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);199 if (ret < 0) {200 dev_err(&pdev->dev, "failed to install cpu hotplug");201 goto err;202 }203 204 vcpu_stall_config.hp_online = ret;205 return 0;206err:207 if (vcpu_stall_config.ppi_irq > 0)208 free_percpu_irq(vcpu_stall_config.ppi_irq,209 vcpu_stall_detectors);210 return ret;211}212 213static void vcpu_stall_detect_remove(struct platform_device *pdev)214{215 int cpu;216 217 cpuhp_remove_state(vcpu_stall_config.hp_online);218 219 if (vcpu_stall_config.ppi_irq > 0)220 free_percpu_irq(vcpu_stall_config.ppi_irq,221 vcpu_stall_detectors);222 223 for_each_possible_cpu(cpu)224 stop_stall_detector_cpu(cpu);225}226 227static const struct of_device_id vcpu_stall_detect_of_match[] = {228 { .compatible = "qemu,vcpu-stall-detector", },229 {}230};231 232MODULE_DEVICE_TABLE(of, vcpu_stall_detect_of_match);233 234static struct platform_driver vcpu_stall_detect_driver = {235 .probe = vcpu_stall_detect_probe,236 .remove_new = vcpu_stall_detect_remove,237 .driver = {238 .name = KBUILD_MODNAME,239 .of_match_table = vcpu_stall_detect_of_match,240 },241};242 243module_platform_driver(vcpu_stall_detect_driver);244 245MODULE_LICENSE("GPL");246MODULE_AUTHOR("Sebastian Ene <sebastianene@google.com>");247MODULE_DESCRIPTION("VCPU stall detector");248