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

[Xen-changelog] [xen-unstable] This patch defines a new P2M type used for sharable/shared pages. It also



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1261031276 0
# Node ID 7d841016536f4e5de66b387114c8e4ed67f76148
# Parent  3a3be3938b2c71d179330b10fcb187db00c837b2
This patch defines a new P2M type used for sharable/shared pages. It also
implements the basic functions to nominate GFNs for sharing, and to break
sharing (either by making page 'private' or creating private copy),
mem_sharing_nominate_page() and mem_sharing_unshare_page() respectively. Note
pages cannot be shared yet, because there is no efficient way to find all GFNs
mapping to the two MFNs scheduled for sharing.

Signed-off-by: Grzegorz Milos <Grzegorz.Milos@xxxxxxxxxx>
---
 tools/xenpaging/xenpaging.c       |    4 
 xen/arch/x86/mm/Makefile          |    1 
 xen/arch/x86/mm/mem_sharing.c     |  268 ++++++++++++++++++++++++++++++++++++++
 xen/arch/x86/mm/p2m.c             |   41 +++++
 xen/include/asm-x86/mem_sharing.h |   38 +++++
 xen/include/asm-x86/p2m.h         |   20 ++
 xen/include/public/mem_event.h    |    4 
 7 files changed, 369 insertions(+), 7 deletions(-)

diff -r 3a3be3938b2c -r 7d841016536f tools/xenpaging/xenpaging.c
--- a/tools/xenpaging/xenpaging.c       Thu Dec 17 06:27:56 2009 +0000
+++ b/tools/xenpaging/xenpaging.c       Thu Dec 17 06:27:56 2009 +0000
@@ -566,11 +566,11 @@ int main(int argc, char *argv[])
             else
             {
                 DPRINTF("page already populated (domain = %d; vcpu = %d; gfn = 
%lx; paused = %ld)\n",
-                        paging->mem_event.domain_id, req.vcpu_id, req.gfn, 
req.flags & MEM_EVENT_FLAG_PAUSED);
+                        paging->mem_event.domain_id, req.vcpu_id, req.gfn, 
req.flags & MEM_EVENT_FLAG_VCPU_PAUSED);
 
                 /* Tell Xen to resume the vcpu */
                 /* XXX: Maybe just check if the vcpu was paused? */
