# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1261031276 0
# Node ID 46754a9f0be1bc2eddb16af5124ff5bb30b93e41
# Parent 7c47306f59bf9128150b0f7d9710b000f6e75b71
This patch establishes a new abstraction of sharing handles (encoded as a 64bit
int), each corresponding to a single sharable pages. Externally all sharing
related
operations (e.g. nominate/share) will use sharing handles, thus solving a lot of
consistency problems (like: is this sharable page still the same sharable page
as before).
Internally, sharing handles can be translated to the MFNs (using a newly created
hashtable), and then for each MFNs a doubly linked list of GFNs translating to
this MFN is maintained. Finally, sharing handle is stored in page_info strucutre
for each sharable MFN.
All this allows to share and unshare pages efficiently. However, at the moment a
single lock is used to protect the sharing handle hash table. For scalability
reasons, the locking needs to be made more granular.
Signed-off-by: Grzegorz Milos <Grzegorz.Milos@xxxxxxxxxx>
---
xen/arch/x86/mm.c | 3
xen/arch/x86/mm/mem_sharing.c | 293 +++++++++++++++++++++++++++++++++++++-
xen/include/asm-x86/mem_sharing.h | 7
xen/include/asm-x86/mm.h | 2
4 files changed, 297 insertions(+), 8 deletions(-)
diff -r 7c47306f59bf -r 46754a9f0be1 xen/arch/x86/mm.c
--- a/xen/arch/x86/mm.c Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/arch/x86/mm.c Thu Dec 17 06:27:56 2009 +0000
@@ -114,6 +114,7 @@
#include <xsm/xsm.h>
#include <xen/trace.h>
#include <asm/setup.h>
+#include <asm/mem_sharing.h>
/*
* Mapping of first 2 or 4 megabytes of memory. This is mapped with 4kB
@@ -314,6 +315,8 @@ void __init arch_init_memory(void)
}
subarch_init_memory();
+
+ mem_sharing_init();
}
int page_is_ram_type(unsigned long mfn, unsigned long mem_type)
diff -r 7c47306f59bf -r 46754a9f0be1 xen/arch/x86/mm/mem_sharing.c
--- a/xen/arch/x86/mm/mem_sharing.c Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/arch/x86/mm/mem_sharing.c Thu Dec 17 06:27:56 2009 +0000
@@ -25,13 +25,158 @@
#include <asm/p2m.h>
#include <asm/mem_event.h>
#include <xen/domain_page.h>
-
+#include <xen/types.h>
+#include <xen/spinlock.h>
+#include <xen/mm.h>
+#include <xen/sched.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 shr_handle_t next_handle = 1;
+
+typedef struct shr_hash_entry
+{
+ shr_handle_t handle;
+ mfn_t mfn;
+ struct shr_hash_entry *next;
+ struct list_head gfns;
+} shr_hash_entry_t;
+
+#define SHR_HASH_LENGTH 1000
+static shr_hash_entry_t *shr_hash[SHR_HASH_LENGTH];
+
+typedef struct gfn_info
+{
+ unsigned long gfn;
+ domid_t domain;
+ struct list_head list;
+} gfn_info_t;
+
+typedef struct shr_lock
+{
+ spinlock_t lock; /* mem sharing lock */
+ int locker; /* processor which holds the lock */
+ const char *locker_function; /* func that took it */
+} shr_lock_t;
+static shr_lock_t shr_lock;
+
+#define shr_lock_init(_i) \
+ do { \
+ spin_lock_init(&shr_lock.lock); \
+ shr_lock.locker = -1; \
+ shr_lock.locker_function = "nobody"; \
+ } while (0)
+
+#define shr_locked_by_me(_i) \
+ (current->processor == shr_lock.locker)
+
+#define shr_lock(_i) \
+ do { \
+ if ( unlikely(shr_lock.locker == current->processor) ) \
+ { \
+ printk("Error: shr lock held by %s\n", \
+ shr_lock.locker_function); \
+ BUG(); \
+ } \
+ spin_lock(&shr_lock.lock); \
+ ASSERT(shr_lock.locker == -1); \
+ shr_lock.locker = current->processor; \
+ shr_lock.locker_function = __func__; \
+ } while (0)
+
+#define shr_unlock(_i) \
+ do { \
+ ASSERT(shr_lock.locker == current->processor); \
+ shr_lock.locker = -1; \
+ shr_lock.locker_function = "nobody"; \
+ spin_unlock(&shr_lock.lock); \
+ } while (0)
+
+
+
+static void mem_sharing_hash_init(void)
+{
+ int i;
+
+ shr_lock_init();
+ for(i=0; i<SHR_HASH_LENGTH; i++)
+ shr_hash[i] = NULL;
+}
+
+static shr_hash_entry_t *mem_sharing_hash_alloc(void)
+{
+ return xmalloc(shr_hash_entry_t);
+}
+
+static void mem_sharing_hash_destroy(shr_hash_entry_t *e)
+{
+ xfree(e);
+}
+
+static gfn_info_t *mem_sharing_gfn_alloc(void)
+{
+ return xmalloc(gfn_info_t);
+}
+
+static void mem_sharing_gfn_destroy(gfn_info_t *gfn_info)
+{
+ xfree(gfn_info);
+}
+
+static shr_hash_entry_t* mem_sharing_hash_lookup(shr_handle_t handle)
+{
+ shr_hash_entry_t *e;
+
+ e = shr_hash[handle % SHR_HASH_LENGTH];
+ while(e != NULL)
+ {
+ if(e->handle == handle)
+ return e;
+ e = e->next;
+ }
+
+ return NULL;
+}
+
+static shr_hash_entry_t* mem_sharing_hash_insert(shr_handle_t handle, mfn_t
mfn)
+{
+ shr_hash_entry_t *e, **ee;
+
+ e = mem_sharing_hash_alloc();
+ if(e == NULL) return NULL;
+ e->handle = handle;
+ e->mfn = mfn;
+ ee = &shr_hash[handle % SHR_HASH_LENGTH];
+ e->next = *ee;
+ *ee = e;
+ return e;
+}
+
+static void mem_sharing_hash_delete(shr_handle_t handle)
+{
+ shr_hash_entry_t **pprev, *e;
+
+ pprev = &shr_hash[handle % SHR_HASH_LENGTH];
+ e = *pprev;
+ while(e != NULL)
+ {
+ if(e->handle == handle)
+ {
+ *pprev = e->next;
+ mem_sharing_hash_destroy(e);
+ return;
+ }
+ pprev = &e->next;
+ e = e->next;
+ }
+ printk("Could not find shr entry for handle %lx\n", handle);
+ BUG();
+}
static struct page_info* mem_sharing_alloc_page(struct domain *d,
@@ -181,12 +326,18 @@ int mem_sharing_debug_gref(struct domain
int mem_sharing_nominate_page(struct domain *d,
unsigned long gfn,
- int expected_refcnt)
+ int expected_refcnt,
+ shr_handle_t *phandle)
{
p2m_type_t p2mt;
mfn_t mfn;
struct page_info *page;
int ret;
+ shr_handle_t handle;
+ shr_hash_entry_t *hash_entry;
+ struct gfn_info *gfn_info;
+
+ *phandle = 0UL;
mfn = gfn_to_mfn(d, gfn, &p2mt);
@@ -204,6 +355,22 @@ int mem_sharing_nominate_page(struct dom
ret = page_make_sharable(d, page, expected_refcnt);
if(ret)
goto out;
+
+ /* Create the handle */
+ ret = -ENOMEM;
+ shr_lock();
+ handle = next_handle++;
+ if((hash_entry = mem_sharing_hash_insert(handle, mfn)) == NULL)
+ {
+ shr_unlock();
+ goto out;
+ }
+ if((gfn_info = mem_sharing_gfn_alloc()) == NULL)
+ {
+ mem_sharing_hash_destroy(hash_entry);
+ shr_unlock();
+ goto out;
+ }
/* Change the p2m type */
if(p2m_change_type(d, gfn, p2mt, p2m_ram_shared) != p2mt)
@@ -213,15 +380,74 @@ int mem_sharing_nominate_page(struct dom
* 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);
+ mem_sharing_hash_destroy(hash_entry);
+ mem_sharing_gfn_destroy(gfn_info);
+ shr_unlock();
goto out;
}
/* Update m2p entry to SHARED_M2P_ENTRY */
set_gpfn_from_mfn(mfn_x(mfn), SHARED_M2P_ENTRY);
+ INIT_LIST_HEAD(&hash_entry->gfns);
+ INIT_LIST_HEAD(&gfn_info->list);
+ list_add(&gfn_info->list, &hash_entry->gfns);
+ gfn_info->gfn = gfn;
+ gfn_info->domain = d->domain_id;
+ page->shr_handle = handle;
+ *phandle = handle;
+ shr_unlock();
+
ret = 0;
out:
+ return ret;
+}
+
+int mem_sharing_share_pages(shr_handle_t sh, shr_handle_t ch)
+{
+ shr_hash_entry_t *se, *ce;
+ struct page_info *spage, *cpage;
+ struct list_head *le, *te;
+ struct gfn_info *gfn;
+ struct domain *d;
+ int ret;
+
+ shr_lock();
+
+ ret = -1;
+ se = mem_sharing_hash_lookup(sh);
+ if(se == NULL) goto err_out;
+ ret = -2;
+ ce = mem_sharing_hash_lookup(ch);
+ if(ce == NULL) goto err_out;
+ spage = mfn_to_page(se->mfn);
+ cpage = mfn_to_page(ce->mfn);
+ list_for_each_safe(le, te, &ce->gfns)
+ {
+ gfn = list_entry(le, struct gfn_info, list);
+ /* Get the source page and type, this should never fail
+ * because we are under shr lock, and got non-null se */
+ BUG_ON(!get_page_and_type(spage, dom_cow, PGT_shared_page));
+ /* Move the gfn_info from ce list to se list */
+ list_del(&gfn->list);
+ d = get_domain_by_id(gfn->domain);
+ BUG_ON(!d);
+ BUG_ON(set_shared_p2m_entry(d, gfn->gfn, se->mfn) == 0);
+ put_domain(d);
+ list_add(&gfn->list, &se->gfns);
+ put_page_and_type(cpage);
+ }
+ ASSERT(list_empty(&ce->gfns));
+ mem_sharing_hash_delete(ch);
+ /* Free the client page */
+ if(test_and_clear_bit(_PGC_allocated, &cpage->count_info))
+ put_page(cpage);
+ ret = 0;
+
+err_out:
+ shr_unlock();
+
return ret;
}
@@ -233,19 +459,63 @@ int mem_sharing_unshare_page(struct doma
mfn_t mfn;
struct page_info *page, *old_page;
void *s, *t;
- int ret;
+ int ret, last_gfn;
+ shr_hash_entry_t *hash_entry;
+ struct gfn_info *gfn_info = NULL;
+ shr_handle_t handle;
+ struct list_head *le;
mfn = gfn_to_mfn(d, gfn, &p2mt);
page = mfn_to_page(mfn);
-
+ handle = page->shr_handle;
+
+ /* Remove the gfn_info from the list */
+ shr_lock();
+ hash_entry = mem_sharing_hash_lookup(handle);
+ list_for_each(le, &hash_entry->gfns)
+ {
+ gfn_info = list_entry(le, struct gfn_info, list);
+ if((gfn_info->gfn == gfn) && (gfn_info->domain == d->domain_id))
+ goto gfn_found;
+ }
+ printk("Could not find gfn_info for shared gfn: %lx\n", gfn);
+ BUG();
+gfn_found:
+ /* Delete gfn_info from the list, but hold on to it, until we've allocated
+ * memory to make a copy */
+ list_del(&gfn_info->list);
+ last_gfn = list_empty(&hash_entry->gfns);
+
+ /* If the GFN is getting destroyed drop the references to MFN
+ * (possibly freeing the page), and exit early */
+ if(flags & MEM_SHARING_DESTROY_GFN)
+ {
+ mem_sharing_gfn_destroy(gfn_info);
+ if(last_gfn) mem_sharing_hash_delete(handle);
+ shr_unlock();
+ put_page_and_type(page);
+ if(last_gfn &&
+ test_and_clear_bit(_PGC_allocated, &page->count_info))
+ put_page(page);
+ return 0;
+ }
+
ret = page_make_private(d, page);
+ BUG_ON(last_gfn & ret);
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;
+ if(!page)
+ {
+ /* We've failed to obtain memory for private page. Need to re-add the
+ * gfn_info to relevant list */
+ list_add(&gfn_info->list, &hash_entry->gfns);
+ shr_unlock();
+ return -ENOMEM;
+ }
s = map_domain_page(__page_to_mfn(old_page));
t = map_domain_page(__page_to_mfn(page));
@@ -257,6 +527,11 @@ int mem_sharing_unshare_page(struct doma
put_page_and_type(old_page);
private_page_found:
+ /* We've got a private page, we can commit the gfn destruction */
+ mem_sharing_gfn_destroy(gfn_info);
+ if(last_gfn) mem_sharing_hash_delete(handle);
+ shr_unlock();
+
if(p2m_change_type(d, gfn, p2m_ram_shared, p2m_ram_rw) !=
p2m_ram_shared)
{
@@ -269,5 +544,9 @@ private_page_found:
return 0;
}
-
-
+void mem_sharing_init(void)
+{
+ printk("Initing memory sharing.\n");
+ mem_sharing_hash_init();
+}
+
diff -r 7c47306f59bf -r 46754a9f0be1 xen/include/asm-x86/mem_sharing.h
--- a/xen/include/asm-x86/mem_sharing.h Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/include/asm-x86/mem_sharing.h Thu Dec 17 06:27:56 2009 +0000
@@ -24,9 +24,13 @@
#define sharing_supported(_d) \
(is_hvm_domain(_d) && (_d)->arch.hvm_domain.hap_enabled)
+
+typedef uint64_t shr_handle_t;
+
int mem_sharing_nominate_page(struct domain *d,
unsigned long gfn,
- int expected_refcnt);
+ int expected_refcnt,
+ shr_handle_t *phandle);
#define MEM_SHARING_MUST_SUCCEED (1<<0)
#define MEM_SHARING_DESTROY_GFN (1<<1)
int mem_sharing_unshare_page(struct domain *d,
@@ -34,5 +38,6 @@ int mem_sharing_unshare_page(struct doma
uint16_t flags);
int mem_sharing_sharing_resume(struct domain *d);
int mem_sharing_cache_resize(struct domain *d, int new_size);
+void mem_sharing_init(void);
#endif /* __MEM_SHARING_H__ */
diff -r 7c47306f59bf -r 46754a9f0be1 xen/include/asm-x86/mm.h
--- a/xen/include/asm-x86/mm.h Thu Dec 17 06:27:56 2009 +0000
+++ b/xen/include/asm-x86/mm.h Thu Dec 17 06:27:56 2009 +0000
@@ -43,6 +43,8 @@ struct page_info
struct page_list_entry list;
/* For non-pinnable shadows, a higher entry that points at us. */
paddr_t up;
+ /* For shared/sharable pages the sharing handle */
+ uint64_t shr_handle;
};
/* Reference count and various PGC_xxx flags and fields. */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|