From: David Gibson <hidden> Date: 2016-01-29 05:23:07
Here's a second prototype of the guest side work for runtime resizing
of the has page table in PAPR guests.
This is now feature complete. It implements the resizing, advertises
it with CAS, and will automatically invoke it to maintain a good HPT
size when memory is hot-added or hot-removed.
Patches 1-5 are standalone prerequisite cleanups that I'll be pushing
concurrently.
David Gibson (9):
memblock: Don't mark memblock_phys_mem_size() as __init
arch/powerpc: Clean up error handling for htab_remove_mapping
arch/powerpc: Handle removing maybe-present bolted HPTEs
arch/powerpc: Clean up memory hotplug failure paths
arch/powerpc: Split hash page table sizing heuristic into a helper
pseries: Add hypercall wrappers for hash page table resizing
pseries: Add support for hash table resizing
pseries: Advertise HPT resizing support via CAS
pseries: Automatically resize HPT for memory hot add/remove
arch/powerpc/include/asm/firmware.h | 5 +-
arch/powerpc/include/asm/hvcall.h | 2 +
arch/powerpc/include/asm/machdep.h | 3 +-
arch/powerpc/include/asm/mmu-hash64.h | 3 +
arch/powerpc/include/asm/plpar_wrappers.h | 12 +++
arch/powerpc/include/asm/prom.h | 1 +
arch/powerpc/include/asm/sparsemem.h | 1 +
arch/powerpc/kernel/prom_init.c | 2 +-
arch/powerpc/mm/hash_utils_64.c | 121 ++++++++++++++++++++++++------
arch/powerpc/mm/init_64.c | 47 ++++++++----
arch/powerpc/mm/mem.c | 14 +++-
arch/powerpc/platforms/pseries/firmware.c | 1 +
arch/powerpc/platforms/pseries/lpar.c | 117 ++++++++++++++++++++++++++++-
mm/memblock.c | 2 +-
14 files changed, 281 insertions(+), 50 deletions(-)
--
2.5.0
From: David Gibson <hidden> Date: 2016-01-29 05:23:07
At the moment memblock_phys_mem_size() is marked as __init, and so is
discarded after boot. This is different from most of the memblock
functions which are marked __init_memblock, and are only discarded after
boot if memory hotplug is not configured.
To allow for upcoming code which will need memblock_phys_mem_size() in the
hotplug path, change it from __init to __init_memblock.
Signed-off-by: David Gibson <redacted>
---
mm/memblock.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: David Gibson <hidden> Date: 2016-01-29 05:23:07
Currently, the only error that htab_remove_mapping() can report is -EINVAL,
if removal of bolted HPTEs isn't implemeted for this platform. We make
a few clean ups to the handling of this:
* EINVAL isn't really the right code - there's nothing wrong with the
function's arguments - use ENODEV instead
* We were also printing a warning message, but that's a decision better
left up to the callers, so remove it
* One caller is vmemmap_remove_mapping(), which will just BUG_ON() on
error, making the warning message irrelevant, so no change is needed
there.
* The other caller is remove_section_mapping(). This is called in the
memory hot remove path at a point after vmemmap_remove_mapping() so
if hpte_removebolted isn't implemented, we'd expect to have already
BUG()ed anyway. Put a WARN_ON() here, in lieu of a printk() since this
really shouldn't be happening.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/mm/hash_utils_64.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
@@ -273,11 +273,8 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,shift=mmu_psize_defs[psize].shift;step=1<<shift;-if(!ppc_md.hpte_removebolted){-printk(KERN_WARNING"Platform doesn't implement "-"hpte_removebolted\n");-return-EINVAL;-}+if(!ppc_md.hpte_removebolted)+return-ENODEV;for(vaddr=vstart;vaddr<vend;vaddr+=step)ppc_md.hpte_removebolted(vaddr,psize,ssize);
@@ -641,8 +638,10 @@ int create_section_mapping(unsigned long start, unsigned long end)intremove_section_mapping(unsignedlongstart,unsignedlongend){-returnhtab_remove_mapping(start,end,mmu_linear_psize,-mmu_kernel_ssize);+intrc=htab_remove_mapping(start,end,mmu_linear_psize,+mmu_kernel_ssize);+WARN_ON(rc<0);+returnrc;}#endif /* CONFIG_MEMORY_HOTPLUG */
From: David Gibson <hidden> Date: 2016-01-29 05:23:08
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/machdep.h | 2 +-
arch/powerpc/mm/hash_utils_64.c | 10 +++++++---
arch/powerpc/mm/init_64.c | 9 +++++----
arch/powerpc/platforms/pseries/lpar.c | 7 +++++--
4 files changed, 18 insertions(+), 10 deletions(-)
@@ -269,6 +269,7 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,{unsignedlongvaddr;unsignedintstep,shift;+intrc=0;shift=mmu_psize_defs[psize].shift;step=1<<shift;
@@ -276,10 +277,13 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,if(!ppc_md.hpte_removebolted)return-ENODEV;-for(vaddr=vstart;vaddr<vend;vaddr+=step)-ppc_md.hpte_removebolted(vaddr,psize,ssize);+for(vaddr=vstart;vaddr<vend;vaddr+=step){+rc=ppc_md.hpte_removebolted(vaddr,psize,ssize);+if((rc<0)&&(rc!=-ENOENT))+returnrc;+}-return0;+returnrc;}#endif /* CONFIG_MEMORY_HOTPLUG */
From: David Gibson <hidden> Date: 2016-01-29 05:23:08
This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:
For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.
For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/mm/hash_utils_64.c | 13 ++++++++++---
arch/powerpc/mm/init_64.c | 38 ++++++++++++++++++++++++++------------
arch/powerpc/mm/mem.c | 10 ++++++++--
3 files changed, 44 insertions(+), 17 deletions(-)
@@ -188,9 +188,9 @@ static int __meminit vmemmap_populated(unsigned long start, int page_size)*/#ifdef CONFIG_PPC_BOOK3E-staticvoid__meminitvmemmap_create_mapping(unsignedlongstart,-unsignedlongpage_size,-unsignedlongphys)+staticint__meminitvmemmap_create_mapping(unsignedlongstart,+unsignedlongpage_size,+unsignedlongphys){/* Create a PTE encoding without page size */unsignedlongi,flags=_PAGE_PRESENT|_PAGE_ACCESSED|
@@ -304,6 +311,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)for(;start<end;start+=page_size){void*p;+intrc;if(vmemmap_populated(start,page_size))continue;
@@ -317,7 +325,13 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)pr_debug(" * %016lx..%016lx allocated at %p\n",start,start+page_size,p);-vmemmap_create_mapping(start,page_size,__pa(p));+rc=vmemmap_create_mapping(start,page_size,__pa(p));+if(rc<0){+pr_warning(+"vmemmap_populate: Unable to create vmemmap mapping: %d\n",+rc);+return-EFAULT;+}}return0;
@@ -119,12 +119,18 @@ int arch_add_memory(int nid, u64 start, u64 size, bool for_device)structzone*zone;unsignedlongstart_pfn=start>>PAGE_SHIFT;unsignedlongnr_pages=size>>PAGE_SHIFT;+intrc;pgdata=NODE_DATA(nid);start=(unsignedlong)__va(start);-if(create_section_mapping(start,start+size))-return-EINVAL;+rc=create_section_mapping(start,start+size);+if(rc){+pr_warning(+"Unable to create mapping for hot added memory 0x%llx..0x%llx: %d\n",+start,start+size,rc);+return-EFAULT;+}/* this should work for most non-highmem platforms */zone=pgdata->node_zones+
From: David Gibson <hidden> Date: 2016-01-29 05:23:09
htab_get_table_size() either retrieve the size of the hash page table (HPT)
from the device tree - if the HPT size is determined by firmware - or
uses a heuristic to determine a good size based on RAM size if the kernel
is responsible for allocating the HPT.
To support a PAPR extension allowing resizing of the HPT, we're going to
want the memory size -> HPT size logic elsewhere, so split it out into a
helper function.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 30 +++++++++++++++++-------------
2 files changed, 20 insertions(+), 13 deletions(-)
@@ -606,10 +606,24 @@ static int __init htab_dt_scan_pftsize(unsigned long node,return0;}-staticunsignedlong__inithtab_get_table_size(void)+unsignedhtab_shift_for_mem_size(unsignedlongmem_size){-unsignedlongmem_size,rnd_mem_size,pteg_count,psize;+unsignedmemshift=__ilog2(mem_size);+unsignedpshift=mmu_psize_defs[mmu_virtual_psize].shift;+unsignedpteg_shift;++/* round mem_size up to next power of 2 */+if((1UL<<memshift)<mem_size)+memshift+=1;++/* aim for 2 pages / pteg */+pteg_shift=memshift-(pshift+1);++returnmax(pteg_shift+7,18U);+}+staticunsignedlong__inithtab_get_table_size(void)+{/* If hash size isn't already provided by the platform, we try to*retrieveitfromthedevice-tree.Ifit'snotthereneither,we*calculateitnowbasedonthetotalRAMsize
@@ -619,17 +633,7 @@ static unsigned long __init htab_get_table_size(void)if(ppc64_pft_size)return1UL<<ppc64_pft_size;-/* round mem_size up to next power of 2 */-mem_size=memblock_phys_mem_size();-rnd_mem_size=1UL<<__ilog2(mem_size);-if(rnd_mem_size<mem_size)-rnd_mem_size<<=1;--/* # pages / 2 */-psize=mmu_psize_defs[mmu_virtual_psize].shift;-pteg_count=max(rnd_mem_size>>(psize+1),1UL<<11);--returnpteg_count<<7;+returnhtab_shift_for_mem_size(memblock_phys_mem_size());}#ifdef CONFIG_MEMORY_HOTPLUG
From: David Gibson <hidden> Date: 2016-01-29 05:23:09
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/firmware.h | 5 +++--
arch/powerpc/include/asm/hvcall.h | 2 ++
arch/powerpc/include/asm/plpar_wrappers.h | 12 ++++++++++++
arch/powerpc/platforms/pseries/firmware.c | 1 +
4 files changed, 18 insertions(+), 2 deletions(-)
@@ -242,6 +242,18 @@ static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,returnplpar_hcall_norets(H_PROTECT,flags,ptex,avpn);}+staticinlinelongplpar_resize_hpt_prepare(unsignedlongflags,+unsignedlongshift)+{+returnplpar_hcall_norets(H_RESIZE_HPT_PREPARE,flags,shift);+}++staticinlinelongplpar_resize_hpt_commit(unsignedlongflags,+unsignedlongshift)+{+returnplpar_hcall_norets(H_RESIZE_HPT_COMMIT,flags,shift);+}+staticinlinelongplpar_tce_get(unsignedlongliobn,unsignedlongioba,unsignedlong*tce_ret){
@@ -63,6 +63,7 @@ hypertas_fw_features_table[] = {{FW_FEATURE_VPHN,"hcall-vphn"},{FW_FEATURE_SET_MODE,"hcall-set-mode"},{FW_FEATURE_BEST_ENERGY,"hcall-best-energy-1*"},+{FW_FEATURE_HPT_RESIZE,"hcall-hpt-resize"},};/* Build up the firmware features bitmask using the contents of
From: David Gibson <hidden> Date: 2016-01-29 05:23:10
This adds support for using experimental hypercalls to change the size
of the main hash page table while running as a PAPR guest. For now these
hypercalls are only in experimental qemu versions.
The interface is two part: first H_RESIZE_HPT_PREPARE is used to allocate
and prepare the new hash table. This may be slow, but can be done
asynchronously. Then, H_RESIZE_HPT_COMMIT is used to switch to the new
hash table. This requires that no CPUs be concurrently updating the HPT,
and so must be run under stop_machine().
This also adds a debugfs file which can be used to manually control
HPT resizing or testing purposes.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/machdep.h | 1 +
arch/powerpc/mm/hash_utils_64.c | 28 +++++++++
arch/powerpc/platforms/pseries/lpar.c | 110 ++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+)
@@ -603,6 +605,113 @@ static int __init disable_bulk_remove(char *str)__setup("bulk_remove=",disable_bulk_remove);+#define HPT_RESIZE_TIMEOUT 10000 /* ms */++structhpt_resize_state{+unsignedlongshift;+intcommit_rc;+};++staticintpseries_lpar_resize_hpt_commit(void*data)+{+structhpt_resize_state*state=data;++state->commit_rc=plpar_resize_hpt_commit(0,state->shift);+if(state->commit_rc!=H_SUCCESS)+return-EIO;++/* Hypervisor has transitioned the HTAB, update our globals */+ppc64_pft_size=state->shift;+htab_size_bytes=1UL<<ppc64_pft_size;+htab_hash_mask=(htab_size_bytes>>7)-1;++return0;+}++/* Must be called in user context */+staticintpseries_lpar_resize_hpt(unsignedlongshift)+{+structhpt_resize_statestate={+.shift=shift,+.commit_rc=H_FUNCTION,+};+unsignedintdelay,total_delay=0;+intrc;+ktime_tt0,t1,t2;++might_sleep();++if(!firmware_has_feature(FW_FEATURE_HPT_RESIZE))+return-ENODEV;++printk(KERN_INFO"lpar: Attempting to resize HPT to shift %lu\n",+shift);++t0=ktime_get();++rc=plpar_resize_hpt_prepare(0,shift);+while(H_IS_LONG_BUSY(rc)){+delay=get_longbusy_msecs(rc);+total_delay+=delay;+if(total_delay>HPT_RESIZE_TIMEOUT){+/* prepare call with shift==0 cancels an+*in-progressresize*/+rc=plpar_resize_hpt_prepare(0,0);+if(rc!=H_SUCCESS)+printk(KERN_WARNING+"lpar: Unexpected error %d cancelling timed out HPT resize\n",+rc);+return-ETIMEDOUT;+}+msleep(delay);+rc=plpar_resize_hpt_prepare(0,shift);+};++switch(rc){+caseH_SUCCESS:+/* Continue on */+break;++caseH_PARAMETER:+return-EINVAL;+caseH_RESOURCE:+return-EPERM;+default:+printk(KERN_WARNING+"lpar: Unexpected error %d from H_RESIZE_HPT_PREPARE\n",+rc);+return-EIO;+}++t1=ktime_get();++rc=stop_machine(pseries_lpar_resize_hpt_commit,&state,NULL);++t2=ktime_get();++if(rc!=0){+switch(state.commit_rc){+caseH_PTEG_FULL:+printk(KERN_WARNING+"lpar: Hash collision while resizing HPT\n");+return-ENOSPC;++default:+printk(KERN_WARNING+"lpar: Unexpected error %d from H_RESIZE_HPT_COMMIT\n",+state.commit_rc);+return-EIO;+};+}++printk(KERN_INFO+"lpar: HPT resize to shift %lu complete (%lld ms / %lld ms)\n",+shift,(longlong)ktime_ms_delta(t1,t0),+(longlong)ktime_ms_delta(t2,t1));++return0;+}+void__inithpte_init_lpar(void){ppc_md.hpte_invalidate=pSeries_lpar_hpte_invalidate;
From: David Gibson <hidden> Date: 2016-01-29 05:23:10
The hypervisor needs to know a guest is capable of using the HPT resizing
PAPR extension in order to make full advantage of it for memory hotplug.
If the hypervisor knows the guest is HPT resize aware, it can size the
initial HPT based on the initial guest RAM size, relying on the guest to
resize the HPT when more memory is hot-added. Without this, the hypervisor
must size the HPT for the maximum possible guest RAM, which can lead to
a huge waste of space if the guest never actually expends to that maximum
size.
This patch advertises the guest's support for HPT resizing via the
ibm,client-architecture-support OF interface. Obviously, the actual
encoding in the CAS vector is tentative until the extension is officially
incorporated into PAPR. For now we use bit 0 of (previously unused) byte 8
of option vector 5.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/prom.h | 1 +
arch/powerpc/kernel/prom_init.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
@@ -713,7 +713,7 @@ unsigned char ibm_architecture_vec[] = {OV5_FEAT(OV5_TYPE1_AFFINITY)|OV5_FEAT(OV5_PRRN),0,0,-0,+OV5_FEAT(OV5_HPT_RESIZE),/* WARNING: The offset of the "number of cores" field below*mustmatchbythemacrobelow.Updatethedefinitionif*thestructurelayoutchanges.
From: David Gibson <hidden> Date: 2016-01-29 05:23:10
We've now implemented code in the pseries platform to use the new PAPR
interface to allow resizing the hash page table (HPT) at runtime.
This patch uses that interface to automatically attempt to resize the HPT
when memory is hot added or removed. This tries to always keep the HPT at
a reasonable size for our current memory size.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/sparsemem.h | 1 +
arch/powerpc/mm/hash_utils_64.c | 29 +++++++++++++++++++++++++++++
arch/powerpc/mm/mem.c | 4 ++++
3 files changed, 34 insertions(+)
@@ -638,6 +638,35 @@ static unsigned long __init htab_get_table_size(void)}#ifdef CONFIG_MEMORY_HOTPLUG+voidresize_hpt_for_hotplug(unsignedlongnew_mem_size)+{+unsignedtarget_hpt_shift;++if(!ppc_md.resize_hpt)+return;++target_hpt_shift=htab_shift_for_mem_size(new_mem_size);++/*+*ToavoidlotsofHPTresizesifmemorysizeisfluctuating+*acrossaboundary,wedeliberatelyhavesomehysterisis+*here:weimmediatelyincreasetheHPTsizeifthetarget+*shiftexceedsthecurrentshift,butwewon'tattemptto+*reduceunlessthetargetshiftisatleast2belowthe+*currentshift+*/+if((target_hpt_shift>ppc64_pft_size)+||(target_hpt_shift<(ppc64_pft_size-1))){+intrc;++rc=ppc_md.resize_hpt(target_hpt_shift);+if(rc)+printk(KERN_WARNING+"Unable to resize hash page table to target order %d: %d\n",+target_hpt_shift,rc);+}+}+intcreate_section_mapping(unsignedlongstart,unsignedlongend){intrc=htab_bolt_mapping(start,end,__pa(start),
Here's a second prototype of the guest side work for runtime resizing
of the has page table in PAPR guests.
This is now feature complete. It implements the resizing, advertises
it with CAS, and will automatically invoke it to maintain a good HPT
size when memory is hot-added or hot-removed.
Patches 1-5 are standalone prerequisite cleanups that I'll be pushing
concurrently.
David Gibson (9):
memblock: Don't mark memblock_phys_mem_size() as __init
arch/powerpc: Clean up error handling for htab_remove_mapping
arch/powerpc: Handle removing maybe-present bolted HPTEs
arch/powerpc: Clean up memory hotplug failure paths
arch/powerpc: Split hash page table sizing heuristic into a helper
A small nit. Please start the above commit message headers as
"powerpc/mm:" instead, which sounds more clear and uniform with
patch series related to other subsystems.
Adding Nathan in the copy, may be he will have some inputs.
At the moment memblock_phys_mem_size() is marked as __init, and so is
discarded after boot. This is different from most of the memblock
functions which are marked __init_memblock, and are only discarded after
boot if memory hotplug is not configured.
To allow for upcoming code which will need memblock_phys_mem_size() in the
hotplug path, change it from __init to __init_memblock.
Signed-off-by: David Gibson <redacted>
Currently, the only error that htab_remove_mapping() can report is -EINVAL,
if removal of bolted HPTEs isn't implemeted for this platform. We make
a few clean ups to the handling of this:
* EINVAL isn't really the right code - there's nothing wrong with the
function's arguments - use ENODEV instead
You are right, guess there are other places with this kind of problem as
well.
* We were also printing a warning message, but that's a decision better
left up to the callers, so remove it
* One caller is vmemmap_remove_mapping(), which will just BUG_ON() on
error, making the warning message irrelevant, so no change is needed
there.
It makes it redundant not irrelevant. It still prints a valid reason why
the remove operation failed.
* The other caller is remove_section_mapping(). This is called in the
memory hot remove path at a point after vmemmap_remove_mapping() so
if hpte_removebolted isn't implemented, we'd expect to have already
BUG()ed anyway. Put a WARN_ON() here, in lieu of a printk() since this
really shouldn't be happening.
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
Yeah makes sense.
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
Right.
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Provided the caller of vmemmap_remove_mapping() which is memory hotplug
path must be handling the returned -ENOENT error correctly. Just curious
and want to make sure that any of the memory sections or pages inside the
section must not be left in a state which makes the next call in the
hotplug path fail.
This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:
For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.
For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up.
Yeah this greatly improves graceful fall back when when memory mapping
failure happens at the last level during memory hotplug.
htab_get_table_size() either retrieve the size of the hash page table (HPT)
from the device tree - if the HPT size is determined by firmware - or
uses a heuristic to determine a good size based on RAM size if the kernel
is responsible for allocating the HPT.
To support a PAPR extension allowing resizing of the HPT, we're going to
want the memory size -> HPT size logic elsewhere, so split it out into a
helper function.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 30 +++++++++++++++++-------------
2 files changed, 20 insertions(+), 13 deletions(-)
@@ -606,10 +606,24 @@ static int __init htab_dt_scan_pftsize(unsigned long node,return0;}-staticunsignedlong__inithtab_get_table_size(void)+unsignedhtab_shift_for_mem_size(unsignedlongmem_size){-unsignedlongmem_size,rnd_mem_size,pteg_count,psize;+unsignedmemshift=__ilog2(mem_size);+unsignedpshift=mmu_psize_defs[mmu_virtual_psize].shift;+unsignedpteg_shift;++/* round mem_size up to next power of 2 */+if((1UL<<memshift)<mem_size)+memshift+=1;++/* aim for 2 pages / pteg */
While here I guess its a good opportunity to write couple of lines
about why one PTE group for every two physical pages on the system,
why minimum (1UL << 11 = 2048) number of PTE groups required, why
(1U << 7 = 128) entries per PTE group and also remove the existing
confusing comments above ? Just a suggestion.
quoted hunk
+ pteg_shift = memshift - (pshift + 1);
+
+ return max(pteg_shift + 7, 18U);
+}
+static unsigned long __init htab_get_table_size(void)
+{
/* If hash size isn't already provided by the platform, we try to
* retrieve it from the device-tree. If it's not there neither, we
* calculate it now based on the total RAM size
@@ -619,17 +633,7 @@ static unsigned long __init htab_get_table_size(void) if (ppc64_pft_size) return 1UL << ppc64_pft_size;- /* round mem_size up to next power of 2 */- mem_size = memblock_phys_mem_size();- rnd_mem_size = 1UL << __ilog2(mem_size);- if (rnd_mem_size < mem_size)- rnd_mem_size <<= 1;-- /* # pages / 2 */- psize = mmu_psize_defs[mmu_virtual_psize].shift;- pteg_count = max(rnd_mem_size >> (psize + 1), 1UL << 11);-- return pteg_count << 7;+ return htab_shift_for_mem_size(memblock_phys_mem_size());
Would it be 1UL << htab_shift_for_mem_size(memblock_phys_mem_size())
instead ? It was returning the size of the HPT not the shift of HPT
originally or I am missing something here.
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
+/* Must be called in user context */
+static int pseries_lpar_resize_hpt(unsigned long shift)
+{
+ struct hpt_resize_state state = {
+ .shift = shift,
+ .commit_rc = H_FUNCTION,
With my limited knowledge of stop_machine, wondering if the current
or any future version of 'pseries_lpar_resize_hpt_commit' function
can cause HPT change (page fault path) while stop is executing it.
The hypervisor needs to know a guest is capable of using the HPT resizing
PAPR extension in order to make full advantage of it for memory hotplug.
If the hypervisor knows the guest is HPT resize aware, it can size the
initial HPT based on the initial guest RAM size, relying on the guest to
resize the HPT when more memory is hot-added. Without this, the hypervisor
must size the HPT for the maximum possible guest RAM, which can lead to
a huge waste of space if the guest never actually expends to that maximum
size.
This patch advertises the guest's support for HPT resizing via the
ibm,client-architecture-support OF interface. Obviously, the actual
encoding in the CAS vector is tentative until the extension is officially
incorporated into PAPR. For now we use bit 0 of (previously unused) byte 8
of option vector 5.
Signed-off-by: David Gibson <redacted>
#ifdef CONFIG_MEMORY_HOTPLUG
+void resize_hpt_for_hotplug(unsigned long new_mem_size)
+{
+ unsigned target_hpt_shift;
+
+ if (!ppc_md.resize_hpt)
+ return;
+
+ target_hpt_shift = htab_shift_for_mem_size(new_mem_size);
+
+ /*
+ * To avoid lots of HPT resizes if memory size is fluctuating
+ * across a boundary, we deliberately have some hysterisis
What do you mean by 'memory size is fluctuating across a boundary' ?
Through memory hotplug interface ? Why some one will do that ? I
can understand why we dont have this check in the sysfs debug path
as we would like to test any memory HPT re sizing scenario we want
in any sequence of increase or decrease we want.
Overall the RFC V2 looks pretty good. Looking forward to see the
host side of the code for this feature.
From: David Gibson <hidden> Date: 2016-02-01 11:07:14
On Mon, Feb 01, 2016 at 02:21:46PM +0530, Anshuman Khandual wrote:
On 01/29/2016 10:54 AM, David Gibson wrote:
quoted
#ifdef CONFIG_MEMORY_HOTPLUG
+void resize_hpt_for_hotplug(unsigned long new_mem_size)
+{
+ unsigned target_hpt_shift;
+
+ if (!ppc_md.resize_hpt)
+ return;
+
+ target_hpt_shift = htab_shift_for_mem_size(new_mem_size);
+
+ /*
+ * To avoid lots of HPT resizes if memory size is fluctuating
+ * across a boundary, we deliberately have some hysterisis
What do you mean by 'memory size is fluctuating across a boundary' ?
Through memory hotplug interface ? Why some one will do that ?
I was thinking it might be possible to have some management system
that automatically adjusts memory size based on load, and if that
happened to land on a boundary you could get nasty behaviour.
I
can understand why we dont have this check in the sysfs debug path
as we would like to test any memory HPT re sizing scenario we want
in any sequence of increase or decrease we want.
Overall the RFC V2 looks pretty good. Looking forward to see the
host side of the code for this feature.
The qemu host side has been posted to qemu-devel@nongnu.org already.
I haven't started on a KVM HV implementation yet.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
+/* Must be called in user context */
+static int pseries_lpar_resize_hpt(unsigned long shift)
+{
+ struct hpt_resize_state state = {
+ .shift = shift,
+ .commit_rc = H_FUNCTION,
With my limited knowledge of stop_machine, wondering if the current
or any future version of 'pseries_lpar_resize_hpt_commit' function
can cause HPT change (page fault path) while stop is executing it.
It can, but the H_RESIZE_HPT_COMMIT hypercall is synchronous so the
cpu executing it can't make any HPT updates during it. The
stop_machine() prevents any other cpus doing HPT updates.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
From: David Gibson <hidden> Date: 2016-02-02 01:08:30
On Mon, Feb 01, 2016 at 11:20:03AM +0530, Anshuman Khandual wrote:
On 01/29/2016 10:53 AM, David Gibson wrote:
quoted
Here's a second prototype of the guest side work for runtime resizing
of the has page table in PAPR guests.
This is now feature complete. It implements the resizing, advertises
it with CAS, and will automatically invoke it to maintain a good HPT
size when memory is hot-added or hot-removed.
Patches 1-5 are standalone prerequisite cleanups that I'll be pushing
concurrently.
David Gibson (9):
memblock: Don't mark memblock_phys_mem_size() as __init
arch/powerpc: Clean up error handling for htab_remove_mapping
arch/powerpc: Handle removing maybe-present bolted HPTEs
arch/powerpc: Clean up memory hotplug failure paths
arch/powerpc: Split hash page table sizing heuristic into a helper
A small nit. Please start the above commit message headers as
"powerpc/mm:" instead, which sounds more clear and uniform with
patch series related to other subsystems.
Ok.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
From: David Gibson <hidden> Date: 2016-02-02 01:08:31
On Mon, Feb 01, 2016 at 12:41:31PM +0530, Anshuman Khandual wrote:
On 01/29/2016 10:54 AM, David Gibson wrote:
quoted
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
From: David Gibson <hidden> Date: 2016-02-02 01:08:31
On Mon, Feb 01, 2016 at 12:34:32PM +0530, Anshuman Khandual wrote:
On 01/29/2016 10:53 AM, David Gibson wrote:
quoted
htab_get_table_size() either retrieve the size of the hash page table (HPT)
from the device tree - if the HPT size is determined by firmware - or
uses a heuristic to determine a good size based on RAM size if the kernel
is responsible for allocating the HPT.
To support a PAPR extension allowing resizing of the HPT, we're going to
want the memory size -> HPT size logic elsewhere, so split it out into a
helper function.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 30 +++++++++++++++++-------------
2 files changed, 20 insertions(+), 13 deletions(-)
@@ -606,10 +606,24 @@ static int __init htab_dt_scan_pftsize(unsigned long node,return0;}-staticunsignedlong__inithtab_get_table_size(void)+unsignedhtab_shift_for_mem_size(unsignedlongmem_size){-unsignedlongmem_size,rnd_mem_size,pteg_count,psize;+unsignedmemshift=__ilog2(mem_size);+unsignedpshift=mmu_psize_defs[mmu_virtual_psize].shift;+unsignedpteg_shift;++/* round mem_size up to next power of 2 */+if((1UL<<memshift)<mem_size)+memshift+=1;++/* aim for 2 pages / pteg */
While here I guess its a good opportunity to write couple of lines
about why one PTE group for every two physical pages on the system,
Well, that don't really know, it's just copied from the existing code.
why minimum (1UL << 11 = 2048) number of PTE groups required,
Ok.
why
(1U << 7 = 128) entries per PTE group
Um.. what? Because that's how big a PTEG is, I don't think
re-explaining the HPT structure here is useful.
and also remove the existing
confusing comments above ? Just a suggestion.
Not sure which comment you mean.
quoted
+ pteg_shift = memshift - (pshift + 1);
+
+ return max(pteg_shift + 7, 18U);
+}
+static unsigned long __init htab_get_table_size(void)
+{
/* If hash size isn't already provided by the platform, we try to
* retrieve it from the device-tree. If it's not there neither, we
* calculate it now based on the total RAM size
@@ -619,17 +633,7 @@ static unsigned long __init htab_get_table_size(void) if (ppc64_pft_size) return 1UL << ppc64_pft_size;- /* round mem_size up to next power of 2 */- mem_size = memblock_phys_mem_size();- rnd_mem_size = 1UL << __ilog2(mem_size);- if (rnd_mem_size < mem_size)- rnd_mem_size <<= 1;-- /* # pages / 2 */- psize = mmu_psize_defs[mmu_virtual_psize].shift;- pteg_count = max(rnd_mem_size >> (psize + 1), 1UL << 11);-- return pteg_count << 7;+ return htab_shift_for_mem_size(memblock_phys_mem_size());
Would it be 1UL << htab_shift_for_mem_size(memblock_phys_mem_size())
instead ? It was returning the size of the HPT not the shift of HPT
originally or I am missing something here.
Oops, yes. That would have broken all non-LPAR platforms.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
From: David Gibson <hidden> Date: 2016-02-02 01:08:31
On Mon, Feb 01, 2016 at 11:28:54AM +0530, Anshuman Khandual wrote:
On 01/29/2016 10:53 AM, David Gibson wrote:
quoted
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
Yeah makes sense.
quoted
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
Right.
quoted
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Provided the caller of vmemmap_remove_mapping() which is memory hotplug
path must be handling the returned -ENOENT error correctly.
vmemmap_remove_mapping() is void, so there's no -ENOENT returned, just
the WARN_ON().
Just curious
and want to make sure that any of the memory sections or pages inside the
section must not be left in a state which makes the next call in the
hotplug path fail.
So, this situation shouldn't happen - the mapping should be complete -
but there's nothing obvious that the caller should do extra. It asked
that the mapping be removed, and we discovered that some of it wasn't
there to begin with. Whether we can continue safely depends on
what exactly caused the mapping not to be fully present in the first
place, and whether that had other conseuqences, but we have no way of
knowing that here.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/machdep.h | 2 +-
arch/powerpc/mm/hash_utils_64.c | 10 +++++++---
arch/powerpc/mm/init_64.c | 9 +++++----
arch/powerpc/platforms/pseries/lpar.c | 7 +++++--
4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/include/asm/machdep.h
b/arch/powerpc/include/asm/machdep.h
index 3f191f5..a7d3f66 100644
long vsid,
}
#endif
-static void pSeries_lpar_hpte_removebolted(unsigned long ea,
+static long pSeries_lpar_hpte_removebolted(unsigned long ea,
int psize, int ssize)
{
unsigned long vpn;
This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:
For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.
For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/mm/hash_utils_64.c | 13 ++++++++++---
arch/powerpc/mm/init_64.c | 38 ++++++++++++++++++++++++++------------
arch/powerpc/mm/mem.c | 10 ++++++++--
3 files changed, 44 insertions(+), 17 deletions(-)
@@ -635,9 +635,16 @@ static unsigned long __init htab_get_table_size(void)#ifdef CONFIG_MEMORY_HOTPLUGintcreate_section_mapping(unsignedlongstart,unsignedlongend){-returnhtab_bolt_mapping(start,end,__pa(start),-pgprot_val(PAGE_KERNEL),mmu_linear_psize,-mmu_kernel_ssize);+intrc=htab_bolt_mapping(start,end,__pa(start),+pgprot_val(PAGE_KERNEL),mmu_linear_psize,+mmu_kernel_ssize);++if(rc<0){+intrc2=htab_remove_mapping(start,end,mmu_linear_psize,+mmu_kernel_ssize);+BUG_ON(rc2&&(rc2!=-ENOENT));+}+returnrc;}
<-- snip -->
quoted hunk
#ifdef CONFIG_MEMORY_HOTPLUG
@@ -217,15 +219,20 @@ static void vmemmap_remove_mapping(unsigned long start, } #endif #else /* CONFIG_PPC_BOOK3E */-static void __meminit vmemmap_create_mapping(unsigned long start,- unsigned long page_size,- unsigned long phys)+static int __meminit vmemmap_create_mapping(unsigned long start,+ unsigned long page_size,+ unsigned long phys) {- int mapped = htab_bolt_mapping(start, start + page_size, phys,- pgprot_val(PAGE_KERNEL),- mmu_vmemmap_psize,- mmu_kernel_ssize);- BUG_ON(mapped < 0);+ int rc = htab_bolt_mapping(start, start + page_size, phys,+ pgprot_val(PAGE_KERNEL),+ mmu_vmemmap_psize, mmu_kernel_ssize);+ if (rc < 0) {+ int rc2 = htab_remove_mapping(start, start + page_size,+ mmu_vmemmap_psize,+ mmu_kernel_ssize);+ BUG_ON(rc2 && (rc2 != -ENOENT));+ }+ return rc; }
If I'm reading this correctly it appears that create_section_mapping() and
vmemmap_create_mapping() for !PPC_BOOK3E are identical. Any reason to not
have one routine, perhaps just have vmemmap_create_mapping() just call
create_section_mapping()?
-Nathan
From: David Gibson <hidden> Date: 2016-02-03 04:31:10
On Tue, Feb 02, 2016 at 09:04:23AM -0600, Nathan Fontenot wrote:
On 01/28/2016 11:23 PM, David Gibson wrote:
quoted
This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:
For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.
For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/mm/hash_utils_64.c | 13 ++++++++++---
arch/powerpc/mm/init_64.c | 38 ++++++++++++++++++++++++++------------
arch/powerpc/mm/mem.c | 10 ++++++++--
3 files changed, 44 insertions(+), 17 deletions(-)
@@ -635,9 +635,16 @@ static unsigned long __init htab_get_table_size(void)#ifdef CONFIG_MEMORY_HOTPLUGintcreate_section_mapping(unsignedlongstart,unsignedlongend){-returnhtab_bolt_mapping(start,end,__pa(start),-pgprot_val(PAGE_KERNEL),mmu_linear_psize,-mmu_kernel_ssize);+intrc=htab_bolt_mapping(start,end,__pa(start),+pgprot_val(PAGE_KERNEL),mmu_linear_psize,+mmu_kernel_ssize);++if(rc<0){+intrc2=htab_remove_mapping(start,end,mmu_linear_psize,+mmu_kernel_ssize);+BUG_ON(rc2&&(rc2!=-ENOENT));+}+returnrc;}
<-- snip -->
quoted
#ifdef CONFIG_MEMORY_HOTPLUG
@@ -217,15 +219,20 @@ static void vmemmap_remove_mapping(unsigned long start, } #endif #else /* CONFIG_PPC_BOOK3E */-static void __meminit vmemmap_create_mapping(unsigned long start,- unsigned long page_size,- unsigned long phys)+static int __meminit vmemmap_create_mapping(unsigned long start,+ unsigned long page_size,+ unsigned long phys) {- int mapped = htab_bolt_mapping(start, start + page_size, phys,- pgprot_val(PAGE_KERNEL),- mmu_vmemmap_psize,- mmu_kernel_ssize);- BUG_ON(mapped < 0);+ int rc = htab_bolt_mapping(start, start + page_size, phys,+ pgprot_val(PAGE_KERNEL),+ mmu_vmemmap_psize, mmu_kernel_ssize);+ if (rc < 0) {+ int rc2 = htab_remove_mapping(start, start + page_size,+ mmu_vmemmap_psize,+ mmu_kernel_ssize);+ BUG_ON(rc2 && (rc2 != -ENOENT));+ }+ return rc; }
If I'm reading this correctly it appears that create_section_mapping() and
vmemmap_create_mapping() for !PPC_BOOK3E are identical. Any reason to not
have one routine, perhaps just have vmemmap_create_mapping() just call
create_section_mapping()?
Not really, apart from documenting what they're used for. They're
both fairly trivial wrappers around htab_bolt_mapping(). I think
cleaning this up is outside the scope of this series though.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
On Mon, Feb 01, 2016 at 12:34:32PM +0530, Anshuman Khandual wrote:
quoted
On 01/29/2016 10:53 AM, David Gibson wrote:
quoted
htab_get_table_size() either retrieve the size of the hash page table (HPT)
from the device tree - if the HPT size is determined by firmware - or
uses a heuristic to determine a good size based on RAM size if the kernel
is responsible for allocating the HPT.
To support a PAPR extension allowing resizing of the HPT, we're going to
want the memory size -> HPT size logic elsewhere, so split it out into a
helper function.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 30 +++++++++++++++++-------------
2 files changed, 20 insertions(+), 13 deletions(-)
@@ -606,10 +606,24 @@ static int __init htab_dt_scan_pftsize(unsigned long node,return0;}-staticunsignedlong__inithtab_get_table_size(void)+unsignedhtab_shift_for_mem_size(unsignedlongmem_size){-unsignedlongmem_size,rnd_mem_size,pteg_count,psize;+unsignedmemshift=__ilog2(mem_size);+unsignedpshift=mmu_psize_defs[mmu_virtual_psize].shift;+unsignedpteg_shift;++/* round mem_size up to next power of 2 */+if((1UL<<memshift)<mem_size)+memshift+=1;++/* aim for 2 pages / pteg */
While here I guess its a good opportunity to write couple of lines
about why one PTE group for every two physical pages on the system,
Well, that don't really know, it's just copied from the existing code.
Aneesh, would you know why ?
quoted
why minimum (1UL << 11 = 2048) number of PTE groups required,
Aneesh, would you know why ?
Ok.
quoted
why
(1U << 7 = 128) entries per PTE group
Um.. what? Because that's how big a PTEG is, I don't think
re-explaining the HPT structure here is useful.
Agreed, though think some where these things should be macros not used
as hard coded numbers like this.
On Mon, Feb 01, 2016 at 12:41:31PM +0530, Anshuman Khandual wrote:
quoted
On 01/29/2016 10:54 AM, David Gibson wrote:
quoted
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
From: David Gibson <hidden> Date: 2016-02-07 23:11:43
On Thu, Feb 04, 2016 at 04:41:10PM +0530, Anshuman Khandual wrote:
On 02/02/2016 06:28 AM, David Gibson wrote:
quoted
On Mon, Feb 01, 2016 at 12:41:31PM +0530, Anshuman Khandual wrote:
quoted
On 01/29/2016 10:54 AM, David Gibson wrote:
quoted
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
Just little bit of change of name of the macro like this
H_RESIZE_HPT_PREPARE --> H_HPT_RESIZE_PREPARE
H_RESIZE_HPT_COMMIT --> H_HPT_RESIZE_COMMIT
Oh, I see. Actually, I'm trying to standardize on "resize hpt" rather
than "hpt resize" everywhere.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
From: Paul Mackerras <hidden> Date: 2016-02-08 05:47:18
On Fri, Jan 29, 2016 at 04:23:55PM +1100, David Gibson wrote:
At the moment memblock_phys_mem_size() is marked as __init, and so is
discarded after boot. This is different from most of the memblock
functions which are marked __init_memblock, and are only discarded after
boot if memory hotplug is not configured.
To allow for upcoming code which will need memblock_phys_mem_size() in the
hotplug path, change it from __init to __init_memblock.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 05:47:19
On Fri, Jan 29, 2016 at 04:23:57PM +1100, David Gibson wrote:
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Signed-off-by: David Gibson <redacted>
@@ -269,6 +269,7 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,{unsignedlongvaddr;unsignedintstep,shift;+intrc=0;shift=mmu_psize_defs[psize].shift;step=1<<shift;
@@ -276,10 +277,13 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,if(!ppc_md.hpte_removebolted)return-ENODEV;-for(vaddr=vstart;vaddr<vend;vaddr+=step)-ppc_md.hpte_removebolted(vaddr,psize,ssize);+for(vaddr=vstart;vaddr<vend;vaddr+=step){+rc=ppc_md.hpte_removebolted(vaddr,psize,ssize);+if((rc<0)&&(rc!=-ENOENT))+returnrc;+}-return0;+returnrc;
This will return the rc from the last hpte_removebolted call, which
might be 0 even if earlier calls had returned -ENOENT. Or, if the
last call fails with -ENOENT, this will return -ENOENT. Is that
exactly what you meant? In the case where some calls to
hpte_removebolted return -ENOENT, I would think we would want a
consistent return value, which could be either 0 or -ENOENT, but it
shouldn't depend on which specific calls fail with -ENOENT, in my
opinion.
Paul.
From: Paul Mackerras <hidden> Date: 2016-02-08 05:47:19
On Fri, Jan 29, 2016 at 04:23:58PM +1100, David Gibson wrote:
This makes a number of cleanups to handling of mapping failures during
memory hotplug on Power:
For errors creating the linear mapping for the hot-added region:
* This is now reported with EFAULT which is more appropriate than the
previous EINVAL (the failure is unlikely to be related to the
function's parameters)
* An error in this path now prints a warning message, rather than just
silently failing to add the extra memory.
* Previously a failure here could result in the region being partially
mapped. We now clean up any partial mapping before failing.
For errors creating the vmemmap for the hot-added region:
* This is now reported with EFAULT instead of causing a BUG() - this
could happen for external reason (e.g. full hash table) so it's better
to handle this non-fatally
* An error message is also printed, so the failure won't be silent
* As above a failure could cause a partially mapped region, we now
clean this up.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 05:47:19
On Fri, Jan 29, 2016 at 04:23:56PM +1100, David Gibson wrote:
Currently, the only error that htab_remove_mapping() can report is -EINVAL,
if removal of bolted HPTEs isn't implemeted for this platform. We make
a few clean ups to the handling of this:
* EINVAL isn't really the right code - there's nothing wrong with the
function's arguments - use ENODEV instead
* We were also printing a warning message, but that's a decision better
left up to the callers, so remove it
* One caller is vmemmap_remove_mapping(), which will just BUG_ON() on
error, making the warning message irrelevant, so no change is needed
there.
* The other caller is remove_section_mapping(). This is called in the
memory hot remove path at a point after vmemmap_remove_mapping() so
if hpte_removebolted isn't implemented, we'd expect to have already
BUG()ed anyway. Put a WARN_ON() here, in lieu of a printk() since this
really shouldn't be happening.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 06:01:19
On Thu, Feb 04, 2016 at 04:26:20PM +0530, Anshuman Khandual wrote:
On 02/02/2016 06:34 AM, David Gibson wrote:
quoted
On Mon, Feb 01, 2016 at 12:34:32PM +0530, Anshuman Khandual wrote:
quoted
On 01/29/2016 10:53 AM, David Gibson wrote:
quoted
htab_get_table_size() either retrieve the size of the hash page table (HPT)
from the device tree - if the HPT size is determined by firmware - or
uses a heuristic to determine a good size based on RAM size if the kernel
is responsible for allocating the HPT.
To support a PAPR extension allowing resizing of the HPT, we're going to
want the memory size -> HPT size logic elsewhere, so split it out into a
helper function.
Signed-off-by: David Gibson <redacted>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 30 +++++++++++++++++-------------
2 files changed, 20 insertions(+), 13 deletions(-)
@@ -606,10 +606,24 @@ static int __init htab_dt_scan_pftsize(unsigned long node,return0;}-staticunsignedlong__inithtab_get_table_size(void)+unsignedhtab_shift_for_mem_size(unsignedlongmem_size){-unsignedlongmem_size,rnd_mem_size,pteg_count,psize;+unsignedmemshift=__ilog2(mem_size);+unsignedpshift=mmu_psize_defs[mmu_virtual_psize].shift;+unsignedpteg_shift;++/* round mem_size up to next power of 2 */+if((1UL<<memshift)<mem_size)+memshift+=1;++/* aim for 2 pages / pteg */
While here I guess its a good opportunity to write couple of lines
about why one PTE group for every two physical pages on the system,
Well, that don't really know, it's just copied from the existing code.
Aneesh, would you know why ?
1 PTEG per 2 pages means 4 HPTEs per page, which means you can map
each page to an average of 4 different virtual addresses. It's a
heuristic that has been around for a long time and dates back to the
early days of AIX. For Linux, running on machines which typically
have quite a lot of memory, it's probably overkill.
quoted
quoted
why minimum (1UL << 11 = 2048) number of PTE groups required,
Aneesh, would you know why ?
It's in the architecture, which specifies the minimum size of the HPT
as 256kB. The reason is because not all of the virtual address bits
are present in the HPT. That's OK because some of the virtual address
bits are implied by the HPTEG index in the hash table. If the HPT was
less than 256kB (2048 HPTEGs) there would be the possibility of
collisions where two different virtual addresses could hash to the
same HPTEG and their HPTEs would be impossible to tell apart.
quoted
Ok.
quoted
why
(1U << 7 = 128) entries per PTE group
Um.. what? Because that's how big a PTEG is, I don't think
re-explaining the HPT structure here is useful.
Agreed, though think some where these things should be macros not used
as hard coded numbers like this.
Using symbols instead of constant numbers is not always clearer. The
symbol name can give some context (but so can a suitable comment) but
has the cost of obscuring the actual numeric value.
Paul.
From: Paul Mackerras <hidden> Date: 2016-02-08 06:01:19
On Fri, Jan 29, 2016 at 04:24:01PM +1100, David Gibson wrote:
This adds support for using experimental hypercalls to change the size
of the main hash page table while running as a PAPR guest. For now these
hypercalls are only in experimental qemu versions.
The interface is two part: first H_RESIZE_HPT_PREPARE is used to allocate
and prepare the new hash table. This may be slow, but can be done
asynchronously. Then, H_RESIZE_HPT_COMMIT is used to switch to the new
hash table. This requires that no CPUs be concurrently updating the HPT,
and so must be run under stop_machine().
This also adds a debugfs file which can be used to manually control
HPT resizing or testing purposes.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 06:01:20
On Fri, Jan 29, 2016 at 04:24:00PM +1100, David Gibson wrote:
This adds the hypercall numbers and wrapper functions for the hash page
table resizing hypercalls.
These are experimental "platform specific" values for now, until we have a
formal PAPR update.
It also adds a new firmware feature flat to track the presence of the
HPT resizing calls.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 06:01:20
On Fri, Jan 29, 2016 at 04:24:02PM +1100, David Gibson wrote:
The hypervisor needs to know a guest is capable of using the HPT resizing
PAPR extension in order to make full advantage of it for memory hotplug.
If the hypervisor knows the guest is HPT resize aware, it can size the
initial HPT based on the initial guest RAM size, relying on the guest to
resize the HPT when more memory is hot-added. Without this, the hypervisor
must size the HPT for the maximum possible guest RAM, which can lead to
a huge waste of space if the guest never actually expends to that maximum
size.
This patch advertises the guest's support for HPT resizing via the
ibm,client-architecture-support OF interface. Obviously, the actual
encoding in the CAS vector is tentative until the extension is officially
incorporated into PAPR. For now we use bit 0 of (previously unused) byte 8
of option vector 5.
Signed-off-by: David Gibson <redacted>
From: Paul Mackerras <hidden> Date: 2016-02-08 06:01:20
On Fri, Jan 29, 2016 at 04:24:03PM +1100, David Gibson wrote:
We've now implemented code in the pseries platform to use the new PAPR
interface to allow resizing the hash page table (HPT) at runtime.
This patch uses that interface to automatically attempt to resize the HPT
when memory is hot added or removed. This tries to always keep the HPT at
a reasonable size for our current memory size.
Signed-off-by: David Gibson <redacted>
From: David Gibson <hidden> Date: 2016-02-09 01:20:43
On Mon, Feb 08, 2016 at 01:54:04PM +1100, Paul Mackerras wrote:
On Fri, Jan 29, 2016 at 04:23:57PM +1100, David Gibson wrote:
quoted
At the moment the hpte_removebolted callback in ppc_md returns void and
will BUG_ON() if the hpte it's asked to remove doesn't exist in the first
place. This is awkward for the case of cleaning up a mapping which was
partially made before failing.
So, we add a return value to hpte_removebolted, and have it return ENOENT
in the case that the HPTE to remove didn't exist in the first place.
In the (sole) caller, we propagate errors in hpte_removebolted to its
caller to handle. However, we handle ENOENT specially, continuing to
complete the unmapping over the specified range before returning the error
to the caller.
This means that htab_remove_mapping() will work sanely on a partially
present mapping, removing any HPTEs which are present, while also returning
ENOENT to its caller in case it's important there.
There are two callers of htab_remove_mapping():
- In remove_section_mapping() we already WARN_ON() any error return,
which is reasonable - in this case the mapping should be fully
present
- In vmemmap_remove_mapping() we BUG_ON() any error. We change that to
just a WARN_ON() in the case of ENOENT, since failing to remove a
mapping that wasn't there in the first place probably shouldn't be
fatal.
Signed-off-by: David Gibson <redacted>
@@ -269,6 +269,7 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,{unsignedlongvaddr;unsignedintstep,shift;+intrc=0;shift=mmu_psize_defs[psize].shift;step=1<<shift;
@@ -276,10 +277,13 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,if(!ppc_md.hpte_removebolted)return-ENODEV;-for(vaddr=vstart;vaddr<vend;vaddr+=step)-ppc_md.hpte_removebolted(vaddr,psize,ssize);+for(vaddr=vstart;vaddr<vend;vaddr+=step){+rc=ppc_md.hpte_removebolted(vaddr,psize,ssize);+if((rc<0)&&(rc!=-ENOENT))+returnrc;+}-return0;+returnrc;
This will return the rc from the last hpte_removebolted call, which
might be 0 even if earlier calls had returned -ENOENT. Or, if the
last call fails with -ENOENT, this will return -ENOENT. Is that
exactly what you meant? In the case where some calls to
hpte_removebolted return -ENOENT, I would think we would want a
consistent return value, which could be either 0 or -ENOENT, but it
shouldn't depend on which specific calls fail with -ENOENT, in my
opinion.
I agree. The intention was that this returned -ENOENT iff any of the
individual calls did, but I messed up the logic; thanks for the catch.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson