]> rtime.felk.cvut.cz Git - can-eth-gw-linux.git/blobdiff - arch/s390/mm/vmem.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[can-eth-gw-linux.git] / arch / s390 / mm / vmem.c
index 387c7c60b5b8d65a50bec087a54288a1cc913519..6ed1426d27c55ff893b22a9ae2d2876c8bccb3c5 100644 (file)
@@ -89,6 +89,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
        int ret = -ENOMEM;
 
        while (address < end) {
+               pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
                pg_dir = pgd_offset_k(address);
                if (pgd_none(*pg_dir)) {
                        pu_dir = vmem_pud_alloc();
@@ -96,18 +97,24 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
                                goto out;
                        pgd_populate(&init_mm, pg_dir, pu_dir);
                }
-
                pu_dir = pud_offset(pg_dir, address);
+#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
+               if (MACHINE_HAS_EDAT2 && pud_none(*pu_dir) && address &&
+                   !(address & ~PUD_MASK) && (address + PUD_SIZE <= end)) {
+                       pte_val(pte) |= _REGION3_ENTRY_LARGE;
+                       pte_val(pte) |= _REGION_ENTRY_TYPE_R3;
+                       pud_val(*pu_dir) = pte_val(pte);
+                       address += PUD_SIZE;
+                       continue;
+               }
+#endif
                if (pud_none(*pu_dir)) {
                        pm_dir = vmem_pmd_alloc();
                        if (!pm_dir)
                                goto out;
                        pud_populate(&init_mm, pu_dir, pm_dir);
                }
-
-               pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
                pm_dir = pmd_offset(pu_dir, address);
-
 #if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
                if (MACHINE_HAS_EDAT1 && pmd_none(*pm_dir) && address &&
                    !(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) {
@@ -160,6 +167,11 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
                        address += PUD_SIZE;
                        continue;
                }
+               if (pud_large(*pu_dir)) {
+                       pud_clear(pu_dir);
+                       address += PUD_SIZE;
+                       continue;
+               }
                pm_dir = pmd_offset(pu_dir, address);
                if (pmd_none(*pm_dir)) {
                        address += PMD_SIZE;
@@ -193,7 +205,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
        start_addr = (unsigned long) start;
        end_addr = (unsigned long) (start + nr);
 
-       for (address = start_addr; address < end_addr; address += PAGE_SIZE) {
+       for (address = start_addr; address < end_addr;) {
                pg_dir = pgd_offset_k(address);
                if (pgd_none(*pg_dir)) {
                        pu_dir = vmem_pud_alloc();
@@ -212,10 +224,33 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
 
                pm_dir = pmd_offset(pu_dir, address);
                if (pmd_none(*pm_dir)) {
+#ifdef CONFIG_64BIT
+                       /* Use 1MB frames for vmemmap if available. We always
+                        * use large frames even if they are only partially
+                        * used.
+                        * Otherwise we would have also page tables since
+                        * vmemmap_populate gets called for each section
+                        * separately. */
+                       if (MACHINE_HAS_EDAT1) {
+                               void *new_page;
+
+                               new_page = vmemmap_alloc_block(PMD_SIZE, node);
+                               if (!new_page)
+                                       goto out;
+                               pte = mk_pte_phys(__pa(new_page), PAGE_RW);
+                               pte_val(pte) |= _SEGMENT_ENTRY_LARGE;
+                               pmd_val(*pm_dir) = pte_val(pte);
+                               address = (address + PMD_SIZE) & PMD_MASK;
+                               continue;
+                       }
+#endif
                        pt_dir = vmem_pte_alloc(address);
                        if (!pt_dir)
                                goto out;
                        pmd_populate(&init_mm, pm_dir, pt_dir);
+               } else if (pmd_large(*pm_dir)) {
+                       address = (address + PMD_SIZE) & PMD_MASK;
+                       continue;
                }
 
                pt_dir = pte_offset_kernel(pm_dir, address);
@@ -228,6 +263,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
                        pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL);
                        *pt_dir = pte;
                }
+               address += PAGE_SIZE;
        }
        memset(start, 0, nr * sizeof(struct page));
        ret = 0;