Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 234690
b: refs/heads/master
c: 51327ad
h: refs/heads/master
v: v3
  • Loading branch information
Thomas Gleixner committed Feb 10, 2011
1 parent 85fcd30 commit 98a86da
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 25 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 986c011ddbb3ed44b35e1bfd67f6aa60b293b495
refs/heads/master: 51327ada7142ab520ed610a42572d1f4cbfbb2dc
36 changes: 34 additions & 2 deletions trunk/include/linux/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <linux/smp.h>
#include <linux/percpu.h>
#include <linux/hrtimer.h>
#include <linux/kref.h>
#include <linux/workqueue.h>

#include <asm/atomic.h>
#include <asm/ptrace.h>
Expand Down Expand Up @@ -55,7 +57,7 @@
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
*
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040
Expand All @@ -67,6 +69,7 @@
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000

#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND)

Expand Down Expand Up @@ -240,6 +243,35 @@ extern int irq_can_set_affinity(unsigned int irq);
extern int irq_select_affinity(unsigned int irq);

extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);

/**
* struct irq_affinity_notify - context for notification of IRQ affinity changes
* @irq: Interrupt to which notification applies
* @kref: Reference count, for internal use
* @work: Work item, for internal use
* @notify: Function to be called on change. This will be
* called in process context.
* @release: Function to be called on release. This will be
* called in process context. Once registered, the
* structure must only be freed when this function is
* called or later.
*/
struct irq_affinity_notify {
unsigned int irq;
struct kref kref;
struct work_struct work;
void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
void (*release)(struct kref *ref);
};

extern int
irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);

static inline void irq_run_affinity_notifiers(void)
{
flush_scheduled_work();
}

#else /* CONFIG_SMP */

static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
Expand All @@ -255,7 +287,7 @@ static inline int irq_can_set_affinity(unsigned int irq)
static inline int irq_select_affinity(unsigned int irq) { return 0; }

static inline int irq_set_affinity_hint(unsigned int irq,
const struct cpumask *m)
const struct cpumask *m)
{
return -EINVAL;
}
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/irqdesc.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* For now it's included from <linux/irq.h>
*/

struct irq_affinity_notify;
struct proc_dir_entry;
struct timer_rand_state;
/**
Expand All @@ -24,6 +25,7 @@ struct timer_rand_state;
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
Expand Down Expand Up @@ -70,6 +72,7 @@ struct irq_desc {
raw_spinlock_t lock;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
Expand Down
17 changes: 0 additions & 17 deletions trunk/kernel/irq/irqdesc.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ int __init early_irq_init(void)
for (i = 0; i < count; i++) {
desc[i].irq_data.irq = i;
desc[i].irq_data.chip = &no_irq_chip;
/* TODO : do this allocation on-demand ... */
desc[i].kstat_irqs = alloc_percpu(unsigned int);
alloc_masks(desc + i, GFP_KERNEL, node);
desc_smp_init(desc + i, node);
Expand All @@ -277,22 +276,6 @@ static void free_desc(unsigned int irq)

static inline int alloc_descs(unsigned int start, unsigned int cnt, int node)
{
#if defined(CONFIG_KSTAT_IRQS_ONDEMAND)
struct irq_desc *desc;
unsigned int i;

for (i = 0; i < cnt; i++) {
desc = irq_to_desc(start + i);
if (desc && !desc->kstat_irqs) {
unsigned int __percpu *stats = alloc_percpu(unsigned int);

if (!stats)
return -1;
if (cmpxchg(&desc->kstat_irqs, NULL, stats) != NULL)
free_percpu(stats);
}
}
#endif
return start;
}
#endif /* !CONFIG_SPARSE_IRQ */
Expand Down
93 changes: 92 additions & 1 deletion trunk/kernel/irq/manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
irq_set_thread_affinity(desc);
}
#endif
if (desc->affinity_notify) {
kref_get(&desc->affinity_notify->kref);
schedule_work(&desc->affinity_notify->work);
}
desc->status |= IRQ_AFFINITY_SET;
raw_spin_unlock_irqrestore(&desc->lock, flags);
return 0;
Expand All @@ -155,6 +159,79 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
}
EXPORT_SYMBOL_GPL(irq_set_affinity_hint);

