Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16.06.15 at 18:39, david.vra...@citrix.com wrote: On 16/06/15 17:19, Jan Beulich wrote: On 16.06.15 at 17:58, david.vra...@citrix.com wrote: On 16/06/15 16:19, David Vrabel wrote: @@ -1221,6 +1277,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) evtchn_port_set_pending(rd, rchn-notify_vcpu_id, rchn); } +spin_unlock(lchn-lock); + spin_unlock(ld-event_lock); } Again I think the event lock can be dropped earlier now. Ditto. Uh, no. This is notify. I've kept the locking like this because of the ld-is_dying check. I think we need the ld-event_lock in case d-is_dying is set and evtchn_destroy(ld) is called. Right, but if evtchn_destroy() was a concern, then this wouldn't apply just here, but also in the sending path you are relaxing. Afaict due to the channel lock being taken in __evtchn_close() you can drop the even lock here as the latest after you acquired the channel one (I haven't been able to convince myself yet that dropping it even before that would be okay). But in the evtchn_send() case, we're in a hypercall so we know ld-is_dying is false and thus cannot be racing with evtchn_destroy(ld). That's only the common code path; there's an evtchn_send() call from __domain_finalise_shutdown() which afaict doesn't make such guarantees (in particular there clearly are d != current-domain cases here). And then - how is being in a hypercall a guard against is_dying not getting set? It would be good to remove event_lock from notify_xen_event_channel() as well since this is heavily used for ioreqs and vm events. Let me have a more careful look. Indeed, thanks. Jan ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16/06/15 16:19, David Vrabel wrote: @@ -1221,6 +1277,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) evtchn_port_set_pending(rd, rchn-notify_vcpu_id, rchn); } +spin_unlock(lchn-lock); + spin_unlock(ld-event_lock); } Again I think the event lock can be dropped earlier now. Ditto. Uh, no. This is notify. I've kept the locking like this because of the ld-is_dying check. I think we need the ld-event_lock in case d-is_dying is set and evtchn_destroy(ld) is called. David ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16.06.15 at 17:19, david.vra...@citrix.com wrote: On 16/06/15 10:18, Jan Beulich wrote: On 15.06.15 at 17:48, david.vra...@citrix.com wrote: @@ -1163,11 +1213,15 @@ int alloc_unbound_xen_event_channel( if ( rc ) goto out; +spin_lock(chn-lock); + 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); + out: spin_unlock(ld-event_lock); I don't see why you shouldn't be able to move up this unlock. Because we need to (also) hold ld-event_lock while changing the state from ECS_FREE or a concurrent get_free_port() will find this port again. I buy this one (and moving the unlock up after the state adjustment is unlikely to be worth it), but ... @@ -1221,6 +1277,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) evtchn_port_set_pending(rd, rchn-notify_vcpu_id, rchn); } +spin_unlock(lchn-lock); + spin_unlock(ld-event_lock); } Again I think the event lock can be dropped earlier now. Ditto. ... there's no state change involved here. Jan ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16/06/15 17:19, Jan Beulich wrote: On 16.06.15 at 17:58, david.vra...@citrix.com wrote: On 16/06/15 16:19, David Vrabel wrote: @@ -1221,6 +1277,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) evtchn_port_set_pending(rd, rchn-notify_vcpu_id, rchn); } +spin_unlock(lchn-lock); + spin_unlock(ld-event_lock); } Again I think the event lock can be dropped earlier now. Ditto. Uh, no. This is notify. I've kept the locking like this because of the ld-is_dying check. I think we need the ld-event_lock in case d-is_dying is set and evtchn_destroy(ld) is called. Right, but if evtchn_destroy() was a concern, then this wouldn't apply just here, but also in the sending path you are relaxing. Afaict due to the channel lock being taken in __evtchn_close() you can drop the even lock here as the latest after you acquired the channel one (I haven't been able to convince myself yet that dropping it even before that would be okay). But in the evtchn_send() case, we're in a hypercall so we know ld-is_dying is false and thus cannot be racing with evtchn_destroy(ld). It would be good to remove event_lock from notify_xen_event_channel() as well since this is heavily used for ioreqs and vm events. Let me have a more careful look. David ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 15.06.15 at 17:48, david.vra...@citrix.com wrote: @@ -609,21 +662,18 @@ int evtchn_send(struct domain *ld, unsigned int lport) struct domain *rd; intrport, ret = 0; -spin_lock(ld-event_lock); - -if ( unlikely(!port_is_valid(ld, lport)) ) -{ -spin_unlock(ld-event_lock); +if ( unlikely(lport = read_atomic(ld-valid_evtchns)) ) return -EINVAL; -} I don't think you really want to open code part of port_is_valid() (and avoid other parts of it) here? Or if really so, I think a comment should be added to explain it. @@ -1163,11 +1213,15 @@ int alloc_unbound_xen_event_channel( if ( rc ) goto out; +spin_lock(chn-lock); + 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); + out: spin_unlock(ld-event_lock); I don't see why you shouldn't be able to move up this unlock. And then - no change to free_xen_event_channel()? @@ -1214,6 +1268,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) lchn = evtchn_from_port(ld, lport); ASSERT(consumer_is_xen(lchn)); +spin_lock(lchn-lock); + if ( likely(lchn-state == ECS_INTERDOMAIN) ) { rd= lchn-u.interdomain.remote_dom; @@ -1221,6 +1277,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) evtchn_port_set_pending(rd, rchn-notify_vcpu_id, rchn); } +spin_unlock(lchn-lock); + spin_unlock(ld-event_lock); } Again I think the event lock can be dropped earlier now. Jan ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16/06/15 10:18, Jan Beulich wrote: On 15.06.15 at 17:48, david.vra...@citrix.com wrote: @@ -609,21 +662,18 @@ int evtchn_send(struct domain *ld, unsigned int lport) struct domain *rd; intrport, ret = 0; -spin_lock(ld-event_lock); - -if ( unlikely(!port_is_valid(ld, lport)) ) -{ -spin_unlock(ld-event_lock); +if ( unlikely(lport = read_atomic(ld-valid_evtchns)) ) return -EINVAL; -} I don't think you really want to open code part of port_is_valid() (and avoid other parts of it) here? Or if really so, I think a comment should be added to explain it. The ld-valid_evtchns is the only field we can safely check without ld-event_lock. We do check the channel state and the code that set this state uses the full port_is_valid() call. I'll add a comment. And then - no change to free_xen_event_channel()? It looked alright to me, but now I see the clear of xen_consumer needs to be done under the channel lock. Probably by moving it to __evtchn_close(). David ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16.06.15 at 11:34, david.vra...@citrix.com wrote: On 16/06/15 10:18, Jan Beulich wrote: On 15.06.15 at 17:48, david.vra...@citrix.com wrote: @@ -609,21 +662,18 @@ int evtchn_send(struct domain *ld, unsigned int lport) struct domain *rd; intrport, ret = 0; -spin_lock(ld-event_lock); - -if ( unlikely(!port_is_valid(ld, lport)) ) -{ -spin_unlock(ld-event_lock); +if ( unlikely(lport = read_atomic(ld-valid_evtchns)) ) return -EINVAL; -} I don't think you really want to open code part of port_is_valid() (and avoid other parts of it) here? Or if really so, I think a comment should be added to explain it. The ld-valid_evtchns is the only field we can safely check without ld-event_lock. We do check the channel state and the code that set this state uses the full port_is_valid() call. I'll add a comment. Hmm, port_is_valid() also checks d-max_evtchns and d-evtchn. The latter is involved in evtchn_from_port(), so I can't see how you checking the channel's state _afterwards_ can leverage that whoever set this state did a full check. Another question is whether with the -valid_evtchns check the -evtchn check is necessary at all anymore. (The check against -max_evtchns isn't wrong with the lock not held, i.e. could only end up being too strict, and hence the open coding would then still be questionable.) Jan ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Re: [Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
On 16/06/15 10:51, Jan Beulich wrote: On 16.06.15 at 11:34, david.vra...@citrix.com wrote: On 16/06/15 10:18, Jan Beulich wrote: On 15.06.15 at 17:48, david.vra...@citrix.com wrote: @@ -609,21 +662,18 @@ int evtchn_send(struct domain *ld, unsigned int lport) struct domain *rd; intrport, ret = 0; -spin_lock(ld-event_lock); - -if ( unlikely(!port_is_valid(ld, lport)) ) -{ -spin_unlock(ld-event_lock); +if ( unlikely(lport = read_atomic(ld-valid_evtchns)) ) return -EINVAL; -} I don't think you really want to open code part of port_is_valid() (and avoid other parts of it) here? Or if really so, I think a comment should be added to explain it. The ld-valid_evtchns is the only field we can safely check without ld-event_lock. We do check the channel state and the code that set this state uses the full port_is_valid() call. I'll add a comment. Hmm, port_is_valid() also checks d-max_evtchns and d-evtchn. The latter is involved in evtchn_from_port(), so I can't see how you checking the channel's state _afterwards_ can leverage that whoever set this state did a full check. Another question is whether with the -valid_evtchns check the -evtchn check is necessary at all anymore. (The check against -max_evtchns isn't wrong with the lock not held, i.e. could only end up being too strict, and hence the open coding would then still be questionable.) Ok. I'll remove the d-evtchn check from port_is_valid() and use it. David ___ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
[Xen-devel] [PATCHv2 3/5] evtchn: use a per-event channel lock for sending events
When sending an event, use a new per-event channel lock to safely validate the event channel state. This new lock must be held when changing event channel state. To avoid having to take the remote event channel locks when sending to an interdomain event channel, the local and remote channel locks are both held when binding or closing an interdomain event channel. This significantly increases the number of events that can be sent from multiple VCPUs. But, struct evtchn increases in size, reducing the number that fit into a single page to 64 (instead of 128). Signed-off-by: David Vrabel david.vra...@citrix.com --- v2: - Compare channel pointers in double_evtchn_lock(). --- xen/common/event_channel.c | 80 ++-- xen/include/xen/sched.h|1 + 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index fd48646..416c76f 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -139,6 +139,7 @@ static struct evtchn *alloc_evtchn_bucket(struct domain *d, unsigned int port) return NULL; } chn[i].port = port + i; +spin_lock_init(chn[i].lock); } return chn; } @@ -228,11 +229,15 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) if ( rc ) goto out; +spin_lock(chn-lock); + 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); + alloc-port = port; out: @@ -243,6 +248,28 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) } +static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) +{ +if ( lchn rchn ) +{ +spin_lock(lchn-lock); +spin_lock(rchn-lock); +} +else +{ +if ( lchn != rchn ) +spin_lock(rchn-lock); +spin_lock(lchn-lock); +} +} + +static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) +{ +spin_unlock(lchn-lock); +if ( lchn != rchn ) +spin_unlock(rchn-lock); +} + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) { struct evtchn *lchn, *rchn; @@ -285,6 +312,8 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) if ( rc ) goto out; +double_evtchn_lock(lchn, rchn); + lchn-u.interdomain.remote_dom = rd; lchn-u.interdomain.remote_port = rport; lchn-state = ECS_INTERDOMAIN; @@ -300,6 +329,8 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) */ evtchn_port_set_pending(ld, lchn-notify_vcpu_id, lchn); +double_evtchn_unlock(lchn, rchn); + bind-local_port = lport; out: @@ -340,11 +371,16 @@ static long evtchn_bind_virq(evtchn_bind_virq_t *bind) ERROR_EXIT(port); chn = evtchn_from_port(d, port); + +spin_lock(chn-lock); + chn-state = ECS_VIRQ; chn-notify_vcpu_id = vcpu; chn-u.virq = virq; evtchn_port_init(d, chn); +spin_unlock(chn-lock); + v-virq_to_evtchn[virq] = bind-port = port; out: @@ -371,10 +407,15 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) ERROR_EXIT(port); chn = evtchn_from_port(d, port); + +spin_lock(chn-lock); + chn-state = ECS_IPI; chn-notify_vcpu_id = vcpu; evtchn_port_init(d, chn); +spin_unlock(chn-lock); + bind-port = port; out: @@ -449,11 +490,15 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) goto out; } +spin_lock(chn-lock); + chn-state = ECS_PIRQ; chn-u.pirq.irq = pirq; link_pirq_port(port, chn, v); evtchn_port_init(d, chn); +spin_unlock(chn-lock); + bind-port = port; #ifdef CONFIG_X86 @@ -467,7 +512,6 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) return rc; } - static long __evtchn_close(struct domain *d1, int port1) { struct domain *d2 = NULL; @@ -574,15 +618,24 @@ static long __evtchn_close(struct domain *d1, int port1) BUG_ON(chn2-state != ECS_INTERDOMAIN); BUG_ON(chn2-u.interdomain.remote_dom != d1); +double_evtchn_lock(chn1, chn2); + +free_evtchn(d1, chn1); + chn2-state = ECS_UNBOUND; chn2-u.unbound.remote_domid = d1-domain_id; -break; + +double_evtchn_unlock(chn1, chn2); + +goto out; default: BUG(); } +spin_lock(chn1-lock); free_evtchn(d1, chn1); +spin_unlock(chn1-lock); out: if ( d2 != NULL ) @@ -609,21 +662,18 @@ int evtchn_send(struct domain *ld, unsigned int lport) struct domain *rd; intrport, ret = 0; -spin_lock(ld-event_lock); - -if ( unlikely(!port_is_valid(ld, lport)) ) -{ -spin_unlock(ld-event_lock); +if (