Files
xen/tools/xenpaging/xenpaging.c
T
Ian Campbell fa2244104b libxc: disable xc_ptrace on x86 Linux
It has been unused since 21732:eb34666befcc.

Removing the include of sys/ptrace.h and threaddb.h exposed a few
places which were using time(2) or gettimeofday(2) without including
time.h or sys/time.h respectively and were relying on an include.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
2010-08-13 14:25:56 +01:00

652 lines
16 KiB
C

/******************************************************************************
* tools/xenpaging/xenpaging.c
*
* Domain paging.
* Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp)
*
* 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 <inttypes.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <xc_private.h>
#include <xen/mem_event.h>
#include "bitops.h"
#include "spinlock.h"
#include "file_ops.h"
#include "xc.h"
#include "policy.h"
#include "xenpaging.h"
#if 0
#undef DPRINTF
#define DPRINTF(...) ((void)0)
#endif
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;
}
xenpaging_t *xenpaging_init(xc_interface **xch_r, domid_t domain_id)
{
xenpaging_t *paging;
xc_interface *xch;
int rc;
xch = xc_interface_open(NULL, NULL, 0);
if ( !xch )
goto err_iface;
DPRINTF("xenpaging init\n");
*xch_r = xch;
/* Allocate memory */
paging = malloc(sizeof(xenpaging_t));
memset(paging, 0, sizeof(xenpaging_t));
/* Open connection to xen */
paging->xc_handle = xch;
/* Set domain id */
paging->mem_event.domain_id = domain_id;
/* Initialise shared page */
paging->mem_event.shared_page = init_page();
if ( paging->mem_event.shared_page == NULL )
{
ERROR("Error initialising shared page");
goto err;
}
/* Initialise ring page */
paging->mem_event.ring_page = init_page();
if ( paging->mem_event.ring_page == NULL )
{
ERROR("Error initialising ring page");
goto err;
}
/* Initialise ring */
SHARED_RING_INIT((mem_event_sring_t *)paging->mem_event.ring_page);
BACK_RING_INIT(&paging->mem_event.back_ring,
(mem_event_sring_t *)paging->mem_event.ring_page,
PAGE_SIZE);
/* Initialise lock */
mem_event_ring_lock_init(&paging->mem_event);
/* Initialise Xen */
rc = xc_mem_event_enable(paging->xc_handle, paging->mem_event.domain_id,
paging->mem_event.shared_page,
paging->mem_event.ring_page);
if ( rc != 0 )
{
ERROR("Error initialising shared page");
goto err;
}
/* Open event channel */
paging->mem_event.xce_handle = xc_evtchn_open();
if ( paging->mem_event.xce_handle < 0 )
{
ERROR("Failed to open event channel");
goto err;
}
/* Bind event notification */
rc = xc_evtchn_bind_interdomain(paging->mem_event.xce_handle,
paging->mem_event.domain_id,
paging->mem_event.shared_page->port);
if ( rc < 0 )
{
ERROR("Failed to bind event channel");
goto err;
}
paging->mem_event.port = rc;
/* Get platform info */
paging->platform_info = malloc(sizeof(xc_platform_info_t));
if ( paging->platform_info == NULL )
{
ERROR("Error allocating memory for platform info");
goto err;
}
rc = xc_get_platform_info(paging->xc_handle, domain_id,
paging->platform_info);
if ( rc != 1 )
{
ERROR("Error getting platform info");
goto err;
}
/* Get domaininfo */
paging->domain_info = malloc(sizeof(xc_domaininfo_t));
if ( paging->domain_info == NULL )
{
ERROR("Error allocating memory for domain info");
goto err;
}
rc = xc_domain_getinfolist(paging->xc_handle, domain_id, 1,
paging->domain_info);
if ( rc != 1 )
{
ERROR("Error getting domain info");
goto err;
}
/* Allocate bitmap for tracking pages that have been paged out */
paging->bitmap_size = (paging->domain_info->max_pages + BITS_PER_LONG) &
~(BITS_PER_LONG - 1);
rc = alloc_bitmap(&paging->bitmap, paging->bitmap_size);
if ( rc != 0 )
{
ERROR("Error allocating bitmap");
goto err;
}
DPRINTF("max_pages = %"PRIx64"\n", paging->domain_info->max_pages);
/* Initialise policy */
rc = policy_init(paging);
if ( rc != 0 )
{
ERROR("Error initialising policy");
goto err;
}
return paging;
err:
if ( paging )
{
if ( paging->mem_event.shared_page )
{
munlock(paging->mem_event.shared_page, PAGE_SIZE);
free(paging->mem_event.shared_page);
}
if ( paging->mem_event.ring_page )
{
munlock(paging->mem_event.ring_page, PAGE_SIZE);
free(paging->mem_event.ring_page);
}
free(paging->bitmap);
free(paging->platform_info);
free(paging->domain_info);
free(paging);
}
err_iface:
return NULL;
}
int xenpaging_teardown(xc_interface *xch, xenpaging_t *paging)
{
int rc;
if ( paging == NULL )
return 0;
/* Tear down domain paging in Xen */
rc = xc_mem_event_disable(paging->xc_handle, paging->mem_event.domain_id);
if ( rc != 0 )
{
ERROR("Error tearing down domain paging in xen");
goto err;
}
/* Unbind VIRQ */
rc = xc_evtchn_unbind(paging->mem_event.xce_handle, paging->mem_event.port);
if ( rc != 0 )
{
ERROR("Error unbinding event port");
goto err;
}
paging->mem_event.port = -1;
/* Close event channel */
rc = xc_evtchn_close(paging->mem_event.xce_handle);
if ( rc != 0 )
{
ERROR("Error closing event channel");
goto err;
}
paging->mem_event.xce_handle = -1;
/* Close connection to Xen */
rc = xc_interface_close(paging->xc_handle);
if ( rc != 0 )
{
ERROR("Error closing connection to xen");
goto err;
}
paging->xc_handle = NULL;
return 0;
err:
return -1;
}
static int get_request(mem_event_t *mem_event, mem_event_request_t *req)
{
mem_event_back_ring_t *back_ring;
RING_IDX req_cons;
mem_event_ring_lock(mem_event);
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;
mem_event_ring_unlock(mem_event);
return 0;
}
static int put_response(mem_event_t *mem_event, mem_event_response_t *rsp)
{
mem_event_back_ring_t *back_ring;
RING_IDX rsp_prod;
mem_event_ring_lock(mem_event);
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);
mem_event_ring_unlock(mem_event);
return 0;
}
int xenpaging_evict_page(xc_interface *xch, xenpaging_t *paging,
xenpaging_victim_t *victim, int fd, int i)
{
void *page;
unsigned long gfn;
int ret;
DECLARE_DOMCTL;
/* Map page */
gfn = victim->gfn;
ret = -EFAULT;
page = xc_map_foreign_pages(paging->xc_handle, victim->domain_id,
PROT_READ | PROT_WRITE, &gfn, 1);
if ( page == NULL )
{
ERROR("Error mapping page");
goto out;
}
/* Copy page */
ret = write_page(fd, page, i);
if ( ret != 0 )
{
munmap(page, PAGE_SIZE);
ERROR("Error copying page");
goto out;
}
/* Clear page */
memset(page, 0, PAGE_SIZE);
munmap(page, PAGE_SIZE);
/* Tell Xen to evict page */
ret = xc_mem_paging_evict(paging->xc_handle, paging->mem_event.domain_id,
victim->gfn);
if ( ret != 0 )
{
ERROR("Error evicting page");
goto out;
}
/* Notify policy of page being paged in */
policy_notify_paged_in(paging->mem_event.domain_id, victim->gfn);
out:
return ret;
}
int xenpaging_resume_page(xenpaging_t *paging, mem_event_response_t *rsp)
{
int ret;
/* Put the page info on the ring */
ret = put_response(&paging->mem_event, rsp);
if ( ret != 0 )
goto out;
/* Notify policy of page being paged in */
policy_notify_paged_in(paging->mem_event.domain_id, rsp->gfn);
/* Tell Xen page is ready */
ret = xc_mem_paging_resume(paging->xc_handle, paging->mem_event.domain_id,
rsp->gfn);
ret = xc_evtchn_notify(paging->mem_event.xce_handle,
paging->mem_event.port);
out:
return ret;
}
static int xenpaging_populate_page(
xc_interface *xch, xenpaging_t *paging,
uint64_t *gfn, int fd, int i)
{
unsigned long _gfn;
void *page;
int ret;
/* Tell Xen to allocate a page for the domain */
ret = xc_mem_paging_prep(paging->xc_handle, paging->mem_event.domain_id,
*gfn);
if ( ret != 0 )
{
ERROR("Error preparing for page in");
goto out_map;
}
/* Map page */
ret = -EFAULT;
_gfn = *gfn;
page = xc_map_foreign_pages(paging->xc_handle, paging->mem_event.domain_id,
PROT_READ | PROT_WRITE, &_gfn, 1);
*gfn = _gfn;
if ( page == NULL )
{
ERROR("Error mapping page: page is null");
goto out_map;
}
/* Read page */
ret = read_page(fd, page, i);
if ( ret != 0 )
{
ERROR("Error reading page");
goto out;
}
out:
munmap(page, PAGE_SIZE);
out_map:
return ret;
}
static int evict_victim(xc_interface *xch, xenpaging_t *paging, domid_t domain_id,
xenpaging_victim_t *victim, int fd, int i)
{
int j = 0;
int ret;
do
{
ret = policy_choose_victim(xch, paging, domain_id, victim);
if ( ret != 0 )
{
ERROR("Error choosing victim");
goto out;
}
ret = xc_mem_paging_nominate(paging->xc_handle,
paging->mem_event.domain_id, victim->gfn);
if ( ret == 0 )
ret = xenpaging_evict_page(xch, paging, victim, fd, i);
else
{
if ( j++ % 1000 == 0 )
if ( xc_mem_paging_flush_ioemu_cache(domain_id) )
ERROR("Error flushing ioemu cache");
}
}
while ( ret );
if ( test_and_set_bit(victim->gfn, paging->bitmap) )
ERROR("Page has been evicted before");
ret = 0;
out:
return ret;
}
int main(int argc, char *argv[])
{
domid_t domain_id;
int num_pages;
xenpaging_t *paging;
xenpaging_victim_t *victims;
mem_event_request_t req;
mem_event_response_t rsp;
int i;
int rc = -1;
int rc1;
xc_interface *xch;
int open_flags = O_CREAT | O_TRUNC | O_RDWR;
mode_t open_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH;
char filename[80];
int fd;
if ( argc != 3 )
{
fprintf(stderr, "Usage: %s <domain_id> <num_pages>\n", argv[0]);
return -1;
}
domain_id = atoi(argv[1]);
num_pages = atoi(argv[2]);
victims = calloc(num_pages, sizeof(xenpaging_victim_t));
/* Open file */
sprintf(filename, "page_cache_%d", domain_id);
fd = open(filename, open_flags, open_mode);
if ( fd < 0 )
{
perror("failed to open file");
return -1;
}
/* Seed random-number generator */
srand(time(NULL));
/* Initialise domain paging */
paging = xenpaging_init(&xch, domain_id);
if ( paging == NULL )
{
ERROR("Error initialising paging");
goto out;
}
/* Evict pages */
memset(victims, 0, sizeof(xenpaging_victim_t) * num_pages);
for ( i = 0; i < num_pages; i++ )
{
evict_victim(xch, paging, domain_id, &victims[i], fd, i);
if ( i % 100 == 0 )
DPRINTF("%d pages evicted\n", i);
}
DPRINTF("pages evicted\n");
/* Swap pages in and out */
while ( 1 )
{
/* Wait for Xen to signal that a page needs paged in */
rc = xc_wait_for_event_or_timeout(xch, paging->mem_event.xce_handle, 100);
if ( rc < -1 )
{
ERROR("Error getting event");
goto out;
}
else if ( rc != -1 )
{
DPRINTF("Got event from Xen\n");
}
while ( RING_HAS_UNCONSUMED_REQUESTS(&paging->mem_event.back_ring) )
{
rc = get_request(&paging->mem_event, &req);
if ( rc != 0 )
{
ERROR("Error getting request");
goto out;
}
/* Check if the page has already been paged in */
if ( test_and_clear_bit(req.gfn, paging->bitmap) )
{
/* Find where in the paging file to read from */
for ( i = 0; i < num_pages; i++ )
{
if ( (victims[i].domain_id == paging->mem_event.domain_id) &&
(victims[i].gfn == req.gfn) )
break;
}
if ( i >= num_pages )
{
DPRINTF("Couldn't find page %"PRIx64"\n", req.gfn);
goto out;
}
/* Populate the page */
rc = xenpaging_populate_page(xch, paging, &req.gfn, fd, i);
if ( rc != 0 )
{
ERROR("Error populating page");
goto out;
}
/* Prepare the response */
rsp.gfn = req.gfn;
rsp.p2mt = req.p2mt;
rsp.vcpu_id = req.vcpu_id;
rsp.flags = req.flags;
rc = xenpaging_resume_page(paging, &rsp);
if ( rc != 0 )
{
ERROR("Error resuming page");
goto out;
}
/* Evict a new page to replace the one we just paged in */
evict_victim(xch, paging, domain_id, &victims[i], fd, i);
}
else
{
DPRINTF("page already populated (domain = %d; vcpu = %d;"
" gfn = %"PRIx64"; paused = %"PRId64")\n",
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_VCPU_PAUSED )
{
/* Prepare the response */
rsp.gfn = req.gfn;
rsp.p2mt = req.p2mt;
rsp.vcpu_id = req.vcpu_id;
rsp.flags = req.flags;
rc = xenpaging_resume_page(paging, &rsp);
if ( rc != 0 )
{
ERROR("Error resuming");
goto out;
}
}
}
}
}
out:
free(victims);
/* Tear down domain paging */
rc1 = xenpaging_teardown(xch, paging);
if ( rc1 != 0 )
ERROR("Error tearing down paging");
if ( rc == 0 )
rc = rc1;
xc_interface_close(xch);
return rc;
}
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/