History

Merge tag 'irqdomain-for-linus' of git://git.secretlab.ca/git/linux-2.6

Pull irqdomain changes from Grant Likely: "Round of refactoring and enhancements to irq_domain infrastructure. This series starts the process of simplifying irqdomain. The ultimate goal is to merge LEGACY, LINEAR and TREE mappings into a single system, but had to back off from that after some last minute bugs. Instead it mainly reorganizes the code and ensures that the reverse map gets populated when the irq is mapped instead of the first time it is looked up. Merging of the irq_domain types is deferred to v3.7 In other news, this series adds helpers for creating static mappings on a linear or tree mapping." * tag 'irqdomain-for-linus' of git://git.secretlab.ca/git/linux-2.6: irqdomain: Improve diagnostics when a domain mapping fails irqdomain: eliminate slow-path revmap lookups irqdomain: Fix irq_create_direct_mapping() to test irq_domain type. irqdomain: Eliminate dedicated radix lookup functions irqdomain: Support for static IRQ mapping and association. irqdomain: Always update revmap when setting up a virq irqdomain: Split disassociating code into separate function irq_domain: correct a minor wrong comment for linear revmap irq_domain: Standardise legacy/linear domain selection irqdomain: Make ops->map hook optional irqdomain: Remove unnecessary test for IRQ_DOMAIN_MAP_LEGACY irqdomain: Simple NUMA awareness. devicetree: add helper inline for retrieving a node's full name

Linus Torvalds committed 14 years ago

Commit 2d534922 parents ac694db f5a1ad07 files · +248 −169Browse files

Showing the changes to kernel (1 of 7 files). Show all

Unified · Split

