Merge branch 'stable/irq.rework' into stable/irq.cleanup

* stable/irq.rework:
  xen/irq: Cleanup up the pirq_to_irq for DomU PV PCI passthrough guests as well.
  xen: Use IRQF_FORCE_RESUME
  xen/timer: Missing IRQF_NO_SUSPEND in timer code broke suspend.
  xen: Fix compile error introduced by "switch to new irq_chip functions"
  xen: Switch to new irq_chip functions
  xen: Remove stale irq_chip.end
  xen: events: do not free legacy IRQs
  xen: events: allocate GSIs and dynamic IRQs from separate IRQ ranges.
  xen: events: add xen_allocate_irq_{dynamic, gsi} and xen_free_irq
  xen:events: move find_unbound_irq inside CONFIG_PCI_MSI
  xen: handled remapped IRQs when enabling a pcifront PCI device.
  genirq: Add IRQF_FORCE_RESUME
This commit is contained in:
Konrad Rzeszutek Wilk 2011-03-10 14:41:43 -05:00
commit 8054c3634c
6 changed files with 163 additions and 178 deletions

View File

@ -226,21 +226,27 @@ static int xen_pcifront_enable_irq(struct pci_dev *dev)
{
int rc;
int share = 1;
u8 gsi;
dev_info(&dev->dev, "Xen PCI enabling IRQ: %d\n", dev->irq);
if (dev->irq < 0)
return -EINVAL;
if (dev->irq < NR_IRQS_LEGACY)
share = 0;
rc = xen_allocate_pirq(dev->irq, share, "pcifront");
rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &gsi);
if (rc < 0) {
dev_warn(&dev->dev, "Xen PCI IRQ: %d, failed to register:%d\n",
dev->irq, rc);
dev_warn(&dev->dev, "Xen PCI: failed to read interrupt line: %d\n",
rc);
return rc;
}
if (gsi < NR_IRQS_LEGACY)
share = 0;
rc = xen_allocate_pirq(gsi, share, "pcifront");
if (rc < 0) {
dev_warn(&dev->dev, "Xen PCI: failed to register GSI%d: %d\n",
gsi, rc);
return rc;
}
dev->irq = rc;
dev_info(&dev->dev, "Xen PCI mapped GSI%d to IRQ%d\n", gsi, dev->irq);
return 0;
}

View File

@ -397,7 +397,9 @@ void xen_setup_timer(int cpu)
name = "<timer kasprintf failed>";
irq = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, xen_timer_interrupt,
IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING|IRQF_TIMER,
IRQF_DISABLED|IRQF_PERCPU|
IRQF_NOBALANCING|IRQF_TIMER|
IRQF_FORCE_RESUME,
name, NULL);
evt = &per_cpu(xen_clock_events, cpu);

View File

