From: Jan Beulich Subject: x86/IRQ: conditionally preserve irq <-> pirq mapping on map error paths Mappings that had been set up before should not be torn down when handling unrelated errors. This is part of XSA-237. Reported-by: HW42 Signed-off-by: Jan Beulich Reviewed-by: George Dunlap --- a/xen/arch/x86/irq.c +++ b/xen/arch/x86/irq.c @@ -1254,7 +1254,8 @@ static int prepare_domain_irq_pirq(struc return -ENOMEM; } *pinfo = info; - return 0; + + return !!err; } static void set_domain_irq_pirq(struct domain *d, int irq, struct pirq *pirq) @@ -1297,7 +1298,10 @@ int init_domain_irq_mapping(struct domai continue; err = prepare_domain_irq_pirq(d, i, i, &info); if ( err ) + { + ASSERT(err < 0); break; + } set_domain_irq_pirq(d, i, info); } @@ -1898,6 +1902,8 @@ int get_free_pirqs(struct domain *d, uns return -ENOSPC; } +#define MAX_MSI_IRQS 32 /* limited by MSI capability struct properties */ + int map_domain_pirq( struct domain *d, int pirq, int irq, int type, void *data) { @@ -1906,6 +1912,7 @@ int map_domain_pirq( struct pirq *info; struct irq_desc *desc; unsigned long flags; + DECLARE_BITMAP(prepared, MAX_MSI_IRQS) = {}; ASSERT(spin_is_locked(&d->event_lock)); @@ -1949,8 +1956,10 @@ int map_domain_pirq( } ret = prepare_domain_irq_pirq(d, irq, pirq, &info); - if ( ret ) + if ( ret < 0 ) goto revoke; + if ( !ret ) + __set_bit(0, prepared); desc = irq_to_desc(irq); @@ -2022,8 +2031,10 @@ int map_domain_pirq( irq = create_irq(NUMA_NO_NODE); ret = irq >= 0 ? prepare_domain_irq_pirq(d, irq, pirq + nr, &info) : irq; - if ( ret ) + if ( ret < 0 ) break; + if ( !ret ) + __set_bit(nr, prepared); msi_desc[nr].irq = irq; if ( irq_permit_access(d, irq) != 0 ) @@ -2056,15 +2067,15 @@ int map_domain_pirq( desc->msi_desc = NULL; spin_unlock_irqrestore(&desc->lock, flags); } - while ( nr-- ) + while ( nr ) { if ( irq >= 0 && irq_deny_access(d, irq) ) printk(XENLOG_G_ERR "dom%d: could not revoke access to IRQ%d (pirq %d)\n", d->domain_id, irq, pirq); - if ( info ) + if ( info && test_bit(nr, prepared) ) cleanup_domain_irq_pirq(d, irq, info); - info = pirq_info(d, pirq + nr); + info = pirq_info(d, pirq + --nr); irq = info->arch.irq; } msi_desc->irq = -1; @@ -2080,12 +2091,14 @@ int map_domain_pirq( spin_lock_irqsave(&desc->lock, flags); set_domain_irq_pirq(d, irq, info); spin_unlock_irqrestore(&desc->lock, flags); + ret = 0; } done: if ( ret ) { - cleanup_domain_irq_pirq(d, irq, info); + if ( test_bit(0, prepared) ) + cleanup_domain_irq_pirq(d, irq, info); revoke: if ( irq_deny_access(d, irq) ) printk(XENLOG_G_ERR @@ -2560,7 +2573,7 @@ static int allocate_pirq(struct domain * } else if ( type == MAP_PIRQ_TYPE_MULTI_MSI ) { - if ( *nr <= 0 || *nr > 32 ) + if ( *nr <= 0 || *nr > MAX_MSI_IRQS ) return -EDOM; if ( *nr != 1 && !iommu_intremap ) return -EOPNOTSUPP;