memory exchange for PV domain

diff -r b736475df064 xen/arch/x86/mm.c
--- a/xen/arch/x86/mm.c	Sun Feb 08 23:20:16 2009 +0800
+++ b/xen/arch/x86/mm.c	Mon Feb 09 01:39:03 2009 +0800
@@ -4812,6 +4812,122 @@ void memguard_guard_stack(void *p)
     memguard_guard_range(p, PAGE_SIZE);
 }
 
+int update_pgtable_entry(struct domain *d, xen_pfn_t in_frame,
+                         xen_pfn_t out_frame, int ref)
+{
+    struct page_info *page;
+    int changed = 0;
+    struct page_info *pg = mfn_to_page(in_frame);
+
+    page_list_for_each ( page, &d->page_list )
+    {
+        unsigned long scan_type;
+
+        scan_type = page->u.inuse.type_info & PGT_type_mask;
+        switch (scan_type)
+        {
+#define REPLACE_L(x)    \
+    case  PGT_l##x##_page_table:    \
+    {   \
+        int i; \
+        unsigned long flags, mfn;    \
+        l##x##_pgentry_t *entry;     \
+        entry = map_domain_page(page_to_mfn(page)); \
+        for (i = 0; i < L##x##_PAGETABLE_ENTRIES; i++)  \
+        {                                                   \
+            mfn = l##x##e_get_pfn(entry[i]);    \
+            flags = l##x##e_get_flags(entry[i]);    \
+            if (mfn == in_frame)            \
+            {\
+                entry[i] = l##x##e_from_pfn(out_frame, flags);\
+                if (ref)    \
+                    put_page_and_type(pg);  \
+                changed = 1;    \
+                printk("update one entry here\n");  \
+            }\
+        }   \
+        unmap_domain_page(entry);   \
+        break;  \
+    }
+        REPLACE_L(1)
+        REPLACE_L(2)
+        REPLACE_L(3)
+        REPLACE_L(4)
+
+        default:
+        break;
+        }
+    }
+
+    return changed;
+}
+
+int replace_page(struct domain *d, xen_pfn_t in_frame,
+                 xen_pfn_t *out_frame, unsigned int memflags)
+{
+    xen_pfn_t out_mfn;
+    unsigned long type_info, count_info;
+    struct page_info *pg = mfn_to_page(in_frame), *out;
+
+    if (d != page_get_owner(pg))
+    {
+        dprintk(XENLOG_ERR, "replace page %lx not owned by domain %x\n",
+                 in_frame, d->domain_id);
+        return -EINVAL;
+    }
+
+    out = alloc_domheap_page(NULL, memflags);
+    if (!out)
+    {
+        put_page(mfn_to_page(in_frame));
+        return -ENOMEM;
+    }
+    out_mfn = page_to_mfn(out);
+
+    spin_lock(&d->page_alloc_lock);
+
+    /* Copy the page_info to keep all count/typecount info */
+    type_info = pg->u.inuse.type_info & PGT_type_mask;
+    count_info = pg->count_info;
+
+    /* get page temp to avoid page be freed in this process */
+    if ( unlikely(!get_page(mfn_to_page(in_frame), d)) )
+    {
+        dprintk(XENLOG_INFO, "Fail to get in_frame %lx when replace page\n",
+                             in_frame);
+        return -EINVAL;
+    }
+
+    update_pgtable_entry(d, in_frame, out_mfn, 1);
+    if ( (pg->count_info & PGC_count_mask) != 2 )
+    {
+        dprintk(XENLOG_WARNING, "page is granted to others? count_info %lx\n", 
+                pg->count_info);
+        dprintk(XENLOG_WARNING, "type info %lx\n", pg->u.inuse.type_info);
+        update_pgtable_entry(d, out_mfn, in_frame, 0);
+        free_domheap_page(out);
+        put_page(mfn_to_page(in_frame));
+        spin_unlock(&d->page_alloc_lock);
+        return -EINVAL;
+    }
+
+    guest_remove_page(d, in_frame);
+
+    out->count_info = count_info;
+    out->u.inuse.type_info = type_info;
+    page_set_owner(out, d);
+    wmb(); /* Domain pointer must be visible before updating refcnt. */
+    page_list_add_tail(out, &d->page_list);
+
+    spin_unlock(&d->page_alloc_lock);
+
+    put_page(mfn_to_page(in_frame));
+
+    *out_frame = out_mfn;
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff -r b736475df064 xen/common/memory.c
--- a/xen/common/memory.c	Sun Feb 08 23:20:16 2009 +0800
+++ b/xen/common/memory.c	Mon Feb 09 01:34:38 2009 +0800
@@ -22,6 +22,7 @@
 #include <asm/current.h>
 #include <asm/hardirq.h>
 #include <xen/numa.h>
+#include <public/sched.h>
 #include <public/memory.h>
 #include <xsm/xsm.h>
 
@@ -214,6 +215,91 @@ static void decrease_reservation(struct 
  out:
     a->nr_done = i;
 }
+
+static long memory_page_offline(XEN_GUEST_HANDLE(xen_memory_page_offline_t) arg)
+{
+    struct xen_memory_page_offline offline;
+    unsigned long i;
+    long          rc = 0;
+    struct domain *d;
+    struct page_info *page;
+
+    if ( copy_from_guest(&offline, arg, 1) )
+        return -EFAULT;
+
+    /* only privileged domain can ask for this */
+    if (!IS_PRIV(current->domain))
+        return -EPERM;
+
+    d = get_domain_by_id(offline.domid);
+
+    if (!d)
+        return -EINVAL;
+    /* Domain must be shutdown in advance */
+    if (!(d->is_shut_down && (d->shutdown_code == SHUTDOWN_suspend)))
+    {
+        dprintk(XENLOG_WARNING, "Try to offline page for running domain \n");
+        put_domain(d);
+        return -EINVAL;
+    }
+
+    for ( i = offline.nr_offlined;
+          i < offline.num;
+          i++ )
+    {
+        unsigned int  node, mem_flags = 0;
+        xen_pfn_t in_frame, out_frame;
+
+        if ( hypercall_preempt_check() )
+        {
+            offline.nr_offlined = i;
+            if ( copy_field_to_guest(arg, &offline, nr_offlined) )
+                return -EFAULT;
+            return hypercall_create_continuation(
+                __HYPERVISOR_memory_op, "lh", XENMEM_page_offline, arg);
+        }
+
+        if (unlikely(__copy_from_guest_offset(
+                  &mem_flags, offline.mem_flags, i, 1)))
+        {
+            rc = -EFAULT;
+            break;
+        }
+
+        if (unlikely(__copy_from_guest_offset(
+                  &in_frame, offline.start_mfn, i, 1)))
+        {
+            rc = -EFAULT;
+            break;
+        }
+
+        page = mfn_to_page(in_frame);
+        node = XENMEMF_get_node(mem_flags);
+        mem_flags |= MEMF_bits(domain_clamp_alloc_bitsize(
+                              d,
+                              XENMEMF_get_address_bits(mem_flags) ? :
+                              (BITS_PER_LONG+PAGE_SHIFT)));
+        if ( node == NUMA_NO_NODE )
+            node = domain_to_node(d);
+        mem_flags |= MEMF_node(node);
+
+        rc = replace_page(d, in_frame,&out_frame, mem_flags);
+
+        /*
+         * No need for rollback since the replace is harmless
+         */
+        if (rc)
+            break;
+
+        __copy_to_guest_offset(
+          offline.out, i, &out_frame, 1);
+    }
+
+    offline.nr_offlined = i;
+    if ( copy_field_to_guest(arg, &offline, nr_offlined) )
+        rc = -EFAULT;
+    return rc;
+} 
 
 static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
 {
@@ -513,6 +599,10 @@ long do_memory_op(unsigned long cmd, XEN
         rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t));
         break;
 
+    case XENMEM_page_offline:
+        rc = memory_page_offline(guest_handle_cast(arg, xen_memory_page_offline_t));
+        break;
+
     case XENMEM_maximum_ram_page:
         rc = max_page;
         break;
diff -r b736475df064 xen/include/asm-x86/mm.h
--- a/xen/include/asm-x86/mm.h	Sun Feb 08 23:20:16 2009 +0800
+++ b/xen/include/asm-x86/mm.h	Sun Feb 08 23:20:19 2009 +0800
@@ -492,6 +492,8 @@ unsigned int domain_clamp_alloc_bitsize(
 # define domain_clamp_alloc_bitsize(d, b) (b)
 #endif
 
+int replace_page(struct domain *d, xen_pfn_t in_frame, xen_pfn_t *out_frame, unsigned int mem_flags);
+
 unsigned long domain_get_maximum_gpfn(struct domain *d);
 
 extern struct domain *dom_xen, *dom_io;	/* for vmcoreinfo */
diff -r b736475df064 xen/include/public/memory.h
--- a/xen/include/public/memory.h	Sun Feb 08 23:20:16 2009 +0800
+++ b/xen/include/public/memory.h	Sun Feb 08 23:20:19 2009 +0800
@@ -129,6 +129,21 @@ typedef struct xen_memory_exchange xen_m
 typedef struct xen_memory_exchange xen_memory_exchange_t;
 DEFINE_XEN_GUEST_HANDLE(xen_memory_exchange_t);
 
+#define XENMEM_page_offline         18
+struct xen_memory_page_offline {
+    uint32_t num;
+    domid_t  domid;
+
+    XEN_GUEST_HANDLE(xen_pfn_t) start_mfn;
+    XEN_GUEST_HANDLE(uint32)  mem_flags;
+
+    XEN_GUEST_HANDLE(xen_pfn_t) out;
+
+    xen_ulong_t nr_offlined;
+};
+typedef struct xen_memory_page_offline xen_memory_page_offline_t;
+DEFINE_XEN_GUEST_HANDLE(xen_memory_page_offline_t);
+
 /*
  * Returns the maximum machine frame number of mapped RAM in this system.
  * This command always succeeds (it never returns an error code).
