Subject: [PATCHv2 2/3] arm64: Add support for ARCH_SUPPORTS_DEBUG_PAGEALLOC

From: [email-masked] (Laura Abbott)
Date: Mon, 1 Feb 2016 13:24:25 -0800

On 02/01/2016 04:29 AM, Mark Rutland wrote:
> Hi,
>
> On Fri, Jan 29, 2016 at 03:46:57PM -0800, Laura Abbott wrote:
>>
>> ARCH_SUPPORTS_DEBUG_PAGEALLOC provides a hook to map and unmap
>> pages for debugging purposes. This requires memory be mapped
>> with PAGE_SIZE mappings since breaking down larger mappings
>> at runtime will lead to TLB conflicts. Check if debug_pagealloc
>> is enabled at runtime and if so, map everyting with PAGE_SIZE
>> pages. Implement the functions to actually map/unmap the
>> pages at runtime.
>>
>>
>> Signed-off-by: Laura Abbott <[email-masked]>
>
> I tried to apply atop of the arm64 for-next/pgtable branch, but git
> wasn't very happy about that -- which branch/patches is this based on?
>
> I'm not sure if I'm missing something, have something I shouldn't, or if
> my MTA is corrupting patches again...
>

Hmmm, I based it off of your arm64-pagetable-rework-20160125 tag and
Ard's patch for vmalloc and set_memory_* . The patches seem to apply
on the for-next/pgtable branch as well so I'm guessing you are missing
Ard's patch.

