diff --git a/xen/common/domain.c b/xen/common/domain.c index 96833c1715..c34dca2184 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -1087,7 +1087,7 @@ int domain_kill(struct domain *d) d->is_dying = DOMDYING_dying; rspin_barrier(&d->domain_lock); argo_destroy(d); - vnuma_destroy(d->vnuma); + vnuma_replace(d, NULL); domain_set_outstanding_pages(d, 0); /* fallthrough */ case DOMDYING_dying: diff --git a/xen/common/domctl.c b/xen/common/domctl.c index a186d145e8..56b4bf4227 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -154,7 +154,7 @@ void domctl_lock_release(void) spin_unlock(¤t->domain->hypercall_deadlock_mutex); } -void vnuma_destroy(struct vnuma_info *vnuma) +static void vnuma_destroy(struct vnuma_info *vnuma) { if ( vnuma ) { @@ -166,6 +166,19 @@ void vnuma_destroy(struct vnuma_info *vnuma) } } +/* Overwrite (replace) vnuma topology for a domain. */ +void vnuma_replace(struct domain *d, struct vnuma_info *vnuma) +{ + struct vnuma_info *old; + + write_lock(&d->vnuma_rwlock); + old = d->vnuma; + d->vnuma = vnuma; + write_unlock(&d->vnuma_rwlock); + + vnuma_destroy(old); +} + /* * Allocates memory for vNUMA, **vnuma should be NULL. * Caller has to make sure that domain has max_pages @@ -916,12 +929,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) break; } - /* overwrite vnuma topology for domain. */ - write_lock(&d->vnuma_rwlock); - vnuma_destroy(d->vnuma); - d->vnuma = vnuma; - write_unlock(&d->vnuma_rwlock); - + vnuma_replace(d, vnuma); break; } diff --git a/xen/common/memory.c b/xen/common/memory.c index 23bb159d9b..7a00bf95dd 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -1802,12 +1802,24 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) goto vnumainfo_out; } + read_lock(&d->vnuma_rwlock); + + /* + * Check d->vnuma again after re-acquiring the lock as we can race + * with domain destruction. + */ + if ( !d->vnuma ) + { + ASSERT(d->is_dying); + read_unlock(&d->vnuma_rwlock); + rc = -ESRCH; + goto vnumainfo_out; + } + /* * Check if vnuma info has changed and if the allocated arrays * are not big enough. */ - read_lock(&d->vnuma_rwlock); - if ( dom_vnodes < d->vnuma->nr_vnodes || dom_vranges < d->vnuma->nr_vmemranges || dom_vcpus < d->max_vcpus ) diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h index ae2b72d5c6..f7a270229e 100644 --- a/xen/include/xen/domain.h +++ b/xen/include/xen/domain.h @@ -164,9 +164,10 @@ struct vnuma_info { }; #ifndef CONFIG_PV_SHIM_EXCLUSIVE -void vnuma_destroy(struct vnuma_info *vnuma); +void vnuma_replace(struct domain *d, struct vnuma_info *vnuma); #else -static inline void vnuma_destroy(struct vnuma_info *vnuma) { ASSERT(!vnuma); } +static inline void vnuma_replace(struct domain *d, struct vnuma_info *vnuma) +{ ASSERT(!vnuma); } #endif extern bool vmtrace_available;