# 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
|