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

[Xen-changelog] [xen-unstable] This patch establishes a new abstraction of sharing handles (encoded as a 64bit



# 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


 


Rackspace

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