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

[Xen-devel] [PATCH] Testing for mem event ring management


  • To: xen-devel@xxxxxxxxxxxxxxxxxxx
  • From: Andres Lagar-Cavilla <andres@xxxxxxxxxxxxxxxx>
  • Date: Thu, 12 Jan 2012 11:41:41 -0500
  • Cc: olaf@xxxxxxxxx, tim@xxxxxxx, andres@xxxxxxxxxxxxxx, adin@xxxxxxxxxxxxxx
  • Delivery-date: Thu, 12 Jan 2012 16:37:36 +0000
  • Domainkey-signature: a=rsa-sha1; c=nofws; d=lagarcavilla.org; h=content-type :mime-version:content-transfer-encoding:subject:message-id:date :from:to:cc; q=dns; s=lagarcavilla.org; b=B+GJQQtCWaCY7sQ4nqys42 owiZUkYWfMBETLSiQOhTUW33KVM+jYrX2fujTm6wUd8KigtfrTGhw2EcOeQRy50A gYedFWJ9xXqsTIA7kv/EORZcR4z9vUTF+skymMbvI6qI8dxH/EbyOgOgTHlCFBq8 moZhsGII231x4L6gzCLAw=
  • List-id: Xen developer discussion <xen-devel.lists.xensource.com>

 tools/xenpaging/Makefile  |    5 +-
 tools/xenpaging/memoper.c |  627 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 631 insertions(+), 1 deletions(-)


This is example code, not meant for tree consumption. So
that others can run tests on the ring management bits.

The testing works as follows:
1. start off a Linux HVM with 4 vcpus
2. ./memoper <domid>
(recommended to run memoper inside a screen -L session as it tends
to output a lot of stuff)
3. xl unpause <domid>
4. fireworks

To really drive the ring (assuming 1GB guest RAM and four vcpus):
6. on the domain console, remount tmpfs to /dev/shm (or wherever) with
size sufficient to consume most of the guest's RAM (e.g.
mount -o remount,size=1000000000)
7. on the domain console
for i in `seq 4`; do (dd if=/dev/zero of=/dev/shm/file.$i bs=1k count=255000)& 
done

More fireworks!

diff -r 24f43fcefc12 -r 330baa2bb903 tools/xenpaging/Makefile
--- a/tools/xenpaging/Makefile
+++ b/tools/xenpaging/Makefile
@@ -15,10 +15,13 @@ CFLAGS   += -Wno-unused
 CFLAGS   += -g
 
 OBJS     = $(SRCS:.c=.o)
-IBINS    = xenpaging
+IBINS    = xenpaging memoper
 
 all: $(IBINS)
 
+memoper: memoper.o
+       $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
 xenpaging: $(OBJS)
        $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(APPEND_LDFLAGS)
 
