aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq/manage.c
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2011-09-23 12:03:06 -0400
committerThomas Gleixner <tglx@linutronix.de>2011-10-03 09:35:26 -0400
commit31d9d9b6d83030f748d013e61502fa5477e2ac0e (patch)
tree503670b94d594c09daa83c047b426e7b5328aa76 /kernel/irq/manage.c
parent60f96b41f71d2a13d1c0a457b8b77958f77142d1 (diff)
genirq: Add support for per-cpu dev_id interrupts
The ARM GIC interrupt controller offers per CPU interrupts (PPIs), which are usually used to connect local timers to each core. Each CPU has its own private interface to the GIC, and only sees the PPIs that are directly connect to it. While these timers are separate devices and have a separate interrupt line to a core, they all use the same IRQ number. For these devices, request_irq() is not the right API as it assumes that an IRQ number is visible by a number of CPUs (through the affinity setting), but makes it very awkward to express that an IRQ number can be handled by all CPUs, and yet be a different interrupt line on each CPU, requiring a different dev_id cookie to be passed back to the handler. The *_percpu_irq() functions is designed to overcome these limitations, by providing a per-cpu dev_id vector: int request_percpu_irq(unsigned int irq, irq_handler_t handler, const char *devname, void __percpu *percpu_dev_id); void free_percpu_irq(unsigned int, void __percpu *); int setup_percpu_irq(unsigned int irq, struct irqaction *new); void remove_percpu_irq(unsigned int irq, struct irqaction *act); void enable_percpu_irq(unsigned int irq); void disable_percpu_irq(unsigned int irq); The API has a number of limitations: - no interrupt sharing - no threading - common handler across all the CPUs Once the interrupt is requested using setup_percpu_irq() or request_percpu_irq(), it must be enabled by each core that wishes its local interrupt to be delivered. Based on an initial patch by Thomas Gleixner. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1316793788-14500-2-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r--kernel/irq/manage.c202
1 files changed, 193 insertions, 9 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 7e1a3ed1e61a..7b4b156d065c 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -195,7 +195,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask)
195int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) 195int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
196{ 196{
197 unsigned long flags; 197 unsigned long flags;
198 struct irq_desc *desc = irq_get_desc_lock(irq, &flags); 198 struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
199 199
200 if (!desc) 200 if (!desc)
201 return -EINVAL; 201 return -EINVAL;
@@ -356,7 +356,7 @@ void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
356static int __disable_irq_nosync(unsigned int irq) 356static int __disable_irq_nosync(unsigned int irq)
357{ 357{
358 unsigned long flags; 358 unsigned long flags;
359 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); 359 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
360 360
361 if (!desc) 361 if (!desc)
362 return -EINVAL; 362 return -EINVAL;
@@ -448,7 +448,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
448void enable_irq(unsigned int irq) 448void enable_irq(unsigned int irq)
449{ 449{
450 unsigned long flags; 450 unsigned long flags;
451 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); 451 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
452 452
453 if (!desc) 453 if (!desc)
454 return; 454 return;
@@ -491,7 +491,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on)
491int irq_set_irq_wake(unsigned int irq, unsigned int on) 491int irq_set_irq_wake(unsigned int irq, unsigned int on)
492{ 492{
493 unsigned long flags; 493 unsigned long flags;
494 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); 494 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
495 int ret = 0; 495 int ret = 0;
496 496
497 if (!desc) 497 if (!desc)
@@ -532,7 +532,7 @@ EXPORT_SYMBOL(irq_set_irq_wake);
532int can_request_irq(unsigned int irq, unsigned long irqflags) 532int can_request_irq(unsigned int irq, unsigned long irqflags)
533{ 533{
534 unsigned long flags; 534 unsigned long flags;
535 struct irq_desc *desc = irq_get_desc_lock(irq, &flags); 535 struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
536 int canrequest = 0; 536 int canrequest = 0;
537 537
538 if (!desc) 538 if (!desc)
@@ -1121,6 +1121,8 @@ int setup_irq(unsigned int irq, struct irqaction *act)
1121 int retval; 1121 int retval;
1122 struct irq_desc *desc = irq_to_desc(irq); 1122 struct irq_desc *desc = irq_to_desc(irq);
1123 1123
1124 if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
1125 return -EINVAL;
1124 chip_bus_lock(desc); 1126 chip_bus_lock(desc);
1125 retval = __setup_irq(irq, desc, act); 1127 retval = __setup_irq(irq, desc, act);
1126 chip_bus_sync_unlock(desc); 1128 chip_bus_sync_unlock(desc);
@@ -1129,7 +1131,7 @@ int setup_irq(unsigned int irq, struct irqaction *act)
1129} 1131}
1130EXPORT_SYMBOL_GPL(setup_irq); 1132EXPORT_SYMBOL_GPL(setup_irq);
1131 1133
1132 /* 1134/*
1133 * Internal function to unregister an irqaction - used to free 1135 * Internal function to unregister an irqaction - used to free
1134 * regular and special interrupts that are part of the architecture. 1136 * regular and special interrupts that are part of the architecture.
1135 */ 1137 */
@@ -1227,7 +1229,10 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
1227 */ 1229 */
1228void remove_irq(unsigned int irq, struct irqaction *act) 1230void remove_irq(unsigned int irq, struct irqaction *act)
1229{ 1231{
1230 __free_irq(irq, act->dev_id); 1232 struct irq_desc *desc = irq_to_desc(irq);
1233
1234 if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc)))
1235 __free_irq(irq, act->dev_id);
1231} 1236}
1232EXPORT_SYMBOL_GPL(remove_irq); 1237EXPORT_SYMBOL_GPL(remove_irq);
1233 1238
@@ -1249,7 +1254,7 @@ void free_irq(unsigned int irq, void *dev_id)
1249{ 1254{
1250 struct irq_desc *desc = irq_to_desc(irq); 1255 struct irq_desc *desc = irq_to_desc(irq);
1251 1256
1252 if (!desc) 1257 if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
1253 return; 1258 return;
1254 1259
1255#ifdef CONFIG_SMP 1260#ifdef CONFIG_SMP
@@ -1327,7 +1332,8 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1327 if (!desc) 1332 if (!desc)
1328 return -EINVAL; 1333 return -EINVAL;
1329 1334
1330 if (!irq_settings_can_request(desc)) 1335 if (!irq_settings_can_request(desc) ||
1336 WARN_ON(irq_settings_is_per_cpu_devid(desc)))
1331 return -EINVAL; 1337 return -EINVAL;
1332 1338
1333 if (!handler) { 1339 if (!handler) {
@@ -1412,3 +1418,181 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler,
1412 return !ret ? IRQC_IS_HARDIRQ : ret; 1418 return !ret ? IRQC_IS_HARDIRQ : ret;
1413} 1419}
1414EXPORT_SYMBOL_GPL(request_any_context_irq); 1420EXPORT_SYMBOL_GPL(request_any_context_irq);
1421
1422void enable_percpu_irq(unsigned int irq)
1423{
1424 unsigned int cpu = smp_processor_id();
1425 unsigned long flags;
1426 struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
1427
1428 if (!desc)
1429 return;
1430
1431 irq_percpu_enable(desc, cpu);
1432 irq_put_desc_unlock(desc, flags);
1433}
1434
1435void disable_percpu_irq(unsigned int irq)
1436{
1437 unsigned int cpu = smp_processor_id();
1438 unsigned long flags;
1439 struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
1440
1441 if (!desc)
1442 return;
1443
1444 irq_percpu_disable(desc, cpu);
1445 irq_put_desc_unlock(desc, flags);
1446}
1447
1448/*
1449 * Internal function to unregister a percpu irqaction.
1450 */
1451static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_id)
1452{
1453 struct irq_desc *desc = irq_to_desc(irq);
1454 struct irqaction *action;
1455 unsigned long flags;
1456
1457 WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
1458
1459 if (!desc)
1460 return NULL;
1461
1462 raw_spin_lock_irqsave(&desc->lock, flags);
1463
1464 action = desc->action;
1465 if (!action || action->percpu_dev_id != dev_id) {
1466 WARN(1, "Trying to free already-free IRQ %d\n", irq);
1467 goto bad;
1468 }
1469
1470 if (!cpumask_empty(desc->percpu_enabled)) {
1471 WARN(1, "percpu IRQ %d still enabled on CPU%d!\n",
1472 irq, cpumask_first(desc->percpu_enabled));
1473 goto bad;
1474 }
1475
1476 /* Found it - now remove it from the list of entries: */
1477 desc->action = NULL;
1478
1479 raw_spin_unlock_irqrestore(&desc->lock, flags);
1480
1481 unregister_handler_proc(irq, action);
1482
1483 module_put(desc->owner);
1484 return action;
1485
1486bad:
1487 raw_spin_unlock_irqrestore(&desc->lock, flags);
1488 return NULL;
1489}
1490
1491/**
1492 * remove_percpu_irq - free a per-cpu interrupt
1493 * @irq: Interrupt line to free
1494 * @act: irqaction for the interrupt
1495 *
1496 * Used to remove interrupts statically setup by the early boot process.
1497 */
1498void remove_percpu_irq(unsigned int irq, struct irqaction *act)
1499{
1500 struct irq_desc *desc = irq_to_desc(irq);
1501
1502 if (desc && irq_settings_is_per_cpu_devid(desc))
1503 __free_percpu_irq(irq, act->percpu_dev_id);
1504}
1505
1506/**
1507 * free_percpu_irq - free an interrupt allocated with request_percpu_irq
1508 * @irq: Interrupt line to free
1509 * @dev_id: Device identity to free
1510 *
1511 * Remove a percpu interrupt handler. The handler is removed, but
1512 * the interrupt line is not disabled. This must be done on each
1513 * CPU before calling this function. The function does not return
1514 * until any executing interrupts for this IRQ have completed.
1515 *
1516 * This function must not be called from interrupt context.
1517 */
1518void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
1519{
1520 struct irq_desc *desc = irq_to_desc(irq);
1521
1522 if (!desc || !irq_settings_is_per_cpu_devid(desc))
1523 return;
1524
1525 chip_bus_lock(desc);
1526 kfree(__free_percpu_irq(irq, dev_id));
1527 chip_bus_sync_unlock(desc);
1528}
1529
1530/**
1531 * setup_percpu_irq - setup a per-cpu interrupt
1532 * @irq: Interrupt line to setup
1533 * @act: irqaction for the interrupt
1534 *
1535 * Used to statically setup per-cpu interrupts in the early boot process.
1536 */
1537int setup_percpu_irq(unsigned int irq, struct irqaction *act)
1538{
1539 struct irq_desc *desc = irq_to_desc(irq);
1540 int retval;
1541
1542 if (!desc || !irq_settings_is_per_cpu_devid(desc))
1543 return -EINVAL;
1544 chip_bus_lock(desc);
1545 retval = __setup_irq(irq, desc, act);
1546 chip_bus_sync_unlock(desc);
1547
1548 return retval;
1549}
1550
1551/**
1552 * request_percpu_irq - allocate a percpu interrupt line
1553 * @irq: Interrupt line to allocate
1554 * @handler: Function to be called when the IRQ occurs.
1555 * @devname: An ascii name for the claiming device
1556 * @dev_id: A percpu cookie passed back to the handler function
1557 *
1558 * This call allocates interrupt resources, but doesn't
1559 * automatically enable the interrupt. It has to be done on each
1560 * CPU using enable_percpu_irq().
1561 *
1562 * Dev_id must be globally unique. It is a per-cpu variable, and
1563 * the handler gets called with the interrupted CPU's instance of
1564 * that variable.
1565 */
1566int request_percpu_irq(unsigned int irq, irq_handler_t handler,
1567 const char *devname, void __percpu *dev_id)
1568{
1569 struct irqaction *action;
1570 struct irq_desc *desc;
1571 int retval;
1572
1573 if (!dev_id)
1574 return -EINVAL;
1575
1576 desc = irq_to_desc(irq);
1577 if (!desc || !irq_settings_can_request(desc) ||
1578 !irq_settings_is_per_cpu_devid(desc))
1579 return -EINVAL;
1580
1581 action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
1582 if (!action)
1583 return -ENOMEM;
1584
1585 action->handler = handler;
1586 action->flags = IRQF_PERCPU;
1587 action->name = devname;
1588 action->percpu_dev_id = dev_id;
1589
1590 chip_bus_lock(desc);
1591 retval = __setup_irq(irq, desc, action);
1592 chip_bus_sync_unlock(desc);
1593
1594 if (retval)
1595 kfree(action);
1596
1597 return retval;
1598}