-                if ( req.flags & MEM_EVENT_FLAG_PAUSED )
+                if ( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
                 {
                     /* Prepare the response */
                     rsp.gfn = req.gfn;
diff -r 3a3be3938b2c -r 7d841016536f xen/arch/x86/mm/Makefile
--- a/xen/arch/x86/mm/Makefile  Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/arch/x86/mm/Makefile  Thu Dec 17 06:27:56 2009 +0000
@@ -8,6 +8,7 @@ obj-$(x86_64) += guest_walk_4.o
 obj-$(x86_64) += guest_walk_4.o
 obj-y += mem_event.o
 obj-y += mem_paging.o
+obj-y += mem_sharing.o
 
 guest_walk_%.o: guest_walk.c Makefile
        $(CC) $(CFLAGS) -DGUEST_PAGING_LEVELS=$* -c $< -o $@
diff -r 3a3be3938b2c -r 7d841016536f xen/arch/x86/mm/mem_sharing.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/mm/mem_sharing.c     Thu Dec 17 06:27:56 2009 +0000
@@ -0,0 +1,268 @@
+/******************************************************************************
+ * arch/x86/mm/mem_sharing.c
+ *
+ * Memory sharing support.
+ *
+ * Copyright (c) 2009 Citrix (R&D) Ltd. (Grzegorz Milos)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <asm/page.h>
+#include <asm/string.h>
+#include <asm/p2m.h>
+#include <asm/mem_event.h>
+#include <xen/domain_page.h>
+
+#undef mfn_to_page
+#define mfn_to_page(_m) __mfn_to_page(mfn_x(_m))
+#undef mfn_valid
+#define mfn_valid(_mfn) __mfn_valid(mfn_x(_mfn))
+#undef page_to_mfn
+#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg))
+
+
+static struct page_info* mem_sharing_alloc_page(struct domain *d, 
+                                                unsigned long gfn,
+                                                int must_succeed)
+{
+    struct page_info* page;
+    struct vcpu *v = current;
+    mem_event_request_t req;
+
+    page = alloc_domheap_page(d, 0); 
+    if(page != NULL) return page;
+
+    memset(&req, 0, sizeof(req));
+    if(must_succeed) 
+    {
+        /* We do not support 'must_succeed' any more. External operations such
+         * as grant table mappings may fail with OOM condition! 
+         */
+        BUG();
+    }
+    else
+    {
+        /* All foreign attempts to unshare pages should be handled through
+         * 'must_succeed' case. */
+        ASSERT(v->domain->domain_id == d->domain_id);
+        vcpu_pause_nosync(v);
+        req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED;
+    }
+        
+    /* XXX: Need to reserve a request, not just check the ring! */
+    if(mem_event_check_ring(d)) return page;
+
+    req.flags |= MEM_EVENT_FLAG_OUT_OF_MEM;
+    req.gfn = gfn;
+    req.p2mt = p2m_ram_shared;
+    req.vcpu_id = v->vcpu_id;
+    mem_event_put_request(d, &req);
+
+    return page;
+}
+
+int mem_sharing_sharing_resume(struct domain *d)
+{
+    mem_event_response_t rsp;
+
+    /* Get request off the ring */
+    mem_event_get_response(d, &rsp);
+
+    /* Unpause domain/vcpu */
+    if( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
+        vcpu_unpause(d->vcpu[rsp.vcpu_id]);
+    if( rsp.flags & MEM_EVENT_FLAG_DOM_PAUSED )
+        domain_unpause(d);
+
+    return 0;
+}
+
+int mem_sharing_debug_mfn(unsigned long mfn)
+{
+    struct page_info *page;
+
+    if(!mfn_valid(_mfn(mfn)))
+    {
+        printk("Invalid MFN=%lx\n", mfn);
+        return -1;
+    }
+    page = mfn_to_page(_mfn(mfn));
+
+    printk("Debug page: MFN=%lx is ci=%lx, ti=%lx, owner_id=%d\n",
+            mfn_x(page_to_mfn(page)), 
+            page->count_info, 
+            page->u.inuse.type_info,
+            page_get_owner(page)->domain_id);
+
+    return 0;
+}
+
+int mem_sharing_debug_gfn(struct domain *d, unsigned long gfn)
+{
+    p2m_type_t p2mt;
+    mfn_t mfn;
+    struct page_info *page;
+
+    mfn = gfn_to_mfn(d, gfn, &p2mt);
+    page = mfn_to_page(mfn);
+
+    printk("Debug for domain=%d, gfn=%lx, ", 
+            d->domain_id, 
+            gfn);
+    return mem_sharing_debug_mfn(mfn_x(mfn));
+}
+
+#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
+#define shared_entry_v1(t, e) \
+    ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
+#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
+#define shared_entry_v2(t, e) \
+    ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
+#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
+#define status_entry(t, e) \
+    ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
+
+static grant_entry_header_t *
+shared_entry_header(struct grant_table *t, grant_ref_t ref)
+{
+    ASSERT(t->gt_version != 0);
+    if (t->gt_version == 1)
+        return (grant_entry_header_t*)&shared_entry_v1(t, ref);
+    else
+        return &shared_entry_v2(t, ref).hdr;
+}
+
+int mem_sharing_debug_gref(struct domain *d, grant_ref_t ref)
+{
+    grant_entry_header_t *shah;
+    uint16_t status;
+    unsigned long gfn;
+
+    if(d->grant_table->gt_version < 1)
+    {
+        printk("Asked to debug [dom=%d,gref=%d], but not yet inited.\n",
+                d->domain_id, ref);
+        return -1;
+    }
+    shah = shared_entry_header(d->grant_table, ref);
+    if (d->grant_table->gt_version == 1) 
+    {
+        grant_entry_v1_t *sha1;
+        sha1 = &shared_entry_v1(d->grant_table, ref);
+        status = shah->flags;
+        gfn = sha1->frame;
+    } 
+    else 
+    {
+        grant_entry_v2_t *sha2;
+        sha2 = &shared_entry_v2(d->grant_table, ref);
+        status = status_entry(d->grant_table, ref);
+        gfn = sha2->full_page.frame;
+    }
+    
+    printk("==> Grant [dom=%d,ref=%d], status=%x. ", 
+            d->domain_id, ref, status);
+
+    return mem_sharing_debug_gfn(d, gfn); 
+}
+
+int mem_sharing_nominate_page(struct domain *d, 
+                              unsigned long gfn,
+                              int expected_refcnt)
+{
+    p2m_type_t p2mt;
+    mfn_t mfn;
+    struct page_info *page;
+    int ret;
+
+    mfn = gfn_to_mfn(d, gfn, &p2mt);
+
+    /* Check if mfn is valid */
+    ret = -EINVAL;
+    if (!mfn_valid(mfn))
+        goto out;
+
+    /* Check p2m type */
+    if (!p2m_is_sharable(p2mt))
+        goto out;
+
+    /* Try to convert the mfn to the sharable type */
+    page = mfn_to_page(mfn);
+    ret = page_make_sharable(d, page, expected_refcnt); 
+    if(ret) 
+        goto out;
+
+    /* Change the p2m type */
+    if(p2m_change_type(d, gfn, p2mt, p2m_ram_shared) != p2mt) 
+    {
+        /* This is unlikely, as the type must have changed since we've checked
+         * it a few lines above.
+         * The mfn needs to revert back to rw type. This should never fail,
+         * since no-one knew that the mfn was temporarily sharable */
+        ASSERT(page_make_private(d, page) == 0);
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+int mem_sharing_unshare_page(struct domain *d, 
+                             unsigned long gfn, 
+                             uint16_t flags)
+{
+    p2m_type_t p2mt;
+    mfn_t mfn;
+    struct page_info *page, *old_page;
+    void *s, *t;
+    int ret;
+
+    mfn = gfn_to_mfn(d, gfn, &p2mt);
+
+    page = mfn_to_page(mfn);
+
+    ret = page_make_private(d, page);
+    if(ret == 0) goto private_page_found;
+        
+    old_page = page;
+    page = mem_sharing_alloc_page(d, gfn, flags & MEM_SHARING_MUST_SUCCEED);
+    BUG_ON(!page && (flags & MEM_SHARING_MUST_SUCCEED));
+    if(!page) return -ENOMEM;
+
+    s = map_domain_page(__page_to_mfn(old_page));
+    t = map_domain_page(__page_to_mfn(page));
+    memcpy(t, s, PAGE_SIZE);
+    unmap_domain_page(s);
+    unmap_domain_page(t);
+
+    ASSERT(set_shared_p2m_entry(d, gfn, page_to_mfn(page)) != 0);
+    put_page_and_type(old_page);
+
+private_page_found:    
+    if(p2m_change_type(d, gfn, p2m_ram_shared, p2m_ram_rw) != 
+                                                p2m_ram_shared) 
+    {
+        printk("Could not change p2m type.\n");
+        BUG();
+    }
+
+    return 0;
+}
+
+
+
diff -r 3a3be3938b2c -r 7d841016536f xen/arch/x86/mm/p2m.c
--- a/xen/arch/x86/mm/p2m.c     Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/arch/x86/mm/p2m.c     Thu Dec 17 06:27:56 2009 +0000
@@ -32,6 +32,7 @@
 #include <xen/iommu.h>
 #include <asm/mem_event.h>
 #include <public/mem_event.h>
+#include <asm/mem_sharing.h>
 #include <xen/event.h>
 
 /* Debugging and auditing of the P2M code? */
@@ -1629,8 +1630,17 @@ void p2m_teardown(struct domain *d)
 {
     struct page_info *pg;
     struct p2m_domain *p2m = d->arch.p2m;
+    unsigned long gfn;
+    p2m_type_t t;
+    mfn_t mfn;
 
     p2m_lock(p2m);
+    for(gfn=0; gfn < p2m->max_mapped_pfn; gfn++)
+    {
+        mfn = p2m->get_entry(d, gfn, &t, p2m_query);
+        if(mfn_valid(mfn) && (t == p2m_ram_shared))
+            BUG_ON(mem_sharing_unshare_page(d, gfn, MEM_SHARING_DESTROY_GFN));
+    }
     d->arch.phys_table = pagetable_null();
 
     while ( (pg = page_list_remove_head(&p2m->pages)) )
@@ -2301,6 +2311,33 @@ clear_mmio_p2m_entry(struct domain *d, u
     return rc;
 }
 
+int
+set_shared_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn)
+{
+    int rc = 0;
+    p2m_type_t ot;
+    mfn_t omfn;
+
+    if ( !paging_mode_translate(d) )
+        return 0;
+
+    omfn = gfn_to_mfn_query(d, gfn, &ot);
+    /* At the moment we only allow p2m change if gfn has already been made
+     * sharable first */
+    ASSERT(p2m_is_shared(ot));
+    ASSERT(mfn_valid(omfn));
+    /* XXX: M2P translations have to be handled properly for shared pages */
+    set_gpfn_from_mfn(mfn_x(omfn), INVALID_M2P_ENTRY);
+
+    P2M_DEBUG("set shared %lx %lx\n", gfn, mfn_x(mfn));
+    rc = set_p2m_entry(d, gfn, mfn, 0, p2m_ram_shared);
+    if ( 0 == rc )
+        gdprintk(XENLOG_ERR,
+            "set_mmio_p2m_entry: set_p2m_entry failed! mfn=%08lx\n",
+            gmfn_to_mfn(d, gfn));
+    return rc;
+}
+
 int p2m_mem_paging_nominate(struct domain *d, unsigned long gfn)
 {
     struct page_info *page;
@@ -2406,7 +2443,7 @@ void p2m_mem_paging_populate(struct doma
     if ( v->domain->domain_id == d->domain_id )
     {
         vcpu_pause_nosync(v);
-        req.flags |= MEM_EVENT_FLAG_PAUSED;
+        req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED;
     }
 
     /* Send request to pager */
@@ -2450,7 +2487,7 @@ void p2m_mem_paging_resume(struct domain
     p2m_unlock(d->arch.p2m);
 
     /* Unpause domain */
-    if ( rsp.flags & MEM_EVENT_FLAG_PAUSED )
+    if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
         vcpu_unpause(d->vcpu[rsp.vcpu_id]);
 
     /* Unpause any domains that were paused because the ring was full */
diff -r 3a3be3938b2c -r 7d841016536f xen/include/asm-x86/mem_sharing.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/asm-x86/mem_sharing.h Thu Dec 17 06:27:56 2009 +0000
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * include/asm-x86/mem_sharing.h
+ *
+ * Memory sharing support.
+ *
+ * Copyright (c) 2009 Citrix (R)&D) Ltd. (Grzegorz Milos)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __MEM_SHARING_H__
+#define __MEM_SHARING_H__
+
+#define sharing_supported(_d) \
+    (is_hvm_domain(_d) && (_d)->arch.hvm_domain.hap_enabled) 
+int mem_sharing_nominate_page(struct domain *d, 
+                              unsigned long gfn,
+                              int expected_refcnt);
+#define MEM_SHARING_MUST_SUCCEED      (1<<0)
+#define MEM_SHARING_DESTROY_GFN       (1<<1)
+int mem_sharing_unshare_page(struct domain *d, 
+                             unsigned long gfn, 
+                             uint16_t flags);
+int mem_sharing_sharing_resume(struct domain *d);
+int mem_sharing_cache_resize(struct domain *d, int new_size);
+
+#endif /* __MEM_SHARING_H__ */
diff -r 3a3be3938b2c -r 7d841016536f xen/include/asm-x86/p2m.h
--- a/xen/include/asm-x86/p2m.h Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/include/asm-x86/p2m.h Thu Dec 17 06:27:56 2009 +0000
@@ -28,6 +28,7 @@
 
 #include <xen/config.h>
 #include <xen/paging.h>
+#include <asm/mem_sharing.h>
 
 /*
  * The phys_to_machine_mapping maps guest physical frame numbers 
@@ -80,6 +81,8 @@ typedef enum {
     p2m_ram_paged = 10,           /* Memory that has been paged out */
     p2m_ram_paging_in = 11,       /* Memory that is being paged in */
     p2m_ram_paging_in_start = 12, /* Memory that is being paged in */
+
+    p2m_ram_shared = 13,          /* Shared or sharable memory */
 } p2m_type_t;
 
 typedef enum {
@@ -98,7 +101,8 @@ typedef enum {
                        | p2m_to_mask(p2m_ram_paging_out)      \
                        | p2m_to_mask(p2m_ram_paged)           \
                        | p2m_to_mask(p2m_ram_paging_in_start) \
-                       | p2m_to_mask(p2m_ram_paging_in))
+                       | p2m_to_mask(p2m_ram_paging_in)       \
+                       | p2m_to_mask(p2m_ram_shared))
 
 /* Grant mapping types, which map to a real machine frame in another
  * VM */
@@ -112,7 +116,8 @@ typedef enum {
 /* Read-only types, which must have the _PAGE_RW bit clear in their PTEs */
 #define P2M_RO_TYPES (p2m_to_mask(p2m_ram_logdirty)     \
                       | p2m_to_mask(p2m_ram_ro)         \
-                      | p2m_to_mask(p2m_grant_map_ro) )
+                      | p2m_to_mask(p2m_grant_map_ro)   \
+                      | p2m_to_mask(p2m_ram_shared) )
 
 #define P2M_MAGIC_TYPES (p2m_to_mask(p2m_populate_on_demand))
 
@@ -125,6 +130,12 @@ typedef enum {
                           | p2m_to_mask(p2m_ram_paging_in))
 
 #define P2M_PAGED_TYPES (p2m_to_mask(p2m_ram_paged))
+
+/* Shared types */
+/* XXX: Sharable types could include p2m_ram_ro too, but we would need to
+ * reinit the type correctly after fault */
+#define P2M_SHARABLE_TYPES (p2m_to_mask(p2m_ram_rw))
+#define P2M_SHARED_TYPES   (p2m_to_mask(p2m_ram_shared))
 
 /* Useful predicates */
 #define p2m_is_ram(_t) (p2m_to_mask(_t) & P2M_RAM_TYPES)
@@ -140,6 +151,8 @@ typedef enum {
 #define p2m_is_pageable(_t) (p2m_to_mask(_t) & P2M_PAGEABLE_TYPES)
 #define p2m_is_paging(_t)   (p2m_to_mask(_t) & P2M_PAGING_TYPES)
 #define p2m_is_paged(_t)    (p2m_to_mask(_t) & P2M_PAGED_TYPES)
+#define p2m_is_sharable(_t) (p2m_to_mask(_t) & P2M_SHARABLE_TYPES)
+#define p2m_is_shared(_t)   (p2m_to_mask(_t) & P2M_SHARED_TYPES)
 
 /* Populate-on-demand */
 #define POPULATE_ON_DEMAND_MFN  (1<<9)
@@ -391,6 +404,9 @@ p2m_type_t p2m_change_type(struct domain
 /* Set mmio addresses in the p2m table (for pass-through) */
 int set_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn);
 int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn);
+/* Modify p2m table for shared gfn */
+int
+set_shared_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn);
 
 /* Check if a nominated gfn is valid to be paged out */
 int p2m_mem_paging_nominate(struct domain *d, unsigned long gfn);
diff -r 3a3be3938b2c -r 7d841016536f xen/include/public/mem_event.h
--- a/xen/include/public/mem_event.h    Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/include/public/mem_event.h    Thu Dec 17 06:27:56 2009 +0000
@@ -34,7 +34,9 @@
 #define MEM_EVENT_MODE_SYNC_ALL (1 << 1)
 
 /* Memory event flags */
-#define MEM_EVENT_FLAG_PAUSED   (1 << 0)
+#define MEM_EVENT_FLAG_VCPU_PAUSED  (1 << 0)
+#define MEM_EVENT_FLAG_DOM_PAUSED   (1 << 1)
+#define MEM_EVENT_FLAG_OUT_OF_MEM   (1 << 2)
 
 
 typedef struct mem_event_shared_page {

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

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