diff options
| -rw-r--r-- | include/linux/interrupt.h | 33 | ||||
| -rw-r--r-- | include/linux/irqdesc.h | 3 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 82 |
3 files changed, 117 insertions, 1 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 55e0d4253e49..63c5ad78e37c 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include <linux/smp.h> | 14 | #include <linux/smp.h> |
| 15 | #include <linux/percpu.h> | 15 | #include <linux/percpu.h> |
| 16 | #include <linux/hrtimer.h> | 16 | #include <linux/hrtimer.h> |
| 17 | #include <linux/kref.h> | ||
| 18 | #include <linux/workqueue.h> | ||
| 17 | 19 | ||
| 18 | #include <asm/atomic.h> | 20 | #include <asm/atomic.h> |
| 19 | #include <asm/ptrace.h> | 21 | #include <asm/ptrace.h> |
| @@ -240,6 +242,35 @@ extern int irq_can_set_affinity(unsigned int irq); | |||
| 240 | extern int irq_select_affinity(unsigned int irq); | 242 | extern int irq_select_affinity(unsigned int irq); |
| 241 | 243 | ||
| 242 | extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); | 244 | extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); |
| 245 | |||
| 246 | /** | ||
| 247 | * struct irq_affinity_notify - context for notification of IRQ affinity changes | ||
| 248 | * @irq: Interrupt to which notification applies | ||
| 249 | * @kref: Reference count, for internal use | ||
| 250 | * @work: Work item, for internal use | ||
| 251 | * @notify: Function to be called on change. This will be | ||
| 252 | * called in process context. | ||
| 253 | * @release: Function to be called on release. This will be | ||
| 254 | * called in process context. Once registered, the | ||
| 255 | * structure must only be freed when this function is | ||
| 256 | * called or later. | ||
| 257 | */ | ||
| 258 | struct irq_affinity_notify { | ||
| 259 | unsigned int irq; | ||
| 260 | struct kref kref; | ||
| 261 | struct work_struct work; | ||
| 262 | void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); | ||
| 263 | void (*release)(struct kref *ref); | ||
| 264 | }; | ||
| 265 | |||
| 266 | extern int | ||
| 267 | irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); | ||
| 268 | |||
| 269 | static inline void irq_run_affinity_notifiers(void) | ||
| 270 | { | ||
| 271 | flush_scheduled_work(); | ||
| 272 | } | ||
| 273 | |||
| 243 | #else /* CONFIG_SMP */ | 274 | #else /* CONFIG_SMP */ |
| 244 | 275 | ||
| 245 | static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) | 276 | static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) |
| @@ -255,7 +286,7 @@ static inline int irq_can_set_affinity(unsigned int irq) | |||
| 255 | static inline int irq_select_affinity(unsigned int irq) { return 0; } | 286 | static inline int irq_select_affinity(unsigned int irq) { return 0; } |
| 256 | 287 | ||
| 257 | static inline int irq_set_affinity_hint(unsigned int irq, | 288 | static inline int irq_set_affinity_hint(unsigned int irq, |
| 258 | const struct cpumask *m) | 289 | const struct cpumask *m) |
| 259 | { | 290 | { |
| 260 | return -EINVAL; | 291 | return -EINVAL; |
| 261 | } | 292 | } |
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index c1a95b7b58de..bfef56dadddb 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * For now it's included from <linux/irq.h> | 8 | * For now it's included from <linux/irq.h> |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | struct irq_affinity_notify; | ||
| 11 | struct proc_dir_entry; | 12 | struct proc_dir_entry; |
| 12 | struct timer_rand_state; | 13 | struct timer_rand_state; |
| 13 | /** | 14 | /** |
| @@ -24,6 +25,7 @@ struct timer_rand_state; | |||
| 24 | * @last_unhandled: aging timer for unhandled count | 25 | * @last_unhandled: aging timer for unhandled count |
| 25 | * @irqs_unhandled: stats field for spurious unhandled interrupts | 26 | * @irqs_unhandled: stats field for spurious unhandled interrupts |
| 26 | * @lock: locking for SMP | 27 | * @lock: locking for SMP |
| 28 | * @affinity_notify: context for notification of affinity changes | ||
| 27 | * @pending_mask: pending rebalanced interrupts | 29 | * @pending_mask: pending rebalanced interrupts |
| 28 | * @threads_active: number of irqaction threads currently running | 30 | * @threads_active: number of irqaction threads currently running |
| 29 | * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers | 31 | * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers |
| @@ -70,6 +72,7 @@ struct irq_desc { | |||
| 70 | raw_spinlock_t lock; | 72 | raw_spinlock_t lock; |
| 71 | #ifdef CONFIG_SMP | 73 | #ifdef CONFIG_SMP |
| 72 | const struct cpumask *affinity_hint; | 74 | const struct cpumask *affinity_hint; |
| 75 | struct irq_affinity_notify *affinity_notify; | ||
| 73 | #ifdef CONFIG_GENERIC_PENDING_IRQ | 76 | #ifdef CONFIG_GENERIC_PENDING_IRQ |
| 74 | cpumask_var_t pending_mask; | 77 | cpumask_var_t pending_mask; |
| 75 | #endif | 78 | #endif |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0caa59f747dd..0587c5ceaed8 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
| @@ -134,6 +134,10 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) | |||
| 134 | irq_set_thread_affinity(desc); | 134 | irq_set_thread_affinity(desc); |
| 135 | } | 135 | } |
| 136 | #endif | 136 | #endif |
| 137 | if (desc->affinity_notify) { | ||
| 138 | kref_get(&desc->affinity_notify->kref); | ||
| 139 | schedule_work(&desc->affinity_notify->work); | ||
| 140 | } | ||
| 137 | desc->status |= IRQ_AFFINITY_SET; | 141 | desc->status |= IRQ_AFFINITY_SET; |
| 138 | raw_spin_unlock_irqrestore(&desc->lock, flags); | 142 | raw_spin_unlock_irqrestore(&desc->lock, flags); |
| 139 | return 0; | 143 | return 0; |
| @@ -155,6 +159,79 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) | |||
| 155 | } | 159 | } |
| 156 | EXPORT_SYMBOL_GPL(irq_set_affinity_hint); | 160 | EXPORT_SYMBOL_GPL(irq_set_affinity_hint); |
| 157 | 161 | ||
| 162 | static void irq_affinity_notify(struct work_struct *work) | ||
| 163 | { | ||
| 164 | struct irq_affinity_notify *notify = | ||
| 165 | container_of(work, struct irq_affinity_notify, work); | ||
| 166 | struct irq_desc *desc = irq_to_desc(notify->irq); | ||
| 167 | cpumask_var_t cpumask; | ||
| 168 | unsigned long flags; | ||
| 169 | |||
| 170 | if (!desc) | ||
| 171 | goto out; | ||
| 172 | |||
| 173 | if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) | ||
| 174 | goto out; | ||
| 175 | |||
| 176 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
| 177 | #ifdef CONFIG_GENERIC_PENDING_IRQ | ||
| 178 | if (desc->status & IRQ_MOVE_PENDING) | ||
| 179 | cpumask_copy(cpumask, desc->pending_mask); | ||
| 180 | else | ||
| 181 | #endif | ||
| 182 | cpumask_copy(cpumask, desc->affinity); | ||
| 183 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
| 184 | |||
| 185 | notify->notify(notify, cpumask); | ||
| 186 | |||
| 187 | free_cpumask_var(cpumask); | ||
| 188 | out: | ||
| 189 | kref_put(¬ify->kref, notify->release); | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * irq_set_affinity_notifier - control notification of IRQ affinity changes | ||
| 194 | * @irq: Interrupt for which to enable/disable notification | ||
| 195 | * @notify: Context for notification, or %NULL to disable | ||
| 196 | * notification. Function pointers must be initialised; | ||
| 197 | * the other fields will be initialised by this function. | ||
| 198 | * | ||
| 199 | * Must be called in process context. Notification may only be enabled | ||
| 200 | * after the IRQ is allocated and must be disabled before the IRQ is | ||
| 201 | * freed using free_irq(). | ||
| 202 | */ | ||
| 203 | int | ||
| 204 | irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) | ||
| 205 | { | ||
| 206 | struct irq_desc *desc = irq_to_desc(irq); | ||
| 207 | struct irq_affinity_notify *old_notify; | ||
| 208 | unsigned long flags; | ||
| 209 | |||
| 210 | /* The release function is promised process context */ | ||
| 211 | might_sleep(); | ||
| 212 | |||
| 213 | if (!desc) | ||
| 214 | return -EINVAL; | ||
| 215 | |||
| 216 | /* Complete initialisation of *notify */ | ||
| 217 | if (notify) { | ||
| 218 | notify->irq = irq; | ||
| 219 | kref_init(¬ify->kref); | ||
| 220 | INIT_WORK(¬ify->work, irq_affinity_notify); | ||
| 221 | } | ||
| 222 | |||
| 223 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
| 224 | old_notify = desc->affinity_notify; | ||
| 225 | desc->affinity_notify = notify; | ||
| 226 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
| 227 | |||
| 228 | if (old_notify) | ||
| 229 | kref_put(&old_notify->kref, old_notify->release); | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | } | ||
| 233 | EXPORT_SYMBOL_GPL(irq_set_affinity_notifier); | ||
| 234 | |||
| 158 | #ifndef CONFIG_AUTO_IRQ_AFFINITY | 235 | #ifndef CONFIG_AUTO_IRQ_AFFINITY |
| 159 | /* | 236 | /* |
| 160 | * Generic version of the affinity autoselector. | 237 | * Generic version of the affinity autoselector. |
| @@ -1004,6 +1081,11 @@ void free_irq(unsigned int irq, void *dev_id) | |||
| 1004 | if (!desc) | 1081 | if (!desc) |
| 1005 | return; | 1082 | return; |
| 1006 | 1083 | ||
| 1084 | #ifdef CONFIG_SMP | ||
| 1085 | if (WARN_ON(desc->affinity_notify)) | ||
| 1086 | desc->affinity_notify = NULL; | ||
| 1087 | #endif | ||
| 1088 | |||
| 1007 | chip_bus_lock(desc); | 1089 | chip_bus_lock(desc); |
| 1008 | kfree(__free_irq(irq, dev_id)); | 1090 | kfree(__free_irq(irq, dev_id)); |
| 1009 | chip_bus_sync_unlock(desc); | 1091 | chip_bus_sync_unlock(desc); |
