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;
+}
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) )
{
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;
- }
-
- 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;
+ break;
}
- 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;