diff options
Diffstat (limited to 'arch/powerpc/mm/slice.c')
-rw-r--r-- | arch/powerpc/mm/slice.c | 177 |
1 files changed, 133 insertions, 44 deletions
diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c index 2bd12d965db1..db44e02e045b 100644 --- a/arch/powerpc/mm/slice.c +++ b/arch/powerpc/mm/slice.c | |||
@@ -215,10 +215,7 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz | |||
215 | mm->context.high_slices_psize); | 215 | mm->context.high_slices_psize); |
216 | 216 | ||
217 | spin_unlock_irqrestore(&slice_convert_lock, flags); | 217 | spin_unlock_irqrestore(&slice_convert_lock, flags); |
218 | mb(); | ||
219 | 218 | ||
220 | /* XXX this is sub-optimal but will do for now */ | ||
221 | on_each_cpu(slice_flush_segments, mm, 1); | ||
222 | #ifdef CONFIG_SPU_BASE | 219 | #ifdef CONFIG_SPU_BASE |
223 | spu_flush_all_slbs(mm); | 220 | spu_flush_all_slbs(mm); |
224 | #endif | 221 | #endif |
@@ -384,17 +381,34 @@ static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len, | |||
384 | return slice_find_area_bottomup(mm, len, mask, psize, use_cache); | 381 | return slice_find_area_bottomup(mm, len, mask, psize, use_cache); |
385 | } | 382 | } |
386 | 383 | ||
384 | #define or_mask(dst, src) do { \ | ||
385 | (dst).low_slices |= (src).low_slices; \ | ||
386 | (dst).high_slices |= (src).high_slices; \ | ||
387 | } while (0) | ||
388 | |||
389 | #define andnot_mask(dst, src) do { \ | ||
390 | (dst).low_slices &= ~(src).low_slices; \ | ||
391 | (dst).high_slices &= ~(src).high_slices; \ | ||
392 | } while (0) | ||
393 | |||
394 | #ifdef CONFIG_PPC_64K_PAGES | ||
395 | #define MMU_PAGE_BASE MMU_PAGE_64K | ||
396 | #else | ||
397 | #define MMU_PAGE_BASE MMU_PAGE_4K | ||
398 | #endif | ||
399 | |||
387 | unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, | 400 | unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, |
388 | unsigned long flags, unsigned int psize, | 401 | unsigned long flags, unsigned int psize, |
389 | int topdown, int use_cache) | 402 | int topdown, int use_cache) |
390 | { | 403 | { |
391 | struct slice_mask mask; | 404 | struct slice_mask mask = {0, 0}; |
392 | struct slice_mask good_mask; | 405 | struct slice_mask good_mask; |
393 | struct slice_mask potential_mask = {0,0} /* silence stupid warning */; | 406 | struct slice_mask potential_mask = {0,0} /* silence stupid warning */; |
394 | int pmask_set = 0; | 407 | struct slice_mask compat_mask = {0, 0}; |
395 | int fixed = (flags & MAP_FIXED); | 408 | int fixed = (flags & MAP_FIXED); |
396 | int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); | 409 | int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); |
397 | struct mm_struct *mm = current->mm; | 410 | struct mm_struct *mm = current->mm; |
411 | unsigned long newaddr; | ||
398 | 412 | ||
399 | /* Sanity checks */ | 413 | /* Sanity checks */ |
400 | BUG_ON(mm->task_size == 0); | 414 | BUG_ON(mm->task_size == 0); |
@@ -416,21 +430,48 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, | |||
416 | if (!fixed && addr) { | 430 | if (!fixed && addr) { |
417 | addr = _ALIGN_UP(addr, 1ul << pshift); | 431 | addr = _ALIGN_UP(addr, 1ul << pshift); |
418 | slice_dbg(" aligned addr=%lx\n", addr); | 432 | slice_dbg(" aligned addr=%lx\n", addr); |
433 | /* Ignore hint if it's too large or overlaps a VMA */ | ||
434 | if (addr > mm->task_size - len || | ||
435 | !slice_area_is_free(mm, addr, len)) | ||
436 | addr = 0; | ||
419 | } | 437 | } |
420 | 438 | ||
421 | /* First makeup a "good" mask of slices that have the right size | 439 | /* First make up a "good" mask of slices that have the right size |
422 | * already | 440 | * already |
423 | */ | 441 | */ |
424 | good_mask = slice_mask_for_size(mm, psize); | 442 | good_mask = slice_mask_for_size(mm, psize); |
425 | slice_print_mask(" good_mask", good_mask); | 443 | slice_print_mask(" good_mask", good_mask); |
426 | 444 | ||
427 | /* First check hint if it's valid or if we have MAP_FIXED */ | 445 | /* |
428 | if ((addr != 0 || fixed) && (mm->task_size - len) >= addr) { | 446 | * Here "good" means slices that are already the right page size, |
447 | * "compat" means slices that have a compatible page size (i.e. | ||
448 | * 4k in a 64k pagesize kernel), and "free" means slices without | ||
449 | * any VMAs. | ||
450 | * | ||
451 | * If MAP_FIXED: | ||
452 | * check if fits in good | compat => OK | ||
453 | * check if fits in good | compat | free => convert free | ||
454 | * else bad | ||
455 | * If have hint: | ||
456 | * check if hint fits in good => OK | ||
457 | * check if hint fits in good | free => convert free | ||
458 | * Otherwise: | ||
459 | * search in good, found => OK | ||
460 | * search in good | free, found => convert free | ||
461 | * search in good | compat | free, found => convert free. | ||
462 | */ | ||
429 | 463 | ||
430 | /* Don't bother with hint if it overlaps a VMA */ | 464 | #ifdef CONFIG_PPC_64K_PAGES |
431 | if (!fixed && !slice_area_is_free(mm, addr, len)) | 465 | /* If we support combo pages, we can allow 64k pages in 4k slices */ |
432 | goto search; | 466 | if (psize == MMU_PAGE_64K) { |
467 | compat_mask = slice_mask_for_size(mm, MMU_PAGE_4K); | ||
468 | if (fixed) | ||
469 | or_mask(good_mask, compat_mask); | ||
470 | } | ||
471 | #endif | ||
433 | 472 | ||
473 | /* First check hint if it's valid or if we have MAP_FIXED */ | ||
474 | if (addr != 0 || fixed) { | ||
434 | /* Build a mask for the requested range */ | 475 | /* Build a mask for the requested range */ |
435 | mask = slice_range_to_mask(addr, len); | 476 | mask = slice_range_to_mask(addr, len); |
436 | slice_print_mask(" mask", mask); | 477 | slice_print_mask(" mask", mask); |
@@ -442,54 +483,66 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, | |||
442 | slice_dbg(" fits good !\n"); | 483 | slice_dbg(" fits good !\n"); |
443 | return addr; | 484 | return addr; |
444 | } | 485 | } |
445 | 486 | } else { | |
446 | /* We don't fit in the good mask, check what other slices are | 487 | /* Now let's see if we can find something in the existing |
447 | * empty and thus can be converted | 488 | * slices for that size |
448 | */ | 489 | */ |
449 | potential_mask = slice_mask_for_free(mm); | 490 | newaddr = slice_find_area(mm, len, good_mask, psize, topdown, |
450 | potential_mask.low_slices |= good_mask.low_slices; | 491 | use_cache); |
451 | potential_mask.high_slices |= good_mask.high_slices; | 492 | if (newaddr != -ENOMEM) { |
452 | pmask_set = 1; | 493 | /* Found within the good mask, we don't have to setup, |
453 | slice_print_mask(" potential", potential_mask); | 494 | * we thus return directly |
454 | if (slice_check_fit(mask, potential_mask)) { | 495 | */ |
455 | slice_dbg(" fits potential !\n"); | 496 | slice_dbg(" found area at 0x%lx\n", newaddr); |
456 | goto convert; | 497 | return newaddr; |
457 | } | 498 | } |
458 | } | 499 | } |
459 | 500 | ||
460 | /* If we have MAP_FIXED and failed the above step, then error out */ | 501 | /* We don't fit in the good mask, check what other slices are |
502 | * empty and thus can be converted | ||
503 | */ | ||
504 | potential_mask = slice_mask_for_free(mm); | ||
505 | or_mask(potential_mask, good_mask); | ||
506 | slice_print_mask(" potential", potential_mask); | ||
507 | |||
508 | if ((addr != 0 || fixed) && slice_check_fit(mask, potential_mask)) { | ||
509 | slice_dbg(" fits potential !\n"); | ||
510 | goto convert; | ||
511 | } | ||
512 | |||
513 | /* If we have MAP_FIXED and failed the above steps, then error out */ | ||
461 | if (fixed) | 514 | if (fixed) |
462 | return -EBUSY; | 515 | return -EBUSY; |
463 | 516 | ||
464 | search: | ||
465 | slice_dbg(" search...\n"); | 517 | slice_dbg(" search...\n"); |
466 | 518 | ||
467 | /* Now let's see if we can find something in the existing slices | 519 | /* If we had a hint that didn't work out, see if we can fit |
468 | * for that size | 520 | * anywhere in the good area. |
469 | */ | 521 | */ |
470 | addr = slice_find_area(mm, len, good_mask, psize, topdown, use_cache); | 522 | if (addr) { |
471 | if (addr != -ENOMEM) { | 523 | addr = slice_find_area(mm, len, good_mask, psize, topdown, |
472 | /* Found within the good mask, we don't have to setup, | 524 | use_cache); |
473 | * we thus return directly | 525 | if (addr != -ENOMEM) { |
474 | */ | 526 | slice_dbg(" found area at 0x%lx\n", addr); |
475 | slice_dbg(" found area at 0x%lx\n", addr); | 527 | return addr; |
476 | return addr; | 528 | } |
477 | } | ||
478 | |||
479 | /* Won't fit, check what can be converted */ | ||
480 | if (!pmask_set) { | ||
481 | potential_mask = slice_mask_for_free(mm); | ||
482 | potential_mask.low_slices |= good_mask.low_slices; | ||
483 | potential_mask.high_slices |= good_mask.high_slices; | ||
484 | pmask_set = 1; | ||
485 | slice_print_mask(" potential", potential_mask); | ||
486 | } | 529 | } |
487 | 530 | ||
488 | /* Now let's see if we can find something in the existing slices | 531 | /* Now let's see if we can find something in the existing slices |
489 | * for that size | 532 | * for that size plus free slices |
490 | */ | 533 | */ |
491 | addr = slice_find_area(mm, len, potential_mask, psize, topdown, | 534 | addr = slice_find_area(mm, len, potential_mask, psize, topdown, |
492 | use_cache); | 535 | use_cache); |
536 | |||
537 | #ifdef CONFIG_PPC_64K_PAGES | ||
538 | if (addr == -ENOMEM && psize == MMU_PAGE_64K) { | ||
539 | /* retry the search with 4k-page slices included */ | ||
540 | or_mask(potential_mask, compat_mask); | ||
541 | addr = slice_find_area(mm, len, potential_mask, psize, | ||
542 | topdown, use_cache); | ||
543 | } | ||
544 | #endif | ||
545 | |||
493 | if (addr == -ENOMEM) | 546 | if (addr == -ENOMEM) |
494 | return -ENOMEM; | 547 | return -ENOMEM; |
495 | 548 | ||
@@ -498,7 +551,13 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len, | |||
498 | slice_print_mask(" mask", mask); | 551 | slice_print_mask(" mask", mask); |
499 | 552 | ||
500 | convert: | 553 | convert: |
501 | slice_convert(mm, mask, psize); | 554 | andnot_mask(mask, good_mask); |
555 | andnot_mask(mask, compat_mask); | ||
556 | if (mask.low_slices || mask.high_slices) { | ||
557 | slice_convert(mm, mask, psize); | ||
558 | if (psize > MMU_PAGE_BASE) | ||
559 | on_each_cpu(slice_flush_segments, mm, 1); | ||
560 | } | ||
502 | return addr; | 561 | return addr; |
503 | 562 | ||
504 | } | 563 | } |
@@ -598,6 +657,36 @@ void slice_set_user_psize(struct mm_struct *mm, unsigned int psize) | |||
598 | spin_unlock_irqrestore(&slice_convert_lock, flags); | 657 | spin_unlock_irqrestore(&slice_convert_lock, flags); |
599 | } | 658 | } |
600 | 659 | ||
660 | void slice_set_psize(struct mm_struct *mm, unsigned long address, | ||
661 | unsigned int psize) | ||
662 | { | ||
663 | unsigned long i, flags; | ||
664 | u64 *p; | ||
665 | |||
666 | spin_lock_irqsave(&slice_convert_lock, flags); | ||
667 | if (address < SLICE_LOW_TOP) { | ||
668 | i = GET_LOW_SLICE_INDEX(address); | ||
669 | p = &mm->context.low_slices_psize; | ||
670 | } else { | ||
671 | i = GET_HIGH_SLICE_INDEX(address); | ||
672 | p = &mm->context.high_slices_psize; | ||
673 | } | ||
674 | *p = (*p & ~(0xful << (i * 4))) | ((unsigned long) psize << (i * 4)); | ||
675 | spin_unlock_irqrestore(&slice_convert_lock, flags); | ||
676 | |||
677 | #ifdef CONFIG_SPU_BASE | ||
678 | spu_flush_all_slbs(mm); | ||
679 | #endif | ||
680 | } | ||
681 | |||
682 | void slice_set_range_psize(struct mm_struct *mm, unsigned long start, | ||
683 | unsigned long len, unsigned int psize) | ||
684 | { | ||
685 | struct slice_mask mask = slice_range_to_mask(start, len); | ||
686 | |||
687 | slice_convert(mm, mask, psize); | ||
688 | } | ||
689 | |||
601 | /* | 690 | /* |
602 | * is_hugepage_only_range() is used by generic code to verify wether | 691 | * is_hugepage_only_range() is used by generic code to verify wether |
603 | * a normal mmap mapping (non hugetlbfs) is valid on a given area. | 692 | * a normal mmap mapping (non hugetlbfs) is valid on a given area. |