kernel/irq/irqdomain.c+207−155
@@ -10,6 +10,7 @@ · 9 unmodified lines
1010 #include <linux/mutex.h>
1111 #include <linux/of.h>
1212 #include <linux/of_address.h>
13+#include <linux/topology.h>
1314 #include <linux/seq_file.h>
1415 #include <linux/slab.h>
1516 #include <linux/smp.h>
@@ -45,7 +46,8 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, · 29 unmodified lines
4546 {
4647 struct irq_domain *domain;
4748
48− domain = kzalloc(sizeof(*domain), GFP_KERNEL);
49+ domain = kzalloc_node(sizeof(*domain), GFP_KERNEL,
50+ of_node_to_nid(of_node));
4951 if (WARN_ON(!domain))
5052 return NULL;
5153
@@ -137,6 +139,36 @@ static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, · 85 unmodified lines
137139 return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
138140 }
139141
142+/**
143+ * irq_domain_add_simple() - Allocate and register a simple irq_domain.
144+ * @of_node: pointer to interrupt controller's device tree node.
145+ * @size: total number of irqs in mapping
146+ * @first_irq: first number of irq block assigned to the domain
147+ * @ops: map/unmap domain callbacks
148+ * @host_data: Controller private data pointer
149+ *
150+ * Allocates a legacy irq_domain if irq_base is positive or a linear
151+ * domain otherwise.
152+ *
153+ * This is intended to implement the expected behaviour for most
154+ * interrupt controllers which is that a linear mapping should
155+ * normally be used unless the system requires a legacy mapping in
156+ * order to support supplying interrupt numbers during non-DT
157+ * registration of devices.
158+ */
159+struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
160+ unsigned int size,
161+ unsigned int first_irq,
162+ const struct irq_domain_ops *ops,
163+ void *host_data)
164+{
165+ if (first_irq > 0)
166+ return irq_domain_add_legacy(of_node, size, first_irq, 0,
167+ ops, host_data);
168+ else
169+ return irq_domain_add_linear(of_node, size, ops, host_data);
170+}
171+
140172 /**
141173 * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
142174 * @of_node: pointer to interrupt controller's device tree node.
@@ -203,7 +235,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, · 60 unmodified lines
203235 * one can then use irq_create_mapping() to
204236 * explicitly change them
205237 */
206− ops->map(domain, irq, hwirq);
238+ if (ops->map)
239+ ops->map(domain, irq, hwirq);
207240
208241 /* Clear norequest flags */
209242 irq_clear_status_flags(irq, IRQ_NOREQUEST);
@@ -215,7 +248,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, · 5 unmodified lines
215248 EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
216249
217250 /**
218− * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
251+ * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
219252 * @of_node: pointer to interrupt controller's device tree node.
220253 * @size: Number of interrupts in the domain.
221254 * @ops: map/unmap domain callbacks
@@ -229,7 +262,8 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, · 7 unmodified lines
229262 struct irq_domain *domain;
230263 unsigned int *revmap;
231264
232− revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
265+ revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL,
266+ of_node_to_nid(of_node));
233267 if (WARN_ON(!revmap))
234268 return NULL;
235269
@@ -330,24 +364,112 @@ void irq_set_default_host(struct irq_domain *domain) · 94 unmodified lines
330364 }
331365 EXPORT_SYMBOL_GPL(irq_set_default_host);
332366
333−static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
334− irq_hw_number_t hwirq)
367+static void irq_domain_disassociate_many(struct irq_domain *domain,
368+ unsigned int irq_base, int count)
335369 {
336− struct irq_data *irq_data = irq_get_irq_data(virq);
370+ /*
371+ * disassociate in reverse order;
372+ * not strictly necessary, but nice for unwinding
373+ */
374+ while (count--) {
375+ int irq = irq_base + count;
376+ struct irq_data *irq_data = irq_get_irq_data(irq);
377+ irq_hw_number_t hwirq = irq_data->hwirq;
378+
379+ if (WARN_ON(!irq_data || irq_data->domain != domain))
380+ continue;
381+
382+ irq_set_status_flags(irq, IRQ_NOREQUEST);
383+
384+ /* remove chip and handler */
385+ irq_set_chip_and_handler(irq, NULL, NULL);
386+
387+ /* Make sure it's completed */
388+ synchronize_irq(irq);
389+
390+ /* Tell the PIC about it */
391+ if (domain->ops->unmap)
392+ domain->ops->unmap(domain, irq);
393+ smp_mb();
337394
338− irq_data->hwirq = hwirq;
339− irq_data->domain = domain;
340− if (domain->ops->map(domain, virq, hwirq)) {
341− pr_debug("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq);
342395 irq_data->domain = NULL;
343396 irq_data->hwirq = 0;
344− return -1;
397+
398+ /* Clear reverse map */
399+ switch(domain->revmap_type) {
400+ case IRQ_DOMAIN_MAP_LINEAR:
401+ if (hwirq < domain->revmap_data.linear.size)
402+ domain->revmap_data.linear.revmap[hwirq] = 0;
403+ break;
404+ case IRQ_DOMAIN_MAP_TREE:
405+ mutex_lock(&revmap_trees_mutex);
406+ radix_tree_delete(&domain->revmap_data.tree, hwirq);
407+ mutex_unlock(&revmap_trees_mutex);
408+ break;
409+ }
345410 }
411+}
412+
413+int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
414+ irq_hw_number_t hwirq_base, int count)
415+{
416+ unsigned int virq = irq_base;
417+ irq_hw_number_t hwirq = hwirq_base;
418+ int i, ret;
419+
420+ pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
421+ of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
422+
423+ for (i = 0; i < count; i++) {
424+ struct irq_data *irq_data = irq_get_irq_data(virq + i);
425+
426+ if (WARN(!irq_data, "error: irq_desc not allocated; "
427+ "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
428+ return -EINVAL;
429+ if (WARN(irq_data->domain, "error: irq_desc already associated; "
430+ "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
431+ return -EINVAL;
432+ };
433+
434+ for (i = 0; i < count; i++, virq++, hwirq++) {
435+ struct irq_data *irq_data = irq_get_irq_data(virq);
436+
437+ irq_data->hwirq = hwirq;
438+ irq_data->domain = domain;
439+ if (domain->ops->map) {
440+ ret = domain->ops->map(domain, virq, hwirq);
441+ if (ret != 0) {
442+ pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
443+ virq, hwirq, ret);
444+ WARN_ON(1);
445+ irq_data->domain = NULL;
446+ irq_data->hwirq = 0;
447+ goto err_unmap;
448+ }
449+ }
346450
347− irq_clear_status_flags(virq, IRQ_NOREQUEST);
451+ switch (domain->revmap_type) {
452+ case IRQ_DOMAIN_MAP_LINEAR:
453+ if (hwirq < domain->revmap_data.linear.size)
454+ domain->revmap_data.linear.revmap[hwirq] = virq;
455+ break;
456+ case IRQ_DOMAIN_MAP_TREE:
457+ mutex_lock(&revmap_trees_mutex);
458+ radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
459+ mutex_unlock(&revmap_trees_mutex);
460+ break;
461+ }
462+
463+ irq_clear_status_flags(virq, IRQ_NOREQUEST);
464+ }
348465
349466 return 0;
467+
468+ err_unmap:
469+ irq_domain_disassociate_many(domain, irq_base, i);
470+ return -EINVAL;
350471 }
472+EXPORT_SYMBOL_GPL(irq_domain_associate_many);
351473
352474 /**
353475 * irq_create_direct_mapping() - Allocate an irq for direct mapping
@@ -364,10 +486,10 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) · 10 unmodified lines
364486 if (domain == NULL)
365487 domain = irq_default_domain;
366488
367− BUG_ON(domain == NULL);
368− WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
489+ if (WARN_ON(!domain || domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP))
490+ return 0;
369491
370− virq = irq_alloc_desc_from(1, 0);
492+ virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
371493 if (!virq) {
372494 pr_debug("create_direct virq allocation failed\n");
373495 return 0;
@@ -380,7 +502,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) · 6 unmodified lines
380502 }
381503 pr_debug("create_direct obtained virq %d\n", virq);
382504
383− if (irq_setup_virq(domain, virq, virq)) {
505+ if (irq_domain_associate(domain, virq, virq)) {
384506 irq_free_desc(virq);
385507 return 0;
386508 }
@@ -433,17 +555,16 @@ unsigned int irq_create_mapping(struct irq_domain *domain, · 46 unmodified lines
433555 hint = hwirq % nr_irqs;
434556 if (hint == 0)
435557 hint++;
436− virq = irq_alloc_desc_from(hint, 0);
558+ virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
437559 if (virq <= 0)
438− virq = irq_alloc_desc_from(1, 0);
560+ virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
439561 if (virq <= 0) {
440562 pr_debug("-> virq allocation failed\n");
441563 return 0;
442564 }
443565
444− if (irq_setup_virq(domain, virq, hwirq)) {
445− if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
446− irq_free_desc(virq);
566+ if (irq_domain_associate(domain, virq, hwirq)) {
567+ irq_free_desc(virq);
447568 return 0;
448569 }
449570
@@ -454,6 +575,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain, · 4 unmodified lines
454575 }
455576 EXPORT_SYMBOL_GPL(irq_create_mapping);
456577
578+/**
579+ * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
580+ * @domain: domain owning the interrupt range
581+ * @irq_base: beginning of linux IRQ range
582+ * @hwirq_base: beginning of hardware IRQ range
583+ * @count: Number of interrupts to map
584+ *
585+ * This routine is used for allocating and mapping a range of hardware
586+ * irqs to linux irqs where the linux irq numbers are at pre-defined
587+ * locations. For use by controllers that already have static mappings
588+ * to insert in to the domain.
589+ *
590+ * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time
591+ * domain insertion.
592+ *
593+ * 0 is returned upon success, while any failure to establish a static
594+ * mapping is treated as an error.
595+ */
596+int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
597+ irq_hw_number_t hwirq_base, int count)
598+{
599+ int ret;
600+
601+ ret = irq_alloc_descs(irq_base, irq_base, count,
602+ of_node_to_nid(domain->of_node));
603+ if (unlikely(ret < 0))
604+ return ret;
605+
606+ ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
607+ if (unlikely(ret < 0)) {
608+ irq_free_descs(irq_base, count);
609+ return ret;
610+ }
611+
612+ return 0;
613+}
614+EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
615+
457616 unsigned int irq_create_of_mapping(struct device_node *controller,
458617 const u32 *intspec, unsigned int intsize)
459618 {
@@ -511,7 +670,6 @@ void irq_dispose_mapping(unsigned int virq) · 51 unmodified lines
511670 {
512671 struct irq_data *irq_data = irq_get_irq_data(virq);
513672 struct irq_domain *domain;
514− irq_hw_number_t hwirq;
515673
516674 if (!virq || !irq_data)
517675 return;
@@ -524,33 +682,7 @@ void irq_dispose_mapping(unsigned int virq) · 6 unmodified lines
524682 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
525683 return;
526684
527− irq_set_status_flags(virq, IRQ_NOREQUEST);
528−
529− /* remove chip and handler */
530− irq_set_chip_and_handler(virq, NULL, NULL);
531−
532− /* Make sure it's completed */
533− synchronize_irq(virq);
534−
535− /* Tell the PIC about it */
536− if (domain->ops->unmap)
537− domain->ops->unmap(domain, virq);
538− smp_mb();
539−
540− /* Clear reverse map */
541− hwirq = irq_data->hwirq;
542− switch(domain->revmap_type) {
543− case IRQ_DOMAIN_MAP_LINEAR:
544− if (hwirq < domain->revmap_data.linear.size)
545− domain->revmap_data.linear.revmap[hwirq] = 0;
546− break;
547− case IRQ_DOMAIN_MAP_TREE:
548− mutex_lock(&revmap_trees_mutex);
549− radix_tree_delete(&domain->revmap_data.tree, hwirq);
550− mutex_unlock(&revmap_trees_mutex);
551− break;
552− }
553−
685+ irq_domain_disassociate_many(domain, virq, 1);
554686 irq_free_desc(virq);
555687 }
556688 EXPORT_SYMBOL_GPL(irq_dispose_mapping);
@@ -559,16 +691,11 @@ EXPORT_SYMBOL_GPL(irq_dispose_mapping); · 2 unmodified lines
559691 * irq_find_mapping() - Find a linux irq from an hw irq number.
560692 * @domain: domain owning this hardware interrupt
561693 * @hwirq: hardware irq number in that domain space
562− *
563− * This is a slow path, for use by generic code. It's expected that an
564− * irq controller implementation directly calls the appropriate low level
565− * mapping function.
566694 */
567695 unsigned int irq_find_mapping(struct irq_domain *domain,
568696 irq_hw_number_t hwirq)
569697 {
570− unsigned int i;
571− unsigned int hint = hwirq % nr_irqs;
698+ struct irq_data *data;
572699
573700 /* Look for default domain if nececssary */
574701 if (domain == NULL)
@@ -576,115 +703,47 @@ unsigned int irq_find_mapping(struct irq_domain *domain, · 1 unmodified line
576703 if (domain == NULL)
577704 return 0;
578705
579− /* legacy -> bail early */
580− if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
706+ switch (domain->revmap_type) {
707+ case IRQ_DOMAIN_MAP_LEGACY:
581708 return irq_domain_legacy_revmap(domain, hwirq);
582−
583− /* Slow path does a linear search of the map */
584− if (hint == 0)
585− hint = 1;
586− i = hint;
587− do {
588− struct irq_data *data = irq_get_irq_data(i);
709+ case IRQ_DOMAIN_MAP_LINEAR:
710+ return irq_linear_revmap(domain, hwirq);
711+ case IRQ_DOMAIN_MAP_TREE:
712+ rcu_read_lock();
713+ data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
714+ rcu_read_unlock();
715+ if (data)
716+ return data->irq;
717+ break;
718+ case IRQ_DOMAIN_MAP_NOMAP:
719+ data = irq_get_irq_data(hwirq);
589720 if (data && (data->domain == domain) && (data->hwirq == hwirq))
590− return i;
591− i++;
592− if (i >= nr_irqs)
593− i = 1;
594− } while(i != hint);
721+ return hwirq;
722+ break;
723+ }
724+
595725 return 0;
596726 }
597727 EXPORT_SYMBOL_GPL(irq_find_mapping);
598728
599−/**
600− * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
601− * @domain: domain owning this hardware interrupt
602− * @hwirq: hardware irq number in that domain space
603− *
604− * This is a fast path, for use by irq controller code that uses radix tree
605− * revmaps
606− */
607−unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
608− irq_hw_number_t hwirq)
609−{
610− struct irq_data *irq_data;
611−
612− if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
613− return irq_find_mapping(domain, hwirq);
614−
615− /*
616− * Freeing an irq can delete nodes along the path to
617− * do the lookup via call_rcu.
618− */
619− rcu_read_lock();
620− irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
621− rcu_read_unlock();
622−
623− /*
624− * If found in radix tree, then fine.
625− * Else fallback to linear lookup - this should not happen in practice
626− * as it means that we failed to insert the node in the radix tree.
627− */
628− return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
629−}
630−EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup);
631−
632−/**
633− * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
634− * @domain: domain owning this hardware interrupt
635− * @virq: linux irq number
636− * @hwirq: hardware irq number in that domain space
637− *
638− * This is for use by irq controllers that use a radix tree reverse
639− * mapping for fast lookup.
640− */
641−void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
642− irq_hw_number_t hwirq)
643−{
644− struct irq_data *irq_data = irq_get_irq_data(virq);
645−
646− if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
647− return;
648−
649− if (virq) {
650− mutex_lock(&revmap_trees_mutex);
651− radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
652− mutex_unlock(&revmap_trees_mutex);
653− }
654−}
655−EXPORT_SYMBOL_GPL(irq_radix_revmap_insert);
656−
657729 /**
658730 * irq_linear_revmap() - Find a linux irq from a hw irq number.
659731 * @domain: domain owning this hardware interrupt
660732 * @hwirq: hardware irq number in that domain space
661733 *
662− * This is a fast path, for use by irq controller code that uses linear
663− * revmaps. It does fallback to the slow path if the revmap doesn't exist
664− * yet and will create the revmap entry with appropriate locking
734+ * This is a fast path that can be called directly by irq controller code to
735+ * save a handful of instructions.
665736 */
666737 unsigned int irq_linear_revmap(struct irq_domain *domain,
667738 irq_hw_number_t hwirq)
668739 {
669− unsigned int *revmap;
670−
671− if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
672− return irq_find_mapping(domain, hwirq);
740+ BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR);
673741
674− /* Check revmap bounds */
675− if (unlikely(hwirq >= domain->revmap_data.linear.size))
676− return irq_find_mapping(domain, hwirq);
677−
678− /* Check if revmap was allocated */
679− revmap = domain->revmap_data.linear.revmap;
680− if (unlikely(revmap == NULL))
681− return irq_find_mapping(domain, hwirq);
682−
683− /* Fill up revmap with slow path if no mapping found */
684− if (unlikely(!revmap[hwirq]))
685− revmap[hwirq] = irq_find_mapping(domain, hwirq);
742+ /* Check revmap bounds; complain if exceeded */
743+ if (WARN_ON(hwirq >= domain->revmap_data.linear.size))
744+ return 0;
686745
687− return revmap[hwirq];
746+ return domain->revmap_data.linear.revmap[hwirq];
688747 }
689748 EXPORT_SYMBOL_GPL(irq_linear_revmap);
690749
@@ -761,12 +820,6 @@ static int __init irq_debugfs_init(void) · 70 unmodified lines
761820 __initcall(irq_debugfs_init);
762821 #endif /* CONFIG_IRQ_DOMAIN_DEBUG */
763822
764−static int irq_domain_simple_map(struct irq_domain *d, unsigned int irq,
765− irq_hw_number_t hwirq)
766−{
767− return 0;
768−}
769−
770823 /**
771824 * irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings
772825 *
@@ -829,7 +882,6 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, · 56 unmodified lines
829882 EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
830883
831884 const struct irq_domain_ops irq_domain_simple_ops = {
832− .map = irq_domain_simple_map,
833885 .xlate = irq_domain_xlate_onetwocell,
834886 };
835887 EXPORT_SYMBOL_GPL(irq_domain_simple_ops);