[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [RFC PATCH 09/10] ARM: vGIC: introduce vgic_get/put_pending_irq
Hi Andre, On 04/05/17 16:31, Andre Przywara wrote: So far there is always a statically allocated struct pending_irq for each interrupt that we deal with. To prepare for dynamically allocated LPIs, introduce a put/get wrapper to get hold of a pending_irq pointer. So far get() just returns the same pointer and put() is empty, but this change allows to introduce ref-counting very easily, to prevent use-after-free usage of struct pending_irq's once LPIs get unmapped from a domain. For convenience reasons we introduce a put_unlock() version, which also drops the pending_irq lock before calling the actual put() function. Please explain where get/put should be used in both the commit message and the code. Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx> --- xen/arch/arm/gic.c | 24 +++++++++++++++------ xen/arch/arm/vgic-v2.c | 4 ++-- xen/arch/arm/vgic-v3.c | 4 ++-- xen/arch/arm/vgic.c | 52 +++++++++++++++++++++++++++++++-------------- xen/include/asm-arm/event.h | 20 +++++++++++------ xen/include/asm-arm/vgic.h | 7 +++++- 6 files changed, 77 insertions(+), 34 deletions(-) diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 737da6b..7147b6c 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -136,9 +136,7 @@ void gic_route_irq_to_xen(struct irq_desc *desc, unsigned int priority) int gic_route_irq_to_guest(struct domain *d, unsigned int virq, struct irq_desc *desc, unsigned int priority) { - /* Use vcpu0 to retrieve the pending_irq struct. Given that we only - * route SPIs to guests, it doesn't make any difference. */ - struct pending_irq *p = irq_to_pending(d->vcpu[0], virq); + struct pending_irq *p = vgic_get_pending_irq(d, NULL, virq); This vgic_get_pending_irq would benefits of an explanation where is the associated put. ASSERT(spin_is_locked(&desc->lock)); /* Caller has already checked that the IRQ is an SPI */ @@ -148,7 +146,10 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq, if ( p->desc || /* The VIRQ should not be already enabled by the guest */ test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) ) + { + vgic_put_pending_irq(d, p); return -EBUSY; + } desc->handler = gic_hw_ops->gic_guest_irq_type; set_bit(_IRQ_GUEST, &desc->status); @@ -159,6 +160,7 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq, p->desc = desc; + vgic_put_pending_irq(d, p); Newline. return 0; } @@ -166,7 +168,7 @@ int gic_route_irq_to_guest(struct domain *d, unsigned int virq, int gic_remove_irq_from_guest(struct domain *d, unsigned int virq, struct irq_desc *desc) { - struct pending_irq *p = irq_to_pending(d->vcpu[0], virq); + struct pending_irq *p = vgic_get_pending_irq(d, NULL, virq); ASSERT(spin_is_locked(&desc->lock)); ASSERT(test_bit(_IRQ_GUEST, &desc->status)); @@ -189,7 +191,10 @@ int gic_remove_irq_from_guest(struct domain *d, unsigned int virq, */ if ( test_bit(_IRQ_INPROGRESS, &desc->status) || !test_bit(_IRQ_DISABLED, &desc->status) ) + { + vgic_put_pending_irq(d, p); return -EBUSY; + } } clear_bit(_IRQ_GUEST, &desc->status); @@ -197,6 +202,8 @@ int gic_remove_irq_from_guest(struct domain *d, unsigned int virq, p->desc = NULL; + vgic_put_pending_irq(d, p); + return 0; } @@ -383,13 +390,14 @@ static inline void gic_add_to_lr_pending(struct vcpu *v, struct pending_irq *n) void gic_remove_from_queues(struct vcpu *v, unsigned int virtual_irq) { - struct pending_irq *p = irq_to_pending(v, virtual_irq); + struct pending_irq *p = vgic_get_pending_irq(v->domain, v, virtual_irq); unsigned long flags; spin_lock_irqsave(&v->arch.vgic.lock, flags); if ( !list_empty(&p->lr_queue) ) list_del_init(&p->lr_queue); spin_unlock_irqrestore(&v->arch.vgic.lock, flags); + vgic_put_pending_irq(v->domain, p); } void gic_raise_inflight_irq(struct vcpu *v, struct pending_irq *n) @@ -406,6 +414,7 @@ void gic_raise_inflight_irq(struct vcpu *v, struct pending_irq *n) gdprintk(XENLOG_DEBUG, "trying to inject irq=%u into d%dv%d, when it is still lr_pending\n", n->irq, v->domain->domain_id, v->vcpu_id); #endif + vgic_put_pending_irq(v->domain, n); Why this one? } void gic_raise_guest_irq(struct vcpu *v, struct pending_irq *p) @@ -440,8 +449,9 @@ static void gic_update_one_lr(struct vcpu *v, int i) gic_hw_ops->read_lr(i, &lr_val); irq = lr_val.virq; - p = irq_to_pending(v, irq); + p = vgic_get_pending_irq(v->domain, v, irq); spin_lock(&p->lock); It sounds like to me that you want to introduce a vgic_get_pending_irq_lock(...). + Spurious change. if ( lr_val.state & GICH_LR_ACTIVE ) { set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); @@ -499,7 +509,7 @@ static void gic_update_one_lr(struct vcpu *v, int i) } } } - spin_unlock(&p->lock); + vgic_put_pending_irq_unlock(v->domain, p); } void gic_clear_lrs(struct vcpu *v) diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c index bf755ae..36ed04f 100644 --- a/xen/arch/arm/vgic-v2.c +++ b/xen/arch/arm/vgic-v2.c @@ -75,11 +75,11 @@ static uint32_t vgic_fetch_itargetsr(struct vcpu *v, unsigned int offset) for ( i = 0; i < NR_TARGETS_PER_ITARGETSR; i++, offset++ ) { - struct pending_irq *p = irq_to_pending(v, offset); + struct pending_irq *p = vgic_get_pending_irq(v->domain, v, offset); spin_lock(&p->lock); reg |= (1 << p->vcpu_id) << (i * NR_BITS_PER_TARGET); - spin_unlock(&p->lock); + vgic_put_pending_irq_unlock(v->domain, p); } return reg; diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c index 15a512a..fff518e 100644 --- a/xen/arch/arm/vgic-v3.c +++ b/xen/arch/arm/vgic-v3.c @@ -105,10 +105,10 @@ static uint64_t vgic_fetch_irouter(struct vcpu *v, unsigned int offset) /* There is exactly 1 vIRQ per IROUTER */ offset /= NR_BYTES_PER_IROUTER; - p = irq_to_pending(v, offset); + p = vgic_get_pending_irq(v->domain, v, offset); spin_lock(&p->lock); aff = vcpuid_to_vaffinity(p->vcpu_id); - spin_unlock(&p->lock); + vgic_put_pending_irq_unlock(v->domain, p); return aff; } diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index 530ac55..c7d645e 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -245,17 +245,16 @@ static void set_config(struct pending_irq *p, unsigned int config) set_bit(GIC_IRQ_GUEST_EDGE, &p->status); } - This newline should not have been introduced at first hand. #define DEFINE_GATHER_IRQ_INFO(name, get_val, shift) \ uint32_t gather_irq_info_##name(struct vcpu *v, unsigned int irq) \ { \ uint32_t ret = 0, i; \ for ( i = 0; i < (32 / shift); i++ ) \ { \ - struct pending_irq *p = irq_to_pending(v, irq + i); \ + struct pending_irq *p = vgic_get_pending_irq(v->domain, v, irq + i); \ spin_lock(&p->lock); \ ret |= get_val(p) << (shift * i); \ - spin_unlock(&p->lock); \ + vgic_put_pending_irq_unlock(v->domain, p); \ } \ return ret; \ } @@ -267,10 +266,10 @@ void scatter_irq_info_##name(struct vcpu *v, unsigned int irq, \ unsigned int i; \ for ( i = 0; i < (32 / shift); i++ ) \ { \ - struct pending_irq *p = irq_to_pending(v, irq + i); \ + struct pending_irq *p = vgic_get_pending_irq(v->domain, v, irq + i); \ spin_lock(&p->lock); \ set_val(p, (value >> (shift * i)) & ((1 << shift) - 1)); \ - spin_unlock(&p->lock); \ + vgic_put_pending_irq_unlock(v->domain, p); \ } \ } @@ -332,13 +331,13 @@ void arch_move_irqs(struct vcpu *v) for ( i = 32; i < vgic_num_irqs(d); i++ ) { - p = irq_to_pending(d->vcpu[0], i); + p = vgic_get_pending_irq(d, NULL, i); spin_lock(&p->lock); v_target = vgic_get_target_vcpu(d, p); if ( v_target == v && !test_bit(GIC_IRQ_GUEST_MIGRATING, &p->status) ) irq_set_affinity(p->desc, cpu_mask); - spin_unlock(&p->lock); + vgic_put_pending_irq_unlock(d, p); } } @@ -351,7 +350,7 @@ void vgic_disable_irqs(struct vcpu *v, unsigned int irq, uint32_t r) struct vcpu *v_target; while ( (i = find_next_bit(&mask, 32, i)) < 32 ) { - p = irq_to_pending(v, irq + i); + p = vgic_get_pending_irq(v->domain, v, irq + i); v_target = vgic_get_target_vcpu(v->domain, p); clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status); gic_remove_from_queues(v_target, irq + i); @@ -361,6 +360,7 @@ void vgic_disable_irqs(struct vcpu *v, unsigned int irq, uint32_t r) p->desc->handler->disable(p->desc); spin_unlock_irqrestore(&p->desc->lock, flags); } + vgic_put_pending_irq(v->domain, p); i++; } } @@ -376,7 +376,7 @@ void vgic_enable_irqs(struct vcpu *v, unsigned int irq, uint32_t r) struct domain *d = v->domain; while ( (i = find_next_bit(&mask, 32, i)) < 32 ) { - p = irq_to_pending(v, irq + i); + p = vgic_get_pending_irq(d, v, irq + i); v_target = vgic_get_target_vcpu(d, p); spin_lock_irqsave(&v_target->arch.vgic.lock, flags); @@ -404,6 +404,7 @@ void vgic_enable_irqs(struct vcpu *v, unsigned int irq, uint32_t r) p->desc->handler->enable(p->desc); spin_unlock_irqrestore(&p->desc->lock, flags); } + vgic_put_pending_irq(v->domain, p); i++; } } @@ -461,23 +462,39 @@ bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode, return true; } -struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq) +struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq) +{ + ASSERT(irq >= NR_LOCAL_IRQS); + + return &d->arch.vgic.pending_irqs[irq - 32]; +} This re-ordering should have been made in a separate patch. Also the change of taking a domain too. But I don't understand why you keep it around. + +struct pending_irq *vgic_get_pending_irq(struct domain *d, struct vcpu *v, + unsigned int irq) { struct pending_irq *n; + /* Pending irqs allocation strategy: the first vgic.nr_spis irqs * are used for SPIs; the rests are used for per cpu irqs */ if ( irq < 32 ) + { + ASSERT(v); n = &v->arch.vgic.pending_irqs[irq]; + } else - n = &v->domain->arch.vgic.pending_irqs[irq - 32]; + n = spi_to_pending(d, irq); + This change does not belong to this patch. return n; } -struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq) +void vgic_put_pending_irq(struct domain *d, struct pending_irq *p) { - ASSERT(irq >= NR_LOCAL_IRQS); +} - return &d->arch.vgic.pending_irqs[irq - 32]; +void vgic_put_pending_irq_unlock(struct domain *d, struct pending_irq *p) +{ + spin_unlock(&p->lock); + vgic_put_pending_irq(d, p); } void vgic_clear_pending_irqs(struct vcpu *v) @@ -494,7 +511,7 @@ void vgic_clear_pending_irqs(struct vcpu *v) void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq) { - struct pending_irq *iter, *n = irq_to_pending(v, virq); + struct pending_irq *iter, *n = vgic_get_pending_irq(v->domain, v, virq); unsigned long flags; bool running; @@ -504,6 +521,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq) if ( test_bit(_VPF_down, &v->pause_flags) ) { spin_unlock_irqrestore(&v->arch.vgic.lock, flags); + vgic_put_pending_irq(v->domain, n); return; } @@ -536,6 +554,7 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq) spin_unlock(&n->lock); out: + vgic_put_pending_irq(v->domain, n); spin_unlock_irqrestore(&v->arch.vgic.lock, flags); /* we have a new higher priority irq, inject it into the guest */ running = v->is_running; @@ -550,12 +569,13 @@ out: void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq) { struct vcpu *v; - struct pending_irq *p = irq_to_pending(d->vcpu[0], virq); + struct pending_irq *p = vgic_get_pending_irq(d, NULL, virq); /* the IRQ needs to be an SPI */ ASSERT(virq >= 32 && virq <= vgic_num_irqs(d)); v = vgic_get_target_vcpu(d, p); + vgic_put_pending_irq(d, p); vgic_vcpu_inject_irq(v, virq); } diff --git a/xen/include/asm-arm/event.h b/xen/include/asm-arm/event.h index 5330dfe..df672e2 100644 --- a/xen/include/asm-arm/event.h +++ b/xen/include/asm-arm/event.h @@ -16,8 +16,7 @@ static inline int vcpu_event_delivery_is_enabled(struct vcpu *v) static inline int local_events_need_delivery_nomask(void) { - struct pending_irq *p = irq_to_pending(current, - current->domain->arch.evtchn_irq); Limiting the scope of pending_irq should be a separate patch. + int ret = 0; /* XXX: if the first interrupt has already been delivered, we should * check whether any other interrupts with priority higher than the @@ -28,11 +27,20 @@ static inline int local_events_need_delivery_nomask(void) * case. */ if ( gic_events_need_delivery() ) - return 1; + { + ret = 1; + } + else + { + struct pending_irq *p; - if ( vcpu_info(current, evtchn_upcall_pending) && - list_empty(&p->inflight) ) - return 1; + p = vgic_get_pending_irq(current->domain, current, + current->domain->arch.evtchn_irq); + if ( vcpu_info(current, evtchn_upcall_pending) && + list_empty(&p->inflight) ) + ret = 1; + vgic_put_pending_irq(current->domain, p); Looking at this code, I think there are a race condition. Because nothing protect list_empty(&p->inflight) (this could be modified by another physical vCPU at the same time). Although, I don't know if this is really an issue. Stefano do you have an opinion? + } return 0; } diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h index 186e6df..36e4de2 100644 --- a/xen/include/asm-arm/vgic.h +++ b/xen/include/asm-arm/vgic.h @@ -299,7 +299,12 @@ extern struct vcpu *vgic_get_target_vcpu(struct domain *d, extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq); extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq); extern void vgic_clear_pending_irqs(struct vcpu *v); -extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq); +extern struct pending_irq *vgic_get_pending_irq(struct domain *d, + struct vcpu *v, + unsigned int irq); +extern void vgic_put_pending_irq(struct domain *d, struct pending_irq *p); +extern void vgic_put_pending_irq_unlock(struct domain *d, + struct pending_irq *p); extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq); extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s); extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq); Cheers, -- Julien Grall _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |