Commit 16ba4482 authored by Heiko Carstens's avatar Heiko Carstens Committed by Vasily Gorbik
Browse files

s390/cmma: fix initial kernel address space page table walk



If the cmma no-dat feature is available the kernel page tables are walked
to identify and mark all pages which are used for address translation (all
region, segment, and page tables). In a subsequent loop all other pages are
marked as "no-dat" pages with the ESSA instruction.

This information is visible to the hypervisor, so that the hypervisor can
optimize purging of guest TLB entries. The initial loop however does not
cover the complete kernel address space. This can result in pages being
marked as not being used for dynamic address translation, even though they
are. In turn guest TLB entries incorrectly may not be purged.

Fix this by adjusting the end address of the kernel address range being
walked.

Cc: <stable@vger.kernel.org>
Reviewed-by: default avatarClaudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent c8b5d574
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -151,15 +151,22 @@ static void mark_kernel_p4d(pgd_t *pgd, unsigned long addr, unsigned long end)

static void mark_kernel_pgd(void)
{
	unsigned long addr, next;
	unsigned long addr, next, max_addr;
	struct page *page;
	pgd_t *pgd;
	int i;

	addr = 0;
	/*
	 * Figure out maximum virtual address accessible with the
	 * kernel ASCE. This is required to keep the page table walker
	 * from accessing non-existent entries.
	 */
	max_addr = (S390_lowcore.kernel_asce.val & _ASCE_TYPE_MASK) >> 2;
	max_addr = 1UL << (max_addr * 11 + 31);
	pgd = pgd_offset_k(addr);
	do {
		next = pgd_addr_end(addr, MODULES_END);
		next = pgd_addr_end(addr, max_addr);
		if (pgd_none(*pgd))
			continue;
		if (!pgd_folded(*pgd)) {
@@ -168,7 +175,7 @@ static void mark_kernel_pgd(void)
				set_bit(PG_arch_1, &page[i].flags);
		}
		mark_kernel_p4d(pgd, addr, next);
	} while (pgd++, addr = next, addr != MODULES_END);
	} while (pgd++, addr = next, addr != max_addr);
}

void __init cmma_init_nodat(void)