mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
x86/physdev: factor out the code to allocate and map a pirq
Move the code to allocate and map a domain pirq (either GSI or MSI) into the x86 irq code base, so that it can be used outside of the physdev ops. This change shouldn't affect the functionality of the already existing physdev ops. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
8fd8951eab
commit
0762e2502f
3 files changed
+173
-117
No files matched your search
@@ -2537,3 +2537,166 @@ bool_t hvm_domain_use_pirq(const struct domain *d, const struct pirq *pirq)
|
||||
return is_hvm_domain(d) && pirq &&
|
||||
pirq->arch.hvm.emuirq != IRQ_UNBOUND;
|
||||
}
|
||||
|
||||
static int allocate_pirq(struct domain *d, int index, int pirq, int irq,
|
||||
int type, int *nr)
|
||||
{
|
||||
int current_pirq;
|
||||
|
||||
ASSERT(spin_is_locked(&d->event_lock));
|
||||
current_pirq = domain_irq_to_pirq(d, irq);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
if ( current_pirq )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: %d:%d already mapped to %d\n",
|
||||
d->domain_id, index, pirq, current_pirq);
|
||||
if ( current_pirq < 0 )
|
||||
return -EBUSY;
|
||||
}
|
||||
else if ( type == MAP_PIRQ_TYPE_MULTI_MSI )
|
||||
{
|
||||
if ( *nr <= 0 || *nr > 32 )
|
||||
return -EDOM;
|
||||
if ( *nr != 1 && !iommu_intremap )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
while ( *nr & (*nr - 1) )
|
||||
*nr += *nr & -*nr;
|
||||
pirq = get_free_pirqs(d, *nr);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
while ( (*nr >>= 1) > 1 )
|
||||
if ( get_free_pirqs(d, *nr) > 0 )
|
||||
break;
|
||||
dprintk(XENLOG_G_ERR, "dom%d: no block of %d free pirqs\n",
|
||||
d->domain_id, *nr << 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pirq = get_free_pirq(d, type);
|
||||
if ( pirq < 0 )
|
||||
dprintk(XENLOG_G_ERR, "dom%d: no free pirq\n", d->domain_id);
|
||||
}
|
||||
}
|
||||
else if ( current_pirq && pirq != current_pirq )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: irq %d already mapped to pirq %d\n",
|
||||
d->domain_id, irq, current_pirq);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
return pirq;
|
||||
}
|
||||
|
||||
int allocate_and_map_gsi_pirq(struct domain *d, int index, int *pirq_p)
|
||||
{
|
||||
int irq, pirq, ret;
|
||||
|
||||
if ( index < 0 || index >= nr_irqs_gsi )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: map invalid irq %d\n", d->domain_id,
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = domain_pirq_to_irq(current->domain, index);
|
||||
if ( irq <= 0 )
|
||||
{
|
||||
if ( is_hardware_domain(current->domain) )
|
||||
irq = index;
|
||||
else
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: map pirq with incorrect irq!\n",
|
||||
d->domain_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify or get pirq. */
|
||||
spin_lock(&d->event_lock);
|
||||
pirq = allocate_pirq(d, index, *pirq_p, irq, MAP_PIRQ_TYPE_GSI, NULL);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
ret = pirq;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = map_domain_pirq(d, pirq, irq, MAP_PIRQ_TYPE_GSI, NULL);
|
||||
if ( !ret )
|
||||
*pirq_p = pirq;
|
||||
|
||||
done:
|
||||
spin_unlock(&d->event_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p,
|
||||
int type, struct msi_info *msi)
|
||||
{
|
||||
int irq, pirq, ret;
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case MAP_PIRQ_TYPE_MSI:
|
||||
if ( !msi->table_base )
|
||||
msi->entry_nr = 1;
|
||||
irq = index;
|
||||
if ( irq == -1 )
|
||||
{
|
||||
case MAP_PIRQ_TYPE_MULTI_MSI:
|
||||
irq = create_irq(NUMA_NO_NODE);
|
||||
}
|
||||
|
||||
if ( irq < nr_irqs_gsi || irq >= nr_irqs )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
|
||||
d->domain_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msi->irq = irq;
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintk(XENLOG_G_ERR, "dom%d: wrong pirq type %x\n",
|
||||
d->domain_id, type);
|
||||
ASSERT_UNREACHABLE();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msi->irq = irq;
|
||||
|
||||
pcidevs_lock();
|
||||
/* Verify or get pirq. */
|
||||
spin_lock(&d->event_lock);
|
||||
pirq = allocate_pirq(d, index, *pirq_p, irq, type, &msi->entry_nr);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
ret = pirq;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = map_domain_pirq(d, pirq, irq, type, msi);
|
||||
if ( !ret )
|
||||
*pirq_p = pirq;
|
||||
|
||||
done:
|
||||
spin_unlock(&d->event_lock);
|
||||
pcidevs_unlock();
|
||||
if ( ret )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case MAP_PIRQ_TYPE_MSI:
|
||||
if ( index == -1 )
|
||||
case MAP_PIRQ_TYPE_MULTI_MSI:
|
||||
destroy_irq(irq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
+5
-117
@@ -92,8 +92,7 @@ int physdev_map_pirq(domid_t domid, int type, int *index, int *pirq_p,
|
||||
struct msi_info *msi)
|
||||
{
|
||||
struct domain *d = current->domain;
|
||||
int pirq, irq, ret = 0;
|
||||
void *map_data = NULL;
|
||||
int ret;
|
||||
|
||||
if ( domid == DOMID_SELF && is_hvm_domain(d) && has_pirq(d) )
|
||||
{
|
||||
@@ -119,135 +118,24 @@ int physdev_map_pirq(domid_t domid, int type, int *index, int *pirq_p,
|
||||
switch ( type )
|
||||
{
|
||||
case MAP_PIRQ_TYPE_GSI:
|
||||
if ( *index < 0 || *index >= nr_irqs_gsi )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: map invalid irq %d\n",
|
||||
d->domain_id, *index);
|
||||
ret = -EINVAL;
|
||||
goto free_domain;
|
||||
}
|
||||
|
||||
irq = domain_pirq_to_irq(current->domain, *index);
|
||||
if ( irq <= 0 )
|
||||
{
|
||||
if ( is_hardware_domain(current->domain) )
|
||||
irq = *index;
|
||||
else {
|
||||
dprintk(XENLOG_G_ERR, "dom%d: map pirq with incorrect irq!\n",
|
||||
d->domain_id);
|
||||
ret = -EINVAL;
|
||||
goto free_domain;
|
||||
}
|
||||
}
|
||||
ret = allocate_and_map_gsi_pirq(d, *index, pirq_p);
|
||||
break;
|
||||
|
||||
case MAP_PIRQ_TYPE_MSI:
|
||||
if ( !msi->table_base )
|
||||
msi->entry_nr = 1;
|
||||
irq = *index;
|
||||
if ( irq == -1 )
|
||||
/* fallthrough */
|
||||
case MAP_PIRQ_TYPE_MULTI_MSI:
|
||||
irq = create_irq(NUMA_NO_NODE);
|
||||
|
||||
if ( irq < nr_irqs_gsi || irq >= nr_irqs )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
|
||||
d->domain_id);
|
||||
ret = -EINVAL;
|
||||
goto free_domain;
|
||||
}
|
||||
|
||||
msi->irq = irq;
|
||||
map_data = msi;
|
||||
ret = allocate_and_map_msi_pirq(d, *index, pirq_p, type, msi);
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintk(XENLOG_G_ERR, "dom%d: wrong map_pirq type %x\n",
|
||||
d->domain_id, type);
|
||||
ret = -EINVAL;
|
||||
goto free_domain;
|
||||
break;
|
||||
}
|
||||
|
||||
pcidevs_lock();
|
||||
/* Verify or get pirq. */
|
||||
spin_lock(&d->event_lock);
|
||||
pirq = domain_irq_to_pirq(d, irq);
|
||||
if ( *pirq_p < 0 )
|
||||
{
|
||||
if ( pirq )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: %d:%d already mapped to %d\n",
|
||||
d->domain_id, *index, *pirq_p, pirq);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if ( type == MAP_PIRQ_TYPE_MULTI_MSI )
|
||||
{
|
||||
if ( msi->entry_nr <= 0 || msi->entry_nr > 32 )
|
||||
ret = -EDOM;
|
||||
else if ( msi->entry_nr != 1 && !iommu_intremap )
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
{
|
||||
while ( msi->entry_nr & (msi->entry_nr - 1) )
|
||||
msi->entry_nr += msi->entry_nr & -msi->entry_nr;
|
||||
pirq = get_free_pirqs(d, msi->entry_nr);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
while ( (msi->entry_nr >>= 1) > 1 )
|
||||
if ( get_free_pirqs(d, msi->entry_nr) > 0 )
|
||||
break;
|
||||
dprintk(XENLOG_G_ERR, "dom%d: no block of %d free pirqs\n",
|
||||
d->domain_id, msi->entry_nr << 1);
|
||||
ret = pirq;
|
||||
}
|
||||
}
|
||||
if ( ret < 0 )
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
pirq = get_free_pirq(d, type);
|
||||
if ( pirq < 0 )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: no free pirq\n", d->domain_id);
|
||||
ret = pirq;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pirq && pirq != *pirq_p )
|
||||
{
|
||||
dprintk(XENLOG_G_ERR, "dom%d: pirq %d conflicts with irq %d\n",
|
||||
d->domain_id, *index, *pirq_p);
|
||||
ret = -EEXIST;
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
pirq = *pirq_p;
|
||||
}
|
||||
|
||||
ret = map_domain_pirq(d, pirq, irq, type, map_data);
|
||||
if ( ret == 0 )
|
||||
*pirq_p = pirq;
|
||||
|
||||
done:
|
||||
spin_unlock(&d->event_lock);
|
||||
pcidevs_unlock();
|
||||
if ( ret != 0 )
|
||||
switch ( type )
|
||||
{
|
||||
case MAP_PIRQ_TYPE_MSI:
|
||||
if ( *index == -1 )
|
||||
case MAP_PIRQ_TYPE_MULTI_MSI:
|
||||
destroy_irq(irq);
|
||||
break;
|
||||
}
|
||||
free_domain:
|
||||
rcu_unlock_domain(d);
|
||||
return ret;
|
||||
|
||||
@@ -200,4 +200,9 @@ bool_t cpu_has_pending_apic_eoi(void);
|
||||
|
||||
static inline void arch_move_irqs(struct vcpu *v) { }
|
||||
|
||||
struct msi_info;
|
||||
int allocate_and_map_gsi_pirq(struct domain *d, int index, int *pirq_p);
|
||||
int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p,
|
||||
int type, struct msi_info *msi);
|
||||
|
||||
#endif /* _ASM_HW_IRQ_H */
|
||||