Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f400169d authored by Isaac J. Manjarres's avatar Isaac J. Manjarres
Browse files

soc: qcom: mem-offline: Restore page table mappings properly in online path



On newer kernels, a feature was added to adjust the linear alias
mappings of pages mapped within the vmalloc space such that the
mappings are consistent with respect to each other (i.e. if the
memory is mapped as R/O in the vmalloc space, then the linear
mapping's permission attributes should also reflect a R/O mapping).
This is known as the rodata full feature.

This sort of protection is not feasible with block mappings, as block
mappings do not allow access control at a page granule, so when this
feature is enabled, or debug pagealloc is active, the kernel will not
use block mappings.

The logic in the memory offlining driver is not aware of the rodata_full
feature and when the memory is being onlined after being offlined,
it blindly assumes that if debug pagealloc is disabled, it can restore
the block mappings for the memory that is being onlined. This leads to
inconsistencies, as the memory is actually mapped without block
mappings, which ultimately results in the page table mapping not being
restored from the time at which the memory was offlined.

Thus, when onlining memory online the memory without block mappings if
the rodata full feature is enabled and debug page alloc is disabled.
Otherwise, restore the mappings using block mappings.

Change-Id: I56a3d3754deb62e0a894a14b06eaab522e6f0a05
Signed-off-by: default avatarIsaac J. Manjarres <isaacm@codeaurora.org>
parent 0f30cf7e
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
 */

#include <linux/memory.h>
#include <linux/module.h>
#include <linux/memblock.h>
#include <linux/mmu_context.h>
#include <linux/mmzone.h>
#include <linux/ktime.h>
#include <linux/of.h>
@@ -93,6 +94,22 @@ static void clear_pgtable_mapping(phys_addr_t start, phys_addr_t end)
	flush_tlb_kernel_range(virt, addr_end);
}

static void init_pgtable_mapping(phys_addr_t start, phys_addr_t end)
{
	unsigned long size = end - start;

	/*
	 * When rodata_full is enabled, memory is mapped at a page size
	 * granule, as opposed to a block mapping, so restore the attribute
	 * of each PTE when rodata_full is enabled.
	 */
	if (rodata_full)
		set_memory_valid((unsigned long)phys_to_virt(start),
				 size >> PAGE_SHIFT, (int)true);
	else
		create_pgtable_mapping(start, end);
}

static void record_stat(unsigned long sec, ktime_t delay, int mode)
{
	unsigned int total_sec = end_section_nr - start_section_nr + 1;
@@ -324,7 +341,7 @@ static int mem_event_callback(struct notifier_block *self,

		if (!debug_pagealloc_enabled()) {
			/* Create kernel page-tables */
			create_pgtable_mapping(start_addr, end_addr);
			init_pgtable_mapping(start_addr, end_addr);
		}

		break;