@@ -1449,6 +1449,175 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
1449
1449
return ret ;
1450
1450
}
1451
1451
1452
+ /* The irq_data was moved, fix the revmap to refer to the new location */
1453
+ static void irq_domain_fix_revmap (struct irq_data * d )
1454
+ {
1455
+ void * * slot ;
1456
+
1457
+ if (d -> hwirq < d -> domain -> revmap_size )
1458
+ return ; /* Not using radix tree. */
1459
+
1460
+ /* Fix up the revmap. */
1461
+ mutex_lock (& revmap_trees_mutex );
1462
+ slot = radix_tree_lookup_slot (& d -> domain -> revmap_tree , d -> hwirq );
1463
+ if (slot )
1464
+ radix_tree_replace_slot (& d -> domain -> revmap_tree , slot , d );
1465
+ mutex_unlock (& revmap_trees_mutex );
1466
+ }
1467
+
1468
+ /**
1469
+ * irq_domain_push_irq() - Push a domain in to the top of a hierarchy.
1470
+ * @domain: Domain to push.
1471
+ * @virq: Irq to push the domain in to.
1472
+ * @arg: Passed to the irq_domain_ops alloc() function.
1473
+ *
1474
+ * For an already existing irqdomain hierarchy, as might be obtained
1475
+ * via a call to pci_enable_msix(), add an additional domain to the
1476
+ * head of the processing chain. Must be called before request_irq()
1477
+ * has been called.
1478
+ */
1479
+ int irq_domain_push_irq (struct irq_domain * domain , int virq , void * arg )
1480
+ {
1481
+ struct irq_data * child_irq_data ;
1482
+ struct irq_data * root_irq_data = irq_get_irq_data (virq );
1483
+ struct irq_desc * desc ;
1484
+ int rv = 0 ;
1485
+
1486
+ /*
1487
+ * Check that no action has been set, which indicates the virq
1488
+ * is in a state where this function doesn't have to deal with
1489
+ * races between interrupt handling and maintaining the
1490
+ * hierarchy. This will catch gross misuse. Attempting to
1491
+ * make the check race free would require holding locks across
1492
+ * calls to struct irq_domain_ops->alloc(), which could lead
1493
+ * to deadlock, so we just do a simple check before starting.
1494
+ */
1495
+ desc = irq_to_desc (virq );
1496
+ if (!desc )
1497
+ return - EINVAL ;
1498
+ if (WARN_ON (desc -> action ))
1499
+ return - EBUSY ;
1500
+
1501
+ if (domain == NULL )
1502
+ return - EINVAL ;
1503
+
1504
+ if (WARN_ON (!irq_domain_is_hierarchy (domain )))
1505
+ return - EINVAL ;
1506
+
1507
+ if (domain -> parent != root_irq_data -> domain )
1508
+ return - EINVAL ;
1509
+
1510
+ if (!root_irq_data )
1511
+ return - EINVAL ;
1512
+
1513
+ child_irq_data = kzalloc_node (sizeof (* child_irq_data ), GFP_KERNEL ,
1514
+ irq_data_get_node (root_irq_data ));
1515
+ if (!child_irq_data )
1516
+ return - ENOMEM ;
1517
+
1518
+ mutex_lock (& irq_domain_mutex );
1519
+
1520
+ /* Copy the original irq_data. */
1521
+ * child_irq_data = * root_irq_data ;
1522
+
1523
+ /*
1524
+ * Overwrite the root_irq_data, which is embedded in struct
1525
+ * irq_desc, with values for this domain.
1526
+ */
1527
+ root_irq_data -> parent_data = child_irq_data ;
1528
+ root_irq_data -> domain = domain ;
1529
+ root_irq_data -> mask = 0 ;
1530
+ root_irq_data -> hwirq = 0 ;
1531
+ root_irq_data -> chip = NULL ;
1532
+ root_irq_data -> chip_data = NULL ;
1533
+
1534
+ /* May (probably does) set hwirq, chip, etc. */
1535
+ rv = irq_domain_alloc_irqs_hierarchy (domain , virq , 1 , arg );
1536
+ if (rv ) {
1537
+ /* Restore the original irq_data. */
1538
+ * root_irq_data = * child_irq_data ;
1539
+ goto error ;
1540
+ }
1541
+
1542
+ irq_domain_fix_revmap (child_irq_data );
1543
+ irq_domain_set_mapping (domain , root_irq_data -> hwirq , root_irq_data );
1544
+
1545
+ error :
1546
+ mutex_unlock (& irq_domain_mutex );
1547
+
1548
+ return rv ;
1549
+ }
1550
+ EXPORT_SYMBOL_GPL (irq_domain_push_irq );
1551
+
1552
+ /**
1553
+ * irq_domain_pop_irq() - Remove a domain from the top of a hierarchy.
1554
+ * @domain: Domain to remove.
1555
+ * @virq: Irq to remove the domain from.
1556
+ *
1557
+ * Undo the effects of a call to irq_domain_push_irq(). Must be
1558
+ * called either before request_irq() or after free_irq().
1559
+ */
1560
+ int irq_domain_pop_irq (struct irq_domain * domain , int virq )
1561
+ {
1562
+ struct irq_data * root_irq_data = irq_get_irq_data (virq );
1563
+ struct irq_data * child_irq_data ;
1564
+ struct irq_data * tmp_irq_data ;
1565
+ struct irq_desc * desc ;
1566
+
1567
+ /*
1568
+ * Check that no action is set, which indicates the virq is in
1569
+ * a state where this function doesn't have to deal with races
1570
+ * between interrupt handling and maintaining the hierarchy.
1571
+ * This will catch gross misuse. Attempting to make the check
1572
+ * race free would require holding locks across calls to
1573
+ * struct irq_domain_ops->free(), which could lead to
1574
+ * deadlock, so we just do a simple check before starting.
1575
+ */
1576
+ desc = irq_to_desc (virq );
1577
+ if (!desc )
1578
+ return - EINVAL ;
1579
+ if (WARN_ON (desc -> action ))
1580
+ return - EBUSY ;
1581
+
1582
+ if (domain == NULL )
1583
+ return - EINVAL ;
1584
+
1585
+ if (!root_irq_data )
1586
+ return - EINVAL ;
1587
+
1588
+ tmp_irq_data = irq_domain_get_irq_data (domain , virq );
1589
+
1590
+ /* We can only "pop" if this domain is at the top of the list */
1591
+ if (WARN_ON (root_irq_data != tmp_irq_data ))
1592
+ return - EINVAL ;
1593
+
1594
+ if (WARN_ON (root_irq_data -> domain != domain ))
1595
+ return - EINVAL ;
1596
+
1597
+ child_irq_data = root_irq_data -> parent_data ;
1598
+ if (WARN_ON (!child_irq_data ))
1599
+ return - EINVAL ;
1600
+
1601
+ mutex_lock (& irq_domain_mutex );
1602
+
1603
+ root_irq_data -> parent_data = NULL ;
1604
+
1605
+ irq_domain_clear_mapping (domain , root_irq_data -> hwirq );
1606
+ irq_domain_free_irqs_hierarchy (domain , virq , 1 );
1607
+
1608
+ /* Restore the original irq_data. */
1609
+ * root_irq_data = * child_irq_data ;
1610
+
1611
+ irq_domain_fix_revmap (root_irq_data );
1612
+
1613
+ mutex_unlock (& irq_domain_mutex );
1614
+
1615
+ kfree (child_irq_data );
1616
+
1617
+ return 0 ;
1618
+ }
1619
+ EXPORT_SYMBOL_GPL (irq_domain_pop_irq );
1620
+
1452
1621
/**
1453
1622
* irq_domain_free_irqs - Free IRQ number and associated data structures
1454
1623
* @virq: base IRQ number
0 commit comments