Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/htirq: Use hierarchical irqdomain to manage Hypertransport interrupts

We have slightly changed the architecture interfaces to support htirq
PCI driver. It's safe because currently Hypertransport interrupt is
only enabled on x86 platforms.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: David Cohen <david.a.cohen@linux.intel.com>
Cc: Sander Eikelenboom <linux@eikelenboom.it>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dimitri Sivanich <sivanich@sgi.com>
Link: http://lkml.kernel.org/r/1428905519-23704-22-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Jiang Liu and committed by
Thomas Gleixner
49e07d8f 0921f1da

+157 -87
+13
arch/x86/include/asm/hw_irq.h
··· 161 161 void *dmar_data; 162 162 }; 163 163 #endif 164 + #ifdef CONFIG_HT_IRQ 165 + struct { 166 + int ht_pos; 167 + int ht_idx; 168 + struct pci_dev *ht_dev; 169 + void *ht_update; 170 + }; 171 + #endif 164 172 }; 165 173 }; 166 174 ··· 234 226 extern void arch_init_msi_domain(struct irq_domain *domain); 235 227 #else 236 228 static inline void arch_init_msi_domain(struct irq_domain *domain) { } 229 + #endif 230 + #ifdef CONFIG_HT_IRQ 231 + extern void arch_init_htirq_domain(struct irq_domain *domain); 232 + #else 233 + static inline void arch_init_htirq_domain(struct irq_domain *domain) { } 237 234 #endif 238 235 239 236 /* Statistics */
+119 -40
arch/x86/kernel/apic/htirq.c
··· 3 3 * 4 4 * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo 5 5 * Moved from arch/x86/kernel/apic/io_apic.c. 6 + * Jiang Liu <jiang.liu@linux.intel.com> 7 + * Add support of hierarchical irqdomain 6 8 * 7 9 * This program is free software; you can redistribute it and/or modify 8 10 * it under the terms of the GNU General Public License version 2 as ··· 21 19 #include <asm/apic.h> 22 20 #include <asm/hypertransport.h> 23 21 22 + static struct irq_domain *htirq_domain; 23 + 24 24 /* 25 25 * Hypertransport interrupt support 26 26 */ 27 - static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) 28 - { 29 - struct ht_irq_msg msg; 30 - 31 - fetch_ht_irq_msg(irq, &msg); 32 - 33 - msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK); 34 - msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK); 35 - 36 - msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest); 37 - msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest); 38 - 39 - write_ht_irq_msg(irq, &msg); 40 - } 41 - 42 27 static int 43 28 ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) 44 29 { 45 - struct irq_cfg *cfg = irqd_cfg(data); 46 - unsigned int dest; 30 + struct irq_data *parent = data->parent_data; 47 31 int ret; 48 32 49 - ret = apic_set_affinity(data, mask, &dest); 50 - if (ret) 51 - return ret; 33 + ret = parent->chip->irq_set_affinity(parent, mask, force); 34 + if (ret >= 0) { 35 + struct ht_irq_msg msg; 36 + struct irq_cfg *cfg = irqd_cfg(data); 52 37 53 - target_ht_irq(data->irq, dest, cfg->vector); 54 - return IRQ_SET_MASK_OK_NOCOPY; 38 + fetch_ht_irq_msg(data->irq, &msg); 39 + msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | 40 + HT_IRQ_LOW_DEST_ID_MASK); 41 + msg.address_lo |= HT_IRQ_LOW_VECTOR(cfg->vector) | 42 + HT_IRQ_LOW_DEST_ID(cfg->dest_apicid); 43 + msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK); 44 + msg.address_hi |= HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid); 45 + write_ht_irq_msg(data->irq, &msg); 46 + } 47 + 48 + return ret; 55 49 } 56 50 57 51 static struct irq_chip ht_irq_chip = { 58 52 .name = "PCI-HT", 59 53 .irq_mask = mask_ht_irq, 60 54 .irq_unmask = unmask_ht_irq, 61 - .irq_ack = apic_ack_edge, 55 + .irq_ack = irq_chip_ack_parent, 62 56 .irq_set_affinity = ht_set_affinity, 63 - .irq_retrigger = apic_retrigger_irq, 57 + .irq_retrigger = irq_chip_retrigger_hierarchy, 64 58 .flags = IRQCHIP_SKIP_SET_WAKE, 65 59 }; 66 60 67 - int arch_alloc_ht_irq(struct pci_dev *dev) 61 + static int htirq_domain_alloc(struct irq_domain *domain, unsigned int virq, 62 + unsigned int nr_irqs, void *arg) 68 63 { 69 - return irq_domain_alloc_irqs(NULL, 1, dev_to_node(&dev->dev), NULL); 64 + struct ht_irq_cfg *ht_cfg; 65 + struct irq_alloc_info *info = arg; 66 + struct pci_dev *dev; 67 + irq_hw_number_t hwirq; 68 + int ret; 69 + 70 + if (nr_irqs > 1 || !info) 71 + return -EINVAL; 72 + 73 + dev = info->ht_dev; 74 + hwirq = (info->ht_idx & 0xFF) | 75 + PCI_DEVID(dev->bus->number, dev->devfn) << 8 | 76 + (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 24; 77 + if (irq_find_mapping(domain, hwirq) > 0) 78 + return -EEXIST; 79 + 80 + ht_cfg = kmalloc(sizeof(*ht_cfg), GFP_KERNEL); 81 + if (!ht_cfg) 82 + return -ENOMEM; 83 + 84 + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, info); 85 + if (ret < 0) { 86 + kfree(ht_cfg); 87 + return ret; 88 + } 89 + 90 + /* Initialize msg to a value that will never match the first write. */ 91 + ht_cfg->msg.address_lo = 0xffffffff; 92 + ht_cfg->msg.address_hi = 0xffffffff; 93 + ht_cfg->dev = info->ht_dev; 94 + ht_cfg->update = info->ht_update; 95 + ht_cfg->pos = info->ht_pos; 96 + ht_cfg->idx = 0x10 + (info->ht_idx * 2); 97 + irq_domain_set_info(domain, virq, hwirq, &ht_irq_chip, ht_cfg, 98 + handle_edge_irq, ht_cfg, "edge"); 99 + 100 + return 0; 70 101 } 71 102 72 - void arch_free_ht_irq(int irq) 103 + static void htirq_domain_free(struct irq_domain *domain, unsigned int virq, 104 + unsigned int nr_irqs) 73 105 { 74 - irq_domain_free_irqs(irq, 1); 106 + struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq); 107 + 108 + BUG_ON(nr_irqs != 1); 109 + kfree(irq_data->chip_data); 110 + irq_domain_free_irqs_top(domain, virq, nr_irqs); 75 111 } 76 112 77 - int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) 113 + static void htirq_domain_activate(struct irq_domain *domain, 114 + struct irq_data *irq_data) 78 115 { 79 - struct irq_cfg *cfg; 80 116 struct ht_irq_msg msg; 117 + struct irq_cfg *cfg = irqd_cfg(irq_data); 81 118 82 - if (disable_apic) 83 - return -ENXIO; 84 - 85 - cfg = irq_cfg(irq); 86 119 msg.address_hi = HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid); 87 - 88 120 msg.address_lo = 89 121 HT_IRQ_LOW_BASE | 90 122 HT_IRQ_LOW_DEST_ID(cfg->dest_apicid) | ··· 131 95 HT_IRQ_LOW_MT_FIXED : 132 96 HT_IRQ_LOW_MT_ARBITRATED) | 133 97 HT_IRQ_LOW_IRQ_MASKED; 98 + write_ht_irq_msg(irq_data->irq, &msg); 99 + } 134 100 135 - write_ht_irq_msg(irq, &msg); 101 + static void htirq_domain_deactivate(struct irq_domain *domain, 102 + struct irq_data *irq_data) 103 + { 104 + struct ht_irq_msg msg; 136 105 137 - irq_set_chip_and_handler_name(irq, &ht_irq_chip, 138 - handle_edge_irq, "edge"); 106 + memset(&msg, 0, sizeof(msg)); 107 + write_ht_irq_msg(irq_data->irq, &msg); 108 + } 139 109 140 - dev_dbg(&dev->dev, "irq %d for HT\n", irq); 110 + static struct irq_domain_ops htirq_domain_ops = { 111 + .alloc = htirq_domain_alloc, 112 + .free = htirq_domain_free, 113 + .activate = htirq_domain_activate, 114 + .deactivate = htirq_domain_deactivate, 115 + }; 141 116 142 - return 0; 117 + void arch_init_htirq_domain(struct irq_domain *parent) 118 + { 119 + if (disable_apic) 120 + return; 121 + 122 + htirq_domain = irq_domain_add_tree(NULL, &htirq_domain_ops, NULL); 123 + if (!htirq_domain) 124 + pr_warn("failed to initialize irqdomain for HTIRQ.\n"); 125 + else 126 + htirq_domain->parent = parent; 127 + } 128 + 129 + int arch_setup_ht_irq(int idx, int pos, struct pci_dev *dev, 130 + ht_irq_update_t *update) 131 + { 132 + struct irq_alloc_info info; 133 + 134 + if (!htirq_domain) 135 + return -ENOSYS; 136 + 137 + init_irq_alloc_info(&info, NULL); 138 + info.ht_idx = idx; 139 + info.ht_pos = pos; 140 + info.ht_dev = dev; 141 + info.ht_update = update; 142 + 143 + return irq_domain_alloc_irqs(htirq_domain, 1, dev_to_node(&dev->dev), 144 + &info); 145 + } 146 + 147 + void arch_teardown_ht_irq(unsigned int irq) 148 + { 149 + irq_domain_free_irqs(irq, 1); 143 150 }
+1
arch/x86/kernel/apic/vector.c
··· 365 365 irq_set_default_host(x86_vector_domain); 366 366 367 367 arch_init_msi_domain(x86_vector_domain); 368 + arch_init_htirq_domain(x86_vector_domain); 368 369 369 370 return arch_early_ioapic_init(); 370 371 }
+6 -41
drivers/pci/htirq.c
··· 23 23 */ 24 24 static DEFINE_SPINLOCK(ht_irq_lock); 25 25 26 - struct ht_irq_cfg { 27 - struct pci_dev *dev; 28 - /* Update callback used to cope with buggy hardware */ 29 - ht_irq_update_t *update; 30 - unsigned pos; 31 - unsigned idx; 32 - struct ht_irq_msg msg; 33 - }; 34 - 35 - 36 26 void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg) 37 27 { 38 28 struct ht_irq_cfg *cfg = irq_get_handler_data(irq); 39 29 unsigned long flags; 30 + 40 31 spin_lock_irqsave(&ht_irq_lock, flags); 41 32 if (cfg->msg.address_lo != msg->address_lo) { 42 33 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); ··· 46 55 void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg) 47 56 { 48 57 struct ht_irq_cfg *cfg = irq_get_handler_data(irq); 58 + 49 59 *msg = cfg->msg; 50 60 } 51 61 ··· 78 86 */ 79 87 int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update) 80 88 { 81 - struct ht_irq_cfg *cfg; 82 89 int max_irq, pos, irq; 83 90 unsigned long flags; 84 91 u32 data; ··· 96 105 if (idx > max_irq) 97 106 return -EINVAL; 98 107 99 - cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); 100 - if (!cfg) 101 - return -ENOMEM; 102 - 103 - cfg->dev = dev; 104 - cfg->update = update; 105 - cfg->pos = pos; 106 - cfg->idx = 0x10 + (idx * 2); 107 - /* Initialize msg to a value that will never match the first write. */ 108 - cfg->msg.address_lo = 0xffffffff; 109 - cfg->msg.address_hi = 0xffffffff; 110 - 111 - irq = arch_alloc_ht_irq(dev); 112 - if (irq <= 0) { 113 - kfree(cfg); 114 - return -EBUSY; 115 - } 116 - irq_set_handler_data(irq, cfg); 117 - 118 - if (arch_setup_ht_irq(irq, dev) < 0) { 119 - ht_destroy_irq(irq); 120 - return -EBUSY; 121 - } 108 + irq = arch_setup_ht_irq(idx, pos, dev, update); 109 + if (irq > 0) 110 + dev_dbg(&dev->dev, "irq %d for HT\n", irq); 122 111 123 112 return irq; 124 113 } ··· 129 158 */ 130 159 void ht_destroy_irq(unsigned int irq) 131 160 { 132 - struct ht_irq_cfg *cfg; 133 - 134 - cfg = irq_get_handler_data(irq); 135 - irq_set_chip(irq, NULL); 136 - irq_set_handler_data(irq, NULL); 137 - arch_free_ht_irq(irq); 138 - kfree(cfg); 161 + arch_teardown_ht_irq(irq); 139 162 } 140 163 EXPORT_SYMBOL(ht_destroy_irq);
+18 -6
include/linux/htirq.h
··· 1 1 #ifndef LINUX_HTIRQ_H 2 2 #define LINUX_HTIRQ_H 3 3 4 + struct pci_dev; 5 + struct irq_data; 6 + 4 7 struct ht_irq_msg { 5 8 u32 address_lo; /* low 32 bits of the ht irq message */ 6 9 u32 address_hi; /* high 32 bits of the it irq message */ 7 10 }; 8 11 12 + typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq, 13 + struct ht_irq_msg *msg); 14 + 15 + struct ht_irq_cfg { 16 + struct pci_dev *dev; 17 + /* Update callback used to cope with buggy hardware */ 18 + ht_irq_update_t *update; 19 + unsigned pos; 20 + unsigned idx; 21 + struct ht_irq_msg msg; 22 + }; 23 + 9 24 /* Helper functions.. */ 10 25 void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg); 11 26 void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg); 12 - struct irq_data; 13 27 void mask_ht_irq(struct irq_data *data); 14 28 void unmask_ht_irq(struct irq_data *data); 15 29 16 30 /* The arch hook for getting things started */ 17 - int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev); 18 - int arch_alloc_ht_irq(struct pci_dev *dev); 19 - void arch_free_ht_irq(int irq); 31 + int arch_setup_ht_irq(int idx, int pos, struct pci_dev *dev, 32 + ht_irq_update_t *update); 33 + void arch_teardown_ht_irq(unsigned int irq); 20 34 21 35 /* For drivers of buggy hardware */ 22 - typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq, 23 - struct ht_irq_msg *msg); 24 36 int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update); 25 37 26 38 #endif /* LINUX_HTIRQ_H */