diff -r 24f43fcefc12 -r 330baa2bb903 tools/xenpaging/memoper.c
--- /dev/null
+++ b/tools/xenpaging/memoper.c
@@ -0,0 +1,627 @@
+/******************************************************************************
+ * tools/xenpaging/memoper.c
+ *
+ * Test tool for log memory accesses by guest.
+ * Copyright (c) 2011 by GridCentric, Inc. (Andres Lagar-Cavilla) 
+ *
+ * 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
+ */
+
+#define _XOPEN_SOURCE  600
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <poll.h>
+#include <xc_private.h>
+#include <xs.h>
+#include "xenpaging.h"
+#include "xc_bitops.h"
+
+#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
+#define NRPAGES(x) (ROUNDUP(x, PAGE_SHIFT) >> PAGE_SHIFT)
+
+/* Whether we signal resume to the hypervisor via domctl or event channel */
+#define USE_EVTCHN_DOMCTL 0
+
+typedef struct memaccess_s {
+    xc_interface *xc_handle;
+    struct xs_handle *xs_handle;
+
+    xc_domaininfo_t    *domain_info;
+
+    mem_event_t mem_event;
+} memaccess_t;
+
+static char watch_token[16];
+static int interrupted;
+DECLARE_HYPERCALL_BUFFER(unsigned long, dirty_bitmap);
+
+#undef DPRINTF
+#define DPRINTF(_f, _a ...) fprintf(stderr, _f, ##_a)
+
+static void close_handler(int sig)
+{
+    interrupted = sig;
+}
+
+static int wait_for_event_or_timeout(memaccess_t *memaccess)
+{
+    xc_interface *xch = memaccess->xc_handle;
+    xc_evtchn *xce = memaccess->mem_event.xce_handle;
+    char **vec;
+    unsigned int num;
+    struct pollfd fd[2];
+    int port;
+    int rc;
+
+    /* Wait for event channel and xenstore */
+    fd[0].fd = xc_evtchn_fd(xce);
+    fd[0].events = POLLIN | POLLERR;
+    fd[1].fd = xs_fileno(memaccess->xs_handle);
+    fd[1].events = POLLIN | POLLERR;
+
+    rc = poll(fd, 2, 100);
+    if ( rc < 0 )
+    {
+        if (errno == EINTR)
+            return 0;
+
+        ERROR("Poll exited with an error");
+        return -errno;
+    }
+
+    /* First check for guest shutdown */
+    if ( rc && fd[1].revents & POLLIN )
+    {
+        DPRINTF("Got event from xenstore\n");
+        vec = xs_read_watch(memaccess->xs_handle, &num);
+        if ( vec )
+        {
+            if ( strcmp(vec[XS_WATCH_TOKEN], watch_token) == 0 )
+            {
+                /* If our guest disappeared, set interrupt flag and fall 
through */
+                if ( xs_is_domain_introduced(memaccess->xs_handle, 
memaccess->mem_event.domain_id) == false )
+                {
+                    xs_unwatch(memaccess->xs_handle, "@releaseDomain", 
watch_token);
+                    interrupted = SIGQUIT;
+                    rc = 0;
+                }
+            }
+            free(vec);
+        }
+    }
+
+    if ( rc && fd[0].revents & POLLIN )
+    {
+       int rv;
+        DPRINTF("Got event from evtchn\n");
+        port = xc_evtchn_pending(xce);
+        if ( port == -1 )
+        {
+            ERROR("Failed to read port from event channel");
+            rc = -1;
+            goto err;
+        }
+
+        rv = xc_evtchn_unmask(xce, port);
+        if ( rv < 0 )
+        {
+            ERROR("Failed to unmask event channel port");
+        }
+    }
+err:
+    return rc;
+}
+
+static void *init_page(void)
+{
+    void *buffer;
+    int ret;
+
+    /* Allocated page memory */
+    ret = posix_memalign(&buffer, PAGE_SIZE, PAGE_SIZE);
+    if ( ret != 0 )
+        goto out_alloc;
+
+    /* Lock buffer in memory so it can't be paged out */
+    ret = mlock(buffer, PAGE_SIZE);
+    if ( ret != 0 )
+        goto out_lock;
+
+    return buffer;
+
+ out_init:
+    munlock(buffer, PAGE_SIZE);
+ out_lock:
+    free(buffer);
+ out_alloc:
+    return NULL;
+}
+
+static memaccess_t *access_init(domid_t domain_id)
+{
+    memaccess_t *memaccess;
+    xc_interface *xch;
+    xentoollog_logger *dbg = NULL;
+    char *p;
+    int rc;
+
+    xch = xc_interface_open(dbg, NULL, 0);
+    if ( !xch )
+        goto err_iface;
+
+    DPRINTF("access init\n");
+
+    /* Allocate memory */
+    memaccess = malloc(sizeof(memaccess_t));
+    memset(memaccess, 0, sizeof(memaccess_t));
+
+    /* Open connection to xenstore */
+    memaccess->xs_handle = xs_open(0);
+    if ( memaccess->xs_handle == NULL )
+    {
+        ERROR("Error initialising xenstore connection");
+        goto err;
+    }
+
+    /* write domain ID to watch so we can ignore other domain shutdowns */
+    snprintf(watch_token, sizeof(watch_token), "%u", domain_id);
+    if ( xs_watch(memaccess->xs_handle, "@releaseDomain", watch_token) == 
false )
+    {
+        ERROR("Could not bind to shutdown watch\n");
+        goto err;
+    }
+
+    /* Open connection to xen */
+    memaccess->xc_handle = xch;
+
+    /* Set domain id */
+    memaccess->mem_event.domain_id = domain_id;
+
+    /* Initialise shared page */
+    memaccess->mem_event.shared_page = init_page();
+    if ( memaccess->mem_event.shared_page == NULL )
+    {
+        ERROR("Error initialising shared page");
+        goto err;
+    }
+
+    /* Initialise ring page */
+    memaccess->mem_event.ring_page = init_page();
+    if ( memaccess->mem_event.ring_page == NULL )
+    {
+        ERROR("Error initialising ring page");
+        goto err;
+    }
+
+    /* Initialise ring */
+    SHARED_RING_INIT((mem_event_sring_t *)memaccess->mem_event.ring_page);
+    BACK_RING_INIT(&memaccess->mem_event.back_ring,
+                   (mem_event_sring_t *)memaccess->mem_event.ring_page,
+                   PAGE_SIZE);
+    
+    /* Initialise Xen */
+    rc = xc_mem_access_enable(xch, memaccess->mem_event.domain_id,
+                             memaccess->mem_event.shared_page,
+                             memaccess->mem_event.ring_page);
+    if ( rc != 0 )
+    {
+        switch ( errno ) {
+            case EBUSY:
+                ERROR("access is (or was) active on this domain");
+                break;
+            case ENODEV:
+                ERROR("EPT not supported for this guest");
+                break;
+            case EXDEV:
+                ERROR("access is not supported in a PoD guest");
+                break;
+            default:
+                ERROR("Error initialising shared page: %s", strerror(errno));
+                break;
+        }
+        goto err;
+    }
+
+    /* Open event channel */
+    memaccess->mem_event.xce_handle = xc_evtchn_open(NULL, 0);
+    if ( memaccess->mem_event.xce_handle == NULL )
+    {
+        ERROR("Failed to open event channel");
+        goto err;
+    }
+
+    /* Bind event notification */
+    rc = xc_evtchn_bind_interdomain(memaccess->mem_event.xce_handle,
+                                    memaccess->mem_event.domain_id,
+                                    memaccess->mem_event.shared_page->port);
+    if ( rc < 0 )
+    {
+        ERROR("Failed to bind event channel");
+        goto err;
+    }
+
+    memaccess->mem_event.port = rc;
+
+    /* Get domaininfo */
+    memaccess->domain_info = malloc(sizeof(xc_domaininfo_t));
+    if ( memaccess->domain_info == NULL )
+    {
+        ERROR("Error allocating memory for domain info");
+        goto err;
+    }
+
+    rc = xc_domain_getinfolist(xch, memaccess->mem_event.domain_id, 1,
+                               memaccess->domain_info);
+    if ( rc != 1 )
+    {
+        ERROR("Error getting domain info");
+        goto err;
+    }
+    DPRINTF("max_pages = %"PRIx64"\n", memaccess->domain_info->max_pages);
+
+    return memaccess;
+
+ err:
+    if ( memaccess )
+    {
+        if ( memaccess->xs_handle )
+            xs_close(memaccess->xs_handle);
+        xc_interface_close(xch);
+        if ( memaccess->mem_event.shared_page )
+        {
+            munlock(memaccess->mem_event.shared_page, PAGE_SIZE);
+            free(memaccess->mem_event.shared_page);
+        }
+
+        if ( memaccess->mem_event.ring_page )
+        {
+            munlock(memaccess->mem_event.ring_page, PAGE_SIZE);
+            free(memaccess->mem_event.ring_page);
+        }
+
+        free(memaccess->domain_info);
+        free(memaccess);
+    }
+
+ err_iface: 
+    return NULL;
+}
+
+static int brick_domain(memaccess_t *memaccess, int *p2m_size)
+{
+    int max, rc;
+    unsigned long mb;
+    xc_interface *xch = memaccess->xc_handle;
+
+    (void)xc_domain_pause(xch, memaccess->mem_event.domain_id);
+
+    max = xc_domain_maximum_gpfn(xch, memaccess->mem_event.domain_id);
+    if (max <= 0) {
+        fprintf(stderr, "MAX GPFN WEIRDO %d\n", max);
+        return 1;
+    }
+    fprintf(stderr, "GPFN MAX %x\n", max);
+    *p2m_size = max;
+
+    dirty_bitmap = xc_hypercall_buffer_alloc_pages(memaccess->xc_handle, 
+                       dirty_bitmap, NRPAGES(bitmap_size(*p2m_size)));
+    if ( !dirty_bitmap )
+    {
+        ERROR("ERROR for bitmap %d %d\n", *p2m_size, errno);
+        return 1;
+    }
+    /* No need to memset, pages are memset by libxc */
+   
+    mb = 64;/* TUNABLE!?!? */ 
+    if ( (rc = xc_shadow_control(xch, memaccess->mem_event.domain_id,
+                               XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION,
+                               NULL, 0, &mb, 0, NULL)) < 0 )
+    {
+        fprintf(stderr, "COULD NOT ALLOCATE SHADOW RAM FOR BITMAP "
+                            "rc %d errno %d\n", rc, errno);
+        return 1;
+    }
+ 
+    rc = xc_shadow_control(xch, memaccess->mem_event.domain_id,
+                                XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
+                                NULL, 0, NULL, 0, NULL);
+    
+    if ( rc < 0 )
+    {
+        fprintf(stderr, "Couldn't enable shadow mode (rc %d) "
+                    "(errno %d)\n", rc, errno );
+        /* log-dirty already enabled? There's no test op,
+           so attempt to disable then reenable it */
+        rc = xc_shadow_control(xch, memaccess->mem_event.domain_id, 
+                                    XEN_DOMCTL_SHADOW_OP_OFF,
+                                    NULL, 0, NULL, 0, NULL);
+        fprintf(stderr, "Disabling shadow log dirty yielded rc %d errno "
+                        "%d\n", rc, errno);
+        rc = xc_shadow_control(xch, memaccess->mem_event.domain_id,
+                                    XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
+                                    NULL, 0, NULL, 0, NULL);
+        
+        if ( rc < 0 )
+        {
+            fprintf(stderr, "Couldn't enable shadow mode (rc %d) "
+                        "(errno %d)\n", rc, errno );
+            return 1;
+        }
+    } else {
+
+        errno = 0;
+        rc = xc_shadow_control(xch, memaccess->mem_event.domain_id, 
+                          XEN_DOMCTL_SHADOW_OP_CLEAN,
+                          NULL, *p2m_size, NULL, 0, NULL);
+        fprintf(stderr, "SHADOW CLEAN RC %x errno %d\n", rc, errno);
+    }
+
+    rc = xc_hvm_set_mem_access(xch, memaccess->mem_event.domain_id,
+                                HVMMEM_access_n2rwx, 0, (uint64_t) *p2m_size);
+    if (rc) {
+        fprintf(stderr, "MEMTYPE RC %d %d\n", rc, errno);
+       if ( errno != ENOMEM )
+               /* We get ENOMEM when trying to set access rights on gfn's that
+                * have _never_ been touched, to the extent the p2m doesn't 
+                * even know about them. This is extremely unlikely to happen
+                * for pages that the VM will end up actually accessing, for 
the 
+                * the physmap is fully populated (pod, paged, shared, normal, 
+                * whatever, populated nonetheless) up front.*/
+        return 1;
+    }
+
+    return 0;
+}
+
+static int access_teardown(memaccess_t *memaccess)
+{
+    int rc;
+    xc_interface *xch;
+
+    if ( memaccess == NULL )
+        return 0;
+
+    xch = memaccess->xc_handle;
+    memaccess->xc_handle = NULL;
+    /* Tear down domain access in Xen */
+    rc = xc_mem_access_disable(xch, memaccess->mem_event.domain_id);
+    if ( rc != 0 )
+    {
+        ERROR("Error tearing down domain access in xen");
+    }
+
+    /* Unbind VIRQ */
+    rc = xc_evtchn_unbind(memaccess->mem_event.xce_handle, 
memaccess->mem_event.port);
+    if ( rc != 0 )
+    {
+        ERROR("Error unbinding event port");
+    }
+    memaccess->mem_event.port = -1;
+
+    /* Close event channel */
+    rc = xc_evtchn_close(memaccess->mem_event.xce_handle);
+    if ( rc != 0 )
+    {
+        ERROR("Error closing event channel");
+    }
+    memaccess->mem_event.xce_handle = NULL;
+    
+    /* Close connection to xenstore */
+    xs_close(memaccess->xs_handle);
+
+    /* Close connection to Xen */
+    rc = xc_interface_close(xch);
+    if ( rc != 0 )
+    {
+        ERROR("Error closing connection to xen");
+    }
+
+    return 0;
+
+ err:
+    return -1;
+}
+
+static void get_request(mem_event_t *mem_event, mem_event_request_t *req)
+{
+    mem_event_back_ring_t *back_ring;
+    RING_IDX req_cons;
+
+    back_ring = &mem_event->back_ring;
+    req_cons = back_ring->req_cons;
+
+    /* Copy request */
+    memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
+    req_cons++;
+
+    /* Update ring */
+    back_ring->req_cons = req_cons;
+    back_ring->sring->req_event = req_cons + 1;
+}
+
+static void put_response(mem_event_t *mem_event, mem_event_response_t *rsp)
+{
+    mem_event_back_ring_t *back_ring;
+    RING_IDX rsp_prod;
+
+    back_ring = &mem_event->back_ring;
+    rsp_prod = back_ring->rsp_prod_pvt;
+
+    /* Copy response */
+    memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
+    rsp_prod++;
+
+    /* Update ring */
+    back_ring->rsp_prod_pvt = rsp_prod;
+    RING_PUSH_RESPONSES(back_ring);
+}
+
+static int resume_page(memaccess_t *memaccess, mem_event_response_t *rsp, int 
notify_policy)
+{
+    int ret;
+
+    /* Put the page info on the ring */
+    put_response(&memaccess->mem_event, rsp);
+
+#if USE_EVTCHN_DOMCTL
+    /* Tell Xen page is ready */
+    ret = xc_mem_access_resume(memaccess->xc_handle, 
memaccess->mem_event.domain_id,
+                               rsp->gfn);
+    if ( ret == 0 ) 
+#endif
+        ret = xc_evtchn_notify(memaccess->mem_event.xce_handle,
+                               memaccess->mem_event.port);
+
+ out:
+    return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+    struct sigaction act;
+    memaccess_t *memaccess;
+    mem_event_request_t req;
+    mem_event_response_t rsp;
+    unsigned long i;
+    int rc = -1;
+    int rc1, frc, p2m_size;
+    xc_interface *xch;
+
+    /* Initialise domain access */
+    memaccess = access_init(atoi(argv[1]));
+    if ( memaccess == NULL )
+    {
+        fprintf(stderr, "Error initialising access");
+        return 1;
+    }
+    xch = memaccess->xc_handle;
+
+    if ( brick_domain(memaccess, &p2m_size) )
+    {
+        int rv;
+        fprintf(stderr, "Error bricking domain\n");
+        if ( (rv = access_teardown(memaccess)) )
+            fprintf(stderr, "Further, error %d errno %d when "
+                    "tearing down\n", rv, errno);
+        return 1;
+    }
+
+    DPRINTF("starting %s %u\n", argv[0], memaccess->mem_event.domain_id);
+
+    /* ensure that if we get a signal, we'll do cleanup, then exit */
+    act.sa_handler = close_handler;
+    act.sa_flags = 0;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIGHUP,  &act, NULL);
+    sigaction(SIGTERM, &act, NULL);
+    sigaction(SIGINT,  &act, NULL);
+    sigaction(SIGALRM, &act, NULL);
+
+    /* Swap pages in and out */
+    while ( 1 )
+    {
+        /* Wait for Xen to signal that a page needs paged in */
+#ifdef SLEEP_TEST_RING
+       /* SLEEP FOR TEST */ usleep(1000*1000*10);
+#endif
+        rc = wait_for_event_or_timeout(memaccess);
+        if ( rc < 0 )
+        {
+            ERROR("Error getting event");
+            goto out;
+        }
+        else if ( rc != 0 )
+        {
+            DPRINTF("Got event from Xen\n");
+        }
+
+        while ( RING_HAS_UNCONSUMED_REQUESTS(&memaccess->mem_event.back_ring) )
+        {
+            get_request(&memaccess->mem_event, &req);
+
+            DPRINTF("page accessed (domain = %d; vcpu = %d;"
+                    " gfn = %"PRIx64"; paused = %d)  %c%c%c\n",
+                    memaccess->mem_event.domain_id, req.vcpu_id, req.gfn,
+                    !!(req.flags & MEM_EVENT_FLAG_VCPU_PAUSED),
+                       req.access_r ? 'R' : '-',
+                       req.access_w ? 'W' : '-',
+                       req.access_x ? 'X' : '-');
+
+            /* Tell Xen we consumed the event so it doesn't stall */
+            /* Prepare the response */
+            rsp.gfn = req.gfn;
+            rsp.vcpu_id = req.vcpu_id;
+            rsp.flags = req.flags;
+
+            rc = resume_page(memaccess, &rsp, 0);
+            if ( rc != 0 )
+            {
+                ERROR("Error resuming");
+                goto out;
+            }
+        }
+
+        /* Exit on any signal */
+        if ( interrupted )
+            break;
+    }
+    DPRINTF("%s got signal %d\n", argv[0], interrupted);
+
+    frc = xc_shadow_control(
+        memaccess->xc_handle, memaccess->mem_event.domain_id, 
+        XEN_DOMCTL_SHADOW_OP_CLEAN, HYPERCALL_BUFFER(dirty_bitmap),
+        p2m_size, NULL, 0, NULL);
+    if ( frc != p2m_size )
+    {
+        ERROR("Error peeking shadow bitmap");
+        xc_hypercall_buffer_free_pages(memaccess->xc_handle, dirty_bitmap, 
+                                    NRPAGES(bitmap_size(p2m_size)));
+        goto out;
+    }
+
+    for ( i = 0; i < p2m_size; i++)
+        if (test_bit(i, dirty_bitmap))
+            DPRINTF("GFN %lx DIRTY\n", i);
+
+    (void)xc_hypercall_buffer_free_pages(memaccess->xc_handle, dirty_bitmap, 
+                                          NRPAGES(bitmap_size(p2m_size)));
+
+ out:
+
+    /* Tear down */
+    rc1 = access_teardown(memaccess);
+    if ( rc1 != 0 )
+        fprintf(stderr, "Error tearing down\n");
+
+    if ( rc == 0 )
+        rc = rc1;
+    return rc;
+}
+
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End: 
+ */

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


 


Rackspace

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