diff options
author | David Vrabel <david.vrabel@citrix.com> | 2015-03-11 10:49:57 -0400 |
---|---|---|
committer | David Vrabel <david.vrabel@citrix.com> | 2015-03-16 10:49:15 -0400 |
commit | 4e8c0c8c4bf3a5b5c98046e146ab3884bf7a7d0e (patch) | |
tree | e2da5980fd405c7109a342b13f0a2a1214f94b61 /arch/x86/xen | |
parent | 628c28eefd6f2cef03b212081b466ae43fd093a3 (diff) |
xen/privcmd: improve performance of MMAPBATCH_V2
Make the IOCTL_PRIVCMD_MMAPBATCH_V2 (and older V1 version) map
multiple frames at a time rather than one at a time, despite the pages
being non-consecutive GFNs.
xen_remap_foreign_mfn_array() is added which maps an array of GFNs
(instead of a consecutive range of GFNs).
Since per-frame errors are returned in an array, privcmd must set the
MMAPBATCH_V1 error bits as part of the "report errors" phase, after
all the frames are mapped.
Migrate times are significantly improved (when using a PV toolstack
domain). For example, for an idle 12 GiB PV guest:
Before After
real 0m38.179s 0m26.868s
user 0m15.096s 0m13.652s
sys 0m28.988s 0m18.732s
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Diffstat (limited to 'arch/x86/xen')
-rw-r--r-- | arch/x86/xen/mmu.c | 101 |
1 files changed, 82 insertions, 19 deletions
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 3d536a56ddf1..29b3be230ede 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c | |||
@@ -2439,7 +2439,8 @@ void __init xen_hvm_init_mmu_ops(void) | |||
2439 | #define REMAP_BATCH_SIZE 16 | 2439 | #define REMAP_BATCH_SIZE 16 |
2440 | 2440 | ||
2441 | struct remap_data { | 2441 | struct remap_data { |
2442 | unsigned long mfn; | 2442 | xen_pfn_t *mfn; |
2443 | bool contiguous; | ||
2443 | pgprot_t prot; | 2444 | pgprot_t prot; |
2444 | struct mmu_update *mmu_update; | 2445 | struct mmu_update *mmu_update; |
2445 | }; | 2446 | }; |
@@ -2448,7 +2449,14 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token, | |||
2448 | unsigned long addr, void *data) | 2449 | unsigned long addr, void *data) |
2449 | { | 2450 | { |
2450 | struct remap_data *rmd = data; | 2451 | struct remap_data *rmd = data; |
2451 | pte_t pte = pte_mkspecial(mfn_pte(rmd->mfn++, rmd->prot)); | 2452 | pte_t pte = pte_mkspecial(mfn_pte(*rmd->mfn, rmd->prot)); |
2453 | |||
2454 | /* If we have a contigious range, just update the mfn itself, | ||
2455 | else update pointer to be "next mfn". */ | ||
2456 | if (rmd->contiguous) | ||
2457 | (*rmd->mfn)++; | ||
2458 | else | ||
2459 | rmd->mfn++; | ||
2452 | 2460 | ||
2453 | rmd->mmu_update->ptr = virt_to_machine(ptep).maddr; | 2461 | rmd->mmu_update->ptr = virt_to_machine(ptep).maddr; |
2454 | rmd->mmu_update->val = pte_val_ma(pte); | 2462 | rmd->mmu_update->val = pte_val_ma(pte); |
@@ -2457,26 +2465,26 @@ static int remap_area_mfn_pte_fn(pte_t *ptep, pgtable_t token, | |||
2457 | return 0; | 2465 | return 0; |
2458 | } | 2466 | } |
2459 | 2467 | ||
2460 | int xen_remap_domain_mfn_range(struct vm_area_struct *vma, | 2468 | static int do_remap_mfn(struct vm_area_struct *vma, |
2461 | unsigned long addr, | 2469 | unsigned long addr, |
2462 | xen_pfn_t mfn, int nr, | 2470 | xen_pfn_t *mfn, int nr, |
2463 | pgprot_t prot, unsigned domid, | 2471 | int *err_ptr, pgprot_t prot, |
2464 | struct page **pages) | 2472 | unsigned domid, |
2465 | 2473 | struct page **pages) | |
2466 | { | 2474 | { |
2475 | int err = 0; | ||
2467 | struct remap_data rmd; | 2476 | struct remap_data rmd; |
2468 | struct mmu_update mmu_update[REMAP_BATCH_SIZE]; | 2477 | struct mmu_update mmu_update[REMAP_BATCH_SIZE]; |
2469 | int batch; | ||
2470 | unsigned long range; | 2478 | unsigned long range; |
2471 | int err = 0; | 2479 | int mapped = 0; |
2472 | 2480 | ||
2473 | BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); | 2481 | BUG_ON(!((vma->vm_flags & (VM_PFNMAP | VM_IO)) == (VM_PFNMAP | VM_IO))); |
2474 | 2482 | ||
2475 | if (xen_feature(XENFEAT_auto_translated_physmap)) { | 2483 | if (xen_feature(XENFEAT_auto_translated_physmap)) { |
2476 | #ifdef CONFIG_XEN_PVH | 2484 | #ifdef CONFIG_XEN_PVH |
2477 | /* We need to update the local page tables and the xen HAP */ | 2485 | /* We need to update the local page tables and the xen HAP */ |
2478 | return xen_xlate_remap_gfn_range(vma, addr, mfn, nr, prot, | 2486 | return xen_xlate_remap_gfn_array(vma, addr, mfn, nr, err_ptr, |
2479 | domid, pages); | 2487 | prot, domid, pages); |
2480 | #else | 2488 | #else |
2481 | return -EINVAL; | 2489 | return -EINVAL; |
2482 | #endif | 2490 | #endif |
@@ -2484,9 +2492,15 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, | |||
2484 | 2492 | ||
2485 | rmd.mfn = mfn; | 2493 | rmd.mfn = mfn; |
2486 | rmd.prot = prot; | 2494 | rmd.prot = prot; |
2495 | /* We use the err_ptr to indicate if there we are doing a contigious | ||
2496 | * mapping or a discontigious mapping. */ | ||
2497 | rmd.contiguous = !err_ptr; | ||
2487 | 2498 | ||
2488 | while (nr) { | 2499 | while (nr) { |
2489 | batch = min(REMAP_BATCH_SIZE, nr); | 2500 | int index = 0; |
2501 | int done = 0; | ||
2502 | int batch = min(REMAP_BATCH_SIZE, nr); | ||
2503 | int batch_left = batch; | ||
2490 | range = (unsigned long)batch << PAGE_SHIFT; | 2504 | range = (unsigned long)batch << PAGE_SHIFT; |
2491 | 2505 | ||
2492 | rmd.mmu_update = mmu_update; | 2506 | rmd.mmu_update = mmu_update; |
@@ -2495,23 +2509,72 @@ int xen_remap_domain_mfn_range(struct vm_area_struct *vma, | |||
2495 | if (err) | 2509 | if (err) |
2496 | goto out; | 2510 | goto out; |
2497 | 2511 | ||
2498 | err = HYPERVISOR_mmu_update(mmu_update, batch, NULL, domid); | 2512 | /* We record the error for each page that gives an error, but |
2499 | if (err < 0) | 2513 | * continue mapping until the whole set is done */ |
2500 | goto out; | 2514 | do { |
2515 | int i; | ||
2516 | |||
2517 | err = HYPERVISOR_mmu_update(&mmu_update[index], | ||
2518 | batch_left, &done, domid); | ||
2519 | |||
2520 | /* | ||
2521 | * @err_ptr may be the same buffer as @mfn, so | ||
2522 | * only clear it after each chunk of @mfn is | ||
2523 | * used. | ||
2524 | */ | ||
2525 | if (err_ptr) { | ||
2526 | for (i = index; i < index + done; i++) | ||
2527 | err_ptr[i] = 0; | ||
2528 | } | ||
2529 | if (err < 0) { | ||
2530 | if (!err_ptr) | ||
2531 | goto out; | ||
2532 | err_ptr[i] = err; | ||
2533 | done++; /* Skip failed frame. */ | ||
2534 | } else | ||
2535 | mapped += done; | ||
2536 | batch_left -= done; | ||
2537 | index += done; | ||
2538 | } while (batch_left); | ||
2501 | 2539 | ||
2502 | nr -= batch; | 2540 | nr -= batch; |
2503 | addr += range; | 2541 | addr += range; |
2542 | if (err_ptr) | ||
2543 | err_ptr += batch; | ||
2504 | } | 2544 | } |
2505 | |||
2506 | err = 0; | ||
2507 | out: | 2545 | out: |
2508 | 2546 | ||
2509 | xen_flush_tlb_all(); | 2547 | xen_flush_tlb_all(); |
2510 | 2548 | ||
2511 | return err; | 2549 | return err < 0 ? err : mapped; |
2550 | } | ||
2551 | |||
2552 | int xen_remap_domain_mfn_range(struct vm_area_struct *vma, | ||
2553 | unsigned long addr, | ||
2554 | xen_pfn_t mfn, int nr, | ||
2555 | pgprot_t prot, unsigned domid, | ||
2556 | struct page **pages) | ||
2557 | { | ||
2558 | return do_remap_mfn(vma, addr, &mfn, nr, NULL, prot, domid, pages); | ||
2512 | } | 2559 | } |
2513 | EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); | 2560 | EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range); |
2514 | 2561 | ||
2562 | int xen_remap_domain_mfn_array(struct vm_area_struct *vma, | ||
2563 | unsigned long addr, | ||
2564 | xen_pfn_t *mfn, int nr, | ||
2565 | int *err_ptr, pgprot_t prot, | ||
2566 | unsigned domid, struct page **pages) | ||
2567 | { | ||
2568 | /* We BUG_ON because it's a programmer error to pass a NULL err_ptr, | ||
2569 | * and the consequences later is quite hard to detect what the actual | ||
2570 | * cause of "wrong memory was mapped in". | ||
2571 | */ | ||
2572 | BUG_ON(err_ptr == NULL); | ||
2573 | return do_remap_mfn(vma, addr, mfn, nr, err_ptr, prot, domid, pages); | ||
2574 | } | ||
2575 | EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_array); | ||
2576 | |||
2577 | |||
2515 | /* Returns: 0 success */ | 2578 | /* Returns: 0 success */ |
2516 | int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, | 2579 | int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, |
2517 | int numpgs, struct page **pages) | 2580 | int numpgs, struct page **pages) |