* [PATCH v6 0/3] evtchn: (not so) recent XSAs follow-on
@ 2021-05-27 11:27 Jan Beulich
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Jan Beulich @ 2021-05-27 11:27 UTC (permalink / raw)
To: xen-devel
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Julien Grall, Wei Liu,
Stefano Stabellini
These are grouped into a series largely because of their origin,
not so much because there are (heavy) dependencies among them.
The main change from v6 is the dropping of three more patches,
and re-basing.
1: slightly defer lock acquire where possible
2: add helper for port_is_valid() + evtchn_from_port()
3: type adjustments
Jan
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-27 11:27 [PATCH v6 0/3] evtchn: (not so) recent XSAs follow-on Jan Beulich
@ 2021-05-27 11:28 ` Jan Beulich
2021-05-27 13:46 ` Roger Pau Monné
2021-05-27 18:48 ` Julien Grall
2021-05-27 11:28 ` [PATCH v6 2/3] evtchn: add helper for port_is_valid() + evtchn_from_port() Jan Beulich
2021-05-27 11:28 ` [PATCH v6 3/3] evtchn: type adjustments Jan Beulich
2 siblings, 2 replies; 15+ messages in thread
From: Jan Beulich @ 2021-05-27 11:28 UTC (permalink / raw)
To: xen-devel
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Julien Grall, Wei Liu,
Stefano Stabellini, Roger Pau Monné
port_is_valid() and evtchn_from_port() are fine to use without holding
any locks. Accordingly acquire the per-domain lock slightly later in
evtchn_close() and evtchn_bind_vcpu().
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v6: Re-base for re-ordering / shrinking of series.
v4: New.
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -606,17 +606,14 @@ int evtchn_close(struct domain *d1, int
int port2;
long rc = 0;
- again:
- spin_lock(&d1->event_lock);
-
if ( !port_is_valid(d1, port1) )
- {
- rc = -EINVAL;
- goto out;
- }
+ return -EINVAL;
chn1 = evtchn_from_port(d1, port1);
+ again:
+ spin_lock(&d1->event_lock);
+
/* Guest cannot close a Xen-attached event channel. */
if ( unlikely(consumer_is_xen(chn1)) && guest )
{
@@ -1041,16 +1038,13 @@ long evtchn_bind_vcpu(unsigned int port,
if ( (v = domain_vcpu(d, vcpu_id)) == NULL )
return -ENOENT;
- spin_lock(&d->event_lock);
-
if ( !port_is_valid(d, port) )
- {
- rc = -EINVAL;
- goto out;
- }
+ return -EINVAL;
chn = evtchn_from_port(d, port);
+ spin_lock(&d->event_lock);
+
/* Guest cannot re-bind a Xen-attached event channel. */
if ( unlikely(consumer_is_xen(chn)) )
{
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 2/3] evtchn: add helper for port_is_valid() + evtchn_from_port()
2021-05-27 11:27 [PATCH v6 0/3] evtchn: (not so) recent XSAs follow-on Jan Beulich
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
@ 2021-05-27 11:28 ` Jan Beulich
2021-05-27 11:28 ` [PATCH v6 3/3] evtchn: type adjustments Jan Beulich
2 siblings, 0 replies; 15+ messages in thread
From: Jan Beulich @ 2021-05-27 11:28 UTC (permalink / raw)
To: xen-devel
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Julien Grall, Wei Liu,
Stefano Stabellini, Roger Pau Monné
The combination is pretty common, so adding a simple local helper seems
worthwhile. Make it const- and type-correct, in turn requiring the
two called function to also be const-correct (and at this occasion also
make them type-correct).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Julien Grall <jgrall@amazon.com>
---
v6: Re-base, also for re-ordering / shrinking of series.
v4: New.
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -147,6 +147,12 @@ static bool virq_is_global(unsigned int
return true;
}
+static struct evtchn *_evtchn_from_port(const struct domain *d,
+ evtchn_port_t port)
+{
+ return port_is_valid(d, port) ? evtchn_from_port(d, port) : NULL;
+}
+
static void free_evtchn_bucket(struct domain *d, struct evtchn *bucket)
{
if ( !bucket )
@@ -319,7 +325,6 @@ static long evtchn_alloc_unbound(evtchn_
return rc;
}
-
static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn)
{
ASSERT(lchn != rchn);
@@ -365,9 +370,9 @@ static long evtchn_bind_interdomain(evtc
ERROR_EXIT(lport);
lchn = evtchn_from_port(ld, lport);
- if ( !port_is_valid(rd, rport) )
+ rchn = _evtchn_from_port(rd, rport);
+ if ( !rchn )
ERROR_EXIT_DOM(-EINVAL, rd);
- rchn = evtchn_from_port(rd, rport);
if ( (rchn->state != ECS_UNBOUND) ||
(rchn->u.unbound.remote_domid != ld->domain_id) )
ERROR_EXIT_DOM(-EINVAL, rd);
@@ -602,15 +607,12 @@ static long evtchn_bind_pirq(evtchn_bind
int evtchn_close(struct domain *d1, int port1, bool guest)
{
struct domain *d2 = NULL;
- struct evtchn *chn1, *chn2;
- int port2;
+ struct evtchn *chn1 = _evtchn_from_port(d1, port1), *chn2;
long rc = 0;
- if ( !port_is_valid(d1, port1) )
+ if ( !chn1 )
return -EINVAL;
- chn1 = evtchn_from_port(d1, port1);
-
again:
spin_lock(&d1->event_lock);
@@ -698,10 +700,8 @@ int evtchn_close(struct domain *d1, int
goto out;
}
- port2 = chn1->u.interdomain.remote_port;
- BUG_ON(!port_is_valid(d2, port2));
-
- chn2 = evtchn_from_port(d2, port2);
+ chn2 = _evtchn_from_port(d2, chn1->u.interdomain.remote_port);
+ BUG_ON(!chn2);
BUG_ON(chn2->state != ECS_INTERDOMAIN);
BUG_ON(chn2->u.interdomain.remote_dom != d1);
@@ -739,15 +739,13 @@ int evtchn_close(struct domain *d1, int
int evtchn_send(struct domain *ld, unsigned int lport)
{
- struct evtchn *lchn, *rchn;
+ struct evtchn *lchn = _evtchn_from_port(ld, lport), *rchn;
struct domain *rd;
int rport, ret = 0;
- if ( !port_is_valid(ld, lport) )
+ if ( !lchn )
return -EINVAL;
- lchn = evtchn_from_port(ld, lport);
-
evtchn_read_lock(lchn);
/* Guest cannot send via a Xen-attached event channel. */
@@ -967,15 +965,15 @@ int evtchn_status(evtchn_status_t *statu
if ( d == NULL )
return -ESRCH;
- spin_lock(&d->event_lock);
-
- if ( !port_is_valid(d, port) )
+ chn = _evtchn_from_port(d, port);
+ if ( !chn )
{
- rc = -EINVAL;
- goto out;
+ rcu_unlock_domain(d);
+ return -EINVAL;
}
- chn = evtchn_from_port(d, port);
+ spin_lock(&d->event_lock);
+
if ( consumer_is_xen(chn) )
{
rc = -EACCES;
@@ -1038,11 +1036,10 @@ long evtchn_bind_vcpu(unsigned int port,
if ( (v = domain_vcpu(d, vcpu_id)) == NULL )
return -ENOENT;
- if ( !port_is_valid(d, port) )
+ chn = _evtchn_from_port(d, port);
+ if ( !chn )
return -EINVAL;
- chn = evtchn_from_port(d, port);
-
spin_lock(&d->event_lock);
/* Guest cannot re-bind a Xen-attached event channel. */
@@ -1088,13 +1085,11 @@ long evtchn_bind_vcpu(unsigned int port,
int evtchn_unmask(unsigned int port)
{
struct domain *d = current->domain;
- struct evtchn *evtchn;
+ struct evtchn *evtchn = _evtchn_from_port(d, port);
- if ( unlikely(!port_is_valid(d, port)) )
+ if ( unlikely(!evtchn) )
return -EINVAL;
- evtchn = evtchn_from_port(d, port);
-
evtchn_read_lock(evtchn);
evtchn_port_unmask(d, evtchn);
@@ -1177,14 +1172,12 @@ static long evtchn_set_priority(const st
{
struct domain *d = current->domain;
unsigned int port = set_priority->port;
- struct evtchn *chn;
+ struct evtchn *chn = _evtchn_from_port(d, port);
long ret;
- if ( !port_is_valid(d, port) )
+ if ( !chn )
return -EINVAL;
- chn = evtchn_from_port(d, port);
-
evtchn_read_lock(chn);
ret = evtchn_port_set_priority(d, chn, set_priority->priority);
@@ -1410,10 +1403,10 @@ void free_xen_event_channel(struct domai
void notify_via_xen_event_channel(struct domain *ld, int lport)
{
- struct evtchn *lchn, *rchn;
+ struct evtchn *lchn = _evtchn_from_port(ld, lport), *rchn;
struct domain *rd;
- if ( !port_is_valid(ld, lport) )
+ if ( !lchn )
{
/*
* Make sure ->is_dying is read /after/ ->valid_evtchns, pairing
@@ -1424,8 +1417,6 @@ void notify_via_xen_event_channel(struct
return;
}
- lchn = evtchn_from_port(ld, lport);
-
if ( !evtchn_read_trylock(lchn) )
return;
@@ -1580,12 +1571,14 @@ static void domain_dump_evtchn_info(stru
spin_lock(&d->event_lock);
- for ( port = 1; port_is_valid(d, port); ++port )
+ for ( port = 1; ; ++port )
{
- const struct evtchn *chn;
+ const struct evtchn *chn = _evtchn_from_port(d, port);
char *ssid;
- chn = evtchn_from_port(d, port);
+ if ( !chn )
+ break;
+
if ( chn->state == ECS_FREE )
continue;
--- a/xen/include/xen/event.h
+++ b/xen/include/xen/event.h
@@ -120,7 +120,7 @@ static inline void evtchn_read_unlock(st
read_unlock(&evtchn->lock);
}
-static inline bool_t port_is_valid(struct domain *d, unsigned int p)
+static inline bool port_is_valid(const struct domain *d, evtchn_port_t p)
{
if ( p >= read_atomic(&d->valid_evtchns) )
return false;
@@ -135,7 +135,8 @@ static inline bool_t port_is_valid(struc
return true;
}
-static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p)
+static inline struct evtchn *evtchn_from_port(const struct domain *d,
+ evtchn_port_t p)
{
if ( p < EVTCHNS_PER_BUCKET )
return &d->evtchn[array_index_nospec(p, EVTCHNS_PER_BUCKET)];
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 3/3] evtchn: type adjustments
2021-05-27 11:27 [PATCH v6 0/3] evtchn: (not so) recent XSAs follow-on Jan Beulich
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
2021-05-27 11:28 ` [PATCH v6 2/3] evtchn: add helper for port_is_valid() + evtchn_from_port() Jan Beulich
@ 2021-05-27 11:28 ` Jan Beulich
2021-05-27 13:52 ` Roger Pau Monné
2 siblings, 1 reply; 15+ messages in thread
From: Jan Beulich @ 2021-05-27 11:28 UTC (permalink / raw)
To: xen-devel
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Julien Grall, Wei Liu,
Stefano Stabellini, Roger Pau Monné
First of all avoid "long" when "int" suffices, i.e. in particular when
merely conveying error codes. 32-bit values are slightly cheaper to
deal with on x86, and their processing is at least no more expensive on
Arm. Where possible use evtchn_port_t for port numbers and unsigned int
for other unsigned quantities in adjacent code. In evtchn_set_priority()
eliminate a local variable altogether instead of changing its type.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v6: Re-base for re-ordering / shrinking of series.
v4: New.
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -284,13 +284,12 @@ void evtchn_free(struct domain *d, struc
xsm_evtchn_close_post(chn);
}
-static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc)
+static int evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc)
{
struct evtchn *chn;
struct domain *d;
- int port;
+ int port, rc;
domid_t dom = alloc->dom;
- long rc;
d = rcu_lock_domain_by_any_id(dom);
if ( d == NULL )
@@ -342,13 +341,13 @@ static void double_evtchn_unlock(struct
evtchn_write_unlock(rchn);
}
-static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
+static int evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
{
struct evtchn *lchn, *rchn;
struct domain *ld = current->domain, *rd;
- int lport, rport = bind->remote_port;
+ int lport, rc;
+ evtchn_port_t rport = bind->remote_port;
domid_t rdom = bind->remote_dom;
- long rc;
if ( (rd = rcu_lock_domain_by_any_id(rdom)) == NULL )
return -ESRCH;
@@ -484,12 +483,12 @@ int evtchn_bind_virq(evtchn_bind_virq_t
}
-static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
+static int evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
{
struct evtchn *chn;
struct domain *d = current->domain;
- int port, vcpu = bind->vcpu;
- long rc = 0;
+ int port, rc = 0;
+ unsigned int vcpu = bind->vcpu;
if ( domain_vcpu(d, vcpu) == NULL )
return -ENOENT;
@@ -543,16 +542,16 @@ static void unlink_pirq_port(struct evtc
}
-static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
+static int evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
{
struct evtchn *chn;
struct domain *d = current->domain;
struct vcpu *v = d->vcpu[0];
struct pirq *info;
- int port = 0, pirq = bind->pirq;
- long rc;
+ int port = 0, rc;
+ unsigned int pirq = bind->pirq;
- if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
+ if ( pirq >= d->nr_pirqs )
return -EINVAL;
if ( !is_hvm_domain(d) && !pirq_access_permitted(d, pirq) )
@@ -608,7 +607,7 @@ int evtchn_close(struct domain *d1, int
{
struct domain *d2 = NULL;
struct evtchn *chn1 = _evtchn_from_port(d1, port1), *chn2;
- long rc = 0;
+ int rc = 0;
if ( !chn1 )
return -EINVAL;
@@ -959,7 +958,7 @@ int evtchn_status(evtchn_status_t *statu
domid_t dom = status->dom;
int port = status->port;
struct evtchn *chn;
- long rc = 0;
+ int rc = 0;
d = rcu_lock_domain_by_any_id(dom);
if ( d == NULL )
@@ -1025,11 +1024,11 @@ int evtchn_status(evtchn_status_t *statu
}
-long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id)
+int evtchn_bind_vcpu(evtchn_port_t port, unsigned int vcpu_id)
{
struct domain *d = current->domain;
struct evtchn *chn;
- long rc = 0;
+ int rc = 0;
struct vcpu *v;
/* Use the vcpu info to prevent speculative out-of-bound accesses */
@@ -1168,12 +1167,11 @@ int evtchn_reset(struct domain *d, bool
return rc;
}
-static long evtchn_set_priority(const struct evtchn_set_priority *set_priority)
+static int evtchn_set_priority(const struct evtchn_set_priority *set_priority)
{
struct domain *d = current->domain;
- unsigned int port = set_priority->port;
- struct evtchn *chn = _evtchn_from_port(d, port);
- long ret;
+ struct evtchn *chn = _evtchn_from_port(d, set_priority->port);
+ int ret;
if ( !chn )
return -EINVAL;
@@ -1189,7 +1187,7 @@ static long evtchn_set_priority(const st
long do_event_channel_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
- long rc;
+ int rc;
switch ( cmd )
{
--- a/xen/include/xen/event.h
+++ b/xen/include/xen/event.h
@@ -54,7 +54,7 @@ void send_guest_pirq(struct domain *, co
int evtchn_send(struct domain *d, unsigned int lport);
/* Bind a local event-channel port to the specified VCPU. */
-long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id);
+int evtchn_bind_vcpu(evtchn_port_t port, unsigned int vcpu_id);
/* Bind a VIRQ. */
int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port);
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
@ 2021-05-27 13:46 ` Roger Pau Monné
2021-05-27 18:48 ` Julien Grall
1 sibling, 0 replies; 15+ messages in thread
From: Roger Pau Monné @ 2021-05-27 13:46 UTC (permalink / raw)
To: Jan Beulich
Cc: xen-devel, Andrew Cooper, George Dunlap, Ian Jackson,
Julien Grall, Wei Liu, Stefano Stabellini
On Thu, May 27, 2021 at 01:28:07PM +0200, Jan Beulich wrote:
> port_is_valid() and evtchn_from_port() are fine to use without holding
> any locks. Accordingly acquire the per-domain lock slightly later in
> evtchn_close() and evtchn_bind_vcpu().
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Thanks, Roger.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 3/3] evtchn: type adjustments
2021-05-27 11:28 ` [PATCH v6 3/3] evtchn: type adjustments Jan Beulich
@ 2021-05-27 13:52 ` Roger Pau Monné
0 siblings, 0 replies; 15+ messages in thread
From: Roger Pau Monné @ 2021-05-27 13:52 UTC (permalink / raw)
To: Jan Beulich
Cc: xen-devel, Andrew Cooper, George Dunlap, Ian Jackson,
Julien Grall, Wei Liu, Stefano Stabellini
On Thu, May 27, 2021 at 01:28:59PM +0200, Jan Beulich wrote:
> First of all avoid "long" when "int" suffices, i.e. in particular when
> merely conveying error codes. 32-bit values are slightly cheaper to
> deal with on x86, and their processing is at least no more expensive on
> Arm. Where possible use evtchn_port_t for port numbers and unsigned int
> for other unsigned quantities in adjacent code. In evtchn_set_priority()
> eliminate a local variable altogether instead of changing its type.
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Just one style nit below.
> -long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id)
> +int evtchn_bind_vcpu(evtchn_port_t port, unsigned int vcpu_id)
> {
> struct domain *d = current->domain;
> struct evtchn *chn;
> - long rc = 0;
> + int rc = 0;
^ I think you are missing one space here, other
functions in the same file don't align at the *.
Thanks, Roger.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
2021-05-27 13:46 ` Roger Pau Monné
@ 2021-05-27 18:48 ` Julien Grall
2021-05-28 8:30 ` Roger Pau Monné
2021-06-01 11:54 ` Jan Beulich
1 sibling, 2 replies; 15+ messages in thread
From: Julien Grall @ 2021-05-27 18:48 UTC (permalink / raw)
To: Jan Beulich, xen-devel
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini, Roger Pau Monné
Hi Jan,
On 27/05/2021 12:28, Jan Beulich wrote:
> port_is_valid() and evtchn_from_port() are fine to use without holding
> any locks. Accordingly acquire the per-domain lock slightly later in
> evtchn_close() and evtchn_bind_vcpu().
So I agree that port_is_valid() and evtchn_from_port() are fine to use
without holding any locks in evtchn_bind_vcpu(). However, this is
misleading to say there is no problem with evtchn_close().
evtchn_close() can be called with current != d and therefore, there is a
risk that port_is_valid() may be valid and then invalid because
d->valid_evtchns is decremented in evtchn_destroy().
Thankfully the memory is still there. So the current code is okayish and
I could reluctantly accept this behavior to be spread. However, I don't
think this should be left uncommented in both the code (maybe on top of
port_is_valid()?) and the commit message.
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> ---
> v6: Re-base for re-ordering / shrinking of series.
> v4: New.
>
> --- a/xen/common/event_channel.c
> +++ b/xen/common/event_channel.c
> @@ -606,17 +606,14 @@ int evtchn_close(struct domain *d1, int
> int port2;
> long rc = 0;
>
> - again:
> - spin_lock(&d1->event_lock);
> -
> if ( !port_is_valid(d1, port1) )
> - {
> - rc = -EINVAL;
> - goto out;
> - }
> + return -EINVAL;
>
> chn1 = evtchn_from_port(d1, port1);
>
> + again:
> + spin_lock(&d1->event_lock);
> +
> /* Guest cannot close a Xen-attached event channel. */
> if ( unlikely(consumer_is_xen(chn1)) && guest )
> {
> @@ -1041,16 +1038,13 @@ long evtchn_bind_vcpu(unsigned int port,
> if ( (v = domain_vcpu(d, vcpu_id)) == NULL )
> return -ENOENT;
>
> - spin_lock(&d->event_lock);
> -
> if ( !port_is_valid(d, port) )
> - {
> - rc = -EINVAL;
> - goto out;
> - }
> + return -EINVAL;
>
> chn = evtchn_from_port(d, port);
>
> + spin_lock(&d->event_lock);
> +
> /* Guest cannot re-bind a Xen-attached event channel. */
> if ( unlikely(consumer_is_xen(chn)) )
> {
>
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-27 18:48 ` Julien Grall
@ 2021-05-28 8:30 ` Roger Pau Monné
2021-05-28 10:23 ` Jan Beulich
2021-06-01 11:54 ` Jan Beulich
1 sibling, 1 reply; 15+ messages in thread
From: Roger Pau Monné @ 2021-05-28 8:30 UTC (permalink / raw)
To: Julien Grall
Cc: Jan Beulich, xen-devel, Andrew Cooper, George Dunlap,
Ian Jackson, Wei Liu, Stefano Stabellini
On Thu, May 27, 2021 at 07:48:41PM +0100, Julien Grall wrote:
> Hi Jan,
>
> On 27/05/2021 12:28, Jan Beulich wrote:
> > port_is_valid() and evtchn_from_port() are fine to use without holding
> > any locks. Accordingly acquire the per-domain lock slightly later in
> > evtchn_close() and evtchn_bind_vcpu().
>
> So I agree that port_is_valid() and evtchn_from_port() are fine to use
> without holding any locks in evtchn_bind_vcpu(). However, this is misleading
> to say there is no problem with evtchn_close().
>
> evtchn_close() can be called with current != d and therefore, there is a
The only instances evtchn_close is called with current != d and the
domain could be unpaused is in free_xen_event_channel AFAICT.
> risk that port_is_valid() may be valid and then invalid because
> d->valid_evtchns is decremented in evtchn_destroy().
Hm, I guess you could indeed have parallel calls to
free_xen_event_channel and evtchn_destroy in a way that
free_xen_event_channel could race with valid_evtchns getting
decreased?
All callers of free_xen_event_channel are internal to the hypervisor,
so maybe with proper ordering of the operations this could be avoided,
albeit it's not easy to assert.
> Thankfully the memory is still there. So the current code is okayish and I
> could reluctantly accept this behavior to be spread. However, I don't think
> this should be left uncommented in both the code (maybe on top of
> port_is_valid()?) and the commit message.
Indeed, I think we need some expansion of the comment in port_is_valid
to clarify all this. I'm not sure I understand it properly myself when
it's fine to use port_is_valid without holding the per domain event
lock.
evtchn_close shouldn't be a very common operation anyway, so it
holding the per domain lock a bit longer doesn't seem that critical to
me anyway.
Thanks, Roger.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-28 8:30 ` Roger Pau Monné
@ 2021-05-28 10:23 ` Jan Beulich
2021-05-28 10:48 ` Julien Grall
0 siblings, 1 reply; 15+ messages in thread
From: Jan Beulich @ 2021-05-28 10:23 UTC (permalink / raw)
To: Roger Pau Monné
Cc: xen-devel, Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini, Julien Grall
On 28.05.2021 10:30, Roger Pau Monné wrote:
> On Thu, May 27, 2021 at 07:48:41PM +0100, Julien Grall wrote:
>> On 27/05/2021 12:28, Jan Beulich wrote:
>>> port_is_valid() and evtchn_from_port() are fine to use without holding
>>> any locks. Accordingly acquire the per-domain lock slightly later in
>>> evtchn_close() and evtchn_bind_vcpu().
>>
>> So I agree that port_is_valid() and evtchn_from_port() are fine to use
>> without holding any locks in evtchn_bind_vcpu(). However, this is misleading
>> to say there is no problem with evtchn_close().
>>
>> evtchn_close() can be called with current != d and therefore, there is a
>
> The only instances evtchn_close is called with current != d and the
> domain could be unpaused is in free_xen_event_channel AFAICT.
As long as the domain is not paused, ->valid_evtchns can't ever
decrease: The only point where this gets done is in evtchn_destroy().
Hence ...
>> risk that port_is_valid() may be valid and then invalid because
>> d->valid_evtchns is decremented in evtchn_destroy().
>
> Hm, I guess you could indeed have parallel calls to
> free_xen_event_channel and evtchn_destroy in a way that
> free_xen_event_channel could race with valid_evtchns getting
> decreased?
... I don't see this as relevant.
>> Thankfully the memory is still there. So the current code is okayish and I
>> could reluctantly accept this behavior to be spread. However, I don't think
>> this should be left uncommented in both the code (maybe on top of
>> port_is_valid()?) and the commit message.
>
> Indeed, I think we need some expansion of the comment in port_is_valid
> to clarify all this. I'm not sure I understand it properly myself when
> it's fine to use port_is_valid without holding the per domain event
> lock.
Because of the above property plus the fact that even if
->valid_evtchns decreases, the underlying struct evtchn instance
will remain valid (i.e. won't get de-allocated, which happens only
in evtchn_destroy_final()), it is always fine to use it without
lock. With this I'm having trouble seeing what would need adding
to port_is_valid()'s commentary.
The only thing which shouldn't happen anywhere is following a
port_is_valid() check which has returned false by code assuming
the port is going to remain invalid. But I think that's obvious
when you don't hold any suitable lock.
I do intend to follow Julien's request to explain things more for
evtchn_close().
Jan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-28 10:23 ` Jan Beulich
@ 2021-05-28 10:48 ` Julien Grall
2021-05-28 13:31 ` Roger Pau Monné
0 siblings, 1 reply; 15+ messages in thread
From: Julien Grall @ 2021-05-28 10:48 UTC (permalink / raw)
To: Jan Beulich, Roger Pau Monné
Cc: xen-devel, Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini
Hi Jan,
On 28/05/2021 11:23, Jan Beulich wrote:
> On 28.05.2021 10:30, Roger Pau Monné wrote:
>> On Thu, May 27, 2021 at 07:48:41PM +0100, Julien Grall wrote:
>>> On 27/05/2021 12:28, Jan Beulich wrote:
>>>> port_is_valid() and evtchn_from_port() are fine to use without holding
>>>> any locks. Accordingly acquire the per-domain lock slightly later in
>>>> evtchn_close() and evtchn_bind_vcpu().
>>>
>>> So I agree that port_is_valid() and evtchn_from_port() are fine to use
>>> without holding any locks in evtchn_bind_vcpu(). However, this is misleading
>>> to say there is no problem with evtchn_close().
>>>
>>> evtchn_close() can be called with current != d and therefore, there is a
>>
>> The only instances evtchn_close is called with current != d and the
>> domain could be unpaused is in free_xen_event_channel AFAICT.
>
> As long as the domain is not paused, ->valid_evtchns can't ever
> decrease: The only point where this gets done is in evtchn_destroy().
> Hence ...
>
>>> risk that port_is_valid() may be valid and then invalid because
>>> d->valid_evtchns is decremented in evtchn_destroy().
>>
>> Hm, I guess you could indeed have parallel calls to
>> free_xen_event_channel and evtchn_destroy in a way that
>> free_xen_event_channel could race with valid_evtchns getting
>> decreased?
>
> ... I don't see this as relevant.
>
>>> Thankfully the memory is still there. So the current code is okayish and I
>>> could reluctantly accept this behavior to be spread. However, I don't think
>>> this should be left uncommented in both the code (maybe on top of
>>> port_is_valid()?) and the commit message.
>>
>> Indeed, I think we need some expansion of the comment in port_is_valid
>> to clarify all this. I'm not sure I understand it properly myself when
>> it's fine to use port_is_valid without holding the per domain event
>> lock.
>
> Because of the above property plus the fact that even if
> ->valid_evtchns decreases, the underlying struct evtchn instance
> will remain valid (i.e. won't get de-allocated, which happens only
> in evtchn_destroy_final()), it is always fine to use it without
> lock. With this I'm having trouble seeing what would need adding
> to port_is_valid()'s commentary.
Lets take the example of free_xen_event_channel(). The function is
checking if the port is valid. If it is, then evtchn_close() will be called.
At this point, it would be fair for a developper to assume that
port_is_valid() will also return true in event_close().
To push to the extreme, if free_xen_event_channel() was the only caller
of evtchn_close(), one could argue that the check in evtchn_close()
could be a BUG_ON().
However, this can't be because this would sooner or later turn to an XSA.
Effectively, it means that is_port_valid() *cannot* be used in an
ASSERT()/BUG_ON() and every caller should check the return even if the
port was previously validated.
So I think a comment on top of is_port_valid() would be really useful
before someone rediscover it the hard way.
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-28 10:48 ` Julien Grall
@ 2021-05-28 13:31 ` Roger Pau Monné
2021-05-28 13:41 ` Jan Beulich
2021-05-28 14:26 ` Julien Grall
0 siblings, 2 replies; 15+ messages in thread
From: Roger Pau Monné @ 2021-05-28 13:31 UTC (permalink / raw)
To: Julien Grall
Cc: Jan Beulich, xen-devel, Andrew Cooper, George Dunlap,
Ian Jackson, Wei Liu, Stefano Stabellini
On Fri, May 28, 2021 at 11:48:51AM +0100, Julien Grall wrote:
> Hi Jan,
>
> On 28/05/2021 11:23, Jan Beulich wrote:
> > On 28.05.2021 10:30, Roger Pau Monné wrote:
> > > On Thu, May 27, 2021 at 07:48:41PM +0100, Julien Grall wrote:
> > > > On 27/05/2021 12:28, Jan Beulich wrote:
> > > > > port_is_valid() and evtchn_from_port() are fine to use without holding
> > > > > any locks. Accordingly acquire the per-domain lock slightly later in
> > > > > evtchn_close() and evtchn_bind_vcpu().
> > > >
> > > > So I agree that port_is_valid() and evtchn_from_port() are fine to use
> > > > without holding any locks in evtchn_bind_vcpu(). However, this is misleading
> > > > to say there is no problem with evtchn_close().
> > > >
> > > > evtchn_close() can be called with current != d and therefore, there is a
> > >
> > > The only instances evtchn_close is called with current != d and the
> > > domain could be unpaused is in free_xen_event_channel AFAICT.
> >
> > As long as the domain is not paused, ->valid_evtchns can't ever
> > decrease: The only point where this gets done is in evtchn_destroy().
> > Hence ...
> >
> > > > risk that port_is_valid() may be valid and then invalid because
> > > > d->valid_evtchns is decremented in evtchn_destroy().
> > >
> > > Hm, I guess you could indeed have parallel calls to
> > > free_xen_event_channel and evtchn_destroy in a way that
> > > free_xen_event_channel could race with valid_evtchns getting
> > > decreased?
> >
> > ... I don't see this as relevant.
> >
> > > > Thankfully the memory is still there. So the current code is okayish and I
> > > > could reluctantly accept this behavior to be spread. However, I don't think
> > > > this should be left uncommented in both the code (maybe on top of
> > > > port_is_valid()?) and the commit message.
> > >
> > > Indeed, I think we need some expansion of the comment in port_is_valid
> > > to clarify all this. I'm not sure I understand it properly myself when
> > > it's fine to use port_is_valid without holding the per domain event
> > > lock.
> >
> > Because of the above property plus the fact that even if
> > ->valid_evtchns decreases, the underlying struct evtchn instance
> > will remain valid (i.e. won't get de-allocated, which happens only
> > in evtchn_destroy_final()), it is always fine to use it without
> > lock. With this I'm having trouble seeing what would need adding
> > to port_is_valid()'s commentary.
>
> Lets take the example of free_xen_event_channel(). The function is checking
> if the port is valid. If it is, then evtchn_close() will be called.
>
> At this point, it would be fair for a developper to assume that
> port_is_valid() will also return true in event_close().
>
> To push to the extreme, if free_xen_event_channel() was the only caller of
> evtchn_close(), one could argue that the check in evtchn_close() could be a
> BUG_ON().
>
> However, this can't be because this would sooner or later turn to an XSA.
>
> Effectively, it means that is_port_valid() *cannot* be used in an
> ASSERT()/BUG_ON() and every caller should check the return even if the port
> was previously validated.
We already have cases of is_port_valid being used in ASSERTs (in the
shim) and a BUG_ON (with the domain event lock held in evtchn_close).
> So I think a comment on top of is_port_valid() would be really useful before
> someone rediscover it the hard way.
I think I'm being extremely dull here, sorry. From your text I
understand that the value returned by is_port_valid could be stale by
the time the user reads it?
I think there's some condition that makes this value stale, and it's
not the common case?
Thanks, Roger.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-28 13:31 ` Roger Pau Monné
@ 2021-05-28 13:41 ` Jan Beulich
2021-05-28 14:26 ` Julien Grall
1 sibling, 0 replies; 15+ messages in thread
From: Jan Beulich @ 2021-05-28 13:41 UTC (permalink / raw)
To: Roger Pau Monné
Cc: xen-devel, Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini, Julien Grall
On 28.05.2021 15:31, Roger Pau Monné wrote:
> I think I'm being extremely dull here, sorry. From your text I
> understand that the value returned by is_port_valid could be stale by
> the time the user reads it?
>
> I think there's some condition that makes this value stale, and it's
> not the common case?
It's evtchn_destroy() running in parallel which can have this effect.
Jan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-28 13:31 ` Roger Pau Monné
2021-05-28 13:41 ` Jan Beulich
@ 2021-05-28 14:26 ` Julien Grall
1 sibling, 0 replies; 15+ messages in thread
From: Julien Grall @ 2021-05-28 14:26 UTC (permalink / raw)
To: Roger Pau Monné
Cc: Jan Beulich, xen-devel, Andrew Cooper, George Dunlap,
Ian Jackson, Wei Liu, Stefano Stabellini
Hi Roger,
On 28/05/2021 14:31, Roger Pau Monné wrote:
> On Fri, May 28, 2021 at 11:48:51AM +0100, Julien Grall wrote:
>> Hi Jan,
>>
>> On 28/05/2021 11:23, Jan Beulich wrote:
>>> On 28.05.2021 10:30, Roger Pau Monné wrote:
>>>> On Thu, May 27, 2021 at 07:48:41PM +0100, Julien Grall wrote:
>>>>> On 27/05/2021 12:28, Jan Beulich wrote:
>>>>>> port_is_valid() and evtchn_from_port() are fine to use without holding
>>>>>> any locks. Accordingly acquire the per-domain lock slightly later in
>>>>>> evtchn_close() and evtchn_bind_vcpu().
>>>>>
>>>>> So I agree that port_is_valid() and evtchn_from_port() are fine to use
>>>>> without holding any locks in evtchn_bind_vcpu(). However, this is misleading
>>>>> to say there is no problem with evtchn_close().
>>>>>
>>>>> evtchn_close() can be called with current != d and therefore, there is a
>>>>
>>>> The only instances evtchn_close is called with current != d and the
>>>> domain could be unpaused is in free_xen_event_channel AFAICT.
>>>
>>> As long as the domain is not paused, ->valid_evtchns can't ever
>>> decrease: The only point where this gets done is in evtchn_destroy().
>>> Hence ...
>>>
>>>>> risk that port_is_valid() may be valid and then invalid because
>>>>> d->valid_evtchns is decremented in evtchn_destroy().
>>>>
>>>> Hm, I guess you could indeed have parallel calls to
>>>> free_xen_event_channel and evtchn_destroy in a way that
>>>> free_xen_event_channel could race with valid_evtchns getting
>>>> decreased?
>>>
>>> ... I don't see this as relevant.
>>>
>>>>> Thankfully the memory is still there. So the current code is okayish and I
>>>>> could reluctantly accept this behavior to be spread. However, I don't think
>>>>> this should be left uncommented in both the code (maybe on top of
>>>>> port_is_valid()?) and the commit message.
>>>>
>>>> Indeed, I think we need some expansion of the comment in port_is_valid
>>>> to clarify all this. I'm not sure I understand it properly myself when
>>>> it's fine to use port_is_valid without holding the per domain event
>>>> lock.
>>>
>>> Because of the above property plus the fact that even if
>>> ->valid_evtchns decreases, the underlying struct evtchn instance
>>> will remain valid (i.e. won't get de-allocated, which happens only
>>> in evtchn_destroy_final()), it is always fine to use it without
>>> lock. With this I'm having trouble seeing what would need adding
>>> to port_is_valid()'s commentary.
>>
>> Lets take the example of free_xen_event_channel(). The function is checking
>> if the port is valid. If it is, then evtchn_close() will be called.
>>
>> At this point, it would be fair for a developper to assume that
>> port_is_valid() will also return true in event_close().
>>
>> To push to the extreme, if free_xen_event_channel() was the only caller of
>> evtchn_close(), one could argue that the check in evtchn_close() could be a
>> BUG_ON().
>>
>> However, this can't be because this would sooner or later turn to an XSA.
>>
>> Effectively, it means that is_port_valid() *cannot* be used in an
>> ASSERT()/BUG_ON() and every caller should check the return even if the port
>> was previously validated.
>
> We already have cases of is_port_valid being used in ASSERTs (in the
> shim) and a BUG_ON (with the domain event lock held in evtchn_close).
I was likely a bit too restrictive in my remark. The BUG_ON() in
evtchn_close() is fine because this is used on a (in theory) an open
port with the both domains event lock held.
This would be more a problem if we have either:
if ( !port_is_valid(d1, port1) )
return -EINVAL;
/* .... */
BUG_ON(port_is_valid(d1, port1));
>
>> So I think a comment on top of is_port_valid() would be really useful before
>> someone rediscover it the hard way.
>
> I think I'm being extremely dull here, sorry. From your text I
> understand that the value returned by is_port_valid could be stale by
> the time the user reads it?
>
> I think there's some condition that makes this value stale, and it's
> not the common case?
It is any code that race with evtchn_destroy().
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-05-27 18:48 ` Julien Grall
2021-05-28 8:30 ` Roger Pau Monné
@ 2021-06-01 11:54 ` Jan Beulich
2021-06-07 18:15 ` Julien Grall
1 sibling, 1 reply; 15+ messages in thread
From: Jan Beulich @ 2021-06-01 11:54 UTC (permalink / raw)
To: Julien Grall, Roger Pau Monné
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini, xen-devel
On 27.05.2021 20:48, Julien Grall wrote:
> On 27/05/2021 12:28, Jan Beulich wrote:
>> port_is_valid() and evtchn_from_port() are fine to use without holding
>> any locks. Accordingly acquire the per-domain lock slightly later in
>> evtchn_close() and evtchn_bind_vcpu().
>
> So I agree that port_is_valid() and evtchn_from_port() are fine to use
> without holding any locks in evtchn_bind_vcpu(). However, this is
> misleading to say there is no problem with evtchn_close().
>
> evtchn_close() can be called with current != d and therefore, there is a
> risk that port_is_valid() may be valid and then invalid because
> d->valid_evtchns is decremented in evtchn_destroy().
While this is the case for other functions as well (and hence a
comment along the lines of what you ask for below should have
been in place already), I've added
/*
* While calling the function is okay without holding a suitable lock yet
* (see the comment ahead of struct evtchn_port_ops for which ones those
* are), for a dying domain it may start returning false at any point - see
* evtchn_destroy(). This is not a fundamental problem though, as the
* struct evtchn instance won't disappear (and will continue to hold valid
* data) until final cleanup of the domain, at which point the domain itself
* cannot be looked up anymore and hence calls here can't occur anymore in
* the first place.
*/
...
> Thankfully the memory is still there. So the current code is okayish and
> I could reluctantly accept this behavior to be spread. However, I don't
> think this should be left uncommented in both the code (maybe on top of
> port_is_valid()?) and the commit message.
... ahead of port_is_valid() (and not, as I did intend originally,
in evtchn_close()). As far as the commit message goes, I'll have it
refer to the comment only.
I hope this satisfies the requests of both of you. I'll take the
liberty and retain your ack, Roger.
Jan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible
2021-06-01 11:54 ` Jan Beulich
@ 2021-06-07 18:15 ` Julien Grall
0 siblings, 0 replies; 15+ messages in thread
From: Julien Grall @ 2021-06-07 18:15 UTC (permalink / raw)
To: Jan Beulich, Roger Pau Monné
Cc: Andrew Cooper, George Dunlap, Ian Jackson, Wei Liu,
Stefano Stabellini, xen-devel
Hi Jan,
On 01/06/2021 12:54, Jan Beulich wrote:
> On 27.05.2021 20:48, Julien Grall wrote:
>> On 27/05/2021 12:28, Jan Beulich wrote:
>>> port_is_valid() and evtchn_from_port() are fine to use without holding
>>> any locks. Accordingly acquire the per-domain lock slightly later in
>>> evtchn_close() and evtchn_bind_vcpu().
>>
>> So I agree that port_is_valid() and evtchn_from_port() are fine to use
>> without holding any locks in evtchn_bind_vcpu(). However, this is
>> misleading to say there is no problem with evtchn_close().
>>
>> evtchn_close() can be called with current != d and therefore, there is a
>> risk that port_is_valid() may be valid and then invalid because
>> d->valid_evtchns is decremented in evtchn_destroy().
>
> While this is the case for other functions as well (and hence a
> comment along the lines of what you ask for below should have
> been in place already), I've added
>
> /*
> * While calling the function is okay without holding a suitable lock yet
> * (see the comment ahead of struct evtchn_port_ops for which ones those
> * are), for a dying domain it may start returning false at any point - see
> * evtchn_destroy(). This is not a fundamental problem though, as the
> * struct evtchn instance won't disappear (and will continue to hold valid
> * data) until final cleanup of the domain, at which point the domain itself
> * cannot be looked up anymore and hence calls here can't occur anymore in
> * the first place.
> */
>
> ...
>
>> Thankfully the memory is still there. So the current code is okayish and
>> I could reluctantly accept this behavior to be spread. However, I don't
>> think this should be left uncommented in both the code (maybe on top of
>> port_is_valid()?) and the commit message.
>
> ... ahead of port_is_valid() (and not, as I did intend originally,
> in evtchn_close()). As far as the commit message goes, I'll have it
> refer to the comment only.
>
> I hope this satisfies the requests of both of you. I'll take the
> liberty and retain your ack, Roger.
Yes, this satistfies my requests. Feel free to add my reviewed-by on the
patch.
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2021-06-07 18:15 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-27 11:27 [PATCH v6 0/3] evtchn: (not so) recent XSAs follow-on Jan Beulich
2021-05-27 11:28 ` [PATCH v6 1/3] evtchn: slightly defer lock acquire where possible Jan Beulich
2021-05-27 13:46 ` Roger Pau Monné
2021-05-27 18:48 ` Julien Grall
2021-05-28 8:30 ` Roger Pau Monné
2021-05-28 10:23 ` Jan Beulich
2021-05-28 10:48 ` Julien Grall
2021-05-28 13:31 ` Roger Pau Monné
2021-05-28 13:41 ` Jan Beulich
2021-05-28 14:26 ` Julien Grall
2021-06-01 11:54 ` Jan Beulich
2021-06-07 18:15 ` Julien Grall
2021-05-27 11:28 ` [PATCH v6 2/3] evtchn: add helper for port_is_valid() + evtchn_from_port() Jan Beulich
2021-05-27 11:28 ` [PATCH v6 3/3] evtchn: type adjustments Jan Beulich
2021-05-27 13:52 ` Roger Pau Monné
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).