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 | |
| 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')
| -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 |
