aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorvenkatesh.pallipadi@intel.com <venkatesh.pallipadi@intel.com>2008-12-18 14:41:30 -0500
committerH. Peter Anvin <hpa@zytor.com>2008-12-18 16:30:16 -0500
commit5899329b19100c0b82dc78e9b21ed8b920c9ffb3 (patch)
treeb4601266a9298f119fcf4432492fc0de6a48ca09 /arch/x86
parent2ab640379a0ab4cef746ced1d7e04a0941774bcb (diff)
x86: PAT: implement track/untrack of pfnmap regions for x86 - v3
Impact: New mm functionality. Hookup remap_pfn_range and vm_insert_pfn and corresponding copy and free routines with reserve and free tracking. reserve and free here only takes care of non RAM region mapping. For RAM region, driver should use set_memory_[uc|wc|wb] to set the cache type and then setup the mapping for user pte. We can bypass below reserve/free in that case. Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/include/asm/pgtable.h10
-rw-r--r--arch/x86/mm/pat.c236
2 files changed, 246 insertions, 0 deletions
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index c012f3b11671..7dcd94c29044 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -219,6 +219,11 @@ static inline unsigned long pte_pfn(pte_t pte)
219 return (pte_val(pte) & PTE_PFN_MASK) >> PAGE_SHIFT; 219 return (pte_val(pte) & PTE_PFN_MASK) >> PAGE_SHIFT;
220} 220}
221 221
222static inline u64 pte_pa(pte_t pte)
223{
224 return pte_val(pte) & PTE_PFN_MASK;
225}
226
222#define pte_page(pte) pfn_to_page(pte_pfn(pte)) 227#define pte_page(pte) pfn_to_page(pte_pfn(pte))
223 228
224static inline int pmd_large(pmd_t pte) 229static inline int pmd_large(pmd_t pte)
@@ -328,6 +333,11 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
328 333
329#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask) 334#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
330 335
336/* Indicate that x86 has its own track and untrack pfn vma functions */
337#define track_pfn_vma_new track_pfn_vma_new
338#define track_pfn_vma_copy track_pfn_vma_copy
339#define untrack_pfn_vma untrack_pfn_vma
340
331#ifndef __ASSEMBLY__ 341#ifndef __ASSEMBLY__
332#define __HAVE_PHYS_MEM_ACCESS_PROT 342#define __HAVE_PHYS_MEM_ACCESS_PROT
333struct file; 343struct file;
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index eb1bf000d12e..1069ffecf77d 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -596,6 +596,242 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_prot)
596 free_memtype(addr, addr + size); 596 free_memtype(addr, addr + size);
597} 597}
598 598
599/*
600 * Internal interface to reserve a range of physical memory with prot.
601 * Reserved non RAM regions only and after successful reserve_memtype,
602 * this func also keeps identity mapping (if any) in sync with this new prot.
603 */
604static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
605{
606 int is_ram = 0;
607 int id_sz, ret;
608 unsigned long flags;
609 unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK);
610
611 is_ram = pagerange_is_ram(paddr, paddr + size);
612
613 if (is_ram != 0) {
614 /*
615 * For mapping RAM pages, drivers need to call
616 * set_memory_[uc|wc|wb] directly, for reserve and free, before
617 * setting up the PTE.
618 */
619 WARN_ON_ONCE(1);
620 return 0;
621 }
622
623 ret = reserve_memtype(paddr, paddr + size, want_flags, &flags);
624 if (ret)
625 return ret;
626
627 if (flags != want_flags) {
628 free_memtype(paddr, paddr + size);
629 printk(KERN_ERR
630 "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n",
631 current->comm, current->pid,
632 cattr_name(want_flags),
633 (unsigned long long)paddr,
634 (unsigned long long)(paddr + size),
635 cattr_name(flags));
636 return -EINVAL;
637 }
638
639 /* Need to keep identity mapping in sync */
640 if (paddr >= __pa(high_memory))
641 return 0;
642
643 id_sz = (__pa(high_memory) < paddr + size) ?
644 __pa(high_memory) - paddr :
645 size;
646
647 if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) {
648 free_memtype(paddr, paddr + size);
649 printk(KERN_ERR
650 "%s:%d reserve_pfn_range ioremap_change_attr failed %s "
651 "for %Lx-%Lx\n",
652 current->comm, current->pid,
653 cattr_name(flags),
654 (unsigned long long)paddr,
655 (unsigned long long)(paddr + size));
656 return -EINVAL;
657 }
658 return 0;
659}
660
661/*
662 * Internal interface to free a range of physical memory.
663 * Frees non RAM regions only.
664 */
665static void free_pfn_range(u64 paddr, unsigned long size)
666{
667 int is_ram;
668
669 is_ram = pagerange_is_ram(paddr, paddr + size);
670 if (is_ram == 0)
671 free_memtype(paddr, paddr + size);
672}
673
674/*
675 * track_pfn_vma_copy is called when vma that is covering the pfnmap gets
676 * copied through copy_page_range().
677 *
678 * If the vma has a linear pfn mapping for the entire range, we get the prot
679 * from pte and reserve the entire vma range with single reserve_pfn_range call.
680 * Otherwise, we reserve the entire vma range, my ging through the PTEs page
681 * by page to get physical address and protection.
682 */
683int track_pfn_vma_copy(struct vm_area_struct *vma)
684{
685 int retval = 0;
686 unsigned long i, j;
687 u64 paddr;
688 pgprot_t prot;
689 pte_t pte;
690 unsigned long vma_start = vma->vm_start;
691 unsigned long vma_end = vma->vm_end;
692 unsigned long vma_size = vma_end - vma_start;
693
694 if (!pat_enabled)
695 return 0;
696
697 if (is_linear_pfn_mapping(vma)) {
698 /*
699 * reserve the whole chunk starting from vm_pgoff,
700 * But, we have to get the protection from pte.
701 */
702 if (follow_pfnmap_pte(vma, vma_start, &pte)) {
703 WARN_ON_ONCE(1);
704 return -1;
705 }
706 prot = pte_pgprot(pte);
707 paddr = (u64)vma->vm_pgoff << PAGE_SHIFT;
708 return reserve_pfn_range(paddr, vma_size, prot);
709 }
710
711 /* reserve entire vma page by page, using pfn and prot from pte */
712 for (i = 0; i < vma_size; i += PAGE_SIZE) {
713 if (follow_pfnmap_pte(vma, vma_start + i, &pte))
714 continue;
715
716 paddr = pte_pa(pte);
717 prot = pte_pgprot(pte);
718 retval = reserve_pfn_range(paddr, PAGE_SIZE, prot);
719 if (retval)
720 goto cleanup_ret;
721 }
722 return 0;
723
724cleanup_ret:
725 /* Reserve error: Cleanup partial reservation and return error */
726 for (j = 0; j < i; j += PAGE_SIZE) {
727 if (follow_pfnmap_pte(vma, vma_start + j, &pte))
728 continue;
729
730 paddr = pte_pa(pte);
731 free_pfn_range(paddr, PAGE_SIZE);
732 }
733
734 return retval;
735}
736
737/*
738 * track_pfn_vma_new is called when a _new_ pfn mapping is being established
739 * for physical range indicated by pfn and size.
740 *
741 * prot is passed in as a parameter for the new mapping. If the vma has a
742 * linear pfn mapping for the entire range reserve the entire vma range with
743 * single reserve_pfn_range call.
744 * Otherwise, we look t the pfn and size and reserve only the specified range
745 * page by page.
746 *
747 * Note that this function can be called with caller trying to map only a
748 * subrange/page inside the vma.
749 */
750int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
751 unsigned long pfn, unsigned long size)
752{
753 int retval = 0;
754 unsigned long i, j;
755 u64 base_paddr;
756 u64 paddr;
757 unsigned long vma_start = vma->vm_start;
758 unsigned long vma_end = vma->vm_end;
759 unsigned long vma_size = vma_end - vma_start;
760
761 if (!pat_enabled)
762 return 0;
763
764 if (is_linear_pfn_mapping(vma)) {
765 /* reserve the whole chunk starting from vm_pgoff */
766 paddr = (u64)vma->vm_pgoff << PAGE_SHIFT;
767 return reserve_pfn_range(paddr, vma_size, prot);
768 }
769
770 /* reserve page by page using pfn and size */
771 base_paddr = (u64)pfn << PAGE_SHIFT;
772 for (i = 0; i < size; i += PAGE_SIZE) {
773 paddr = base_paddr + i;
774 retval = reserve_pfn_range(paddr, PAGE_SIZE, prot);
775 if (retval)
776 goto cleanup_ret;
777 }
778 return 0;
779
780cleanup_ret:
781 /* Reserve error: Cleanup partial reservation and return error */
782 for (j = 0; j < i; j += PAGE_SIZE) {
783 paddr = base_paddr + j;
784 free_pfn_range(paddr, PAGE_SIZE);
785 }
786
787 return retval;
788}
789
790/*
791 * untrack_pfn_vma is called while unmapping a pfnmap for a region.
792 * untrack can be called for a specific region indicated by pfn and size or
793 * can be for the entire vma (in which case size can be zero).
794 */
795void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
796 unsigned long size)
797{
798 unsigned long i;
799 u64 paddr;
800 unsigned long vma_start = vma->vm_start;
801 unsigned long vma_end = vma->vm_end;
802 unsigned long vma_size = vma_end - vma_start;
803
804 if (!pat_enabled)
805 return;
806
807 if (is_linear_pfn_mapping(vma)) {
808 /* free the whole chunk starting from vm_pgoff */
809 paddr = (u64)vma->vm_pgoff << PAGE_SHIFT;
810 free_pfn_range(paddr, vma_size);
811 return;
812 }
813
814 if (size != 0 && size != vma_size) {
815 /* free page by page, using pfn and size */
816 paddr = (u64)pfn << PAGE_SHIFT;
817 for (i = 0; i < size; i += PAGE_SIZE) {
818 paddr = paddr + i;
819 free_pfn_range(paddr, PAGE_SIZE);
820 }
821 } else {
822 /* free entire vma, page by page, using the pfn from pte */
823 for (i = 0; i < vma_size; i += PAGE_SIZE) {
824 pte_t pte;
825
826 if (follow_pfnmap_pte(vma, vma_start + i, &pte))
827 continue;
828
829 paddr = pte_pa(pte);
830 free_pfn_range(paddr, PAGE_SIZE);
831 }
832 }
833}
834
599#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT) 835#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT)
600 836
601/* get Nth element of the linked list */ 837/* get Nth element of the linked list */