soc: qcom: mem-offline: Clear page-table entries after offline
After a memory block is offlined, the page-table entries should be removed in order to avoid any CPU speculative accesses happening on this region. Clear off the entries at the leaf levels (PUD or PMD) on a block mapped memory region. This only zeroes the entries and does not free the page table memory. During online, we create mappings again for these regions. Change-Id: I71091c33589645567c60457ba569dd393ddcab59 Signed-off-by: Sudarshan Rajagopalan <sudaraja@codeaurora.org>
This commit is contained in:
@@ -399,6 +399,14 @@ static phys_addr_t pgd_pgtable_alloc(void)
|
||||
return __pa(ptr);
|
||||
}
|
||||
|
||||
void create_pgtable_mapping(phys_addr_t start, phys_addr_t end)
|
||||
{
|
||||
unsigned long virt = (unsigned long)phys_to_virt(start);
|
||||
|
||||
__create_pgd_mapping(init_mm.pgd, start, virt, end - start,
|
||||
PAGE_KERNEL, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function can only be used to modify existing table entries,
|
||||
* without allocating new levels of table. Note that this permits the
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/mailbox/qmp.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define AOP_MSG_ADDR_MASK 0xffffffff
|
||||
#define AOP_MSG_ADDR_HIGH_SHIFT 32
|
||||
@@ -50,6 +52,43 @@ static struct mem_offline_mailbox {
|
||||
|
||||
static struct section_stat *mem_info;
|
||||
|
||||
static void clear_pgtable_mapping(phys_addr_t start, phys_addr_t end)
|
||||
{
|
||||
unsigned long size = end - start;
|
||||
unsigned long virt = (unsigned long)phys_to_virt(start);
|
||||
unsigned long addr_end = virt + size;
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
|
||||
pgd = pgd_offset_k(virt);
|
||||
|
||||
while (virt < addr_end) {
|
||||
|
||||
/* Check if we have PUD section mapping */
|
||||
pud = pud_offset(pgd, virt);
|
||||
if (pud_sect(*pud)) {
|
||||
pud_clear(pud);
|
||||
virt += PUD_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if we have PMD section mapping */
|
||||
pmd = pmd_offset(pud, virt);
|
||||
if (pmd_sect(*pmd)) {
|
||||
pmd_clear(pmd);
|
||||
virt += PMD_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Clear mapping for page entry */
|
||||
set_memory_valid(virt, 1, (int)false);
|
||||
virt += PAGE_SIZE;
|
||||
}
|
||||
|
||||
virt = (unsigned long)phys_to_virt(start);
|
||||
flush_tlb_kernel_range(virt, addr_end);
|
||||
}
|
||||
void record_stat(unsigned long sec, ktime_t delay, int mode)
|
||||
{
|
||||
unsigned int total_sec = end_section_nr - start_section_nr + 1;
|
||||
@@ -137,6 +176,10 @@ static int mem_event_callback(struct notifier_block *self,
|
||||
if (aop_send_msg(__pfn_to_phys(start), true))
|
||||
pr_err("PASR: AOP online request addr:0x%llx failed\n",
|
||||
__pfn_to_phys(start));
|
||||
if (!debug_pagealloc_enabled()) {
|
||||
/* Create kernel page-tables */
|
||||
create_pgtable_mapping(start_addr, end_addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case MEM_ONLINE:
|
||||
@@ -154,6 +197,10 @@ static int mem_event_callback(struct notifier_block *self,
|
||||
cur = ktime_get();
|
||||
break;
|
||||
case MEM_OFFLINE:
|
||||
if (!debug_pagealloc_enabled()) {
|
||||
/* Clear kernel page-tables */
|
||||
clear_pgtable_mapping(start_addr, end_addr);
|
||||
}
|
||||
if (aop_send_msg(__pfn_to_phys(start), false))
|
||||
pr_err("PASR: AOP offline request addr:0x%llx failed\n",
|
||||
__pfn_to_phys(start));
|
||||
|
||||
@@ -140,7 +140,7 @@ void __next_reserved_mem_region(u64 *idx, phys_addr_t *out_start,
|
||||
|
||||
void __memblock_free_early(phys_addr_t base, phys_addr_t size);
|
||||
void __memblock_free_late(phys_addr_t base, phys_addr_t size);
|
||||
|
||||
void create_pgtable_mapping(phys_addr_t start, phys_addr_t end);
|
||||
/**
|
||||
* for_each_mem_range - iterate through memblock areas from type_a and not
|
||||
* included in type_b. Or just type_a if type_b is NULL.
|
||||
|
||||
Reference in New Issue
Block a user