diff options
-rw-r--r-- | arch/x86/include/asm/uv/uv_irq.h | 15 | ||||
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 49 | ||||
-rw-r--r-- | arch/x86/kernel/uv_irq.c | 128 | ||||
-rw-r--r-- | drivers/misc/sgi-xp/xpc_uv.c | 5 |
4 files changed, 180 insertions, 17 deletions
diff --git a/arch/x86/include/asm/uv/uv_irq.h b/arch/x86/include/asm/uv/uv_irq.h index 9613c8c0b647..5397e1290952 100644 --- a/arch/x86/include/asm/uv/uv_irq.h +++ b/arch/x86/include/asm/uv/uv_irq.h | |||
@@ -25,12 +25,21 @@ struct uv_IO_APIC_route_entry { | |||
25 | dest : 32; | 25 | dest : 32; |
26 | }; | 26 | }; |
27 | 27 | ||
28 | enum { | ||
29 | UV_AFFINITY_ALL, | ||
30 | UV_AFFINITY_NODE, | ||
31 | UV_AFFINITY_CPU | ||
32 | }; | ||
33 | |||
28 | extern struct irq_chip uv_irq_chip; | 34 | extern struct irq_chip uv_irq_chip; |
29 | 35 | ||
30 | extern int arch_enable_uv_irq(char *, unsigned int, int, int, unsigned long); | 36 | extern int |
37 | arch_enable_uv_irq(char *, unsigned int, int, int, unsigned long, int); | ||
31 | extern void arch_disable_uv_irq(int, unsigned long); | 38 | extern void arch_disable_uv_irq(int, unsigned long); |
39 | extern int uv_set_irq_affinity(unsigned int, const struct cpumask *); | ||
32 | 40 | ||
33 | extern int uv_setup_irq(char *, int, int, unsigned long); | 41 | extern int uv_irq_2_mmr_info(int, unsigned long *, int *); |
34 | extern void uv_teardown_irq(unsigned int, int, unsigned long); | 42 | extern int uv_setup_irq(char *, int, int, unsigned long, int); |
43 | extern void uv_teardown_irq(unsigned int); | ||
35 | 44 | ||
36 | #endif /* _ASM_X86_UV_UV_IRQ_H */ | 45 | #endif /* _ASM_X86_UV_UV_IRQ_H */ |
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 8c718c93d079..bb52e7f6e953 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -3731,9 +3731,10 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) | |||
3731 | * on the specified blade to allow the sending of MSIs to the specified CPU. | 3731 | * on the specified blade to allow the sending of MSIs to the specified CPU. |
3732 | */ | 3732 | */ |
3733 | int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade, | 3733 | int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade, |
3734 | unsigned long mmr_offset) | 3734 | unsigned long mmr_offset, int restrict) |
3735 | { | 3735 | { |
3736 | const struct cpumask *eligible_cpu = cpumask_of(cpu); | 3736 | const struct cpumask *eligible_cpu = cpumask_of(cpu); |
3737 | struct irq_desc *desc = irq_to_desc(irq); | ||
3737 | struct irq_cfg *cfg; | 3738 | struct irq_cfg *cfg; |
3738 | int mmr_pnode; | 3739 | int mmr_pnode; |
3739 | unsigned long mmr_value; | 3740 | unsigned long mmr_value; |
@@ -3749,6 +3750,11 @@ int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade, | |||
3749 | if (err != 0) | 3750 | if (err != 0) |
3750 | return err; | 3751 | return err; |
3751 | 3752 | ||
3753 | if (restrict == UV_AFFINITY_CPU) | ||
3754 | desc->status |= IRQ_NO_BALANCING; | ||
3755 | else | ||
3756 | desc->status |= IRQ_MOVE_PCNTXT; | ||
3757 | |||
3752 | spin_lock_irqsave(&vector_lock, flags); | 3758 | spin_lock_irqsave(&vector_lock, flags); |
3753 | set_irq_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq, | 3759 | set_irq_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq, |
3754 | irq_name); | 3760 | irq_name); |
@@ -3777,11 +3783,10 @@ int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade, | |||
3777 | * Disable the specified MMR located on the specified blade so that MSIs are | 3783 | * Disable the specified MMR located on the specified blade so that MSIs are |
3778 | * longer allowed to be sent. | 3784 | * longer allowed to be sent. |
3779 | */ | 3785 | */ |
3780 | void arch_disable_uv_irq(int mmr_blade, unsigned long mmr_offset) | 3786 | void arch_disable_uv_irq(int mmr_pnode, unsigned long mmr_offset) |
3781 | { | 3787 | { |
3782 | unsigned long mmr_value; | 3788 | unsigned long mmr_value; |
3783 | struct uv_IO_APIC_route_entry *entry; | 3789 | struct uv_IO_APIC_route_entry *entry; |
3784 | int mmr_pnode; | ||
3785 | 3790 | ||
3786 | BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long)); | 3791 | BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long)); |
3787 | 3792 | ||
@@ -3789,9 +3794,45 @@ void arch_disable_uv_irq(int mmr_blade, unsigned long mmr_offset) | |||
3789 | entry = (struct uv_IO_APIC_route_entry *)&mmr_value; | 3794 | entry = (struct uv_IO_APIC_route_entry *)&mmr_value; |
3790 | entry->mask = 1; | 3795 | entry->mask = 1; |
3791 | 3796 | ||
3792 | mmr_pnode = uv_blade_to_pnode(mmr_blade); | ||
3793 | uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value); | 3797 | uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value); |
3794 | } | 3798 | } |
3799 | |||
3800 | int uv_set_irq_affinity(unsigned int irq, const struct cpumask *mask) | ||
3801 | { | ||
3802 | struct irq_desc *desc = irq_to_desc(irq); | ||
3803 | struct irq_cfg *cfg = desc->chip_data; | ||
3804 | unsigned int dest; | ||
3805 | unsigned long mmr_value; | ||
3806 | struct uv_IO_APIC_route_entry *entry; | ||
3807 | unsigned long mmr_offset; | ||
3808 | unsigned mmr_pnode; | ||
3809 | |||
3810 | dest = set_desc_affinity(desc, mask); | ||
3811 | if (dest == BAD_APICID) | ||
3812 | return -1; | ||
3813 | |||
3814 | mmr_value = 0; | ||
3815 | entry = (struct uv_IO_APIC_route_entry *)&mmr_value; | ||
3816 | |||
3817 | entry->vector = cfg->vector; | ||
3818 | entry->delivery_mode = apic->irq_delivery_mode; | ||
3819 | entry->dest_mode = apic->irq_dest_mode; | ||
3820 | entry->polarity = 0; | ||
3821 | entry->trigger = 0; | ||
3822 | entry->mask = 0; | ||
3823 | entry->dest = dest; | ||
3824 | |||
3825 | /* Get previously stored MMR and pnode of hub sourcing interrupts */ | ||
3826 | if (uv_irq_2_mmr_info(irq, &mmr_offset, &mmr_pnode)) | ||
3827 | return -1; | ||
3828 | |||
3829 | uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value); | ||
3830 | |||
3831 | if (cfg->move_in_progress) | ||
3832 | send_cleanup_vector(cfg); | ||
3833 | |||
3834 | return 0; | ||
3835 | } | ||
3795 | #endif /* CONFIG_X86_64 */ | 3836 | #endif /* CONFIG_X86_64 */ |
3796 | 3837 | ||
3797 | int __init io_apic_get_redir_entries (int ioapic) | 3838 | int __init io_apic_get_redir_entries (int ioapic) |
diff --git a/arch/x86/kernel/uv_irq.c b/arch/x86/kernel/uv_irq.c index aeef529917e4..9a83775ab0f3 100644 --- a/arch/x86/kernel/uv_irq.c +++ b/arch/x86/kernel/uv_irq.c | |||
@@ -9,10 +9,22 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/rbtree.h> | ||
12 | #include <linux/irq.h> | 13 | #include <linux/irq.h> |
13 | 14 | ||
14 | #include <asm/apic.h> | 15 | #include <asm/apic.h> |
15 | #include <asm/uv/uv_irq.h> | 16 | #include <asm/uv/uv_irq.h> |
17 | #include <asm/uv/uv_hub.h> | ||
18 | |||
19 | /* MMR offset and pnode of hub sourcing interrupts for a given irq */ | ||
20 | struct uv_irq_2_mmr_pnode{ | ||
21 | struct rb_node list; | ||
22 | unsigned long offset; | ||
23 | int pnode; | ||
24 | int irq; | ||
25 | }; | ||
26 | static spinlock_t uv_irq_lock; | ||
27 | static struct rb_root uv_irq_root; | ||
16 | 28 | ||
17 | static void uv_noop(unsigned int irq) | 29 | static void uv_noop(unsigned int irq) |
18 | { | 30 | { |
@@ -39,25 +51,106 @@ struct irq_chip uv_irq_chip = { | |||
39 | .unmask = uv_noop, | 51 | .unmask = uv_noop, |
40 | .eoi = uv_ack_apic, | 52 | .eoi = uv_ack_apic, |
41 | .end = uv_noop, | 53 | .end = uv_noop, |
54 | .set_affinity = uv_set_irq_affinity, | ||
42 | }; | 55 | }; |
43 | 56 | ||
44 | /* | 57 | /* |
58 | * Add offset and pnode information of the hub sourcing interrupts to the | ||
59 | * rb tree for a specific irq. | ||
60 | */ | ||
61 | static int uv_set_irq_2_mmr_info(int irq, unsigned long offset, unsigned blade) | ||
62 | { | ||
63 | struct rb_node **link = &uv_irq_root.rb_node; | ||
64 | struct rb_node *parent = NULL; | ||
65 | struct uv_irq_2_mmr_pnode *n; | ||
66 | struct uv_irq_2_mmr_pnode *e; | ||
67 | unsigned long irqflags; | ||
68 | |||
69 | n = kmalloc_node(sizeof(struct uv_irq_2_mmr_pnode), GFP_KERNEL, | ||
70 | uv_blade_to_memory_nid(blade)); | ||
71 | if (!n) | ||
72 | return -ENOMEM; | ||
73 | |||
74 | n->irq = irq; | ||
75 | n->offset = offset; | ||
76 | n->pnode = uv_blade_to_pnode(blade); | ||
77 | spin_lock_irqsave(&uv_irq_lock, irqflags); | ||
78 | /* Find the right place in the rbtree: */ | ||
79 | while (*link) { | ||
80 | parent = *link; | ||
81 | e = rb_entry(parent, struct uv_irq_2_mmr_pnode, list); | ||
82 | |||
83 | if (unlikely(irq == e->irq)) { | ||
84 | /* irq entry exists */ | ||
85 | e->pnode = uv_blade_to_pnode(blade); | ||
86 | e->offset = offset; | ||
87 | spin_unlock_irqrestore(&uv_irq_lock, irqflags); | ||
88 | kfree(n); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | if (irq < e->irq) | ||
93 | link = &(*link)->rb_left; | ||
94 | else | ||
95 | link = &(*link)->rb_right; | ||
96 | } | ||
97 | |||
98 | /* Insert the node into the rbtree. */ | ||
99 | rb_link_node(&n->list, parent, link); | ||
100 | rb_insert_color(&n->list, &uv_irq_root); | ||
101 | |||
102 | spin_unlock_irqrestore(&uv_irq_lock, irqflags); | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* Retrieve offset and pnode information from the rb tree for a specific irq */ | ||
107 | int uv_irq_2_mmr_info(int irq, unsigned long *offset, int *pnode) | ||
108 | { | ||
109 | struct uv_irq_2_mmr_pnode *e; | ||
110 | struct rb_node *n; | ||
111 | unsigned long irqflags; | ||
112 | |||
113 | spin_lock_irqsave(&uv_irq_lock, irqflags); | ||
114 | n = uv_irq_root.rb_node; | ||
115 | while (n) { | ||
116 | e = rb_entry(n, struct uv_irq_2_mmr_pnode, list); | ||
117 | |||
118 | if (e->irq == irq) { | ||
119 | *offset = e->offset; | ||
120 | *pnode = e->pnode; | ||
121 | spin_unlock_irqrestore(&uv_irq_lock, irqflags); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | if (irq < e->irq) | ||
126 | n = n->rb_left; | ||
127 | else | ||
128 | n = n->rb_right; | ||
129 | } | ||
130 | spin_unlock_irqrestore(&uv_irq_lock, irqflags); | ||
131 | return -1; | ||
132 | } | ||
133 | |||
134 | /* | ||
45 | * Set up a mapping of an available irq and vector, and enable the specified | 135 | * Set up a mapping of an available irq and vector, and enable the specified |
46 | * MMR that defines the MSI that is to be sent to the specified CPU when an | 136 | * MMR that defines the MSI that is to be sent to the specified CPU when an |
47 | * interrupt is raised. | 137 | * interrupt is raised. |
48 | */ | 138 | */ |
49 | int uv_setup_irq(char *irq_name, int cpu, int mmr_blade, | 139 | int uv_setup_irq(char *irq_name, int cpu, int mmr_blade, |
50 | unsigned long mmr_offset) | 140 | unsigned long mmr_offset, int restrict) |
51 | { | 141 | { |
52 | int irq; | 142 | int irq, ret; |
53 | int ret; | 143 | |
144 | irq = create_irq_nr(NR_IRQS_LEGACY, uv_blade_to_memory_nid(mmr_blade)); | ||
54 | 145 | ||
55 | irq = create_irq(); | ||
56 | if (irq <= 0) | 146 | if (irq <= 0) |
57 | return -EBUSY; | 147 | return -EBUSY; |
58 | 148 | ||
59 | ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset); | 149 | ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset, |
60 | if (ret != irq) | 150 | restrict); |
151 | if (ret == irq) | ||
152 | uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade); | ||
153 | else | ||
61 | destroy_irq(irq); | 154 | destroy_irq(irq); |
62 | 155 | ||
63 | return ret; | 156 | return ret; |
@@ -71,9 +164,28 @@ EXPORT_SYMBOL_GPL(uv_setup_irq); | |||
71 | * | 164 | * |
72 | * Set mmr_blade and mmr_offset to what was passed in on uv_setup_irq(). | 165 | * Set mmr_blade and mmr_offset to what was passed in on uv_setup_irq(). |
73 | */ | 166 | */ |
74 | void uv_teardown_irq(unsigned int irq, int mmr_blade, unsigned long mmr_offset) | 167 | void uv_teardown_irq(unsigned int irq) |
75 | { | 168 | { |
76 | arch_disable_uv_irq(mmr_blade, mmr_offset); | 169 | struct uv_irq_2_mmr_pnode *e; |
170 | struct rb_node *n; | ||
171 | unsigned long irqflags; | ||
172 | |||
173 | spin_lock_irqsave(&uv_irq_lock, irqflags); | ||
174 | n = uv_irq_root.rb_node; | ||
175 | while (n) { | ||
176 | e = rb_entry(n, struct uv_irq_2_mmr_pnode, list); | ||
177 | if (e->irq == irq) { | ||
178 | arch_disable_uv_irq(e->pnode, e->offset); | ||
179 | rb_erase(n, &uv_irq_root); | ||
180 | kfree(e); | ||
181 | break; | ||
182 | } | ||
183 | if (irq < e->irq) | ||
184 | n = n->rb_left; | ||
185 | else | ||
186 | n = n->rb_right; | ||
187 | } | ||
188 | spin_unlock_irqrestore(&uv_irq_lock, irqflags); | ||
77 | destroy_irq(irq); | 189 | destroy_irq(irq); |
78 | } | 190 | } |
79 | EXPORT_SYMBOL_GPL(uv_teardown_irq); | 191 | EXPORT_SYMBOL_GPL(uv_teardown_irq); |
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index c76677afda1b..b5bbe59f9c57 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c | |||
@@ -106,7 +106,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) | |||
106 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); | 106 | int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); |
107 | 107 | ||
108 | #if defined CONFIG_X86_64 | 108 | #if defined CONFIG_X86_64 |
109 | mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset); | 109 | mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset, |
110 | UV_AFFINITY_CPU); | ||
110 | if (mq->irq < 0) { | 111 | if (mq->irq < 0) { |
111 | dev_err(xpc_part, "uv_setup_irq() returned error=%d\n", | 112 | dev_err(xpc_part, "uv_setup_irq() returned error=%d\n", |
112 | -mq->irq); | 113 | -mq->irq); |
@@ -136,7 +137,7 @@ static void | |||
136 | xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) | 137 | xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) |
137 | { | 138 | { |
138 | #if defined CONFIG_X86_64 | 139 | #if defined CONFIG_X86_64 |
139 | uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset); | 140 | uv_teardown_irq(mq->irq); |
140 | 141 | ||
141 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV | 142 | #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV |
142 | int mmr_pnode; | 143 | int mmr_pnode; |