Commit 6c6ea537 authored by Nathan Fontenot's avatar Nathan Fontenot Committed by Michael Ellerman

powerpc/mm: Separate ibm, dynamic-memory data from DT format

We currently have code to parse the dynamic reconfiguration LMB
information from the ibm,dynamic-meory device tree property in
multiple locations; numa.c, prom.c, and pseries/hotplug-memory.c.
In anticipation of adding support for a version 2 of the
ibm,dynamic-memory property this patch aims to separate the device
tree information from the device tree format.

Doing this requires a two step process to avoid a possibly very large
bootmem allocation early in boot. During initial boot, new routines
are provided to walk the device tree property and make a call-back
for each LMB.

The second step (introduced in later patches) will allocate an
array of LMB information that can be used directly without needing
to know the DT format.

This approach provides the benefit of consolidating the device tree
property parsing to a single location and (eventually) providing
a common data structure for retrieving LMB information.

This patch introduces a routine to walk the ibm,dynamic-memory
property in the flattened device tree and updates the prom.c code
to use this to initialize memory.
Signed-off-by: default avatarNathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent b88fc309
/*
* drmem.h: Power specific logical memory block representation
*
* Copyright 2017 IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _ASM_POWERPC_LMB_H
#define _ASM_POWERPC_LMB_H
struct drmem_lmb {
u64 base_addr;
u32 drc_index;
u32 aa_index;
u32 flags;
};
struct drmem_lmb_info {
struct drmem_lmb *lmbs;
int n_lmbs;
u32 lmb_size;
};
extern struct drmem_lmb_info *drmem_info;
#define for_each_drmem_lmb_in_range(lmb, start, end) \
for ((lmb) = (start); (lmb) <= (end); (lmb)++)
#define for_each_drmem_lmb(lmb) \
for_each_drmem_lmb_in_range((lmb), \
&drmem_info->lmbs[0], \
&drmem_info->lmbs[drmem_info->n_lmbs - 1])
static inline u32 drmem_lmb_size(void)
{
return drmem_info->lmb_size;
}
#ifdef CONFIG_PPC_PSERIES
void __init walk_drmem_lmbs_early(unsigned long node,
void (*func)(struct drmem_lmb *, const __be32 **));
#endif
#endif /* _ASM_POWERPC_LMB_H */
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#include <asm/epapr_hcalls.h> #include <asm/epapr_hcalls.h>
#include <asm/firmware.h> #include <asm/firmware.h>
#include <asm/dt_cpu_ftrs.h> #include <asm/dt_cpu_ftrs.h>
#include <asm/drmem.h>
#include <mm/mmu_decl.h> #include <mm/mmu_decl.h>
...@@ -455,92 +456,74 @@ static int __init early_init_dt_scan_chosen_ppc(unsigned long node, ...@@ -455,92 +456,74 @@ static int __init early_init_dt_scan_chosen_ppc(unsigned long node,
#ifdef CONFIG_PPC_PSERIES #ifdef CONFIG_PPC_PSERIES
/* /*
* Interpret the ibm,dynamic-memory property in the * Interpret the ibm dynamic reconfiguration memory LMBs.
* /ibm,dynamic-reconfiguration-memory node.
* This contains a list of memory blocks along with NUMA affinity * This contains a list of memory blocks along with NUMA affinity
* information. * information.
*/ */
static int __init early_init_dt_scan_drconf_memory(unsigned long node) static void __init early_init_drmem_lmb(struct drmem_lmb *lmb,
const __be32 **usm)
{ {
const __be32 *dm, *ls, *usm; u64 base, size;
int l; int is_kexec_kdump = 0, rngs;
unsigned long n, flags;
u64 base, size, memblock_size;
unsigned int is_kexec_kdump = 0, rngs;
ls = of_get_flat_dt_prop(node, "ibm,lmb-size", &l);
if (ls == NULL || l < dt_root_size_cells * sizeof(__be32))
return 0;
memblock_size = dt_mem_next_cell(dt_root_size_cells, &ls);
dm = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &l); base = lmb->base_addr;
if (dm == NULL || l < sizeof(__be32)) size = drmem_lmb_size();
return 0; rngs = 1;
n = of_read_number(dm++, 1); /* number of entries */ /*
if (l < (n * (dt_root_addr_cells + 4) + 1) * sizeof(__be32)) * Skip this block if the reserved bit is set in flags
return 0; * or if the block is not assigned to this partition.
*/
if ((lmb->flags & DRCONF_MEM_RESERVED) ||
!(lmb->flags & DRCONF_MEM_ASSIGNED))
return;
/* check if this is a kexec/kdump kernel. */ if (*usm)
usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory",
&l);
if (usm != NULL)
is_kexec_kdump = 1; is_kexec_kdump = 1;
for (; n != 0; --n) {
base = dt_mem_next_cell(dt_root_addr_cells, &dm);
flags = of_read_number(&dm[3], 1);
/* skip DRC index, pad, assoc. list index, flags */
dm += 4;
/* skip this block if the reserved bit is set in flags
or if the block is not assigned to this partition */
if ((flags & DRCONF_MEM_RESERVED) ||
!(flags & DRCONF_MEM_ASSIGNED))
continue;
size = memblock_size;
rngs = 1;
if (is_kexec_kdump) { if (is_kexec_kdump) {
/* /*
* For each memblock in ibm,dynamic-memory, a corresponding * For each memblock in ibm,dynamic-memory, a
* entry in linux,drconf-usable-memory property contains * corresponding entry in linux,drconf-usable-memory
* a counter 'p' followed by 'p' (base, size) duple. * property contains a counter 'p' followed by 'p'
* Now read the counter from * (base, size) duple. Now read the counter from
* linux,drconf-usable-memory property * linux,drconf-usable-memory property
*/ */
rngs = dt_mem_next_cell(dt_root_size_cells, &usm); rngs = dt_mem_next_cell(dt_root_size_cells, usm);
if (!rngs) /* there are no (base, size) duple */ if (!rngs) /* there are no (base, size) duple */
continue; return;
} }
do { do {
if (is_kexec_kdump) { if (is_kexec_kdump) {
base = dt_mem_next_cell(dt_root_addr_cells, base = dt_mem_next_cell(dt_root_addr_cells, usm);
&usm); size = dt_mem_next_cell(dt_root_size_cells, usm);
size = dt_mem_next_cell(dt_root_size_cells,
&usm);
} }
if (iommu_is_off) { if (iommu_is_off) {
if (base >= 0x80000000ul) if (base >= 0x80000000ul)
continue; continue;
if ((base + size) > 0x80000000ul) if ((base + size) > 0x80000000ul)
size = 0x80000000ul - base; size = 0x80000000ul - base;
} }
DBG("Adding: %llx -> %llx\n", base, size);
memblock_add(base, size); memblock_add(base, size);
} while (--rngs); } while (--rngs);
}
memblock_dump_all();
return 0;
} }
#else
#define early_init_dt_scan_drconf_memory(node) 0
#endif /* CONFIG_PPC_PSERIES */ #endif /* CONFIG_PPC_PSERIES */
static int __init early_init_dt_scan_memory_ppc(unsigned long node, static int __init early_init_dt_scan_memory_ppc(unsigned long node,
const char *uname, const char *uname,
int depth, void *data) int depth, void *data)
{ {
#ifdef CONFIG_PPC_PSERIES
if (depth == 1 && if (depth == 1 &&
strcmp(uname, "ibm,dynamic-reconfiguration-memory") == 0) strcmp(uname, "ibm,dynamic-reconfiguration-memory") == 0) {
return early_init_dt_scan_drconf_memory(node); walk_drmem_lmbs_early(node, early_init_drmem_lmb);
return 0;
}
#endif
return early_init_dt_scan_memory(node, uname, depth, data); return early_init_dt_scan_memory(node, uname, depth, data);
} }
......
...@@ -9,7 +9,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) ...@@ -9,7 +9,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
obj-y := fault.o mem.o pgtable.o mmap.o \ obj-y := fault.o mem.o pgtable.o mmap.o \
init_$(BITS).o pgtable_$(BITS).o \ init_$(BITS).o pgtable_$(BITS).o \
init-common.o mmu_context.o init-common.o mmu_context.o drmem.o
obj-$(CONFIG_PPC_MMU_NOHASH) += mmu_context_nohash.o tlb_nohash.o \ obj-$(CONFIG_PPC_MMU_NOHASH) += mmu_context_nohash.o tlb_nohash.o \
tlb_nohash_low.o tlb_nohash_low.o
obj-$(CONFIG_PPC_BOOK3E) += tlb_low_$(BITS)e.o obj-$(CONFIG_PPC_BOOK3E) += tlb_low_$(BITS)e.o
......
/*
* Dynamic reconfiguration memory support
*
* Copyright 2017 IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define pr_fmt(fmt) "drmem: " fmt
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/memblock.h>
#include <asm/prom.h>
#include <asm/drmem.h>
static struct drmem_lmb_info __drmem_info;
struct drmem_lmb_info *drmem_info = &__drmem_info;
#ifdef CONFIG_PPC_PSERIES
static void __init read_drconf_v1_cell(struct drmem_lmb *lmb,
const __be32 **prop)
{
const __be32 *p = *prop;
lmb->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p);
lmb->drc_index = of_read_number(p++, 1);
p++; /* skip reserved field */
lmb->aa_index = of_read_number(p++, 1);
lmb->flags = of_read_number(p++, 1);
*prop = p;
}
static void __init __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm,
void (*func)(struct drmem_lmb *, const __be32 **))
{
struct drmem_lmb lmb;
u32 i, n_lmbs;
n_lmbs = of_read_number(prop++, 1);
for (i = 0; i < n_lmbs; i++) {
read_drconf_v1_cell(&lmb, &prop);
func(&lmb, &usm);
}
}
void __init walk_drmem_lmbs_early(unsigned long node,
void (*func)(struct drmem_lmb *, const __be32 **))
{
const __be32 *prop, *usm;
int len;
prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
if (!prop || len < dt_root_size_cells * sizeof(__be32))
return;
drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
if (prop)
__walk_drmem_v1_lmbs(prop, usm, func);
memblock_dump_all();
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment