Commit 0b8f1efa authored by Yinghai Lu's avatar Yinghai Lu Committed by Ingo Molnar

sparse irq_desc[] array: core kernel and x86 changes

Impact: new feature

Problem on distro kernels: irq_desc[NR_IRQS] takes megabytes of RAM with
NR_CPUS set to large values. The goal is to be able to scale up to much
larger NR_IRQS value without impacting the (important) common case.

To solve this, we generalize irq_desc[NR_IRQS] to an (optional) array of
irq_desc pointers.

When CONFIG_SPARSE_IRQ=y is used, we use kzalloc_node to get irq_desc,
this also makes the IRQ descriptors NUMA-local (to the site that calls
request_irq()).

This gets rid of the irq_cfg[] static array on x86 as well: irq_cfg now
uses desc->chip_data for x86 to store irq_cfg.
Signed-off-by: default avatarYinghai Lu <yinghai@kernel.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 218d11a8
...@@ -238,6 +238,16 @@ config X86_HAS_BOOT_CPU_ID ...@@ -238,6 +238,16 @@ config X86_HAS_BOOT_CPU_ID
def_bool y def_bool y
depends on X86_VOYAGER depends on X86_VOYAGER
config SPARSE_IRQ
bool "Support sparse irq numbering"
depends on PCI_MSI || HT_IRQ
default y
help
This enables support for sparse irq, esp for msi/msi-x. You may need
if you have lots of cards supports msi-x installed.
If you don't know what to do here, say Y.
config X86_FIND_SMP_CONFIG config X86_FIND_SMP_CONFIG
def_bool y def_bool y
depends on X86_MPPARSE || X86_VOYAGER depends on X86_MPPARSE || X86_VOYAGER
......
...@@ -102,11 +102,20 @@ ...@@ -102,11 +102,20 @@
#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15) #define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
#if defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_VOYAGER) #if defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_VOYAGER)
#ifndef CONFIG_SPARSE_IRQ
# if NR_CPUS < MAX_IO_APICS # if NR_CPUS < MAX_IO_APICS
# define NR_IRQS (NR_VECTORS + (32 * NR_CPUS)) # define NR_IRQS (NR_VECTORS + (32 * NR_CPUS))
# else # else
# define NR_IRQS (NR_VECTORS + (32 * MAX_IO_APICS)) # define NR_IRQS (NR_VECTORS + (32 * MAX_IO_APICS))
# endif # endif
#else
# if (8 * NR_CPUS) > (32 * MAX_IO_APICS)
# define NR_IRQS (NR_VECTORS + (8 * NR_CPUS))
# else
# define NR_IRQS (NR_VECTORS + (32 * MAX_IO_APICS))
# endif
#endif
#elif defined(CONFIG_X86_VOYAGER) #elif defined(CONFIG_X86_VOYAGER)
......
This diff is collapsed.
...@@ -118,6 +118,9 @@ int show_interrupts(struct seq_file *p, void *v) ...@@ -118,6 +118,9 @@ int show_interrupts(struct seq_file *p, void *v)
} }
desc = irq_to_desc(i); desc = irq_to_desc(i);
if (!desc)
return 0;
spin_lock_irqsave(&desc->lock, flags); spin_lock_irqsave(&desc->lock, flags);
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
any_count = kstat_irqs(i); any_count = kstat_irqs(i);
......
...@@ -242,6 +242,8 @@ void fixup_irqs(cpumask_t map) ...@@ -242,6 +242,8 @@ void fixup_irqs(cpumask_t map)
for_each_irq_desc(irq, desc) { for_each_irq_desc(irq, desc) {
cpumask_t mask; cpumask_t mask;
if (!desc)
continue;
if (irq == 2) if (irq == 2)
continue; continue;
......
...@@ -94,6 +94,8 @@ void fixup_irqs(cpumask_t map) ...@@ -94,6 +94,8 @@ void fixup_irqs(cpumask_t map)
int break_affinity = 0; int break_affinity = 0;
int set_affinity = 1; int set_affinity = 1;
if (!desc)
continue;
if (irq == 2) if (irq == 2)
continue; continue;
......
...@@ -69,7 +69,6 @@ void __init init_ISA_irqs (void) ...@@ -69,7 +69,6 @@ void __init init_ISA_irqs (void)
* 16 old-style INTA-cycle interrupts: * 16 old-style INTA-cycle interrupts:
*/ */
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
/* first time call this irq_desc */
struct irq_desc *desc = irq_to_desc(i); struct irq_desc *desc = irq_to_desc(i);
desc->status = IRQ_DISABLED; desc->status = IRQ_DISABLED;
......
...@@ -143,7 +143,6 @@ void __init init_ISA_irqs(void) ...@@ -143,7 +143,6 @@ void __init init_ISA_irqs(void)
init_8259A(0); init_8259A(0);
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
/* first time call this irq_desc */
struct irq_desc *desc = irq_to_desc(i); struct irq_desc *desc = irq_to_desc(i);
desc->status = IRQ_DISABLED; desc->status = IRQ_DISABLED;
......
...@@ -558,23 +558,9 @@ struct timer_rand_state { ...@@ -558,23 +558,9 @@ struct timer_rand_state {
unsigned dont_count_entropy:1; unsigned dont_count_entropy:1;
}; };
static struct timer_rand_state *irq_timer_state[NR_IRQS]; #ifndef CONFIG_SPARSE_IRQ
struct timer_rand_state *irq_timer_state[NR_IRQS];
static struct timer_rand_state *get_timer_rand_state(unsigned int irq) #endif
{
if (irq >= nr_irqs)
return NULL;
return irq_timer_state[irq];
}
static void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state)
{
if (irq >= nr_irqs)
return;
irq_timer_state[irq] = state;
}
static struct timer_rand_state input_timer_state; static struct timer_rand_state input_timer_state;
...@@ -933,8 +919,10 @@ void rand_initialize_irq(int irq) ...@@ -933,8 +919,10 @@ void rand_initialize_irq(int irq)
{ {
struct timer_rand_state *state; struct timer_rand_state *state;
#ifndef CONFIG_SPARSE_IRQ
if (irq >= nr_irqs) if (irq >= nr_irqs)
return; return;
#endif
state = get_timer_rand_state(irq); state = get_timer_rand_state(irq);
......
...@@ -19,17 +19,75 @@ struct irq_2_iommu { ...@@ -19,17 +19,75 @@ struct irq_2_iommu {
u8 irte_mask; u8 irte_mask;
}; };
static struct irq_2_iommu irq_2_iommuX[NR_IRQS]; #ifdef CONFIG_SPARSE_IRQ
static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
{
struct irq_2_iommu *iommu;
int node;
node = cpu_to_node(cpu);
iommu = kzalloc_node(sizeof(*iommu), GFP_ATOMIC, node);
printk(KERN_DEBUG "alloc irq_2_iommu on cpu %d node %d\n", cpu, node);
return iommu;
}
static struct irq_2_iommu *irq_2_iommu(unsigned int irq) static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
{ {
return (irq < nr_irqs) ? irq_2_iommuX + irq : NULL; struct irq_desc *desc;
desc = irq_to_desc(irq);
if (WARN_ON_ONCE(!desc))
return NULL;
return desc->irq_2_iommu;
}
static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
{
struct irq_desc *desc;
struct irq_2_iommu *irq_iommu;
/*
* alloc irq desc if not allocated already.
*/
desc = irq_to_desc_alloc_cpu(irq, cpu);
if (!desc) {
printk(KERN_INFO "can not get irq_desc for %d\n", irq);
return NULL;
}
irq_iommu = desc->irq_2_iommu;
if (!irq_iommu)
desc->irq_2_iommu = get_one_free_irq_2_iommu(cpu);
return desc->irq_2_iommu;
} }
static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
{
return irq_2_iommu_alloc_cpu(irq, boot_cpu_id);
}
#else /* !CONFIG_SPARSE_IRQ */
static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
{
if (irq < nr_irqs)
return &irq_2_iommuX[irq];
return NULL;
}
static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq) static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
{ {
return irq_2_iommu(irq); return irq_2_iommu(irq);
} }
#endif
static DEFINE_SPINLOCK(irq_2_ir_lock); static DEFINE_SPINLOCK(irq_2_ir_lock);
...@@ -86,9 +144,11 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) ...@@ -86,9 +144,11 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
if (!count) if (!count)
return -1; return -1;
#ifndef CONFIG_SPARSE_IRQ
/* protect irq_2_iommu_alloc later */ /* protect irq_2_iommu_alloc later */
if (irq >= nr_irqs) if (irq >= nr_irqs)
return -1; return -1;
#endif
/* /*
* start the IRTE search from index 0. * start the IRTE search from index 0.
...@@ -130,6 +190,12 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) ...@@ -130,6 +190,12 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
table->base[i].present = 1; table->base[i].present = 1;
irq_iommu = irq_2_iommu_alloc(irq); irq_iommu = irq_2_iommu_alloc(irq);
if (!irq_iommu) {
spin_unlock(&irq_2_ir_lock);
printk(KERN_ERR "can't allocate irq_2_iommu\n");
return -1;
}
irq_iommu->iommu = iommu; irq_iommu->iommu = iommu;
irq_iommu->irte_index = index; irq_iommu->irte_index = index;
irq_iommu->sub_handle = 0; irq_iommu->sub_handle = 0;
...@@ -177,6 +243,12 @@ int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle) ...@@ -177,6 +243,12 @@ int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
irq_iommu = irq_2_iommu_alloc(irq); irq_iommu = irq_2_iommu_alloc(irq);
if (!irq_iommu) {
spin_unlock(&irq_2_ir_lock);
printk(KERN_ERR "can't allocate irq_2_iommu\n");
return -1;
}
irq_iommu->iommu = iommu; irq_iommu->iommu = iommu;
irq_iommu->irte_index = index; irq_iommu->irte_index = index;
irq_iommu->sub_handle = subhandle; irq_iommu->sub_handle = subhandle;
......
...@@ -141,8 +141,12 @@ static void init_evtchn_cpu_bindings(void) ...@@ -141,8 +141,12 @@ static void init_evtchn_cpu_bindings(void)
int i; int i;
/* By default all event channels notify CPU#0. */ /* By default all event channels notify CPU#0. */
for_each_irq_desc(i, desc) for_each_irq_desc(i, desc) {
if (!desc)
continue;
desc->affinity = cpumask_of_cpu(0); desc->affinity = cpumask_of_cpu(0);
}
#endif #endif
memset(cpu_evtchn, 0, sizeof(cpu_evtchn)); memset(cpu_evtchn, 0, sizeof(cpu_evtchn));
...@@ -231,7 +235,7 @@ static int find_unbound_irq(void) ...@@ -231,7 +235,7 @@ static int find_unbound_irq(void)
int irq; int irq;
/* Only allocate from dynirq range */ /* Only allocate from dynirq range */
for_each_irq_nr(irq) for (irq = 0; irq < nr_irqs; irq++)
if (irq_bindcount[irq] == 0) if (irq_bindcount[irq] == 0)
break; break;
...@@ -792,7 +796,7 @@ void xen_irq_resume(void) ...@@ -792,7 +796,7 @@ void xen_irq_resume(void)
mask_evtchn(evtchn); mask_evtchn(evtchn);
/* No IRQ <-> event-channel mappings. */ /* No IRQ <-> event-channel mappings. */
for_each_irq_nr(irq) for (irq = 0; irq < nr_irqs; irq++)
irq_info[irq].evtchn = 0; /* zap event-channel binding */ irq_info[irq].evtchn = 0; /* zap event-channel binding */
for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
...@@ -824,7 +828,7 @@ void __init xen_init_IRQ(void) ...@@ -824,7 +828,7 @@ void __init xen_init_IRQ(void)
mask_evtchn(i); mask_evtchn(i);
/* Dynamic IRQ space is currently unbound. Zero the refcnts. */ /* Dynamic IRQ space is currently unbound. Zero the refcnts. */
for_each_irq_nr(i) for (i = 0; i < nr_irqs; i++)
irq_bindcount[i] = 0; irq_bindcount[i] = 0;
irq_ctx_init(smp_processor_id()); irq_ctx_init(smp_processor_id());
......
...@@ -27,6 +27,7 @@ static int show_stat(struct seq_file *p, void *v) ...@@ -27,6 +27,7 @@ static int show_stat(struct seq_file *p, void *v)
u64 sum = 0; u64 sum = 0;
struct timespec boottime; struct timespec boottime;
unsigned int per_irq_sum; unsigned int per_irq_sum;
struct irq_desc *desc;
user = nice = system = idle = iowait = user = nice = system = idle = iowait =
irq = softirq = steal = cputime64_zero; irq = softirq = steal = cputime64_zero;
...@@ -44,10 +45,11 @@ static int show_stat(struct seq_file *p, void *v) ...@@ -44,10 +45,11 @@ static int show_stat(struct seq_file *p, void *v)
softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq); softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq);
steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal); steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal);
guest = cputime64_add(guest, kstat_cpu(i).cpustat.guest); guest = cputime64_add(guest, kstat_cpu(i).cpustat.guest);
for_each_irq_desc(j, desc) {
for_each_irq_nr(j) if (!desc)
continue;
sum += kstat_irqs_cpu(j, i); sum += kstat_irqs_cpu(j, i);
}
sum += arch_irq_stat_cpu(i); sum += arch_irq_stat_cpu(i);
} }
sum += arch_irq_stat(); sum += arch_irq_stat();
...@@ -90,11 +92,14 @@ static int show_stat(struct seq_file *p, void *v) ...@@ -90,11 +92,14 @@ static int show_stat(struct seq_file *p, void *v)
seq_printf(p, "intr %llu", (unsigned long long)sum); seq_printf(p, "intr %llu", (unsigned long long)sum);
/* sum again ? it could be updated? */ /* sum again ? it could be updated? */
for_each_irq_nr(j) { for (j = 0; j < NR_IRQS; j++) {
desc = irq_to_desc(j);
per_irq_sum = 0; per_irq_sum = 0;
for_each_possible_cpu(i) if (desc) {
per_irq_sum += kstat_irqs_cpu(j, i); for_each_possible_cpu(i)
per_irq_sum += kstat_irqs_cpu(j, i);
}
seq_printf(p, " %u", per_irq_sum); seq_printf(p, " %u", per_irq_sum);
} }
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/system.h> #include <asm/system.h>
extern int nr_irqs;
/* /*
* These correspond to the IORESOURCE_IRQ_* defines in * These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When * linux/ioport.h to select the interrupt line behaviour. When
......
...@@ -129,6 +129,8 @@ struct irq_chip { ...@@ -129,6 +129,8 @@ struct irq_chip {
const char *typename; const char *typename;
}; };
struct timer_rand_state;
struct irq_2_iommu;
/** /**
* struct irq_desc - interrupt descriptor * struct irq_desc - interrupt descriptor
* @irq: interrupt number for this descriptor * @irq: interrupt number for this descriptor
...@@ -154,6 +156,13 @@ struct irq_chip { ...@@ -154,6 +156,13 @@ struct irq_chip {
*/ */
struct irq_desc { struct irq_desc {
unsigned int irq; unsigned int irq;
#ifdef CONFIG_SPARSE_IRQ
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
# ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
# endif
#endif
irq_flow_handler_t handle_irq; irq_flow_handler_t handle_irq;
struct irq_chip *chip; struct irq_chip *chip;
struct msi_desc *msi_desc; struct msi_desc *msi_desc;
...@@ -181,14 +190,52 @@ struct irq_desc { ...@@ -181,14 +190,52 @@ struct irq_desc {
const char *name; const char *name;
} ____cacheline_internodealigned_in_smp; } ____cacheline_internodealigned_in_smp;
extern void early_irq_init(void);
extern void arch_early_irq_init(void);
extern void arch_init_chip_data(struct irq_desc *desc, int cpu);
extern void arch_init_copy_chip_data(struct irq_desc *old_desc,
struct irq_desc *desc, int cpu);
extern void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc);
#ifndef CONFIG_SPARSE_IRQ
extern struct irq_desc irq_desc[NR_IRQS]; extern struct irq_desc irq_desc[NR_IRQS];
static inline struct irq_desc *irq_to_desc(unsigned int irq) static inline struct irq_desc *irq_to_desc(unsigned int irq)
{ {
return (irq < nr_irqs) ? irq_desc + irq : NULL; return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
static inline struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu)
{
return irq_to_desc(irq);
} }
#ifdef CONFIG_GENERIC_HARDIRQS
# define for_each_irq_desc(irq, desc) \
for (irq = 0, desc = irq_desc; irq < nr_irqs; irq++, desc++)
# define for_each_irq_desc_reverse(irq, desc) \
for (irq = nr_irqs - 1, desc = irq_desc + (nr_irqs - 1); \
irq >= 0; irq--, desc--)
#endif
#else
extern struct irq_desc *irq_to_desc(unsigned int irq);
extern struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu);
extern struct irq_desc *move_irq_desc(struct irq_desc *old_desc, int cpu);
# define for_each_irq_desc(irq, desc) \
for (irq = 0, desc = irq_to_desc(irq); irq < nr_irqs; irq++, desc = irq_to_desc(irq))
# define for_each_irq_desc_reverse(irq, desc) \
for (irq = nr_irqs - 1, desc = irq_to_desc(irq); irq >= 0; irq--, desc = irq_to_desc(irq))
#define kstat_irqs_this_cpu(DESC) \
((DESC)->kstat_irqs[smp_processor_id()])
#define kstat_incr_irqs_this_cpu(irqno, DESC) \
((DESC)->kstat_irqs[smp_processor_id()]++)
#endif
/* /*
* Migration helpers for obsolete names, they will go away: * Migration helpers for obsolete names, they will go away:
*/ */
...@@ -380,6 +427,11 @@ extern int set_irq_msi(unsigned int irq, struct msi_desc *entry); ...@@ -380,6 +427,11 @@ extern int set_irq_msi(unsigned int irq, struct msi_desc *entry);
#define get_irq_data(irq) (irq_to_desc(irq)->handler_data) #define get_irq_data(irq) (irq_to_desc(irq)->handler_data)
#define get_irq_msi(irq) (irq_to_desc(irq)->msi_desc) #define get_irq_msi(irq) (irq_to_desc(irq)->msi_desc)
#define get_irq_desc_chip(desc) ((desc)->chip)
#define get_irq_desc_chip_data(desc) ((desc)->chip_data)
#define get_irq_desc_data(desc) ((desc)->handler_data)
#define get_irq_desc_msi(desc) ((desc)->msi_desc)
#endif /* CONFIG_GENERIC_HARDIRQS */ #endif /* CONFIG_GENERIC_HARDIRQS */
#endif /* !CONFIG_S390 */ #endif /* !CONFIG_S390 */
......
...@@ -7,18 +7,10 @@ ...@@ -7,18 +7,10 @@
# define for_each_irq_desc(irq, desc) \ # define for_each_irq_desc(irq, desc) \
for (irq = 0; irq < nr_irqs; irq++) for (irq = 0; irq < nr_irqs; irq++)
#else
extern int nr_irqs;
# define for_each_irq_desc(irq, desc) \ static inline early_sparse_irq_init(void)
for (irq = 0, desc = irq_desc; irq < nr_irqs; irq++, desc++) {
}
# define for_each_irq_desc_reverse(irq, desc) \
for (irq = nr_irqs - 1, desc = irq_desc + (nr_irqs - 1); \
irq >= 0; irq--, desc--)
#endif #endif
#define for_each_irq_nr(irq) \
for (irq = 0; irq < nr_irqs; irq++)
#endif #endif
...@@ -28,7 +28,9 @@ struct cpu_usage_stat { ...@@ -28,7 +28,9 @@ struct cpu_usage_stat {
struct kernel_stat { struct kernel_stat {
struct cpu_usage_stat cpustat; struct cpu_usage_stat cpustat;
unsigned int irqs[NR_IRQS]; #ifndef CONFIG_SPARSE_IRQ
unsigned int irqs[NR_IRQS];
#endif
}; };
DECLARE_PER_CPU(struct kernel_stat, kstat); DECLARE_PER_CPU(struct kernel_stat, kstat);
...@@ -39,6 +41,10 @@ DECLARE_PER_CPU(struct kernel_stat, kstat); ...@@ -39,6 +41,10 @@ DECLARE_PER_CPU(struct kernel_stat, kstat);
extern unsigned long long nr_context_switches(void); extern unsigned long long nr_context_switches(void);
#ifndef CONFIG_SPARSE_IRQ
#define kstat_irqs_this_cpu(irq) \
(kstat_this_cpu.irqs[irq])
struct irq_desc; struct irq_desc;
static inline void kstat_incr_irqs_this_cpu(unsigned int irq, static inline void kstat_incr_irqs_this_cpu(unsigned int irq,
...@@ -46,11 +52,17 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, ...@@ -46,11 +52,17 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq,
{ {
kstat_this_cpu.irqs[irq]++; kstat_this_cpu.irqs[irq]++;
} }
#endif
#ifndef CONFIG_SPARSE_IRQ
static inline unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) static inline unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
{ {
return kstat_cpu(cpu).irqs[irq]; return kstat_cpu(cpu).irqs[irq];
} }
#else
extern unsigned int kstat_irqs_cpu(unsigned int irq, int cpu);
#endif
/* /*
* Number of interrupts per specific IRQ source, since bootup * Number of interrupts per specific IRQ source, since bootup
......
...@@ -44,6 +44,57 @@ struct rand_pool_info { ...@@ -44,6 +44,57 @@ struct rand_pool_info {
extern void rand_initialize_irq(int irq); extern void rand_initialize_irq(int irq);
struct timer_rand_state;
#ifndef CONFIG_SPARSE_IRQ
extern struct timer_rand_state *irq_timer_state[];
extern int nr_irqs;
static inline struct timer_rand_state *get_timer_rand_state(unsigned int irq)
{
if (irq >= nr_irqs)
return NULL;
return irq_timer_state[irq];
}
static inline void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state)
{
if (irq >= nr_irqs)
return;
irq_timer_state[irq] = state;
}
#else
#include <linux/irq.h>
static inline struct timer_rand_state *get_timer_rand_state(unsigned int irq)
{
struct irq_desc *desc;
desc = irq_to_desc(irq);
if (!desc)
return NULL;
return desc->timer_rand_state;
}
static inline void set_timer_rand_state(unsigned int irq, struct timer_rand_state *state)
{
struct irq_desc *desc;
desc = irq_to_desc(irq);
if (!desc)
return;
desc->timer_rand_state = state;
}
#endif
extern void add_input_randomness(unsigned int type, unsigned int code, extern void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value); unsigned int value);
extern void add_interrupt_randomness(int irq); extern void add_interrupt_randomness(int irq);
......
...@@ -539,6 +539,15 @@ void __init __weak thread_info_cache_init(void) ...@@ -539,6 +539,15 @@ void __init __weak thread_info_cache_init(void)
{ {
} }
void __init __weak arch_early_irq_init(void)
{
}
void __init __weak early_irq_init(void)
{
arch_early_irq_init();
}
asmlinkage void __init start_kernel(void) asmlinkage void __init start_kernel(void)
{ {
char * command_line; char * command_line;
...@@ -603,6 +612,8 @@ asmlinkage void __init start_kernel(void) ...@@ -603,6 +612,8 @@ asmlinkage void __init start_kernel(void)
sort_main_extable(); sort_main_extable();
trap_init(); trap_init();
rcu_init(); rcu_init();
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ(); init_IRQ();
pidhash_init(); pidhash_init();
init_timers(); init_timers();
......
...@@ -40,6 +40,9 @@ unsigned long probe_irq_on(void) ...@@ -40,6 +40,9 @@ unsigned long probe_irq_on(void)
* flush such a longstanding irq before considering it as spurious. * flush such a longstanding irq before considering it as spurious.
*/ */
for_each_irq_desc_reverse(i, desc) { for_each_irq_desc_reverse(i, desc) {
if (!desc)
continue;
spin_lock_irq(&desc->lock); spin_lock_irq(&desc->lock);
if (!desc->action && !(desc->status & IRQ_NOPROBE)) { if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
/* /*
...@@ -68,6 +71,9 @@ unsigned long probe_irq_on(void) ...@@ -68,6 +71,9 @@ unsigned long probe_irq_on(void)
* happened in the previous stage, it may have masked itself) * happened in the previous stage, it may have masked itself)
*/ */
for_each_irq_desc_reverse(i, desc) { for_each_irq_desc_reverse(i, desc) {
if (!desc)
continue;
spin_lock_irq(&desc->lock); spin_lock_irq(&desc->lock);
if (!desc->action && !(desc->status & IRQ_NOPROBE)) { if (!desc->action && !(desc->status & IRQ_NOPROBE)) {
desc->status |= IRQ_AUTODETECT | IRQ_WAITING; desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
...@@ -86,6 +92,9 @@ unsigned long probe_irq_on(void) ...@@ -86,6 +92,9 @@ unsigned long probe_irq_on(void)
* Now filter out any obviously spurious interrupts * Now filter out any obviously spurious interrupts
*/ */
for_each_irq_desc(i, desc) { for_each_irq_desc(i, desc) {
if (!desc)
continue;
spin_lock_irq(&desc->lock); spin_lock_irq(&desc->lock);
status = desc->status; status = desc->status;
...@@ -124,6 +133,9 @@ unsigned int probe_irq_mask(unsigned long val) ...@@ -124,6 +133,9 @@ unsigned int probe_irq_mask(unsigned long val)
int i; int i;
for_each_irq_desc(i, desc) { for_each_irq_desc(i, desc) {
if (!desc)
continue;
spin_lock_irq(&desc->lock); spin_lock_irq(&desc->lock);
status = desc->status; status = desc->status;
...@@ -166,6 +178,9 @@ int probe_irq_off(unsigned long val) ...@@ -166,6 +178,9 @@ int probe_irq_off(unsigned long val)
unsigned int status; unsigned int status;
for_each_irq_desc(i, desc) { for_each_irq_desc(i, desc) {
if (!desc)
continue;
spin_lock_irq(&desc->lock); spin_lock_irq(&desc->lock);
status = desc->status; status = desc->status;
......
...@@ -24,9 +24,10 @@ ...@@ -24,9 +24,10 @@
*/ */
void dynamic_irq_init(unsigned int irq) void dynamic_irq_init(unsigned int irq)
{ {
struct irq_desc *desc = irq_to_desc(irq); struct irq_desc *desc;
unsigned long flags; unsigned long flags;
desc = irq_to_desc(irq);
if (!desc) { if (!desc) {
WARN(1, KERN_ERR "Trying to initialize invalid IRQ%d\n", irq); WARN(1, KERN_ERR "Trying to initialize invalid IRQ%d\n", irq);
return; return;
......
...@@ -15,9 +15,16 @@ ...@@ -15,9 +15,16 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/rculist.h>
#include <linux/hash.h>
#include "internals.h" #include "internals.h"
/*
* lockdep: we want to handle all irq_desc locks as a single lock-class:
*/
static struct lock_class_key irq_desc_lock_class;
/** /**
* handle_bad_irq - handle spurious and unhandled irqs * handle_bad_irq - handle spurious and unhandled irqs
* @irq: the interrupt number * @irq: the interrupt number
...@@ -49,6 +56,155 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc) ...@@ -49,6 +56,155 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc)
int nr_irqs = NR_IRQS; int nr_irqs = NR_IRQS;
EXPORT_SYMBOL_GPL(nr_irqs); EXPORT_SYMBOL_GPL(nr_irqs);
void __init __attribute__((weak)) arch_early_irq_init(void)
{
}
#ifdef CONFIG_SPARSE_IRQ
static struct irq_desc irq_desc_init = {
.irq = -1,
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
};
static void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr)
{
unsigned long bytes;
char *ptr;
int node;
/* Compute how many bytes we need per irq and allocate them */
bytes = nr * sizeof(unsigned int);
node = cpu_to_node(cpu);
ptr = kzalloc_node(bytes, GFP_ATOMIC, node);
printk(KERN_DEBUG " alloc kstat_irqs on cpu %d node %d\n", cpu, node);
if (ptr)
desc->kstat_irqs = (unsigned int *)ptr;
}
void __attribute__((weak)) arch_init_chip_data(struct irq_desc *desc, int cpu)
{
}
static void init_one_irq_desc(int irq, struct irq_desc *desc, int cpu)
{
memcpy(desc, &irq_desc_init, sizeof(struct irq_desc));
desc->irq = irq;
#ifdef CONFIG_SMP
desc->cpu = cpu;
#endif
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
init_kstat_irqs(desc, cpu, nr_cpu_ids);
if (!desc->kstat_irqs) {
printk(KERN_ERR "can not alloc kstat_irqs\n");
BUG_ON(1);
}
arch_init_chip_data(desc, cpu);
}
/*
* Protect the sparse_irqs:
*/
static DEFINE_SPINLOCK(sparse_irq_lock);
struct irq_desc *irq_desc_ptrs[NR_IRQS] __read_mostly;
static struct irq_desc irq_desc_legacy[16] __cacheline_aligned_in_smp = {
[0 ... 15] = {
.irq = -1,
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock),
#ifdef CONFIG_SMP
.affinity = CPU_MASK_ALL
#endif
}
};
/* FIXME: use bootmem alloc ...*/
static unsigned int kstat_irqs_legacy[16][NR_CPUS];
void __init early_irq_init(void)
{
struct irq_desc *desc;
int legacy_count;
int i;
desc = irq_desc_legacy;
legacy_count = ARRAY_SIZE(irq_desc_legacy);
for (i = 0; i < legacy_count; i++) {
desc[i].irq = i;
desc[i].kstat_irqs = kstat_irqs_legacy[i];
irq_desc_ptrs[i] = desc + i;
}
for (i = legacy_count; i < NR_IRQS; i++)
irq_desc_ptrs[i] = NULL;
arch_early_irq_init();
}
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc_ptrs[irq] : NULL;
}
struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu)
{
struct irq_desc *desc;
unsigned long flags;
int node;
if (irq >= NR_IRQS) {
printk(KERN_WARNING "irq >= NR_IRQS in irq_to_desc_alloc: %d %d\n",
irq, NR_IRQS);
WARN_ON(1);
return NULL;
}
desc = irq_desc_ptrs[irq];
if (desc)
return desc;
spin_lock_irqsave(&sparse_irq_lock, flags);
/* We have to check it to avoid races with another CPU */
desc = irq_desc_ptrs[irq];
if (desc)
goto out_unlock;
node = cpu_to_node(cpu);
desc = kzalloc_node(sizeof(*desc), GFP_ATOMIC, node);
printk(KERN_DEBUG " alloc irq_desc for %d on cpu %d node %d\n",
irq, cpu, node);
if (!desc) {
printk(KERN_ERR "can not alloc irq_desc\n");
BUG_ON(1);
}
init_one_irq_desc(irq, desc, cpu);
irq_desc_ptrs[irq] = desc;
out_unlock:
spin_unlock_irqrestore(&sparse_irq_lock, flags);
return desc;
}
#else
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = { [0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED, .status = IRQ_DISABLED,
...@@ -62,6 +218,8 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { ...@@ -62,6 +218,8 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
} }
}; };
#endif
/* /*
* What should we do if we get a hw irq event on an illegal vector? * What should we do if we get a hw irq event on an illegal vector?
* Each architecture has to answer this themself. * Each architecture has to answer this themself.
...@@ -261,17 +419,28 @@ out: ...@@ -261,17 +419,28 @@ out:
#ifdef CONFIG_TRACE_IRQFLAGS #ifdef CONFIG_TRACE_IRQFLAGS
/*
* lockdep: we want to handle all irq_desc locks as a single lock-class:
*/
static struct lock_class_key irq_desc_lock_class;
void early_init_irq_lock_class(void) void early_init_irq_lock_class(void)
{ {
#ifndef CONFIG_SPARSE_IRQ
struct irq_desc *desc; struct irq_desc *desc;
int i; int i;
for_each_irq_desc(i, desc) for_each_irq_desc(i, desc) {
if (!desc)
continue;
lockdep_set_class(&desc->lock, &irq_desc_lock_class); lockdep_set_class(&desc->lock, &irq_desc_lock_class);
}
#endif
}
#endif
#ifdef CONFIG_SPARSE_IRQ
unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
{
struct irq_desc *desc = irq_to_desc(irq);
return desc->kstat_irqs[cpu];
} }
#endif #endif
EXPORT_SYMBOL(kstat_irqs_cpu);
...@@ -243,7 +243,11 @@ void init_irq_proc(void) ...@@ -243,7 +243,11 @@ void init_irq_proc(void)
/* /*
* Create entries for all existing IRQs. * Create entries for all existing IRQs.
*/ */
for_each_irq_desc(irq, desc) for_each_irq_desc(irq, desc) {
if (!desc)
continue;
register_irq_proc(irq, desc); register_irq_proc(irq, desc);
}
} }
...@@ -91,6 +91,9 @@ static int misrouted_irq(int irq) ...@@ -91,6 +91,9 @@ static int misrouted_irq(int irq)
int i, ok = 0; int i, ok = 0;
for_each_irq_desc(i, desc) { for_each_irq_desc(i, desc) {
if (!desc)
continue;
if (!i) if (!i)
continue; continue;
...@@ -112,6 +115,8 @@ static void poll_spurious_irqs(unsigned long dummy) ...@@ -112,6 +115,8 @@ static void poll_spurious_irqs(unsigned long dummy)
for_each_irq_desc(i, desc) { for_each_irq_desc(i, desc) {
unsigned int status; unsigned int status;
if (!desc)
continue;
if (!i) if (!i)
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment