diff --git a/xen/common/domain.c b/xen/common/domain.c index 69fe1debcc..2a22cf5769 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -1255,7 +1255,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 36ef760c2c..811e68b005 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -142,7 +142,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 ) { @@ -154,6 +154,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 @@ -907,12 +920,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 3412c7fb4a..76a1bf1b11 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -1804,12 +1804,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 613b5762b5..90a1777ff0 100644 --- a/xen/include/xen/domain.h +++ b/xen/include/xen/domain.h @@ -188,9 +188,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;