Skip to content

Commit

Permalink
x86/irq: Use hierarchical irqdomain to manage CPU interrupt vectors
Browse files Browse the repository at this point in the history
Abstract CPU local APIC as an interrupt controller and create an
irqdomain for it to manage CPU interrupt vectors. It's the base to
enable hierarchical irqdomains on x86 systems. 

The final irqdomain hierarchy will look like this:

IOAPIC domain    ----|
MSI/MSI-x domain ----> [Interrupt Remapping domain] -> CPU vector domain
HPET_IRQ domain  ----|                                         ^
                                                               |
DMAR domain      ----------------------------------------------|
HT_IRQ domain    ----------------------------------------------|

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: Prarit Bhargava <prarit@redhat.com>
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>
Cc: Grant Likely <grant.likely@linaro.org>
Link: http://lkml.kernel.org/r/1428905519-23704-3-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
  • Loading branch information
Jiang Liu authored and KAGA-KOKO committed Apr 24, 2015
1 parent 5f0052f commit b5dc8e6
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 18 deletions.
3 changes: 1 addition & 2 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ config X86_INTEL_CE
select X86_REBOOTFIXUPS
select OF
select OF_EARLY_FLATTREE
select IRQ_DOMAIN
---help---
Select for the Intel CE media processor (CE4100) SOC.
This option compiles in support for the CE4100 SOC for settop
Expand Down Expand Up @@ -914,11 +913,11 @@ config X86_LOCAL_APIC
def_bool y
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
select IRQ_DOMAIN_HIERARCHY

config X86_IO_APIC
def_bool y
depends on X86_LOCAL_APIC || X86_UP_IOAPIC
select IRQ_DOMAIN

config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
bool "Reroute for broken boot IRQs"
Expand Down
17 changes: 17 additions & 0 deletions arch/x86/include/asm/hw_irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ struct irq_2_irte {

#ifdef CONFIG_X86_LOCAL_APIC
struct irq_data;
struct irq_domain;

struct irq_alloc_info {
u32 flags;
const struct cpumask *mask; /* CPU mask for vector allocation */
};

enum {
/* Allocate contiguous CPU vectors */
X86_IRQ_ALLOC_CONTIGUOUS_VECTORS = 0x1,
};

struct irq_cfg {
cpumask_var_t domain;
Expand All @@ -135,6 +146,12 @@ struct irq_cfg {
};
};

extern struct irq_domain *x86_vector_domain;

extern void init_irq_alloc_info(struct irq_alloc_info *info,
const struct cpumask *mask);
extern void copy_irq_alloc_info(struct irq_alloc_info *dst,
struct irq_alloc_info *src);
extern struct irq_cfg *irq_cfg(unsigned int irq);
extern struct irq_cfg *irqd_cfg(struct irq_data *irq_data);
extern struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node);
Expand Down
3 changes: 0 additions & 3 deletions arch/x86/kernel/apic/io_apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2356,9 +2356,6 @@ static int mp_irqdomain_create(int ioapic)
ioapic_dynirq_base = max(ioapic_dynirq_base,
gsi_cfg->gsi_end + 1);

if (gsi_cfg->gsi_base == 0)
irq_set_default_host(ip->irqdomain);

return 0;
}

Expand Down
155 changes: 142 additions & 13 deletions arch/x86/kernel/apic/vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
* Moved from arch/x86/kernel/apic/io_apic.c.
* Jiang Liu <jiang.liu@linux.intel.com>
* Enable support of hierarchical irqdomains
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
Expand All @@ -19,7 +21,9 @@
#include <asm/desc.h>
#include <asm/irq_remapping.h>

struct irq_domain *x86_vector_domain;
static DEFINE_RAW_SPINLOCK(vector_lock);
static struct irq_chip lapic_controller;

void lock_vector_lock(void)
{
Expand All @@ -36,15 +40,21 @@ void unlock_vector_lock(void)

struct irq_cfg *irq_cfg(unsigned int irq)
{
return irq_get_chip_data(irq);
return irqd_cfg(irq_get_irq_data(irq));
}

struct irq_cfg *irqd_cfg(struct irq_data *irq_data)
{
if (!irq_data)
return NULL;

while (irq_data->parent_data)
irq_data = irq_data->parent_data;

return irq_data->chip_data;
}

static struct irq_cfg *alloc_irq_cfg(unsigned int irq, int node)
static struct irq_cfg *alloc_irq_cfg(int node)
{
struct irq_cfg *cfg;

Expand Down Expand Up @@ -79,22 +89,21 @@ struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node)
return cfg;
}

cfg = alloc_irq_cfg(at, node);
cfg = alloc_irq_cfg(node);
if (cfg)
irq_set_chip_data(at, cfg);
else
irq_free_desc(at);
return cfg;
}

static void free_irq_cfg(unsigned int at, struct irq_cfg *cfg)
static void free_irq_cfg(struct irq_cfg *cfg)
{
if (!cfg)
return;
irq_set_chip_data(at, NULL);
free_cpumask_var(cfg->domain);
free_cpumask_var(cfg->old_domain);
kfree(cfg);
if (cfg) {
free_cpumask_var(cfg->domain);
free_cpumask_var(cfg->old_domain);
kfree(cfg);
}
}

static int
Expand Down Expand Up @@ -241,6 +250,90 @@ void clear_irq_vector(int irq, struct irq_cfg *cfg)
raw_spin_unlock_irqrestore(&vector_lock, flags);
}

void init_irq_alloc_info(struct irq_alloc_info *info,
const struct cpumask *mask)
{
memset(info, 0, sizeof(*info));
info->mask = mask;
}

void copy_irq_alloc_info(struct irq_alloc_info *dst, struct irq_alloc_info *src)
{
if (src)
*dst = *src;
else
memset(dst, 0, sizeof(*dst));
}

static inline const struct cpumask *
irq_alloc_info_get_mask(struct irq_alloc_info *info)
{
return (!info || !info->mask) ? apic->target_cpus() : info->mask;
}

static void x86_vector_free_irqs(struct irq_domain *domain,
unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *irq_data;
int i;

for (i = 0; i < nr_irqs; i++) {
irq_data = irq_domain_get_irq_data(x86_vector_domain, virq + i);
if (irq_data && irq_data->chip_data) {
free_remapped_irq(virq);
clear_irq_vector(virq + i, irq_data->chip_data);
free_irq_cfg(irq_data->chip_data);
irq_domain_reset_irq_data(irq_data);
}
}
}

static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct irq_alloc_info *info = arg;
const struct cpumask *mask;
struct irq_data *irq_data;
struct irq_cfg *cfg;
int i, err;

if (disable_apic)
return -ENXIO;

/* Currently vector allocator can't guarantee contiguous allocations */
if ((info->flags & X86_IRQ_ALLOC_CONTIGUOUS_VECTORS) && nr_irqs > 1)
return -ENOSYS;

mask = irq_alloc_info_get_mask(info);
for (i = 0; i < nr_irqs; i++) {
irq_data = irq_domain_get_irq_data(domain, virq + i);
BUG_ON(!irq_data);
cfg = alloc_irq_cfg(irq_data->node);
if (!cfg) {
err = -ENOMEM;
goto error;
}

irq_data->chip = &lapic_controller;
irq_data->chip_data = cfg;
irq_data->hwirq = virq + i;
err = assign_irq_vector(virq, cfg, mask);
if (err)
goto error;
}

return 0;

error:
x86_vector_free_irqs(domain, virq, i + 1);
return err;
}

static struct irq_domain_ops x86_vector_domain_ops = {
.alloc = x86_vector_alloc_irqs,
.free = x86_vector_free_irqs,
};

int __init arch_probe_nr_irqs(void)
{
int nr;
Expand All @@ -266,6 +359,11 @@ int __init arch_probe_nr_irqs(void)

int __init arch_early_irq_init(void)
{
x86_vector_domain = irq_domain_add_tree(NULL, &x86_vector_domain_ops,
NULL);
BUG_ON(x86_vector_domain == NULL);
irq_set_default_host(x86_vector_domain);

return arch_early_ioapic_init();
}

Expand Down Expand Up @@ -380,6 +478,36 @@ int apic_set_affinity(struct irq_data *data, const struct cpumask *mask,
return 0;
}

static int vector_set_affinity(struct irq_data *irq_data,
const struct cpumask *dest, bool force)
{
struct irq_cfg *cfg = irq_data->chip_data;
int err, irq = irq_data->irq;

if (!config_enabled(CONFIG_SMP))
return -EPERM;

if (!cpumask_intersects(dest, cpu_online_mask))
return -EINVAL;

err = assign_irq_vector(irq, cfg, dest);
if (err) {
struct irq_data *top = irq_get_irq_data(irq);

if (assign_irq_vector(irq, cfg, top->affinity))
pr_err("Failed to recover vector for irq %d\n", irq);
return err;
}

return IRQ_SET_MASK_OK;
}

static struct irq_chip lapic_controller = {
.irq_ack = apic_ack_edge,
.irq_set_affinity = vector_set_affinity,
.irq_retrigger = apic_retrigger_irq,
};

#ifdef CONFIG_SMP
void send_cleanup_vector(struct irq_cfg *cfg)
{
Expand Down Expand Up @@ -497,7 +625,7 @@ int arch_setup_hwirq(unsigned int irq, int node)
unsigned long flags;
int ret;

cfg = alloc_irq_cfg(irq, node);
cfg = alloc_irq_cfg(node);
if (!cfg)
return -ENOMEM;

Expand All @@ -508,7 +636,7 @@ int arch_setup_hwirq(unsigned int irq, int node)
if (!ret)
irq_set_chip_data(irq, cfg);
else
free_irq_cfg(irq, cfg);
free_irq_cfg(cfg);
return ret;
}

Expand All @@ -518,7 +646,8 @@ void arch_teardown_hwirq(unsigned int irq)

free_remapped_irq(irq);
clear_irq_vector(irq, cfg);
free_irq_cfg(irq, cfg);
irq_set_chip_data(irq, NULL);
free_irq_cfg(cfg);
}

static void __init print_APIC_field(int base)
Expand Down

0 comments on commit b5dc8e6

Please sign in to comment.