debuggers.hg

view tools/xenpaging/xenpaging.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents c5b42971234a
children
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 */
22 #define _XOPEN_SOURCE 600
24 #include <inttypes.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <time.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <xc_private.h>
32 #include <xen/mem_event.h>
34 #include "bitops.h"
35 #include "spinlock.h"
36 #include "file_ops.h"
37 #include "xc.h"
39 #include "policy.h"
40 #include "xenpaging.h"
42 static char filename[80];
43 static int interrupted;
44 static void close_handler(int sig)
45 {
46 interrupted = sig;
47 if ( filename[0] )
48 unlink(filename);
49 }
51 static void *init_page(void)
52 {
53 void *buffer;
54 int ret;
56 /* Allocated page memory */
57 ret = posix_memalign(&buffer, PAGE_SIZE, PAGE_SIZE);
58 if ( ret != 0 )
59 goto out_alloc;
61 /* Lock buffer in memory so it can't be paged out */
62 ret = mlock(buffer, PAGE_SIZE);
63 if ( ret != 0 )
64 goto out_lock;
66 return buffer;
68 out_init:
69 munlock(buffer, PAGE_SIZE);
70 out_lock:
71 free(buffer);
72 out_alloc:
73 return NULL;
74 }
76 static xenpaging_t *xenpaging_init(domid_t domain_id)
77 {
78 xenpaging_t *paging;
79 xc_interface *xch;
80 xentoollog_logger *dbg = NULL;
81 char *p;
82 int rc;
84 if ( getenv("XENPAGING_DEBUG") )
85 dbg = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr, XTL_DEBUG, 0);
86 xch = xc_interface_open(dbg, NULL, 0);
87 if ( !xch )
88 goto err_iface;
90 DPRINTF("xenpaging init\n");
92 /* Allocate memory */
93 paging = malloc(sizeof(xenpaging_t));
94 memset(paging, 0, sizeof(xenpaging_t));
96 p = getenv("XENPAGING_POLICY_MRU_SIZE");
97 if ( p && *p )
98 {
99 paging->policy_mru_size = atoi(p);
100 DPRINTF("Setting policy mru_size to %d\n", paging->policy_mru_size);
101 }
103 /* Open connection to xen */
104 paging->xc_handle = xch;
106 /* Set domain id */
107 paging->mem_event.domain_id = domain_id;
109 /* Initialise shared page */
110 paging->mem_event.shared_page = init_page();
111 if ( paging->mem_event.shared_page == NULL )
112 {
113 ERROR("Error initialising shared page");
114 goto err;
115 }
117 /* Initialise ring page */
118 paging->mem_event.ring_page = init_page();
119 if ( paging->mem_event.ring_page == NULL )
120 {
121 ERROR("Error initialising ring page");
122 goto err;
123 }
125 /* Initialise ring */
126 SHARED_RING_INIT((mem_event_sring_t *)paging->mem_event.ring_page);
127 BACK_RING_INIT(&paging->mem_event.back_ring,
128 (mem_event_sring_t *)paging->mem_event.ring_page,
129 PAGE_SIZE);
131 /* Initialise lock */
132 mem_event_ring_lock_init(&paging->mem_event);
134 /* Initialise Xen */
135 rc = xc_mem_event_enable(xch, paging->mem_event.domain_id,
136 paging->mem_event.shared_page,
137 paging->mem_event.ring_page);
138 if ( rc != 0 )
139 {
140 switch ( errno ) {
141 case EBUSY:
142 ERROR("xenpaging is (or was) active on this domain");
143 break;
144 case ENODEV:
145 ERROR("EPT not supported for this guest");
146 break;
147 default:
148 ERROR("Error initialising shared page: %s", strerror(errno));
149 break;
150 }
151 goto err;
152 }
154 /* Open event channel */
155 paging->mem_event.xce_handle = xc_evtchn_open(NULL, 0);
156 if ( paging->mem_event.xce_handle == NULL )
157 {
158 ERROR("Failed to open event channel");
159 goto err;
160 }
162 /* Bind event notification */
163 rc = xc_evtchn_bind_interdomain(paging->mem_event.xce_handle,
164 paging->mem_event.domain_id,
165 paging->mem_event.shared_page->port);
166 if ( rc < 0 )
167 {
168 ERROR("Failed to bind event channel");
169 goto err;
170 }
172 paging->mem_event.port = rc;
174 /* Get platform info */
175 paging->platform_info = malloc(sizeof(xc_platform_info_t));
176 if ( paging->platform_info == NULL )
177 {
178 ERROR("Error allocating memory for platform info");
179 goto err;
180 }
182 rc = xc_get_platform_info(xch, paging->mem_event.domain_id,
183 paging->platform_info);
184 if ( rc != 1 )
185 {
186 ERROR("Error getting platform info");
187 goto err;
188 }
190 /* Get domaininfo */
191 paging->domain_info = malloc(sizeof(xc_domaininfo_t));
192 if ( paging->domain_info == NULL )
193 {
194 ERROR("Error allocating memory for domain info");
195 goto err;
196 }
198 rc = xc_domain_getinfolist(xch, paging->mem_event.domain_id, 1,
199 paging->domain_info);
200 if ( rc != 1 )
201 {
202 ERROR("Error getting domain info");
203 goto err;
204 }
206 /* Allocate bitmap for tracking pages that have been paged out */
207 paging->bitmap_size = (paging->domain_info->max_pages + BITS_PER_LONG) &
208 ~(BITS_PER_LONG - 1);
210 rc = alloc_bitmap(&paging->bitmap, paging->bitmap_size);
211 if ( rc != 0 )
212 {
213 ERROR("Error allocating bitmap");
214 goto err;
215 }
216 DPRINTF("max_pages = %"PRIx64"\n", paging->domain_info->max_pages);
218 /* Initialise policy */
219 rc = policy_init(paging);
220 if ( rc != 0 )
221 {
222 ERROR("Error initialising policy");
223 goto err;
224 }
226 return paging;
228 err:
229 if ( paging )
230 {
231 xc_interface_close(xch);
232 if ( paging->mem_event.shared_page )
233 {
234 munlock(paging->mem_event.shared_page, PAGE_SIZE);
235 free(paging->mem_event.shared_page);
236 }
238 if ( paging->mem_event.ring_page )
239 {
240 munlock(paging->mem_event.ring_page, PAGE_SIZE);
241 free(paging->mem_event.ring_page);
242 }
244 free(paging->bitmap);
245 free(paging->platform_info);
246 free(paging->domain_info);
247 free(paging);
248 }
250 err_iface:
251 return NULL;
252 }
254 static int xenpaging_teardown(xenpaging_t *paging)
255 {
256 int rc;
257 xc_interface *xch;
259 if ( paging == NULL )
260 return 0;
262 xch = paging->xc_handle;
263 paging->xc_handle = NULL;
264 /* Tear down domain paging in Xen */
265 rc = xc_mem_event_disable(xch, paging->mem_event.domain_id);
266 if ( rc != 0 )
267 {
268 ERROR("Error tearing down domain paging in xen");
269 }
271 /* Unbind VIRQ */
272 rc = xc_evtchn_unbind(paging->mem_event.xce_handle, paging->mem_event.port);
273 if ( rc != 0 )
274 {
275 ERROR("Error unbinding event port");
276 }
277 paging->mem_event.port = -1;
279 /* Close event channel */
280 rc = xc_evtchn_close(paging->mem_event.xce_handle);
281 if ( rc != 0 )
282 {
283 ERROR("Error closing event channel");
284 }
285 paging->mem_event.xce_handle = NULL;
287 /* Close connection to Xen */
288 rc = xc_interface_close(xch);
289 if ( rc != 0 )
290 {
291 ERROR("Error closing connection to xen");
292 }
294 return 0;
296 err:
297 return -1;
298 }
300 static int get_request(mem_event_t *mem_event, mem_event_request_t *req)
301 {
302 mem_event_back_ring_t *back_ring;
303 RING_IDX req_cons;
305 mem_event_ring_lock(mem_event);
307 back_ring = &mem_event->back_ring;
308 req_cons = back_ring->req_cons;
310 /* Copy request */
311 memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
312 req_cons++;
314 /* Update ring */
315 back_ring->req_cons = req_cons;
316 back_ring->sring->req_event = req_cons + 1;
318 mem_event_ring_unlock(mem_event);
320 return 0;
321 }
323 static int put_response(mem_event_t *mem_event, mem_event_response_t *rsp)
324 {
325 mem_event_back_ring_t *back_ring;
326 RING_IDX rsp_prod;
328 mem_event_ring_lock(mem_event);
330 back_ring = &mem_event->back_ring;
331 rsp_prod = back_ring->rsp_prod_pvt;
333 /* Copy response */
334 memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
335 rsp_prod++;
337 /* Update ring */
338 back_ring->rsp_prod_pvt = rsp_prod;
339 RING_PUSH_RESPONSES(back_ring);
341 mem_event_ring_unlock(mem_event);
343 return 0;
344 }
346 static int xenpaging_evict_page(xenpaging_t *paging,
347 xenpaging_victim_t *victim, int fd, int i)
348 {
349 xc_interface *xch = paging->xc_handle;
350 void *page;
351 unsigned long gfn;
352 int ret;
354 DECLARE_DOMCTL;
356 /* Map page */
357 gfn = victim->gfn;
358 ret = -EFAULT;
359 page = xc_map_foreign_pages(xch, paging->mem_event.domain_id,
360 PROT_READ | PROT_WRITE, &gfn, 1);
361 if ( page == NULL )
362 {
363 ERROR("Error mapping page");
364 goto out;
365 }
367 /* Copy page */
368 ret = write_page(fd, page, i);
369 if ( ret != 0 )
370 {
371 munmap(page, PAGE_SIZE);
372 ERROR("Error copying page");
373 goto out;
374 }
376 /* Clear page */
377 memset(page, 0, PAGE_SIZE);
379 munmap(page, PAGE_SIZE);
381 /* Tell Xen to evict page */
382 ret = xc_mem_paging_evict(xch, paging->mem_event.domain_id,
383 victim->gfn);
384 if ( ret != 0 )
385 {
386 ERROR("Error evicting page");
387 goto out;
388 }
390 DPRINTF("evict_page > gfn %lx pageslot %d\n", victim->gfn, i);
391 /* Notify policy of page being paged out */
392 policy_notify_paged_out(victim->gfn);
394 out:
395 return ret;
396 }
398 static int xenpaging_resume_page(xenpaging_t *paging, mem_event_response_t *rsp, int notify_policy)
399 {
400 int ret;
402 /* Put the page info on the ring */
403 ret = put_response(&paging->mem_event, rsp);
404 if ( ret != 0 )
405 goto out;
407 /* Notify policy of page being paged in */
408 if ( notify_policy )
409 policy_notify_paged_in(rsp->gfn);
411 /* Tell Xen page is ready */
412 ret = xc_mem_paging_resume(paging->xc_handle, paging->mem_event.domain_id,
413 rsp->gfn);
414 ret = xc_evtchn_notify(paging->mem_event.xce_handle,
415 paging->mem_event.port);
417 out:
418 return ret;
419 }
421 static int xenpaging_populate_page(xenpaging_t *paging,
422 uint64_t *gfn, int fd, int i)
423 {
424 xc_interface *xch = paging->xc_handle;
425 unsigned long _gfn;
426 void *page;
427 int ret;
428 unsigned char oom = 0;
430 _gfn = *gfn;
431 DPRINTF("populate_page < gfn %lx pageslot %d\n", _gfn, i);
432 do
433 {
434 /* Tell Xen to allocate a page for the domain */
435 ret = xc_mem_paging_prep(xch, paging->mem_event.domain_id,
436 _gfn);
437 if ( ret != 0 )
438 {
439 if ( errno == ENOMEM )
440 {
441 if ( oom++ == 0 )
442 DPRINTF("ENOMEM while preparing gfn %lx\n", _gfn);
443 sleep(1);
444 continue;
445 }
446 ERROR("Error preparing for page in");
447 goto out_map;
448 }
449 }
450 while ( ret && !interrupted );
452 /* Map page */
453 ret = -EFAULT;
454 page = xc_map_foreign_pages(xch, paging->mem_event.domain_id,
455 PROT_READ | PROT_WRITE, &_gfn, 1);
456 *gfn = _gfn;
457 if ( page == NULL )
458 {
459 ERROR("Error mapping page: page is null");
460 goto out_map;
461 }
463 /* Read page */
464 ret = read_page(fd, page, i);
465 if ( ret != 0 )
466 {
467 ERROR("Error reading page");
468 goto out;
469 }
471 out:
472 munmap(page, PAGE_SIZE);
473 out_map:
474 return ret;
475 }
477 static int evict_victim(xenpaging_t *paging,
478 xenpaging_victim_t *victim, int fd, int i)
479 {
480 xc_interface *xch = paging->xc_handle;
481 int j = 0;
482 int ret;
484 do
485 {
486 ret = policy_choose_victim(paging, victim);
487 if ( ret != 0 )
488 {
489 if ( ret != -ENOSPC )
490 ERROR("Error choosing victim");
491 goto out;
492 }
494 if ( interrupted )
495 {
496 ret = -EINTR;
497 goto out;
498 }
499 ret = xc_mem_paging_nominate(xch, paging->mem_event.domain_id, victim->gfn);
500 if ( ret == 0 )
501 ret = xenpaging_evict_page(paging, victim, fd, i);
502 else
503 {
504 if ( j++ % 1000 == 0 )
505 if ( xc_mem_paging_flush_ioemu_cache(paging->mem_event.domain_id) )
506 ERROR("Error flushing ioemu cache");
507 }
508 }
509 while ( ret );
511 if ( test_and_set_bit(victim->gfn, paging->bitmap) )
512 ERROR("Page has been evicted before");
514 ret = 0;
516 out:
517 return ret;
518 }
520 int main(int argc, char *argv[])
521 {
522 struct sigaction act;
523 domid_t domain_id;
524 int num_pages;
525 xenpaging_t *paging;
526 xenpaging_victim_t *victims;
527 mem_event_request_t req;
528 mem_event_response_t rsp;
529 int i;
530 int rc = -1;
531 int rc1;
532 xc_interface *xch;
534 int open_flags = O_CREAT | O_TRUNC | O_RDWR;
535 mode_t open_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH;
536 int fd;
538 if ( argc != 3 )
539 {
540 fprintf(stderr, "Usage: %s <domain_id> <num_pages>\n", argv[0]);
541 return -1;
542 }
544 domain_id = atoi(argv[1]);
545 num_pages = atoi(argv[2]);
547 /* Seed random-number generator */
548 srand(time(NULL));
550 /* Initialise domain paging */
551 paging = xenpaging_init(domain_id);
552 if ( paging == NULL )
553 {
554 fprintf(stderr, "Error initialising paging");
555 return 1;
556 }
557 xch = paging->xc_handle;
559 DPRINTF("starting %s %u %d\n", argv[0], domain_id, num_pages);
561 /* Open file */
562 sprintf(filename, "page_cache_%d", domain_id);
563 fd = open(filename, open_flags, open_mode);
564 if ( fd < 0 )
565 {
566 perror("failed to open file");
567 return 2;
568 }
570 if ( num_pages < 0 || num_pages > paging->domain_info->max_pages )
571 {
572 num_pages = paging->domain_info->max_pages;
573 DPRINTF("setting num_pages to %d\n", num_pages);
574 }
575 victims = calloc(num_pages, sizeof(xenpaging_victim_t));
577 /* ensure that if we get a signal, we'll do cleanup, then exit */
578 act.sa_handler = close_handler;
579 act.sa_flags = 0;
580 sigemptyset(&act.sa_mask);
581 sigaction(SIGHUP, &act, NULL);
582 sigaction(SIGTERM, &act, NULL);
583 sigaction(SIGINT, &act, NULL);
584 sigaction(SIGALRM, &act, NULL);
586 /* Evict pages */
587 memset(victims, 0, sizeof(xenpaging_victim_t) * num_pages);
588 for ( i = 0; i < num_pages; i++ )
589 {
590 rc = evict_victim(paging, &victims[i], fd, i);
591 if ( rc == -ENOSPC )
592 break;
593 if ( rc == -EINTR )
594 break;
595 if ( i % 100 == 0 )
596 DPRINTF("%d pages evicted\n", i);
597 }
599 DPRINTF("%d pages evicted. Done.\n", i);
601 /* Swap pages in and out */
602 while ( !interrupted )
603 {
604 /* Wait for Xen to signal that a page needs paged in */
605 rc = xc_wait_for_event_or_timeout(xch, paging->mem_event.xce_handle, 100);
606 if ( rc < -1 )
607 {
608 ERROR("Error getting event");
609 goto out;
610 }
611 else if ( rc != -1 )
612 {
613 DPRINTF("Got event from Xen\n");
614 }
616 while ( RING_HAS_UNCONSUMED_REQUESTS(&paging->mem_event.back_ring) )
617 {
618 rc = get_request(&paging->mem_event, &req);
619 if ( rc != 0 )
620 {
621 ERROR("Error getting request");
622 goto out;
623 }
625 /* Check if the page has already been paged in */
626 if ( test_and_clear_bit(req.gfn, paging->bitmap) )
627 {
628 /* Find where in the paging file to read from */
629 for ( i = 0; i < num_pages; i++ )
630 {
631 if ( victims[i].gfn == req.gfn )
632 break;
633 }
635 if ( i >= num_pages )
636 {
637 DPRINTF("Couldn't find page %"PRIx64"\n", req.gfn);
638 goto out;
639 }
641 if ( req.flags & MEM_EVENT_FLAG_DROP_PAGE )
642 {
643 DPRINTF("drop_page ^ gfn %"PRIx64" pageslot %d\n", req.gfn, i);
644 /* Notify policy of page being dropped */
645 policy_notify_paged_in(req.gfn);
646 }
647 else
648 {
649 /* Populate the page */
650 rc = xenpaging_populate_page(paging, &req.gfn, fd, i);
651 if ( rc != 0 )
652 {
653 ERROR("Error populating page");
654 goto out;
655 }
657 /* Prepare the response */
658 rsp.gfn = req.gfn;
659 rsp.p2mt = req.p2mt;
660 rsp.vcpu_id = req.vcpu_id;
661 rsp.flags = req.flags;
663 rc = xenpaging_resume_page(paging, &rsp, 1);
664 if ( rc != 0 )
665 {
666 ERROR("Error resuming page");
667 goto out;
668 }
669 }
671 /* Evict a new page to replace the one we just paged in */
672 evict_victim(paging, &victims[i], fd, i);
673 }
674 else
675 {
676 DPRINTF("page already populated (domain = %d; vcpu = %d;"
677 " p2mt = %x;"
678 " gfn = %"PRIx64"; paused = %d)\n",
679 paging->mem_event.domain_id, req.vcpu_id,
680 req.p2mt,
681 req.gfn, req.flags & MEM_EVENT_FLAG_VCPU_PAUSED);
683 /* Tell Xen to resume the vcpu */
684 /* XXX: Maybe just check if the vcpu was paused? */
685 if ( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
686 {
687 /* Prepare the response */
688 rsp.gfn = req.gfn;
689 rsp.p2mt = req.p2mt;
690 rsp.vcpu_id = req.vcpu_id;
691 rsp.flags = req.flags;
693 rc = xenpaging_resume_page(paging, &rsp, 0);
694 if ( rc != 0 )
695 {
696 ERROR("Error resuming");
697 goto out;
698 }
699 }
700 }
701 }
702 }
703 DPRINTF("xenpaging got signal %d\n", interrupted);
705 out:
706 close(fd);
707 free(victims);
709 /* Tear down domain paging */
710 rc1 = xenpaging_teardown(paging);
711 if ( rc1 != 0 )
712 fprintf(stderr, "Error tearing down paging");
714 if ( rc == 0 )
715 rc = rc1;
716 return rc;
717 }
720 /*
721 * Local variables:
722 * mode: C
723 * c-set-style: "BSD"
724 * c-basic-offset: 4
725 * indent-tabs-mode: nil
726 * End:
727 */