@ -277,7 +277,7 @@ static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu)
BUG_ON(irq == -1);
#ifdef CONFIG_SMP
cpumask_copy(irq_to_desc(irq)->affinity, cpumask_of(cpu));
cpumask_copy(irq_to_desc(irq)->irq_data.affinity, cpumask_of(cpu));
#endif
clear_bit(chn, cpu_evtchn_mask(cpu_from_irq(irq)));
@ -294,7 +294,7 @@ static void init_evtchn_cpu_bindings(void)
/* By default all event channels notify CPU#0. */
for_each_irq_desc(i, desc) {
cpumask_copy(desc->affinity, cpumask_of(0));
cpumask_copy(desc->irq_data.affinity, cpumask_of(0));
}
#endif
@ -376,81 +376,69 @@ static void unmask_evtchn(int port)
put_cpu();
}
static int get_nr_hw_irqs(void)
static int xen_allocate_irq_dynamic(void)
{
int ret = 1;
int first = 0;
int irq;
#ifdef CONFIG_X86_IO_APIC
ret = get_nr_irqs_gsi();
/*
* For an HVM guest or domain 0 which see "real" (emulated or
* actual repectively) GSIs we allocate dynamic IRQs
* e.g. those corresponding to event channels or MSIs
* etc. from the range above those "real" GSIs to avoid
* collisions.
*/
if (xen_initial_domain() || xen_hvm_domain())
first = get_nr_irqs_gsi();
#endif
return ret;
}
retry:
irq = irq_alloc_desc_from(first, -1);
static int find_unbound_pirq(int type)
{
int rc, i;
struct physdev_get_free_pirq op_get_free_pirq;
op_get_free_pirq.type = type;
rc = HYPERVISOR_physdev_op(PHYSDEVOP_get_free_pirq, &op_get_free_pirq);
if (!rc)
return op_get_free_pirq.pirq;
for (i = 0; i < nr_irqs; i++) {
if (pirq_to_irq[i] < 0)
return i;
}
return -1;
}
static int find_unbound_irq(void)
{
struct irq_data *data;
int irq, res;
int bottom = get_nr_hw_irqs();
int top = nr_irqs-1;
if (bottom == nr_irqs)
goto no_irqs;
/* This loop starts from the top of IRQ space and goes down.
* We need this b/c if we have a PCI device in a Xen PV guest
* we do not have an IO-APIC (though the backend might have them)
* mapped in. To not have a collision of physical IRQs with the Xen
* event channels start at the top of the IRQ space for virtual IRQs.
*/
for (irq = top; irq > bottom; irq--) {
data = irq_get_irq_data(irq);
/* only 15->0 have init'd desc; handle irq > 16 */
if (!data)
break;
if (data->chip == &no_irq_chip)
break;
if (data->chip != &xen_dynamic_chip)
continue;
if (irq_info[irq].type == IRQT_UNBOUND)
return irq;
if (irq == -ENOMEM && first > NR_IRQS_LEGACY) {
printk(KERN_ERR "Out of dynamic IRQ space and eating into GSI space. You should increase nr_irqs\n");
first = max(NR_IRQS_LEGACY, first - NR_IRQS_LEGACY);
goto retry;
}
if (irq == bottom)
goto no_irqs;
res = irq_alloc_desc_at(irq, -1);
if (WARN_ON(res != irq))
return -1;
return irq;
no_irqs:
if (irq < 0)
panic("No available IRQ to bind to: increase nr_irqs!\n");
return irq;
}
static bool identity_mapped_irq(unsigned irq)
static int xen_allocate_irq_gsi(unsigned gsi)
{
/* identity map all the hardware irqs */
return irq < get_nr_hw_irqs();
int irq;
/*
* A PV guest has no concept of a GSI (since it has no ACPI
* nor access to/knowledge of the physical APICs). Therefore
* all IRQs are dynamically allocated from the entire IRQ
* space.
*/
if (xen_pv_domain() && !xen_initial_domain())
return xen_allocate_irq_dynamic();
/* Legacy IRQ descriptors are already allocated by the arch. */
if (gsi < NR_IRQS_LEGACY)
return gsi;
irq = irq_alloc_desc_at(gsi, -1);
if (irq < 0)
panic("Unable to allocate to IRQ%d (%d)\n", gsi, irq);
return irq;
}
static void xen_free_irq(unsigned irq)
{
/* Legacy IRQ descriptors are managed by the arch. */
if (irq < NR_IRQS_LEGACY)
return;
irq_free_desc(irq);
}
static void pirq_unmask_notify(int irq)
@ -486,7 +474,7 @@ static bool probing_irq(int irq)
return desc && desc->action == NULL;
}
static unsigned int startup_pirq(unsigned int irq)
static unsigned int __startup_pirq(unsigned int irq)
{
struct evtchn_bind_pirq bind_pirq;
struct irq_info *info = info_for_irq(irq);
@ -524,9 +512,15 @@ out:
return 0;
}
static void shutdown_pirq(unsigned int irq)
static unsigned int startup_pirq(struct irq_data *data)
{
return __startup_pirq(data->irq);
}
static void shutdown_pirq(struct irq_data *data)
{
struct evtchn_close close;
unsigned int irq = data->irq;
struct irq_info *info = info_for_irq(irq);
int evtchn = evtchn_from_irq(irq);
@ -546,20 +540,20 @@ static void shutdown_pirq(unsigned int irq)
info->evtchn = 0;
}
static void enable_pirq(unsigned int irq)
static void enable_pirq(struct irq_data *data)
{
startup_pirq(irq);
startup_pirq(data);
}
static void disable_pirq(unsigned int irq)
static void disable_pirq(struct irq_data *data)
{
}
static void ack_pirq(unsigned int irq)
static void ack_pirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(irq);
int evtchn = evtchn_from_irq(data->irq);
move_native_irq(irq);
move_native_irq(data->irq);
if (VALID_EVTCHN(evtchn)) {
mask_evtchn(evtchn);
@ -567,23 +561,6 @@ static void ack_pirq(unsigned int irq)
}
}
static void end_pirq(unsigned int irq)
{
int evtchn = evtchn_from_irq(irq);
struct irq_desc *desc = irq_to_desc(irq);
if (WARN_ON(!desc))
return;
if ((desc->status & (IRQ_DISABLED|IRQ_PENDING)) ==
(IRQ_DISABLED|IRQ_PENDING)) {
shutdown_pirq(irq);
} else if (VALID_EVTCHN(evtchn)) {
unmask_evtchn(evtchn);
pirq_unmask_notify(irq);
}
}
static int find_irq_by_gsi(unsigned gsi)
{
int irq;
@ -638,14 +615,7 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name)
goto out; /* XXX need refcount? */
}
/* If we are a PV guest, we don't have GSIs (no ACPI passed). Therefore
* we are using the !xen_initial_domain() to drop in the function.*/
if (identity_mapped_irq(gsi) || (!xen_initial_domain() &&
xen_pv_domain())) {
irq = gsi;
irq_alloc_desc_at(irq, -1);
} else
irq = find_unbound_irq();
irq = xen_allocate_irq_gsi(gsi);
set_irq_chip_and_handler_name(irq, &xen_pirq_chip,
handle_level_irq, name);
@ -658,7 +628,7 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name)
* this in the priv domain. */
if (xen_initial_domain() &&
HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &irq_op)) {
irq_free_desc(irq);
xen_free_irq(irq);
irq = -ENOSPC;
goto out;
}
@ -677,12 +647,29 @@ out:
#include <linux/msi.h>
#include "../pci/msi.h"
static int find_unbound_pirq(int type)
{
int rc, i;
struct physdev_get_free_pirq op_get_free_pirq;
op_get_free_pirq.type = type;
rc = HYPERVISOR_physdev_op(PHYSDEVOP_get_free_pirq, &op_get_free_pirq);
if (!rc)
return op_get_free_pirq.pirq;
for (i = 0; i < nr_irqs; i++) {
if (pirq_to_irq[i] < 0)
return i;
}
return -1;
}
void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc)
{
spin_lock(&irq_mapping_update_lock);
if (alloc & XEN_ALLOC_IRQ) {
*irq = find_unbound_irq();
*irq = xen_allocate_irq_dynamic();
if (*irq == -1)
goto out;
}
@ -732,7 +719,7 @@ int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type)
spin_lock(&irq_mapping_update_lock);
irq = find_unbound_irq();
irq = xen_allocate_irq_dynamic();
if (irq == -1)
goto out;
@ -741,7 +728,7 @@ int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type)
if (rc) {
printk(KERN_WARNING "xen map irq failed %d\n", rc);
irq_free_desc(irq);
xen_free_irq(irq);
irq = -1;
goto out;
@ -779,11 +766,12 @@ int xen_destroy_irq(int irq)
printk(KERN_WARNING "unmap irq failed %d\n", rc);
goto out;
}
pirq_to_irq[info->u.pirq.pirq] = -1;
}
pirq_to_irq[info->u.pirq.pirq] = -1;
irq_info[irq] = mk_unbound_info();
irq_free_desc(irq);
xen_free_irq(irq);
out:
spin_unlock(&irq_mapping_update_lock);
@ -814,7 +802,7 @@ int bind_evtchn_to_irq(unsigned int evtchn)
irq = evtchn_to_irq[evtchn];
if (irq == -1) {
irq = find_unbound_irq();
irq = xen_allocate_irq_dynamic();
set_irq_chip_and_handler_name(irq, &xen_dynamic_chip,
handle_fasteoi_irq, "event");
@ -839,7 +827,7 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
irq = per_cpu(ipi_to_irq, cpu)[ipi];
if (irq == -1) {
irq = find_unbound_irq();
irq = xen_allocate_irq_dynamic();
if (irq < 0)
goto out;
@ -875,7 +863,7 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
irq = per_cpu(virq_to_irq, cpu)[virq];
if (irq == -1) {
irq = find_unbound_irq();
irq = xen_allocate_irq_dynamic();
set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
handle_percpu_irq, "virq");
@ -934,7 +922,7 @@ static void unbind_from_irq(unsigned int irq)
if (irq_info[irq].type != IRQT_UNBOUND) {
irq_info[irq] = mk_unbound_info();
irq_free_desc(irq);
xen_free_irq(irq);
}
spin_unlock(&irq_mapping_update_lock);
@ -990,7 +978,7 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi,
if (irq < 0)
return irq;
irqflags |= IRQF_NO_SUSPEND;
irqflags |= IRQF_NO_SUSPEND | IRQF_FORCE_RESUME;
retval = request_irq(irq, handler, irqflags, devname, dev_id);
if (retval != 0) {
unbind_from_irq(irq);
@ -1234,11 +1222,12 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
return 0;
}
static int set_affinity_irq(unsigned irq, const struct cpumask *dest)
static int set_affinity_irq(struct irq_data *data, const struct cpumask *dest,
bool force)
{
unsigned tcpu = cpumask_first(dest);
return rebind_irq_to_cpu(irq, tcpu);
return rebind_irq_to_cpu(data->irq, tcpu);
}
int resend_irq_on_evtchn(unsigned int irq)
@ -1257,35 +1246,35 @@ int resend_irq_on_evtchn(unsigned int irq)
return 1;
}
static void enable_dynirq(unsigned int irq)
static void enable_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(irq);
int evtchn = evtchn_from_irq(data->irq);
if (VALID_EVTCHN(evtchn))
unmask_evtchn(evtchn);
}
static void disable_dynirq(unsigned int irq)
static void disable_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(irq);
int evtchn = evtchn_from_irq(data->irq);
if (VALID_EVTCHN(evtchn))
mask_evtchn(evtchn);
}
static void ack_dynirq(unsigned int irq)
static void ack_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(irq);
int evtchn = evtchn_from_irq(data->irq);
move_masked_irq(irq);
move_masked_irq(data->irq);
if (VALID_EVTCHN(evtchn))
unmask_evtchn(evtchn);
}
static int retrigger_dynirq(unsigned int irq)
static int retrigger_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(irq);
int evtchn = evtchn_from_irq(data->irq);
struct shared_info *sh = HYPERVISOR_shared_info;
int ret = 0;
@ -1334,7 +1323,7 @@ static void restore_cpu_pirqs(void)
printk(KERN_DEBUG "xen: --> irq=%d, pirq=%d\n", irq, map_irq.pirq);
startup_pirq(irq);
__startup_pirq(irq);
}
}
@ -1445,7 +1434,6 @@ void xen_poll_irq(int irq)
void xen_irq_resume(void)
{
unsigned int cpu, irq, evtchn;
struct irq_desc *desc;
init_evtchn_cpu_bindings();
@ -1465,66 +1453,48 @@ void xen_irq_resume(void)
restore_cpu_ipis(cpu);
}
/*
* Unmask any IRQF_NO_SUSPEND IRQs which are enabled. These
* are not handled by the IRQ core.
*/
for_each_irq_desc(irq, desc) {
if (!desc->action || !(desc->action->flags & IRQF_NO_SUSPEND))
continue;
if (desc->status & IRQ_DISABLED)
continue;
evtchn = evtchn_from_irq(irq);
if (evtchn == -1)
continue;
unmask_evtchn(evtchn);
}
restore_cpu_pirqs();
}
static struct irq_chip xen_dynamic_chip __read_mostly = {
.name = "xen-dyn",
.disable = disable_dynirq,
.mask = disable_dynirq,
.unmask = enable_dynirq,
.irq_disable = disable_dynirq,
.irq_mask = disable_dynirq,
.irq_unmask = enable_dynirq,
.eoi = ack_dynirq,
.set_affinity = set_affinity_irq,
.retrigger = retrigger_dynirq,
.irq_eoi = ack_dynirq,
.irq_set_affinity = set_affinity_irq,
.irq_retrigger = retrigger_dynirq,
};
static struct irq_chip xen_pirq_chip __read_mostly = {
.name = "xen-pirq",
.startup = startup_pirq,
.shutdown = shutdown_pirq,
.irq_startup = startup_pirq,
.irq_shutdown = shutdown_pirq,
.enable = enable_pirq,
.unmask = enable_pirq,
.irq_enable = enable_pirq,
.irq_unmask = enable_pirq,
.disable = disable_pirq,
.mask = disable_pirq,
.irq_disable = disable_pirq,
.irq_mask = disable_pirq,
.ack = ack_pirq,
.end = end_pirq,
.irq_ack = ack_pirq,
.set_affinity = set_affinity_irq,
.irq_set_affinity = set_affinity_irq,
.retrigger = retrigger_dynirq,
.irq_retrigger = retrigger_dynirq,
};
static struct irq_chip xen_percpu_chip __read_mostly = {
.name = "xen-percpu",
.disable = disable_dynirq,
.mask = disable_dynirq,
.unmask = enable_dynirq,
.irq_disable = disable_dynirq,
.irq_mask = disable_dynirq,
.irq_unmask = enable_dynirq,
.ack = ack_dynirq,
.irq_ack = ack_dynirq,
};
int xen_set_callback_via(uint64_t via)

View File

@ -55,7 +55,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
@ -67,6 +67,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)

View File

@ -282,8 +282,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:

View File

@ -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);