From: Jan Beulich Subject: evtchn: convert per-channel lock to be IRQ-safe ... in order for send_guest_{global,vcpu}_virq() to be able to make use of it. This is part of XSA-343. Signed-off-by: Jan Beulich Acked-by: Julien Grall --- v6: New. --- TBD: This is the "dumb" conversion variant. In a couple of cases the slightly simpler spin_{,un}lock_irq() could apparently be used. --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ int port; domid_t dom = alloc->dom; long rc; + unsigned long flags; d = rcu_lock_domain_by_any_id(dom); if ( d == NULL ) @@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) chn->u.unbound.remote_domid = current->domain->domain_id; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); alloc->port = port; @@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ } -static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) +static unsigned long double_evtchn_lock(struct evtchn *lchn, + struct evtchn *rchn) { - if ( lchn < rchn ) + unsigned long flags; + + if ( lchn <= rchn ) { - spin_lock(&lchn->lock); - spin_lock(&rchn->lock); + spin_lock_irqsave(&lchn->lock, flags); + if ( lchn != rchn ) + spin_lock(&rchn->lock); } else { - if ( lchn != rchn ) - spin_lock(&rchn->lock); + spin_lock_irqsave(&rchn->lock, flags); spin_lock(&lchn->lock); } + + return flags; } -static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) +static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, + unsigned long flags) { - spin_unlock(&lchn->lock); if ( lchn != rchn ) - spin_unlock(&rchn->lock); + spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&rchn->lock, flags); } static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) @@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc int lport, rport = bind->remote_port; domid_t rdom = bind->remote_dom; long rc; + unsigned long flags; if ( rdom == DOMID_SELF ) rdom = current->domain->domain_id; @@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc if ( rc ) goto out; - double_evtchn_lock(lchn, rchn); + flags = double_evtchn_lock(lchn, rchn); lchn->u.interdomain.remote_dom = rd; lchn->u.interdomain.remote_port = rport; @@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc */ evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - double_evtchn_unlock(lchn, rchn); + double_evtchn_unlock(lchn, rchn, flags); bind->local_port = lport; @@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t struct domain *d = current->domain; int virq = bind->virq, vcpu = bind->vcpu; int rc = 0; + unsigned long flags; if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) return -EINVAL; @@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_VIRQ; chn->notify_vcpu_id = vcpu; chn->u.virq = virq; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); v->virq_to_evtchn[virq] = bind->port = port; @@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ struct domain *d = current->domain; int port, vcpu = bind->vcpu; long rc = 0; + unsigned long flags; if ( domain_vcpu(d, vcpu) == NULL ) return -ENOENT; @@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ chn = evtchn_from_port(d, port); - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_IPI; chn->notify_vcpu_id = vcpu; evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind struct pirq *info; int port = 0, pirq = bind->pirq; long rc; + unsigned long flags; if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) return -EINVAL; @@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind goto out; } - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_PIRQ; chn->u.pirq.irq = pirq; link_pirq_port(port, chn, v); evtchn_port_init(d, chn); - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); bind->port = port; @@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int struct evtchn *chn1, *chn2; int port2; long rc = 0; + unsigned long flags; again: spin_lock(&d1->event_lock); @@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int BUG_ON(chn2->state != ECS_INTERDOMAIN); BUG_ON(chn2->u.interdomain.remote_dom != d1); - double_evtchn_lock(chn1, chn2); + flags = double_evtchn_lock(chn1, chn2); evtchn_free(d1, chn1); chn2->state = ECS_UNBOUND; chn2->u.unbound.remote_domid = d1->domain_id; - double_evtchn_unlock(chn1, chn2); + double_evtchn_unlock(chn1, chn2, flags); goto out; @@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int BUG(); } - spin_lock(&chn1->lock); + spin_lock_irqsave(&chn1->lock, flags); evtchn_free(d1, chn1); - spin_unlock(&chn1->lock); + spin_unlock_irqrestore(&chn1->lock, flags); out: if ( d2 != NULL ) @@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig struct evtchn *lchn, *rchn; struct domain *rd; int rport, ret = 0; + unsigned long flags; if ( !port_is_valid(ld, lport) ) return -EINVAL; lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); /* Guest cannot send via a Xen-attached event channel. */ if ( unlikely(consumer_is_xen(lchn)) ) @@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig } out: - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); return ret; } @@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( { struct evtchn *chn; int port, rc; + unsigned long flags; spin_lock(&ld->event_lock); @@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( if ( rc ) goto out; - spin_lock(&chn->lock); + spin_lock_irqsave(&chn->lock, flags); chn->state = ECS_UNBOUND; chn->xen_consumer = get_xen_consumer(notification_fn); chn->notify_vcpu_id = lvcpu; chn->u.unbound.remote_domid = remote_domid; - spin_unlock(&chn->lock); + spin_unlock_irqrestore(&chn->lock, flags); write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); @@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct { struct evtchn *lchn, *rchn; struct domain *rd; + unsigned long flags; ASSERT(port_is_valid(ld, lport)); lchn = evtchn_from_port(ld, lport); - spin_lock(&lchn->lock); + spin_lock_irqsave(&lchn->lock, flags); if ( likely(lchn->state == ECS_INTERDOMAIN) ) { @@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); } - spin_unlock(&lchn->lock); + spin_unlock_irqrestore(&lchn->lock, flags); } void evtchn_check_pollers(struct domain *d, unsigned int port)