debuggers.hg

view tools/xenpaging/xenpaging.c @ 20837:0b138a019292

libxc: use new (replacement) mmap-batch ioctl

Replace all calls to xc_map_foreign_batch() where the caller doesn't
look at the passed in array to check for errors by calls to
xc_map_foreign_pages(). Replace all remaining calls by such to the
newly introduced xc_map_foreign_bulk().

As a sideband modification (needed while writing the patch to ensure
they're unused) eliminate unused parameters to
uncanonicalize_pagetable() and xc_map_foreign_batch_single(). Also
unmap live_p2m_frame_list earlier in map_and_save_p2m_table(),
reducing the peak amount of virtual address space required.

All supported OSes other than Linux continue to use the old ioctl for
the time being.

Also change libxc's MAJOR to 4.0 to reflect the API change.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jan 13 08:12:56 2010 +0000 (2010-01-13)
parents 26822073c426
children eb05347ac47a
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 /* Tear down domain paging in Xen */
216 rc = xc_mem_event_disable(paging->xc_handle, paging->mem_event.domain_id);
217 if ( rc != 0 )
218 {
219 ERROR("Error tearing down domain paging in xen");
220 goto err;
221 }
223 /* Unbind VIRQ */
224 rc = xc_evtchn_unbind(paging->mem_event.xce_handle, paging->mem_event.port);
225 if ( rc != 0 )
226 {
227 ERROR("Error unbinding event port");
228 goto err;
229 }
230 paging->mem_event.port = -1;
232 /* Close event channel */
233 rc = xc_evtchn_close(paging->mem_event.xce_handle);
234 if ( rc != 0 )
235 {
236 ERROR("Error closing event channel");
237 goto err;
238 }
239 paging->mem_event.xce_handle = -1;
241 /* Close connection to Xen */
242 rc = xc_interface_close(paging->xc_handle);
243 if ( rc != 0 )
244 {
245 ERROR("Error closing connection to xen");
246 goto err;
247 }
248 paging->xc_handle = -1;
250 return 0;
252 err:
253 return -1;
254 }
256 static int get_request(mem_event_t *mem_event, mem_event_request_t *req)
257 {
258 mem_event_back_ring_t *back_ring;
259 RING_IDX req_cons;
261 mem_event_ring_lock(mem_event);
263 back_ring = &mem_event->back_ring;
264 req_cons = back_ring->req_cons;
266 /* Copy request */
267 memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
268 req_cons++;
270 /* Update ring */
271 back_ring->req_cons = req_cons;
272 back_ring->sring->req_event = req_cons + 1;
274 mem_event_ring_unlock(mem_event);
276 return 0;
277 }
279 static int put_response(mem_event_t *mem_event, mem_event_response_t *rsp)
280 {
281 mem_event_back_ring_t *back_ring;
282 RING_IDX rsp_prod;
284 mem_event_ring_lock(mem_event);
286 back_ring = &mem_event->back_ring;
287 rsp_prod = back_ring->rsp_prod_pvt;
289 /* Copy response */
290 memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
291 rsp_prod++;
293 /* Update ring */
294 back_ring->rsp_prod_pvt = rsp_prod;
295 RING_PUSH_RESPONSES(back_ring);
297 mem_event_ring_unlock(mem_event);
299 return 0;
300 }
302 int xenpaging_evict_page(xenpaging_t *paging, xenpaging_victim_t *victim, int fd, int i)
303 {
304 void *page;
305 unsigned long gfn;
306 int ret;
308 DECLARE_DOMCTL;
310 /* Map page */
311 gfn = victim->gfn;
312 ret = -EFAULT;
313 page = xc_map_foreign_pages(paging->xc_handle, victim->domain_id,
314 PROT_READ | PROT_WRITE, &gfn, 1);
315 if ( page == NULL )
316 {
317 ERROR("Error mapping page");
318 goto out;
319 }
321 /* Copy page */
322 ret = write_page(fd, page, i);
323 if ( ret != 0 )
324 {
325 munmap(page, PAGE_SIZE);
326 ERROR("Error copying page");
327 goto out;
328 }
330 /* Clear page */
331 memset(page, 0, PAGE_SIZE);
333 munmap(page, PAGE_SIZE);
335 /* Tell Xen to evict page */
336 ret = xc_mem_paging_evict(paging->xc_handle, paging->mem_event.domain_id,
337 victim->gfn);
338 if ( ret != 0 )
339 {
340 ERROR("Error evicting page");
341 goto out;
342 }
344 /* Notify policy of page being paged in */
345 policy_notify_paged_in(paging->mem_event.domain_id, victim->gfn);
347 out:
348 return ret;
349 }
351 int xenpaging_resume_page(xenpaging_t *paging, mem_event_response_t *rsp)
352 {
353 int ret;
355 /* Put the page info on the ring */
356 ret = put_response(&paging->mem_event, rsp);
357 if ( ret != 0 )
358 goto out;
360 /* Notify policy of page being paged in */
361 policy_notify_paged_in(paging->mem_event.domain_id, rsp->gfn);
363 /* Tell Xen page is ready */
364 ret = xc_mem_paging_resume(paging->xc_handle, paging->mem_event.domain_id,
365 rsp->gfn);
366 ret = xc_evtchn_notify(paging->mem_event.xce_handle,
367 paging->mem_event.port);
369 out:
370 return ret;
371 }
373 int xenpaging_populate_page(xenpaging_t *paging, unsigned long *gfn, int fd, int i)
374 {
375 void *page;
376 int ret;
378 /* Tell Xen to allocate a page for the domain */
379 ret = xc_mem_paging_prep(paging->xc_handle, paging->mem_event.domain_id,
380 *gfn);
381 if ( ret != 0 )
382 {
383 ERROR("Error preparing for page in");
384 goto out_map;
385 }
387 /* Map page */
388 ret = -EFAULT;
389 page = xc_map_foreign_pages(paging->xc_handle, paging->mem_event.domain_id,
390 PROT_READ | PROT_WRITE, gfn, 1);
391 if ( page == NULL )
392 {
393 ERROR("Error mapping page: page is null");
394 goto out_map;
395 }
397 /* Read page */
398 ret = read_page(fd, page, i);
399 if ( ret != 0 )
400 {
401 ERROR("Error reading page");
402 goto out;
403 }
405 out:
406 munmap(page, PAGE_SIZE);
407 out_map:
408 return ret;
409 }
411 static int evict_victim(xenpaging_t *paging, domid_t domain_id,
412 xenpaging_victim_t *victim, int fd, int i)
413 {
414 int j = 0;
415 int ret;
417 do
418 {
419 ret = policy_choose_victim(paging, domain_id, victim);
420 if ( ret != 0 )
421 {
422 ERROR("Error choosing victim");
423 goto out;
424 }
426 ret = xc_mem_paging_nominate(paging->xc_handle,
427 paging->mem_event.domain_id, victim->gfn);
428 if ( ret == 0 )
429 ret = xenpaging_evict_page(paging, victim, fd, i);
430 else
431 {
432 if ( j++ % 1000 == 0 )
433 if ( xc_mem_paging_flush_ioemu_cache(domain_id) )
434 ERROR("Error flushing ioemu cache");
435 }
436 }
437 while ( ret );
439 if ( test_and_set_bit(victim->gfn, paging->bitmap) )
440 ERROR("Page has been evicted before");
442 ret = 0;
444 out:
445 return ret;
446 }
448 int main(int argc, char *argv[])
449 {
450 domid_t domain_id = atoi(argv[1]);
451 int num_pages = atoi(argv[2]);
452 xenpaging_t *paging;
453 xenpaging_victim_t victims[num_pages];
454 mem_event_request_t req;
455 mem_event_response_t rsp;
456 int i;
457 int rc;
459 int open_flags = O_CREAT | O_TRUNC | O_RDWR;
460 mode_t open_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH;
461 char filename[80];
462 int fd;
464 /* Open file */
465 sprintf(filename, "page_cache_%d", domain_id);
466 fd = open(filename, open_flags, open_mode);
467 if ( fd < 0 )
468 {
469 perror("failed to open file");
470 return -1;
471 }
473 /* Seed random-number generator */
474 srand(time(NULL));
476 /* Initialise domain paging */
477 paging = xenpaging_init(domain_id);
478 if ( paging == NULL )
479 {
480 ERROR("Error initialising paging");
481 goto out;
482 }
484 /* Evict pages */
485 memset(victims, 0, sizeof(xenpaging_victim_t) * num_pages);
486 for ( i = 0; i < num_pages; i++ )
487 {
488 evict_victim(paging, domain_id, &victims[i], fd, i);
489 if ( i % 100 == 0 )
490 DPRINTF("%d pages evicted\n", i);
491 }
493 DPRINTF("pages evicted\n");
495 /* Swap pages in and out */
496 while ( 1 )
497 {
498 /* Wait for Xen to signal that a page needs paged in */
499 rc = xc_wait_for_event_or_timeout(paging->mem_event.xce_handle, 100);
500 if ( rc < -1 )
501 {
502 ERROR("Error getting event");
503 goto out;
504 }
505 else if ( rc != -1 )
506 {
507 DPRINTF("Got event from Xen\n");
508 }
510 while ( RING_HAS_UNCONSUMED_REQUESTS(&paging->mem_event.back_ring) )
511 {
512 rc = get_request(&paging->mem_event, &req);
513 if ( rc != 0 )
514 {
515 ERROR("Error getting request");
516 goto out;
517 }
519 /* Check if the page has already been paged in */
520 if ( test_and_clear_bit(req.gfn, paging->bitmap) )
521 {
522 /* Find where in the paging file to read from */
523 for ( i = 0; i < num_pages; i++ )
524 {
525 if ( (victims[i].domain_id == paging->mem_event.domain_id) &&
526 (victims[i].gfn == req.gfn) )
527 break;
528 }
530 if ( i >= num_pages )
531 {
532 DPRINTF("Couldn't find page %lx\n", req.gfn);
533 goto out;
534 }
536 /* Populate the page */
537 rc = xenpaging_populate_page(paging, &req.gfn, fd, i);
538 if ( rc != 0 )
539 {
540 ERROR("Error populating page");
541 goto out;
542 }
544 /* Prepare the response */
545 rsp.gfn = req.gfn;
546 rsp.p2mt = req.p2mt;
547 rsp.vcpu_id = req.vcpu_id;
548 rsp.flags = req.flags;
550 rc = xenpaging_resume_page(paging, &rsp);
551 if ( rc != 0 )
552 {
553 ERROR("Error resuming page");
554 goto out;
555 }
557 /* Evict a new page to replace the one we just paged in */
558 evict_victim(paging, domain_id, &victims[i], fd, i);
559 }
560 else
561 {
562 DPRINTF("page already populated (domain = %d; vcpu = %d;"
563 " gfn = %lx; paused = %"PRId64")\n",
564 paging->mem_event.domain_id, req.vcpu_id,
565 req.gfn, req.flags & MEM_EVENT_FLAG_VCPU_PAUSED);
567 /* Tell Xen to resume the vcpu */
568 /* XXX: Maybe just check if the vcpu was paused? */
569 if ( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
570 {
571 /* Prepare the response */
572 rsp.gfn = req.gfn;
573 rsp.p2mt = req.p2mt;
574 rsp.vcpu_id = req.vcpu_id;
575 rsp.flags = req.flags;
577 rc = xenpaging_resume_page(paging, &rsp);
578 if ( rc != 0 )
579 {
580 ERROR("Error resuming");
581 goto out;
582 }
583 }
584 }
585 }
586 }
588 out:
589 /* Tear down domain paging */
590 rc = xenpaging_teardown(paging);
591 if ( rc != 0 )
592 {
593 ERROR("Error tearing down paging");
594 exit(1);
595 }
597 return 0;
598 }
601 /*
602 * Local variables:
603 * mode: C
604 * c-set-style: "BSD"
605 * c-basic-offset: 4
606 * indent-tabs-mode: nil
607 * End:
608 */