[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 4/4] xen/mm: Recall claims when offlining pages if needed



Fix a bug where offlining pages could cause an unsigned underflow
in total_avail_pages - outstanding_claims, leading to incorrect
claim behavior.

This issue arises when outstanding claims are close to the total
available pages. It occurse when domain_set_outstanding_claims()
and domain_install_claim_set effectively do this:

 unsigned long avail_pages = total_avail_pages - outstanding_claims;

When this unsigned subtraction underflows, staking claims can succeed
even when there is insufficient unclaimed memory for the new claim.
This leads to a state where claims always succeed, regardless of
actual memory availability.

To prevent this, recall claims when offlining pages if needed to maintain
equilibrium between `total_avail_pages` and outstanding claims for global
and for per-NUMA-node claims.

Signed-off-by: Bernhard Kaindl <bernhard.kaindl@xxxxxxxxxx>
---
 tools/tests/alloc/test-offlining-claims.c |  4 ---
 xen/common/page_alloc.c                   | 42 +++++++++++++++++++++++
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/tools/tests/alloc/test-offlining-claims.c 
b/tools/tests/alloc/test-offlining-claims.c
index d22d270ceeb4..0844c792e740 100644
--- a/tools/tests/alloc/test-offlining-claims.c
+++ b/tools/tests/alloc/test-offlining-claims.c
@@ -41,9 +41,7 @@ static void test_offlining_with_global_claims(int mfn)
     /* ASSERT */
     CHECK_BUDDY(page, "After offlining the 2nd page");
     CHECK(FREE_PAGES == 2, "Expect 2 free pages after offlining two pages");
-    EXPECTED_TO_FAIL_BEGIN();
     CHECK(TOTAL_CLAIMS == 2, "Expect 2 claims after offlining two pages");
-    EXPECTED_TO_FAIL_END(1);
 }
 
 
@@ -75,9 +73,7 @@ static void test_offlining_with_node_claims(int mfn)
     /* ASSERT */
     CHECK_BUDDY(page, "After offlining the 2nd page");
     CHECK(FREE_PAGES == 2, "Expect 2 free pages after offlining two pages");
-    EXPECTED_TO_FAIL_BEGIN();
     CHECK(TOTAL_CLAIMS == 2, "Expect 2 claims after offlining two pages");
-    EXPECTED_TO_FAIL_END(1);
 }
 
 int main(int argc, char *argv[])
diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
index 6101bd6be9a9..adedf6fae590 100644
--- a/xen/common/page_alloc.c
+++ b/xen/common/page_alloc.c
@@ -1575,6 +1575,48 @@ static int reserve_offlined_page(struct page_info *head)
         count++;
     }
 
+    if ( count )
+    {
+        long recall_pages;
+        struct domain *d;
+
+        /* Ensure that claims on the node are in line with its free memory. */
+        recall_pages = node_outstanding_claims[node] - node_avail_pages[node];
+        if ( recall_pages > 0 )
+            /*
+             * node_avail_pages slipped below node_outstanding_claims.
+             * We need to recall claimed pages until the amount of claimed
+             * memory is in line with the amount of available memory again.
+             */
+            for_each_domain ( d )
+            {
+                if ( d->claims[node] )
+                {
+                    recall_pages -= deduct_node_claims(d, node, recall_pages);
+                    if ( recall_pages <= 0 )
+                        break;
+                }
+            }
+
+        /* Ensure that outstanding claims are in line with available memory. */
+        recall_pages = outstanding_claims - total_avail_pages;
+        if ( recall_pages > 0 )
+            /*
+             * total_avail_pages slipped below outstanding_claims.
+             * We need to recall claimed pages until the amount of claimed
+             * memory is in line with the amount of available memory again.
+             */
+            for_each_domain ( d )
+            {
+                if ( d->global_claims )
+                {
+                    recall_pages -= deduct_global_claims(d, recall_pages);
+                    if ( recall_pages <= 0 )
+                        break;
+                }
+            }
+    }
+
     return count;
 }
 
-- 
2.39.5




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.