static void irq_affinity_notify(struct work_struct *work)
{
struct irq_affinity_notify *notify =
container_of(work, struct irq_affinity_notify, work);
struct irq_desc *desc = irq_to_desc(notify->irq);
cpumask_var_t cpumask;
unsigned long flags;

if (!desc)
goto out;

if (!alloc_cpumask_var(&cpumask, GFP_KERNEL))
goto out;

raw_spin_lock_irqsave(&desc->lock, flags);
#ifdef CONFIG_GENERIC_PENDING_IRQ
if (desc->status & IRQ_MOVE_PENDING)
cpumask_copy(cpumask, desc->pending_mask);
else
#endif
cpumask_copy(cpumask, desc->irq_data.affinity);
raw_spin_unlock_irqrestore(&desc->lock, flags);

notify->notify(notify, cpumask);

free_cpumask_var(cpumask);
out:
kref_put(&notify->kref, notify->release);
}

/**
* irq_set_affinity_notifier - control notification of IRQ affinity changes
* @irq: Interrupt for which to enable/disable notification
* @notify: Context for notification, or %NULL to disable
* notification. Function pointers must be initialised;
* the other fields will be initialised by this function.
*
* Must be called in process context. Notification may only be enabled
* after the IRQ is allocated and must be disabled before the IRQ is
* freed using free_irq().
*/
int
irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irq_affinity_notify *old_notify;
unsigned long flags;

/* The release function is promised process context */
might_sleep();

if (!desc)
return -EINVAL;

/* Complete initialisation of *notify */
if (notify) {
notify->irq = irq;
kref_init(&notify->kref);
INIT_WORK(&notify->work, irq_affinity_notify);
}

raw_spin_lock_irqsave(&desc->lock, flags);
old_notify = desc->affinity_notify;
desc->affinity_notify = notify;
raw_spin_unlock_irqrestore(&desc->lock, flags);

if (old_notify)
kref_put(&old_notify->kref, old_notify->release);

return 0;
}
EXPORT_SYMBOL_GPL(irq_set_affinity_notifier);

#ifndef CONFIG_AUTO_IRQ_AFFINITY
/*
* Generic version of the affinity autoselector.
Expand Down Expand Up @@ -282,8 +359,17 @@ EXPORT_SYMBOL(disable_irq);

void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
{
if (resume)
if (resume) {
if (!(desc->status & IRQ_SUSPENDED)) {
if (!desc->action)
return;
if (!(desc->action->flags & IRQF_FORCE_RESUME))
return;
/* Pretend that it got disabled ! */
desc->depth++;
}
desc->status &= ~IRQ_SUSPENDED;
}

switch (desc->depth) {
case 0:
Expand Down Expand Up @@ -1009,6 +1095,11 @@ void free_irq(unsigned int irq, void *dev_id)
if (!desc)
return;

#ifdef CONFIG_SMP
if (WARN_ON(desc->affinity_notify))
desc->affinity_notify = NULL;
#endif

chip_bus_lock(desc);
kfree(__free_irq(irq, dev_id));
chip_bus_sync_unlock(desc);
Expand Down
3 changes: 0 additions & 3 deletions trunk/kernel/irq/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ void resume_device_irqs(void)
for_each_irq_desc(irq, desc) {
unsigned long flags;

if (!(desc->status & IRQ_SUSPENDED))
continue;

raw_spin_lock_irqsave(&desc->lock, flags);
__enable_irq(desc, irq, true);
raw_spin_unlock_irqrestore(&desc->lock, flags);
Expand Down
5 changes: 4 additions & 1 deletion trunk/kernel/softirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,10 @@ static int run_ksoftirqd(void * __bind_cpu)
don't process */
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq();
local_irq_disable();
if (local_softirq_pending())
__do_softirq();
local_irq_enable();
preempt_enable_no_resched();
cond_resched();
preempt_disable();
Expand Down

0 comments on commit 98a86da

Please sign in to comment.