debuggers.hg

view tools/xenpaging/xenpaging.c @ 21067:b4a1832a916f

Update Xen version to 4.0.0-rc6
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 09 18:18:05 2010 +0000 (2010-03-09)
parents 714a3152102c
children 779c0ef9682c
line source
1 /******************************************************************************
2 * tools/xenpaging/xenpaging.c
3 *
4 * Domain paging.
5 * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <xc_private.h>
27 #include <xen/mem_event.h>
29 #include "bitops.h"
30 #include "spinlock.h"
31 #include "file_ops.h"
32 #include "xc.h"
34 #include "policy.h"
35 #include "xenpaging.h"
38 #if 0
39 #undef DPRINTF
40 #define DPRINTF(...) ((void)0)
41 #endif
44 static void *init_page(void)
45 {
46 void *buffer;
47 int ret;
49 /* Allocated page memory */
50 ret = posix_memalign(&buffer, PAGE_SIZE, PAGE_SIZE);
51 if ( ret != 0 )
52 goto out_alloc;
54 /* Lock buffer in memory so it can't be paged out */
55 ret = mlock(buffer, PAGE_SIZE);
56 if ( ret != 0 )
57 goto out_lock;
59 return buffer;
61 out_init:
62 munlock(buffer, PAGE_SIZE);
63 out_lock:
64 free(buffer);
65 out_alloc:
66 return NULL;
67 }
69 xenpaging_t *xenpaging_init(domid_t domain_id)
70 {
71 xenpaging_t *paging;
72 int rc;
74 DPRINTF("xenpaging init\n");
76 /* Allocate memory */
77 paging = malloc(sizeof(xenpaging_t));
78 memset(paging, 0, sizeof(xenpaging_t));
80 /* Open connection to xen */
81 paging->xc_handle = xc_interface_open();
82 if ( paging->xc_handle < 0 )
83 {
84 ERROR("Failed to open connection to Xen");
85 goto err;
86 }
88 /* Set domain id */
89 paging->mem_event.domain_id = domain_id;
91 /* Initialise shared page */
92 paging->mem_event.shared_page = init_page();
93 if ( paging->mem_event.shared_page == NULL )
94 {
95 ERROR("Error initialising shared page");
96 goto err;
97 }
99 /* Initialise ring page */
100 paging->mem_event.ring_page = init_page();
101 if ( paging->mem_event.ring_page == NULL )
102 {
103 ERROR("Error initialising shared page");
104 goto err;
105 }
107 /* Initialise ring */
108 SHARED_RING_INIT((mem_event_sring_t *)paging->mem_event.ring_page);
109 BACK_RING_INIT(&paging->mem_event.back_ring,
110 (mem_event_sring_t *)paging->mem_event.ring_page,
111 PAGE_SIZE);
113 /* Initialise lock */
114 mem_event_ring_lock_init(&paging->mem_event);
116 /* Initialise Xen */
117 rc = xc_mem_event_enable(paging->xc_handle, paging->mem_event.domain_id,
118 paging->mem_event.shared_page,
119 paging->mem_event.ring_page);
120 if ( rc != 0 )
121 {
122 ERROR("Error initialising shared page");
123 goto err;
124 }
126 /* Open event channel */
127 paging->mem_event.xce_handle = xc_evtchn_open();
128 if ( paging->mem_event.xce_handle < 0 )
129 {
130 ERROR("Failed to open event channel");
131 goto err;
132 }
134 /* Bind event notification */
135 rc = xc_evtchn_bind_interdomain(paging->mem_event.xce_handle,
136 paging->mem_event.domain_id,
137 paging->mem_event.shared_page->port);
138 if ( rc < 0 )
139 {
140 ERROR("Failed to bind event channel");
141 goto err;
142 }
144 paging->mem_event.port = rc;
146 /* Get platform info */
147 paging->platform_info = malloc(sizeof(xc_platform_info_t));
148 if ( paging->platform_info == NULL )
149 {
150 ERROR("Error allocating memory for platform info");
151 goto err;
152 }
154 rc = xc_get_platform_info(paging->xc_handle, domain_id,
155 paging->platform_info);
156 if ( rc != 1 )
157 {
158 ERROR("Error getting platform info");
159 goto err;
160 }
162 /* Get domaininfo */
163 paging->domain_info = malloc(sizeof(xc_domaininfo_t));
164 if ( paging->domain_info == NULL )
165 {
166 ERROR("Error allocating memory for domain info");
167 goto err;
168 }
170 rc = xc_domain_getinfolist(paging->xc_handle, domain_id, 1,
171 paging->domain_info);
172 if ( rc != 1 )
173 {
174 ERROR("Error getting domain info");
175 goto err;
176 }
178 /* Allocate bitmap for tracking pages that have been paged out */
179 paging->bitmap_size = (paging->domain_info->max_pages + BITS_PER_LONG) &
180 ~(BITS_PER_LONG - 1);
182 rc = alloc_bitmap(&paging->bitmap, paging->bitmap_size);
183 if ( rc != 0 )
184 {
185 ERROR("Error allocating bitmap");
186 goto err;
187 }
188 DPRINTF("max_pages = %"PRIx64"\n", paging->domain_info->max_pages);
190 /* Initialise policy */
191 rc = policy_init(paging);
192 if ( rc != 0 )
193 {
194 ERROR("Error initialising policy");
195 goto err;
196 }
198 return paging;
200 err:
201 if ( paging->bitmap )
202 free(paging->bitmap);
203 if ( paging->platform_info )
204 free(paging->platform_info);
205 if ( paging )
206 free(paging);
208 return NULL;
209 }
211 int xenpaging_teardown(xenpaging_t *paging)
212 {
213 int rc;
215 if ( paging == NULL )
216 return 0;
218 /* Tear down domain paging in Xen */
219 rc = xc_mem_event_disable(paging->xc_handle, paging->mem_event.domain_id);
220 if ( rc != 0 )
221 {
222 ERROR("Error tearing down domain paging in xen");
223 goto err;
224 }
226 /* Unbind VIRQ */
227 rc = xc_evtchn_unbind(paging->mem_event.xce_handle, paging->mem_event.port);
228 if ( rc != 0 )
229 {
230 ERROR("Error unbinding event port");
231 goto err;
232 }
233 paging->mem_event.port = -1;
235 /* Close event channel */
236 rc = xc_evtchn_close(paging->mem_event.xce_handle);
237 if ( rc != 0 )
238 {
239 ERROR("Error closing event channel");
240 goto err;
241 }
242 paging->mem_event.xce_handle = -1;
244 /* Close connection to Xen */
245 rc = xc_interface_close(paging->xc_handle);
246 if ( rc != 0 )
247 {
248 ERROR("Error closing connection to xen");
249 goto err;
250 }
251 paging->xc_handle = -1;
253 return 0;
255 err:
256 return -1;
257 }
259 static int get_request(mem_event_t *mem_event, mem_event_request_t *req)
260 {
261 mem_event_back_ring_t *back_ring;
262 RING_IDX req_cons;
264 mem_event_ring_lock(mem_event);
266 back_ring = &mem_event->back_ring;
267 req_cons = back_ring->req_cons;
269 /* Copy request */
270 memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
271 req_cons++;
273 /* Update ring */
274 back_ring->req_cons = req_cons;
275 back_ring->sring->req_event = req_cons + 1;
277 mem_event_ring_unlock(mem_event);
279 return 0;
280 }
282 static int put_response(mem_event_t *mem_event, mem_event_response_t *rsp)
283 {
284 mem_event_back_ring_t *back_ring;
285 RING_IDX rsp_prod;
287 mem_event_ring_lock(mem_event);
289 back_ring = &mem_event->back_ring;
290 rsp_prod = back_ring->rsp_prod_pvt;
292 /* Copy response */
293 memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
294 rsp_prod++;
296 /* Update ring */
297 back_ring->rsp_prod_pvt = rsp_prod;
298 RING_PUSH_RESPONSES(back_ring);
300 mem_event_ring_unlock(mem_event);
302 return 0;
303 }
305 int xenpaging_evict_page(xenpaging_t *paging, xenpaging_victim_t *victim, int fd, int i)
306 {
307 void *page;
308 unsigned long gfn;
309 int ret;
311 DECLARE_DOMCTL;
313 /* Map page */
314 gfn = victim->gfn;
315 ret = -EFAULT;
316 page = xc_map_foreign_pages(paging->xc_handle, victim->domain_id,
317 PROT_READ | PROT_WRITE, &gfn, 1);
318 if ( page == NULL )
319 {
320 ERROR("Error mapping page");
321 goto out;
322 }
324 /* Copy page */
325 ret = write_page(fd, page, i);
326 if ( ret != 0 )
327 {
328 munmap(page, PAGE_SIZE);
329 ERROR("Error copying page");
330 goto out;
331 }
333 /* Clear page */
334 memset(page, 0, PAGE_SIZE);
336 munmap(page, PAGE_SIZE);
338 /* Tell Xen to evict page */
339 ret = xc_mem_paging_evict(paging->xc_handle, paging->mem_event.domain_id,
340 victim->gfn);
341 if ( ret != 0 )
342 {
343 ERROR("Error evicting page");
344 goto out;
345 }
347 /* Notify policy of page being paged in */
348 policy_notify_paged_in(paging->mem_event.domain_id, victim->gfn);
350 out:
351 return ret;
352 }
354 int xenpaging_resume_page(xenpaging_t *paging, mem_event_response_t *rsp)
355 {
356 int ret;
358 /* Put the page info on the ring */
359 ret = put_response(&paging->mem_event, rsp);
360 if ( ret != 0 )
361 goto out;
363 /* Notify policy of page being paged in */
364 policy_notify_paged_in(paging->mem_event.domain_id, rsp->gfn);
366 /* Tell Xen page is ready */
367 ret = xc_mem_paging_resume(paging->xc_handle, paging->mem_event.domain_id,
368 rsp->gfn);
369 ret = xc_evtchn_notify(paging->mem_event.xce_handle,
370 paging->mem_event.port);
372 out:
373 return ret;
374 }
376 int xenpaging_populate_page(xenpaging_t *paging, unsigned long *gfn, int fd, int i)
377 {
378 void *page;
379 int ret;
381 /* Tell Xen to allocate a page for the domain */
382 ret = xc_mem_paging_prep(paging->xc_handle, paging->mem_event.domain_id,
383 *gfn);
384 if ( ret != 0 )
385 {
386 ERROR("Error preparing for page in");
387 goto out_map;
388 }
390 /* Map page */
391 ret = -EFAULT;
392 page = xc_map_foreign_pages(paging->xc_handle, paging->mem_event.domain_id,
393 PROT_READ | PROT_WRITE, gfn, 1);
394 if ( page == NULL )
395 {
396 ERROR("Error mapping page: page is null");
397 goto out_map;
398 }
400 /* Read page */
401 ret = read_page(fd, page, i);
402 if ( ret != 0 )
403 {
404 ERROR("Error reading page");
405 goto out;
406 }
408 out:
409 munmap(page, PAGE_SIZE);
410 out_map:
411 return ret;
412 }
414 static int evict_victim(xenpaging_t *paging, domid_t domain_id,
415 xenpaging_victim_t *victim, int fd, int i)
416 {
417 int j = 0;
418 int ret;
420 do
421 {
422 ret = policy_choose_victim(paging, domain_id, victim);
423 if ( ret != 0 )
424 {
425 ERROR("Error choosing victim");
426 goto out;
427 }
429 ret = xc_mem_paging_nominate(paging->xc_handle,
430 paging->mem_event.domain_id, victim->gfn);
431 if ( ret == 0 )
432 ret = xenpaging_evict_page(paging, victim, fd, i);
433 else
434 {
435 if ( j++ % 1000 == 0 )
436 if ( xc_mem_paging_flush_ioemu_cache(domain_id) )
437 ERROR("Error flushing ioemu cache");
438 }
439 }
440 while ( ret );
442 if ( test_and_set_bit(victim->gfn, paging->bitmap) )
443 ERROR("Page has been evicted before");
445 ret = 0;
447 out:
448 return ret;
449 }
451 int main(int argc, char *argv[])
452 {
453 domid_t domain_id;
454 int num_pages;
455 xenpaging_t *paging;
456 xenpaging_victim_t *victims;
457 mem_event_request_t req;
458 mem_event_response_t rsp;
459 int i;
460 int rc = -1;
461 int rc1;
463 int open_flags = O_CREAT | O_TRUNC | O_RDWR;
464 mode_t open_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH;
465 char filename[80];
466 int fd;
468 if ( argc != 3 )
469 {
470 fprintf(stderr, "Usage: %s <domain_id> <num_pages>\n", argv[0]);
471 return -1;
472 }
474 domain_id = atoi(argv[1]);
475 num_pages = atoi(argv[2]);
477 victims = calloc(num_pages, sizeof(xenpaging_victim_t));
479 /* Open file */
480 sprintf(filename, "page_cache_%d", domain_id);
481 fd = open(filename, open_flags, open_mode);
482 if ( fd < 0 )
483 {
484 perror("failed to open file");
485 return -1;
486 }
488 /* Seed random-number generator */
489 srand(time(NULL));
491 /* Initialise domain paging */
492 paging = xenpaging_init(domain_id);
493 if ( paging == NULL )
494 {
495 ERROR("Error initialising paging");
496 goto out;
497 }
499 /* Evict pages */
500 memset(victims, 0, sizeof(xenpaging_victim_t) * num_pages);
501 for ( i = 0; i < num_pages; i++ )
502 {
503 evict_victim(paging, domain_id, &victims[i], fd, i);
504 if ( i % 100 == 0 )
505 DPRINTF("%d pages evicted\n", i);
506 }
508 DPRINTF("pages evicted\n");
510 /* Swap pages in and out */
511 while ( 1 )
512 {
513 /* Wait for Xen to signal that a page needs paged in */
514 rc = xc_wait_for_event_or_timeout(paging->mem_event.xce_handle, 100);
515 if ( rc < -1 )
516 {
517 ERROR("Error getting event");
518 goto out;
519 }
520 else if ( rc != -1 )
521 {
522 DPRINTF("Got event from Xen\n");
523 }
525 while ( RING_HAS_UNCONSUMED_REQUESTS(&paging->mem_event.back_ring) )
526 {
527 rc = get_request(&paging->mem_event, &req);
528 if ( rc != 0 )
529 {
530 ERROR("Error getting request");
531 goto out;
532 }
534 /* Check if the page has already been paged in */
535 if ( test_and_clear_bit(req.gfn, paging->bitmap) )
536 {
537 /* Find where in the paging file to read from */
538 for ( i = 0; i < num_pages; i++ )
539 {
540 if ( (victims[i].domain_id == paging->mem_event.domain_id) &&
541 (victims[i].gfn == req.gfn) )
542 break;
543 }
545 if ( i >= num_pages )
546 {
547 DPRINTF("Couldn't find page %lx\n", req.gfn);
548 goto out;
549 }
551 /* Populate the page */
552 rc = xenpaging_populate_page(paging, &req.gfn, fd, i);
553 if ( rc != 0 )
554 {
555 ERROR("Error populating page");
556 goto out;
557 }
559 /* Prepare the response */
560 rsp.gfn = req.gfn;
561 rsp.p2mt = req.p2mt;
562 rsp.vcpu_id = req.vcpu_id;
563 rsp.flags = req.flags;
565 rc = xenpaging_resume_page(paging, &rsp);
566 if ( rc != 0 )
567 {
568 ERROR("Error resuming page");
569 goto out;
570 }
572 /* Evict a new page to replace the one we just paged in */
573 evict_victim(paging, domain_id, &victims[i], fd, i);
574 }
575 else
576 {
577 DPRINTF("page already populated (domain = %d; vcpu = %d;"
578 " gfn = %lx; paused = %"PRId64")\n",
579 paging->mem_event.domain_id, req.vcpu_id,
580 req.gfn, req.flags & MEM_EVENT_FLAG_VCPU_PAUSED);
582 /* Tell Xen to resume the vcpu */
583 /* XXX: Maybe just check if the vcpu was paused? */
584 if ( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
585 {
586 /* Prepare the response */
587 rsp.gfn = req.gfn;
588 rsp.p2mt = req.p2mt;
589 rsp.vcpu_id = req.vcpu_id;
590 rsp.flags = req.flags;
592 rc = xenpaging_resume_page(paging, &rsp);
593 if ( rc != 0 )
594 {
595 ERROR("Error resuming");
596 goto out;
597 }
598 }
599 }
600 }
601 }
603 out:
604 free(victims);
606 /* Tear down domain paging */
607 rc1 = xenpaging_teardown(paging);
608 if ( rc1 != 0 )
609 ERROR("Error tearing down paging");
611 if ( rc == 0 )
612 rc = rc1;
614 return rc;
615 }
618 /*
619 * Local variables:
620 * mode: C
621 * c-set-style: "BSD"
622 * c-basic-offset: 4
623 * indent-tabs-mode: nil
624 * End:
625 */