diff options
Diffstat (limited to 'arch/s390/mm/vmem.c')
| -rw-r--r-- | arch/s390/mm/vmem.c | 46 | 
1 files changed, 41 insertions, 5 deletions
| diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 387c7c60b5b8..6ed1426d27c5 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -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; |