>> ---
>>   arch/arm64/Kconfig       |  3 +++
>>   arch/arm64/mm/mmu.c      | 10 +++++++---
>>   arch/arm64/mm/pageattr.c | 40 +++++++++++++++++++++++++++++++++-------
>>   3 files changed, 43 insertions(+), 10 deletions(-)
>>
>> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>> index 8cc6228..0f33218 100644
>> --- a/arch/arm64/Kconfig
>> +++ b/arch/arm64/Kconfig
>> @@ -537,6 +537,9 @@ config HOTPLUG_CPU
>>   source kernel/Kconfig.preempt
>>   source kernel/Kconfig.hz
>>
>> +config ARCH_SUPPORTS_DEBUG_PAGEALLOC
>> +	def_bool y
>> +
>>   config ARCH_HAS_HOLES_MEMORYMODEL
>>   	def_bool y if SPARSEMEM
>>
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 103ebc0..5abbd30 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -181,7 +181,8 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
>>   	do {
>>   		next = pmd_addr_end(addr, end);
>>   		/* try section mapping first */
>> -		if (((addr | next | phys) & ~SECTION_MASK) == 0) {
>> +		if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
>> +		      (!debug_pagealloc_enabled() || !pgtable_alloc)) {
>>   			pmd_t old_pmd =*pmd;
>>   			set_pmd(pmd, __pmd(phys |
>>   					   pgprot_val(mk_sect_prot(prot))));
>> @@ -208,8 +209,11 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end,
>>   }
>>
>>   static inline bool use_1G_block(unsigned long addr, unsigned long next,
>> -			unsigned long phys)
>> +			unsigned long phys, phys_addr_t (*pgtable_alloc)(void))
>>   {
>> +	if (pgtable_alloc && debug_pagealloc_enabled())
>> +		return false;
>> +
>>   	if (PAGE_SHIFT != 12)
>>   		return false;
>>
>> @@ -241,7 +245,7 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
>>   		/*
>>   		 * For 4K granule only, attempt to put down a 1GB block
>>   		 */
>> -		if (use_1G_block(addr, next, phys)) {
>> +		if (use_1G_block(addr, next, phys, pgtable_alloc)) {
>>   			pud_t old_pud = *pud;
>>   			set_pud(pud, __pud(phys |
>>   					   pgprot_val(mk_sect_prot(prot))));
>
> The checks for pgtable_alloc is to allow create_mapping_noalloc to use
> sections regardless, right?
>
> I got a little confused by that initially, but that seems to make sense.
>

Yeah, that was what I was going for. I went for the implication with pgtable_alloc
over adding a bool variable to create_mapping. I'll see if I can put a comment
in somewhere to clarify the intentions.
  
>> diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
>> index 1360a02..69a8a7d 100644
>> --- a/arch/arm64/mm/pageattr.c
>> +++ b/arch/arm64/mm/pageattr.c
>> @@ -38,7 +38,8 @@ static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
>>   }
>>
>>   static int change_memory_common(unsigned long addr, int numpages,
>> -				pgprot_t set_mask, pgprot_t clear_mask)
>> +				pgprot_t set_mask, pgprot_t clear_mask,
>> +				bool ignore_vma_check)
>>   {
>>   	unsigned long start = addr;
>>   	unsigned long size = PAGE_SIZE*numpages;
>> @@ -65,11 +66,14 @@ static int change_memory_common(unsigned long addr, int numpages,
>>   	 *
>>   	 * So check whether the [addr, addr + size) interval is entirely
>>   	 * covered by precisely one VM area that has the VM_ALLOC flag set.
>> +	 *
>> +	 * The one exception to this is is we're forcing everything to be
>> +	 * page mapped with DEBUG_PAGEALLOC
>>   	 */
>>   	area = find_vm_area((void *)addr);
>> -	if (!area ||
>> +	if (!ignore_vma_check && (!area ||
>>   	    end > (unsigned long)area->addr + area->size ||
>> -	    !(area->flags & VM_ALLOC))
>> +	    !(area->flags & VM_ALLOC)))
>>   		return -EINVAL;
>
> Perhaps we could pull out everything but the VMA checks into a static
> __change_memory_common. Then we can call that directly in
> __kernel_map_pages, and don't need to pass a confusing boolean
> parameter.

Sounds fine.

>
> Otherwise, this looks good to me.
>  
> Mark.
>

Thanks,
Laura
  
>>
>>   	data.set_mask = set_mask;
>> @@ -86,21 +90,24 @@ int set_memory_ro(unsigned long addr, int numpages)
>>   {
>>   	return change_memory_common(addr, numpages,
>>   					__pgprot(PTE_RDONLY),
>> -					__pgprot(PTE_WRITE));
>> +					__pgprot(PTE_WRITE),
>> +					false);
>>   }
>>
>>   int set_memory_rw(unsigned long addr, int numpages)
>>   {
>>   	return change_memory_common(addr, numpages,
>>   					__pgprot(PTE_WRITE),
>> -					__pgprot(PTE_RDONLY));
>> +					__pgprot(PTE_RDONLY),
>> +					false);
>>   }
>>
>>   int set_memory_nx(unsigned long addr, int numpages)
>>   {
>>   	return change_memory_common(addr, numpages,
>>   					__pgprot(PTE_PXN),
>> -					__pgprot(0));
>> +					__pgprot(0),
>> +					false);
>>   }
>>   EXPORT_SYMBOL_GPL(set_memory_nx);
>>
>> @@ -108,6 +115,25 @@ int set_memory_x(unsigned long addr, int numpages)
>>   {
>>   	return change_memory_common(addr, numpages,
>>   					__pgprot(0),
>> -					__pgprot(PTE_PXN));
>> +					__pgprot(PTE_PXN),
>> +					false);
>>   }
>>   EXPORT_SYMBOL_GPL(set_memory_x);
>> +
>> +#ifdef CONFIG_DEBUG_PAGEALLOC
>> +void __kernel_map_pages(struct page *page, int numpages, int enable)
>> +{
>> +	unsigned long addr = (unsigned long) page_address(page);
>> +
>> +	if (enable)
>> +		change_memory_common(addr, numpages,
>> +					__pgprot(PTE_VALID),
>> +					__pgprot(0),
>> +					true);
>> +	else
>> +		change_memory_common(addr, numpages,
>> +					__pgprot(0),
>> +					__pgprot(PTE_VALID),
>> +					true);
>> +}
>> +#endif
>> --
>> 2.5.0
>>