diff options
author | Julien Thierry <julien.thierry@arm.com> | 2019-01-31 09:53:59 -0500 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2019-02-05 09:36:58 -0500 |
commit | 4b078c3f1a26487c39363089ba0d5c6b09f2a89f (patch) | |
tree | 3536174761fea16583c3e365501f7e812e6f6529 /kernel/irq/manage.c | |
parent | b525903c254dab2491410f0f23707691b7c2c317 (diff) |
genirq: Provide NMI management for percpu_devid interrupts
Add support for percpu_devid interrupts treated as NMIs.
Percpu_devid NMIs need to be setup/torn down on each CPU they target.
The same restrictions as for global NMIs still apply for percpu_devid NMIs.
Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 9472ae987946..0a1ebc004a59 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -2182,6 +2182,11 @@ out: | |||
2182 | } | 2182 | } |
2183 | EXPORT_SYMBOL_GPL(enable_percpu_irq); | 2183 | EXPORT_SYMBOL_GPL(enable_percpu_irq); |
2184 | 2184 | ||
2185 | void enable_percpu_nmi(unsigned int irq, unsigned int type) | ||
2186 | { | ||
2187 | enable_percpu_irq(irq, type); | ||
2188 | } | ||
2189 | |||
2185 | /** | 2190 | /** |
2186 | * irq_percpu_is_enabled - Check whether the per cpu irq is enabled | 2191 | * irq_percpu_is_enabled - Check whether the per cpu irq is enabled |
2187 | * @irq: Linux irq number to check for | 2192 | * @irq: Linux irq number to check for |
@@ -2221,6 +2226,11 @@ void disable_percpu_irq(unsigned int irq) | |||
2221 | } | 2226 | } |
2222 | EXPORT_SYMBOL_GPL(disable_percpu_irq); | 2227 | EXPORT_SYMBOL_GPL(disable_percpu_irq); |
2223 | 2228 | ||
2229 | void disable_percpu_nmi(unsigned int irq) | ||
2230 | { | ||
2231 | disable_percpu_irq(irq); | ||
2232 | } | ||
2233 | |||
2224 | /* | 2234 | /* |
2225 | * Internal function to unregister a percpu irqaction. | 2235 | * Internal function to unregister a percpu irqaction. |
2226 | */ | 2236 | */ |
@@ -2252,6 +2262,8 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_ | |||
2252 | /* Found it - now remove it from the list of entries: */ | 2262 | /* Found it - now remove it from the list of entries: */ |
2253 | desc->action = NULL; | 2263 | desc->action = NULL; |
2254 | 2264 | ||
2265 | desc->istate &= ~IRQS_NMI; | ||
2266 | |||
2255 | raw_spin_unlock_irqrestore(&desc->lock, flags); | 2267 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
2256 | 2268 | ||
2257 | unregister_handler_proc(irq, action); | 2269 | unregister_handler_proc(irq, action); |
@@ -2305,6 +2317,19 @@ void free_percpu_irq(unsigned int irq, void __percpu *dev_id) | |||
2305 | } | 2317 | } |
2306 | EXPORT_SYMBOL_GPL(free_percpu_irq); | 2318 | EXPORT_SYMBOL_GPL(free_percpu_irq); |
2307 | 2319 | ||
2320 | void free_percpu_nmi(unsigned int irq, void __percpu *dev_id) | ||
2321 | { | ||
2322 | struct irq_desc *desc = irq_to_desc(irq); | ||
2323 | |||
2324 | if (!desc || !irq_settings_is_per_cpu_devid(desc)) | ||
2325 | return; | ||
2326 | |||
2327 | if (WARN_ON(!(desc->istate & IRQS_NMI))) | ||
2328 | return; | ||
2329 | |||
2330 | kfree(__free_percpu_irq(irq, dev_id)); | ||
2331 | } | ||
2332 | |||
2308 | /** | 2333 | /** |
2309 | * setup_percpu_irq - setup a per-cpu interrupt | 2334 | * setup_percpu_irq - setup a per-cpu interrupt |
2310 | * @irq: Interrupt line to setup | 2335 | * @irq: Interrupt line to setup |
@@ -2395,6 +2420,158 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler, | |||
2395 | EXPORT_SYMBOL_GPL(__request_percpu_irq); | 2420 | EXPORT_SYMBOL_GPL(__request_percpu_irq); |
2396 | 2421 | ||
2397 | /** | 2422 | /** |
2423 | * request_percpu_nmi - allocate a percpu interrupt line for NMI delivery | ||
2424 | * @irq: Interrupt line to allocate | ||
2425 | * @handler: Function to be called when the IRQ occurs. | ||
2426 | * @name: An ascii name for the claiming device | ||
2427 | * @dev_id: A percpu cookie passed back to the handler function | ||
2428 | * | ||
2429 | * This call allocates interrupt resources for a per CPU NMI. Per CPU NMIs | ||
2430 | * have to be setup on each CPU by calling ready_percpu_nmi() before being | ||
2431 | * enabled on the same CPU by using enable_percpu_nmi(). | ||
2432 | * | ||
2433 | * Dev_id must be globally unique. It is a per-cpu variable, and | ||
2434 | * the handler gets called with the interrupted CPU's instance of | ||
2435 | * that variable. | ||
2436 | * | ||
2437 | * Interrupt lines requested for NMI delivering should have auto enabling | ||
2438 | * setting disabled. | ||
2439 | * | ||
2440 | * If the interrupt line cannot be used to deliver NMIs, function | ||
2441 | * will fail returning a negative value. | ||
2442 | */ | ||
2443 | int request_percpu_nmi(unsigned int irq, irq_handler_t handler, | ||
2444 | const char *name, void __percpu *dev_id) | ||
2445 | { | ||
2446 | struct irqaction *action; | ||
2447 | struct irq_desc *desc; | ||
2448 | unsigned long flags; | ||
2449 | int retval; | ||
2450 | |||
2451 | if (!handler) | ||
2452 | return -EINVAL; | ||
2453 | |||
2454 | desc = irq_to_desc(irq); | ||
2455 | |||
2456 | if (!desc || !irq_settings_can_request(desc) || | ||
2457 | !irq_settings_is_per_cpu_devid(desc) || | ||
2458 | irq_settings_can_autoenable(desc) || | ||
2459 | !irq_supports_nmi(desc)) | ||
2460 | return -EINVAL; | ||
2461 | |||
2462 | /* The line cannot already be NMI */ | ||
2463 | if (desc->istate & IRQS_NMI) | ||
2464 | return -EINVAL; | ||
2465 | |||
2466 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); | ||
2467 | if (!action) | ||
2468 | return -ENOMEM; | ||
2469 | |||
2470 | action->handler = handler; | ||
2471 | action->flags = IRQF_PERCPU | IRQF_NO_SUSPEND | IRQF_NO_THREAD | ||
2472 | | IRQF_NOBALANCING; | ||
2473 | action->name = name; | ||
2474 | action->percpu_dev_id = dev_id; | ||
2475 | |||
2476 | retval = irq_chip_pm_get(&desc->irq_data); | ||
2477 | if (retval < 0) | ||
2478 | goto err_out; | ||
2479 | |||
2480 | retval = __setup_irq(irq, desc, action); | ||
2481 | if (retval) | ||
2482 | goto err_irq_setup; | ||
2483 | |||
2484 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
2485 | desc->istate |= IRQS_NMI; | ||
2486 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
2487 | |||
2488 | return 0; | ||
2489 | |||
2490 | err_irq_setup: | ||
2491 | irq_chip_pm_put(&desc->irq_data); | ||
2492 | err_out: | ||
2493 | kfree(action); | ||
2494 | |||
2495 | return retval; | ||
2496 | } | ||
2497 | |||
2498 | /** | ||
2499 | * prepare_percpu_nmi - performs CPU local setup for NMI delivery | ||
2500 | * @irq: Interrupt line to prepare for NMI delivery | ||
2501 | * | ||
2502 | * This call prepares an interrupt line to deliver NMI on the current CPU, | ||
2503 | * before that interrupt line gets enabled with enable_percpu_nmi(). | ||
2504 | * | ||
2505 | * As a CPU local operation, this should be called from non-preemptible | ||
2506 | * context. | ||
2507 | * | ||
2508 | * If the interrupt line cannot be used to deliver NMIs, function | ||
2509 | * will fail returning a negative value. | ||
2510 | */ | ||
2511 | int prepare_percpu_nmi(unsigned int irq) | ||
2512 | { | ||
2513 | unsigned long flags; | ||
2514 | struct irq_desc *desc; | ||
2515 | int ret = 0; | ||
2516 | |||
2517 | WARN_ON(preemptible()); | ||
2518 | |||
2519 | desc = irq_get_desc_lock(irq, &flags, | ||
2520 | IRQ_GET_DESC_CHECK_PERCPU); | ||
2521 | if (!desc) | ||
2522 | return -EINVAL; | ||
2523 | |||
2524 | if (WARN(!(desc->istate & IRQS_NMI), | ||
2525 | KERN_ERR "prepare_percpu_nmi called for a non-NMI interrupt: irq %u\n", | ||
2526 | irq)) { | ||
2527 | ret = -EINVAL; | ||
2528 | goto out; | ||
2529 | } | ||
2530 | |||
2531 | ret = irq_nmi_setup(desc); | ||
2532 | if (ret) { | ||
2533 | pr_err("Failed to setup NMI delivery: irq %u\n", irq); | ||
2534 | goto out; | ||
2535 | } | ||
2536 | |||
2537 | out: | ||
2538 | irq_put_desc_unlock(desc, flags); | ||
2539 | return ret; | ||
2540 | } | ||
2541 | |||
2542 | /** | ||
2543 | * teardown_percpu_nmi - undoes NMI setup of IRQ line | ||
2544 | * @irq: Interrupt line from which CPU local NMI configuration should be | ||
2545 | * removed | ||
2546 | * | ||
2547 | * This call undoes the setup done by prepare_percpu_nmi(). | ||
2548 | * | ||
2549 | * IRQ line should not be enabled for the current CPU. | ||
2550 | * | ||
2551 | * As a CPU local operation, this should be called from non-preemptible | ||
2552 | * context. | ||
2553 | */ | ||
2554 | void teardown_percpu_nmi(unsigned int irq) | ||
2555 | { | ||
2556 | unsigned long flags; | ||
2557 | struct irq_desc *desc; | ||
2558 | |||
2559 | WARN_ON(preemptible()); | ||
2560 | |||
2561 | desc = irq_get_desc_lock(irq, &flags, | ||
2562 | IRQ_GET_DESC_CHECK_PERCPU); | ||
2563 | if (!desc) | ||
2564 | return; | ||
2565 | |||
2566 | if (WARN_ON(!(desc->istate & IRQS_NMI))) | ||
2567 | goto out; | ||
2568 | |||
2569 | irq_nmi_teardown(desc); | ||
2570 | out: | ||
2571 | irq_put_desc_unlock(desc, flags); | ||
2572 | } | ||
2573 | |||
2574 | /** | ||
2398 | * irq_get_irqchip_state - returns the irqchip state of a interrupt. | 2575 | * irq_get_irqchip_state - returns the irqchip state of a interrupt. |
2399 | * @irq: Interrupt line that is forwarded to a VM | 2576 | * @irq: Interrupt line that is forwarded to a VM |
2400 | * @which: One of IRQCHIP_STATE_* the caller wants to know about | 2577 | * @which: One of IRQCHIP_STATE_* the caller wants to know about |