diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c index 054b7edb39..bbb8578459 100644 --- a/xen/common/page_alloc.c +++ b/xen/common/page_alloc.c @@ -475,34 +475,9 @@ static long outstanding_claims; /* total outstanding claims by all domains */ unsigned long domain_adjust_tot_pages(struct domain *d, long pages) { - long dom_before, dom_after, dom_claimed, sys_before, sys_after; - ASSERT(rspin_is_locked(&d->page_alloc_lock)); d->tot_pages += pages; - /* - * can test d->claimed_pages race-free because it can only change - * if d->page_alloc_lock and heap_lock are both held, see also - * domain_set_outstanding_pages below - */ - if ( !d->outstanding_pages ) - goto out; - - spin_lock(&heap_lock); - /* adjust domain outstanding pages; may not go negative */ - dom_before = d->outstanding_pages; - dom_after = dom_before - pages; - BUG_ON(dom_before < 0); - dom_claimed = dom_after < 0 ? 0 : dom_after; - d->outstanding_pages = dom_claimed; - /* flag accounting bug if system outstanding_claims would go negative */ - sys_before = outstanding_claims; - sys_after = sys_before - (dom_before - dom_claimed); - BUG_ON(sys_after < 0); - outstanding_claims = sys_after; - spin_unlock(&heap_lock); - -out: return d->tot_pages; } @@ -512,9 +487,10 @@ int domain_set_outstanding_pages(struct domain *d, unsigned long pages) unsigned long claim, avail_pages; /* - * take the domain's page_alloc_lock, else all d->tot_page adjustments - * must always take the global heap_lock rather than only in the much - * rarer case that d->outstanding_pages is non-zero + * Two locks are needed here: + * - d->page_alloc_lock: protects accesses to d->{tot,max,extra}_pages. + * - heap_lock: protects accesses to d->outstanding_pages, total_avail_pages + * and outstanding_claims. */ nrspin_lock(&d->page_alloc_lock); spin_lock(&heap_lock); @@ -1014,6 +990,30 @@ static struct page_info *alloc_heap_pages( total_avail_pages -= request; ASSERT(total_avail_pages >= 0); + if ( d && d->outstanding_pages && !(memflags & MEMF_no_refcount) ) + { + /* + * Adjust claims in the same locked region where total_avail_pages is + * adjusted, not doing so would lead to a window where the amount of + * free memory (avail - claimed) would be incorrect. + * + * Note that by adjusting the claimed amount here it's possible for + * pages to fail to be assigned to the claiming domain while already + * having been subtracted from d->outstanding_pages. Such claimed + * amount is then lost, as the pages that fail to be assigned to the + * domain are freed without replenishing the claim. This is fine given + * claims are only to be used during physmap population as part of + * domain build, and any failure in assign_pages() there will result in + * the domain being destroyed before creation is finished. Losing part + * of the claim makes no difference. + */ + unsigned long outstanding = min(d->outstanding_pages + 0UL, request); + + BUG_ON(outstanding > outstanding_claims); + outstanding_claims -= outstanding; + d->outstanding_pages -= outstanding; + } + check_low_mem_virq(); if ( d != NULL ) diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index 2dcd1d1a4f..2a83b9dacf 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -393,7 +393,8 @@ struct domain unsigned int tot_pages; unsigned int xenheap_pages; /* pages allocated from Xen heap */ - unsigned int outstanding_pages; /* pages claimed but not possessed */ + /* Pages claimed but not possessed, protected by global heap_lock. */ + unsigned int outstanding_pages; unsigned int max_pages; /* maximum value for domain_tot_pages() */ unsigned int extra_pages; /* pages not included in domain_tot_pages() */