xen: events: allocate GSIs and dynamic IRQs from separate IRQ ranges.

There are three cases which we need to care about, PV guest, PV domain
0 and HVM guest.

The PV guest case is simple since it has no access to ACPI or real
APICs and therefore has no GSIs therefore we simply dynamically
allocate all IRQs. The potentially interesting case here is PIRQ type
event channels associated with passed through PCI devices. However
even in this case the guest has no direct interaction with the
physical GSI since that happens in the PCI backend.

The PV domain 0 and HVM guest cases are actually the same. In domain 0
case the kernel sees the host ACPI and GSIs (although it only sees the
APIC indirectly via the hypervisor) and in the HVM guest case it sees
the virtualised ACPI and emulated APICs. In these cases we start
allocating dynamic IRQs at nr_irqs_gsi so that they cannot clash with
any GSI.

Currently xen_allocate_irq_dynamic starts at nr_irqs and works
backwards looking for a free IRQ in order to (try and) avoid clashing
with GSIs used in domain 0 and in HVM guests. This change avoids that
although we retain the behaviour of allowing dynamic IRQs to encroach
on the GSI range if no suitable IRQs are available since a future IRQ
clash is deemed preferable to failure right now.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
This commit is contained in:
Ian Campbell 2011-03-03 11:57:44 -05:00 committed by Konrad Rzeszutek Wilk
parent c9df1ce585
commit 89911501f3

View file

@ -376,72 +376,49 @@ static void unmask_evtchn(int port)
put_cpu(); put_cpu();
} }
static int get_nr_hw_irqs(void)
{
int ret = 1;
#ifdef CONFIG_X86_IO_APIC
ret = get_nr_irqs_gsi();
#endif
return ret;
}
static int xen_allocate_irq_dynamic(void) static int xen_allocate_irq_dynamic(void)
{ {
struct irq_data *data; int first = 0;
int irq, res; int irq;
int bottom = get_nr_hw_irqs();
int top = nr_irqs-1;
if (bottom == nr_irqs) #ifdef CONFIG_X86_IO_APIC
goto no_irqs; /*
* For an HVM guest or domain 0 which see "real" (emulated or
/* This loop starts from the top of IRQ space and goes down. * actual repectively) GSIs we allocate dynamic IRQs
* We need this b/c if we have a PCI device in a Xen PV guest * e.g. those corresponding to event channels or MSIs
* we do not have an IO-APIC (though the backend might have them) * etc. from the range above those "real" GSIs to avoid
* mapped in. To not have a collision of physical IRQs with the Xen * collisions.
* event channels start at the top of the IRQ space for virtual IRQs.
*/ */
for (irq = top; irq > bottom; irq--) { if (xen_initial_domain() || xen_hvm_domain())
data = irq_get_irq_data(irq); first = get_nr_irqs_gsi();
/* only 15->0 have init'd desc; handle irq > 16 */ #endif
if (!data)
break; retry:
if (data->chip == &no_irq_chip) irq = irq_alloc_desc_from(first, -1);
break;
if (data->chip != &xen_dynamic_chip) if (irq == -ENOMEM && first > NR_IRQS_LEGACY) {
continue; printk(KERN_ERR "Out of dynamic IRQ space and eating into GSI space. You should increase nr_irqs\n");
if (irq_info[irq].type == IRQT_UNBOUND) first = max(NR_IRQS_LEGACY, first - NR_IRQS_LEGACY);
return irq; goto retry;
} }
if (irq == bottom) if (irq < 0)
goto no_irqs; panic("No available IRQ to bind to: increase nr_irqs!\n");
res = irq_alloc_desc_at(irq, -1);
if (WARN_ON(res != irq))
return -1;
return irq; return irq;
no_irqs:
panic("No available IRQ to bind to: increase nr_irqs!\n");
}
static bool identity_mapped_irq(unsigned irq)
{
/* identity map all the hardware irqs */
return irq < get_nr_hw_irqs();
} }
static int xen_allocate_irq_gsi(unsigned gsi) static int xen_allocate_irq_gsi(unsigned gsi)
{ {
int irq; int irq;
if (!identity_mapped_irq(gsi) && /*
(xen_initial_domain() || !xen_pv_domain())) * 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(); return xen_allocate_irq_dynamic();
/* Legacy IRQ descriptors are already allocated by the arch. */ /* Legacy IRQ descriptors are already allocated by the arch. */