# HG changeset patch
# Parent 5a299906312e606553e6dd2acbe44ab692722a75
xenpaging: improve evict error handling

Adjust return codes in Xen and handle errors in evict_victim() properly.

p2m_mem_paging_nominate() returns -EAGAIN, p2m_mem_paging_evict()
returns -EBUSY.  Other errors indicate guest failures, which
xenpaging_evict_page() can now catch correctly. Also write() failures
are fatal.

Without this change, evict_victim() may spin forever if the guest is
killed because this function does not get a signal.

Signed-off-by: Olaf Hering <olaf@aepfle.de>

---
 tools/xenpaging/xenpaging.c    |   47 ++++++++++++++++++++++++++---------------
 xen/arch/x86/mm/p2m.c          |    7 +-----
 xen/include/public/mem_event.h |    2 -
 3 files changed, 33 insertions(+), 23 deletions(-)

--- a/tools/xenpaging/xenpaging.c
+++ b/tools/xenpaging/xenpaging.c
@@ -569,29 +569,35 @@ static int xenpaging_evict_page(xenpagin
     xc_interface *xch = paging->xc_handle;
     void *page;
     unsigned long gfn;
-    int ret;
+    int ret = -1;
 
     DECLARE_DOMCTL;
 
     /* Map page to get a handle */
     gfn = victim->gfn;
-    ret = -EFAULT;
     page = xc_map_foreign_pages(xch, paging->mem_event.domain_id,
                                 PROT_READ | PROT_WRITE, &gfn, 1);
     if ( page == NULL )
     {
-        PERROR("Error mapping page %lx", victim->gfn);
+        if ( errno == EINVAL )
+            ret = 1;
+        else
+            PERROR("Error mapping page %lx", victim->gfn);
         goto out;
     }
 
     /* Nominate the page */
-    ret = xc_mem_paging_nominate(xch, paging->mem_event.domain_id, gfn);
-    if ( ret != 0 )
+    if ( xc_mem_paging_nominate(xch, paging->mem_event.domain_id, gfn) )
+    {
+        if ( errno == EAGAIN )
+            ret = 1;
+        else
+            PERROR("Error nominating page %lx", victim->gfn);
         goto out;
+    }
 
     /* Copy page */
-    ret = write_page(fd, page, i);
-    if ( ret != 0 )
+    if ( write_page(fd, page, i) )
     {
         PERROR("Error copying page %lx", victim->gfn);
         goto out;
@@ -601,10 +607,10 @@ static int xenpaging_evict_page(xenpagin
     page = NULL;
 
     /* Tell Xen to evict page */
-    ret = xc_mem_paging_evict(xch, paging->mem_event.domain_id,
-                              victim->gfn);
-    if ( ret != 0 )
+    if ( xc_mem_paging_evict(xch, paging->mem_event.domain_id, victim->gfn) )
     {
+        if ( errno == EBUSY )
+            ret = 1;
         PERROR("Error evicting page %lx", victim->gfn);
         goto out;
     }
@@ -616,6 +622,8 @@ static int xenpaging_evict_page(xenpagin
     /* Record number of evicted pages */
     paging->num_paged_out++;
 
+    ret = 0;
+
  out:
     if (page)
         munmap(page, PAGE_SIZE);
@@ -724,7 +732,7 @@ static int evict_victim(xenpaging_t *pag
                         xenpaging_victim_t *victim, int fd, int i)
 {
     xc_interface *xch = paging->xc_handle;
-    int j = 0;
+    static int num_paged_out;
     int ret;
 
     do
@@ -732,9 +740,13 @@ static int evict_victim(xenpaging_t *pag
         ret = policy_choose_victim(paging, victim);
         if ( ret != 0 )
         {
-            if ( ret != -ENOSPC )
-                ERROR("Error choosing victim");
-            goto out;
+            if ( num_paged_out != paging->num_paged_out ) {
+                DPRINTF("Flushing qemu cache\n");
+                xenpaging_mem_paging_flush_ioemu_cache(paging);
+                num_paged_out = paging->num_paged_out;
+             }
+             ret = -ENOSPC;
+             goto out;
         }
 
         if ( interrupted )
@@ -742,11 +754,12 @@ static int evict_victim(xenpaging_t *pag
             ret = -EINTR;
             goto out;
         }
+
         ret = xenpaging_evict_page(paging, victim, fd, i);
-        if ( ret && j++ % 1000 == 0 )
+        if ( ret < 0 )
         {
-            if ( xenpaging_mem_paging_flush_ioemu_cache(paging) )
-                PERROR("Error flushing ioemu cache");
+            ret = -EINTR;
+            goto out;
         }
     }
     while ( ret );
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -2872,19 +2872,17 @@ int p2m_mem_paging_nominate(struct p2m_d
     p2m_type_t p2mt;
     p2m_access_t a;
     mfn_t mfn;
-    int ret;
+    int ret = -EAGAIN;
 
     p2m_lock(p2m);
 
     mfn = p2m->get_entry(p2m, gfn, &p2mt, &a, p2m_query);
 
     /* Check if mfn is valid */
-    ret = -EINVAL;
     if ( !mfn_valid(mfn) )
         goto out;
 
     /* Check p2m type */
-    ret = -EAGAIN;
     if ( !p2m_is_pageable(p2mt) )
         goto out;
 
@@ -2937,7 +2935,7 @@ int p2m_mem_paging_evict(struct p2m_doma
     p2m_access_t a;
     mfn_t mfn;
     struct domain *d = p2m->domain;
-    int ret = -EINVAL;
+    int ret = -EBUSY;
 
     p2m_lock(p2m);
 
@@ -2950,7 +2948,6 @@ int p2m_mem_paging_evict(struct p2m_doma
     if ( p2mt != p2m_ram_paging_out )
         goto out;
 
-    ret = -EBUSY;
     /* Get the page so it doesn't get modified under Xen's feet */
     page = mfn_to_page(mfn);
     if ( unlikely(!get_page(page, d)) )
--- a/xen/include/public/mem_event.h
+++ b/xen/include/public/mem_event.h
@@ -49,7 +49,7 @@
 #define MEM_EVENT_REASON_INT3        5    /* int3 was hit: gla/gfn are RIP */
 #define MEM_EVENT_REASON_SINGLESTEP  6    /* single step was invoked: gla/gfn are RIP */
 
-#define MEM_EVENT_PAGING_AGE         2UL  /* Number distinguish the mem_paging <-> pager interface */
+#define MEM_EVENT_PAGING_AGE         3UL  /* Number distinguish the mem_paging <-> pager interface */
 
 typedef struct mem_event_shared_page {
     uint32_t port;
