* [RFCv2 01/25] powerpc/mm: Clean up error handling for htab_remove_mapping
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 02/25] powerpc/mm: Handle removing maybe-present bolted HPTEs David Gibson
` (23 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 redundant, 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 <david@gibson.dropbear.id.au>
---
arch/powerpc/mm/hash_utils_64.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index ba59d59..9f7d727 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -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)
int remove_section_mapping(unsigned long start, unsigned long end)
{
- return htab_remove_mapping(start, end, mmu_linear_psize,
- mmu_kernel_ssize);
+ int rc = htab_remove_mapping(start, end, mmu_linear_psize,
+ mmu_kernel_ssize);
+ WARN_ON(rc < 0);
+ return rc;
}
#endif /* CONFIG_MEMORY_HOTPLUG */
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 02/25] powerpc/mm: Handle removing maybe-present bolted HPTEs
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
2016-03-08 3:08 ` [RFCv2 01/25] powerpc/mm: Clean up error handling for htab_remove_mapping David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 03/25] powerpc/mm: Clean up memory hotplug failure paths David Gibson
` (22 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/machdep.h | 2 +-
arch/powerpc/mm/hash_utils_64.c | 15 ++++++++++++---
arch/powerpc/mm/init_64.c | 9 +++++----
arch/powerpc/platforms/pseries/lpar.c | 9 ++++++---
4 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 3f191f5..fa25643 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -54,7 +54,7 @@ struct machdep_calls {
int psize, int apsize,
int ssize);
long (*hpte_remove)(unsigned long hpte_group);
- void (*hpte_removebolted)(unsigned long ea,
+ int (*hpte_removebolted)(unsigned long ea,
int psize, int ssize);
void (*flush_hash_range)(unsigned long number, int local);
void (*hugepage_invalidate)(unsigned long vsid,
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 9f7d727..99fbee0 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -269,6 +269,8 @@ int htab_remove_mapping(unsigned long vstart, unsigned long vend,
{
unsigned long vaddr;
unsigned int step, shift;
+ int rc;
+ int ret = 0;
shift = mmu_psize_defs[psize].shift;
step = 1 << shift;
@@ -276,10 +278,17 @@ 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 == -ENOENT) {
+ ret = -ENOENT;
+ continue;
+ }
+ if (rc < 0)
+ return rc;
+ }
- return 0;
+ return ret;
}
#endif /* CONFIG_MEMORY_HOTPLUG */
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index 379a6a9..baa1a23 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -232,10 +232,11 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
static void vmemmap_remove_mapping(unsigned long start,
unsigned long page_size)
{
- int mapped = htab_remove_mapping(start, start + page_size,
- mmu_vmemmap_psize,
- mmu_kernel_ssize);
- BUG_ON(mapped < 0);
+ int rc = htab_remove_mapping(start, start + page_size,
+ mmu_vmemmap_psize,
+ mmu_kernel_ssize);
+ BUG_ON((rc < 0) && (rc != -ENOENT));
+ WARN_ON(rc == -ENOENT);
}
#endif
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 477290a..2415a0d 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -505,8 +505,8 @@ static void pSeries_lpar_hugepage_invalidate(unsigned long vsid,
}
#endif
-static void pSeries_lpar_hpte_removebolted(unsigned long ea,
- int psize, int ssize)
+static int pSeries_lpar_hpte_removebolted(unsigned long ea,
+ int psize, int ssize)
{
unsigned long vpn;
unsigned long slot, vsid;
@@ -515,11 +515,14 @@ static void pSeries_lpar_hpte_removebolted(unsigned long ea,
vpn = hpt_vpn(ea, vsid, ssize);
slot = pSeries_lpar_hpte_find(vpn, psize, ssize);
- BUG_ON(slot == -1);
+ if (slot == -1)
+ return -ENOENT;
+
/*
* lpar doesn't use the passed actual page size
*/
pSeries_lpar_hpte_invalidate(slot, vpn, psize, 0, ssize, 0);
+ return 0;
}
/*
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 03/25] powerpc/mm: Clean up memory hotplug failure paths
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
2016-03-08 3:08 ` [RFCv2 01/25] powerpc/mm: Clean up error handling for htab_remove_mapping David Gibson
2016-03-08 3:08 ` [RFCv2 02/25] powerpc/mm: Handle removing maybe-present bolted HPTEs David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 04/25] powerpc/mm: Split hash page table sizing heuristic into a helper David Gibson
` (21 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
Reviewed-by: Paul Mackerras <paulus@samba.org>
---
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(-)
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 99fbee0..fdcf9d1 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -640,9 +640,16 @@ static unsigned long __init htab_get_table_size(void)
#ifdef CONFIG_MEMORY_HOTPLUG
int create_section_mapping(unsigned long start, unsigned long end)
{
- return htab_bolt_mapping(start, end, __pa(start),
- pgprot_val(PAGE_KERNEL), mmu_linear_psize,
- mmu_kernel_ssize);
+ int rc = htab_bolt_mapping(start, end, __pa(start),
+ pgprot_val(PAGE_KERNEL), mmu_linear_psize,
+ mmu_kernel_ssize);
+
+ if (rc < 0) {
+ int rc2 = htab_remove_mapping(start, end, mmu_linear_psize,
+ mmu_kernel_ssize);
+ BUG_ON(rc2 && (rc2 != -ENOENT));
+ }
+ return rc;
}
int remove_section_mapping(unsigned long start, unsigned long end)
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index baa1a23..fbc9448 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -188,9 +188,9 @@ static int __meminit vmemmap_populated(unsigned long start, int page_size)
*/
#ifdef 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)
{
/* Create a PTE encoding without page size */
unsigned long i, flags = _PAGE_PRESENT | _PAGE_ACCESSED |
@@ -208,6 +208,8 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
*/
for (i = 0; i < page_size; i += PAGE_SIZE)
BUG_ON(map_kernel_page(start + i, phys, flags));
+
+ return 0;
}
#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;
}
#ifdef CONFIG_MEMORY_HOTPLUG
@@ -304,6 +311,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
for (; start < end; start += page_size) {
void *p;
+ int rc;
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;
+ }
}
return 0;
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index d0f0a51..f980da6 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -119,12 +119,18 @@ int arch_add_memory(int nid, u64 start, u64 size, bool for_device)
struct zone *zone;
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;
+ int rc;
pgdata = NODE_DATA(nid);
start = (unsigned long)__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 +
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 04/25] powerpc/mm: Split hash page table sizing heuristic into a helper
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (2 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 03/25] powerpc/mm: Clean up memory hotplug failure paths David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 05/25] pseries: Add hypercall wrappers for hash page table resizing David Gibson
` (20 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/mmu-hash64.h | 3 +++
arch/powerpc/mm/hash_utils_64.c | 32 +++++++++++++++++++-------------
2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h
index 7352d3f..cf070fd 100644
--- a/arch/powerpc/include/asm/mmu-hash64.h
+++ b/arch/powerpc/include/asm/mmu-hash64.h
@@ -607,6 +607,9 @@ static inline unsigned long get_kernel_vsid(unsigned long ea, int ssize)
context = (MAX_USER_CONTEXT) + ((ea >> 60) - 0xc) + 1;
return get_vsid(context, ea, ssize);
}
+
+unsigned htab_shift_for_mem_size(unsigned long mem_size);
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_POWERPC_MMU_HASH64_H_ */
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index fdcf9d1..da5d279 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -611,10 +611,26 @@ static int __init htab_dt_scan_pftsize(unsigned long node,
return 0;
}
-static unsigned long __init htab_get_table_size(void)
+unsigned htab_shift_for_mem_size(unsigned long mem_size)
{
- unsigned long mem_size, rnd_mem_size, pteg_count, psize;
+ unsigned memshift = __ilog2(mem_size);
+ unsigned pshift = mmu_psize_defs[mmu_virtual_psize].shift;
+ unsigned pteg_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);
+
+ /* 2^11 PTEGS / 2^18 bytes is the minimum htab size permitted
+ * by the architecture */
+ 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
@@ -624,17 +640,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 1UL << htab_shift_for_mem_size(memblock_phys_mem_size());
}
#ifdef CONFIG_MEMORY_HOTPLUG
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 05/25] pseries: Add hypercall wrappers for hash page table resizing
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (3 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 04/25] powerpc/mm: Split hash page table sizing heuristic into a helper David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 06/25] pseries: Add support for hash " David Gibson
` (19 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 flag to track the presence of the
HPT resizing calls.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Paul Mackerras <paulus@samba.org>
---
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(-)
diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h
index b062924..32435d2 100644
--- a/arch/powerpc/include/asm/firmware.h
+++ b/arch/powerpc/include/asm/firmware.h
@@ -42,7 +42,7 @@
#define FW_FEATURE_SPLPAR ASM_CONST(0x0000000000100000)
#define FW_FEATURE_LPAR ASM_CONST(0x0000000000400000)
#define FW_FEATURE_PS3_LV1 ASM_CONST(0x0000000000800000)
-/* Free ASM_CONST(0x0000000001000000) */
+#define FW_FEATURE_HPT_RESIZE ASM_CONST(0x0000000001000000)
#define FW_FEATURE_CMO ASM_CONST(0x0000000002000000)
#define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000)
#define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000)
@@ -66,7 +66,8 @@ enum {
FW_FEATURE_MULTITCE | FW_FEATURE_SPLPAR | FW_FEATURE_LPAR |
FW_FEATURE_CMO | FW_FEATURE_VPHN | FW_FEATURE_XCMO |
FW_FEATURE_SET_MODE | FW_FEATURE_BEST_ENERGY |
- FW_FEATURE_TYPE1_AFFINITY | FW_FEATURE_PRRN,
+ FW_FEATURE_TYPE1_AFFINITY | FW_FEATURE_PRRN |
+ FW_FEATURE_HPT_RESIZE,
FW_FEATURE_PSERIES_ALWAYS = 0,
FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL,
FW_FEATURE_POWERNV_ALWAYS = 0,
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index e3b54dd..195e080 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -293,6 +293,8 @@
/* Platform specific hcalls, used by KVM */
#define H_RTAS 0xf000
+#define H_RESIZE_HPT_PREPARE 0xf003
+#define H_RESIZE_HPT_COMMIT 0xf004
/* "Platform specific hcalls", provided by PHYP */
#define H_GET_24X7_CATALOG_PAGE 0xF078
diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h
index 1b39424..b7ee6d9 100644
--- a/arch/powerpc/include/asm/plpar_wrappers.h
+++ b/arch/powerpc/include/asm/plpar_wrappers.h
@@ -242,6 +242,18 @@ static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,
return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
}
+static inline long plpar_resize_hpt_prepare(unsigned long flags,
+ unsigned long shift)
+{
+ return plpar_hcall_norets(H_RESIZE_HPT_PREPARE, flags, shift);
+}
+
+static inline long plpar_resize_hpt_commit(unsigned long flags,
+ unsigned long shift)
+{
+ return plpar_hcall_norets(H_RESIZE_HPT_COMMIT, flags, shift);
+}
+
static inline long plpar_tce_get(unsigned long liobn, unsigned long ioba,
unsigned long *tce_ret)
{
diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c
index 8c80588..7b287be 100644
--- a/arch/powerpc/platforms/pseries/firmware.c
+++ b/arch/powerpc/platforms/pseries/firmware.c
@@ -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
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 06/25] pseries: Add support for hash table resizing
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (4 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 05/25] pseries: Add hypercall wrappers for hash page table resizing David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 07/25] pseries: Advertise HPT resizing support via CAS David Gibson
` (18 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
Reviewed-by: Paul Mackerras <paulus@samba.org>
---
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(+)
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index fa25643..1e23898 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -61,6 +61,7 @@ struct machdep_calls {
unsigned long addr,
unsigned char *hpte_slot_array,
int psize, int ssize, int local);
+ int (*resize_hpt)(unsigned long shift);
/*
* Special for kexec.
* To be called in real mode with interrupts disabled. No locks are
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index da5d279..0809bea 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -34,6 +34,7 @@
#include <linux/signal.h>
#include <linux/memblock.h>
#include <linux/context_tracking.h>
+#include <linux/debugfs.h>
#include <asm/processor.h>
#include <asm/pgtable.h>
@@ -1585,3 +1586,30 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
/* Finally limit subsequent allocations */
memblock_set_current_limit(ppc64_rma_size);
}
+
+static int ppc64_pft_size_get(void *data, u64 *val)
+{
+ *val = ppc64_pft_size;
+ return 0;
+}
+
+static int ppc64_pft_size_set(void *data, u64 val)
+{
+ if (!ppc_md.resize_hpt)
+ return -ENODEV;
+ return ppc_md.resize_hpt(val);
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_ppc64_pft_size,
+ ppc64_pft_size_get, ppc64_pft_size_set, "%llu\n");
+
+static int __init hash64_debugfs(void)
+{
+ if (!debugfs_create_file("pft-size", 0600, powerpc_debugfs_root,
+ NULL, &fops_ppc64_pft_size)) {
+ pr_err("lpar: unable to create ppc64_pft_size debugsfs file\n");
+ }
+
+ return 0;
+}
+machine_device_initcall(pseries, hash64_debugfs);
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 2415a0d..ed9738d 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -27,6 +27,8 @@
#include <linux/console.h>
#include <linux/export.h>
#include <linux/jump_label.h>
+#include <linux/delay.h>
+#include <linux/stop_machine.h>
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/page.h>
@@ -603,6 +605,113 @@ static int __init disable_bulk_remove(char *str)
__setup("bulk_remove=", disable_bulk_remove);
+#define HPT_RESIZE_TIMEOUT 10000 /* ms */
+
+struct hpt_resize_state {
+ unsigned long shift;
+ int commit_rc;
+};
+
+static int pseries_lpar_resize_hpt_commit(void *data)
+{
+ struct hpt_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;
+
+ return 0;
+}
+
+/* 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,
+ };
+ unsigned int delay, total_delay = 0;
+ int rc;
+ ktime_t t0, 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-progress resize */
+ 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) {
+ case H_SUCCESS:
+ /* Continue on */
+ break;
+
+ case H_PARAMETER:
+ return -EINVAL;
+ case H_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) {
+ case H_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, (long long) ktime_ms_delta(t1, t0),
+ (long long) ktime_ms_delta(t2, t1));
+
+ return 0;
+}
+
void __init hpte_init_lpar(void)
{
ppc_md.hpte_invalidate = pSeries_lpar_hpte_invalidate;
@@ -614,6 +723,7 @@ void __init hpte_init_lpar(void)
ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range;
ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear;
ppc_md.hugepage_invalidate = pSeries_lpar_hugepage_invalidate;
+ ppc_md.resize_hpt = pseries_lpar_resize_hpt;
}
#ifdef CONFIG_PPC_SMLPAR
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 07/25] pseries: Advertise HPT resizing support via CAS
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (5 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 06/25] pseries: Add support for hash " David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 08/25] pseries: Automatically resize HPT for memory hot add/remove David Gibson
` (17 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
Reviewed-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Reviewed-by: Paul Mackerras <paulus@samba.org>
---
arch/powerpc/include/asm/prom.h | 1 +
arch/powerpc/kernel/prom_init.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h
index 7f436ba..ef08208 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -151,6 +151,7 @@ struct of_drconf_cell {
#define OV5_XCMO 0x0440 /* Page Coalescing */
#define OV5_TYPE1_AFFINITY 0x0580 /* Type 1 NUMA affinity */
#define OV5_PRRN 0x0540 /* Platform Resource Reassignment */
+#define OV5_HPT_RESIZE 0x0880 /* Hash Page Table resizing */
#define OV5_PFO_HW_RNG 0x0E80 /* PFO Random Number Generator */
#define OV5_PFO_HW_842 0x0E40 /* PFO Compression Accelerator */
#define OV5_PFO_HW_ENCR 0x0E20 /* PFO Encryption Accelerator */
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index da51925..c6feafb 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -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
* must match by the macro below. Update the definition if
* the structure layout changes.
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 08/25] pseries: Automatically resize HPT for memory hot add/remove
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (6 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 07/25] pseries: Advertise HPT resizing support via CAS David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 09/25] powerpc/kvm: Corectly report KVM_CAP_PPC_ALLOC_HTAB David Gibson
` (16 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
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 <david@gibson.dropbear.id.au>
Reviewed-by: Paul Mackerras <paulus@samba.org>
---
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(+)
diff --git a/arch/powerpc/include/asm/sparsemem.h b/arch/powerpc/include/asm/sparsemem.h
index f6fc0ee..737335c 100644
--- a/arch/powerpc/include/asm/sparsemem.h
+++ b/arch/powerpc/include/asm/sparsemem.h
@@ -16,6 +16,7 @@
#endif /* CONFIG_SPARSEMEM */
#ifdef CONFIG_MEMORY_HOTPLUG
+extern void resize_hpt_for_hotplug(unsigned long new_mem_size);
extern int create_section_mapping(unsigned long start, unsigned long end);
extern int remove_section_mapping(unsigned long start, unsigned long end);
#ifdef CONFIG_NUMA
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 0809bea..6fbc27a 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -645,6 +645,35 @@ static unsigned long __init htab_get_table_size(void)
}
#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
+ * here: we immediately increase the HPT size if the target
+ * shift exceeds the current shift, but we won't attempt to
+ * reduce unless the target shift is at least 2 below the
+ * current shift
+ */
+ if ((target_hpt_shift > ppc64_pft_size)
+ || (target_hpt_shift < (ppc64_pft_size - 1))) {
+ int rc;
+
+ 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);
+ }
+}
+
int create_section_mapping(unsigned long start, unsigned long end)
{
int rc = htab_bolt_mapping(start, end, __pa(start),
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index f980da6..4938ee7 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -121,6 +121,8 @@ int arch_add_memory(int nid, u64 start, u64 size, bool for_device)
unsigned long nr_pages = size >> PAGE_SHIFT;
int rc;
+ resize_hpt_for_hotplug(memblock_phys_mem_size());
+
pgdata = NODE_DATA(nid);
start = (unsigned long)__va(start);
@@ -161,6 +163,8 @@ int arch_remove_memory(u64 start, u64 size)
*/
vm_unmap_aliases();
+ resize_hpt_for_hotplug(memblock_phys_mem_size());
+
return ret;
}
#endif
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 09/25] powerpc/kvm: Corectly report KVM_CAP_PPC_ALLOC_HTAB
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (7 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 08/25] pseries: Automatically resize HPT for memory hot add/remove David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 10/25] powerpc/kvm: Add capability flag for hashed page table resizing David Gibson
` (15 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
At present KVM on powerpc always reports KVM_CAP_PPC_ALLOC_HTAB as enabled.
However, the ioctl() it advertises (KVM_PPC_ALLOCATE_HTAB) only actually
works on KVM HV. On KVM PR it will fail with ENOTTY.
qemu already has a workaround for this, so it's not breaking things in
practice, but it would be better to advertise this correctly.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/powerpc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index a3b182d..2f21ab7 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -509,7 +509,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
#ifdef CONFIG_PPC_BOOK3S_64
case KVM_CAP_SPAPR_TCE:
- case KVM_CAP_PPC_ALLOC_HTAB:
case KVM_CAP_PPC_RTAS:
case KVM_CAP_PPC_FIXUP_HCALL:
case KVM_CAP_PPC_ENABLE_HCALL:
@@ -518,6 +517,10 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
#endif
r = 1;
break;
+
+ case KVM_CAP_PPC_ALLOC_HTAB:
+ r = hv_enabled;
+ break;
#endif /* CONFIG_PPC_BOOK3S_64 */
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
case KVM_CAP_PPC_SMT:
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 10/25] powerpc/kvm: Add capability flag for hashed page table resizing
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (8 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 09/25] powerpc/kvm: Corectly report KVM_CAP_PPC_ALLOC_HTAB David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 11/25] powerpc/kvm: Rename kvm_alloc_hpt() for clarity David Gibson
` (14 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This adds a new powerpc-specific KVM_CAP_SPAPR_RESIZE_HPT capability to
advertise whether KVM is capable of handling the PAPR extensions for
resizing the hashed page table during guest runtime.
At present, HPT resizing is possible with KVM PR without kernel
modification, since the HPT is managed within qemu. It's not possible yet
with KVM HV, because the HPT is managed by KVM. At present, qemu has to
use other capabilities which (by accident) reveal whether PR or HV is in
use to know if it can advertise HPT resizing capability to the guest.
To avoid ambiguity with existing kernels, the encoding is a bit odd.
0 means "unknown" since that's what previous kernels will return
1 means "HPT resize possible if available if and only if the HPT is allocated in
userspace, rather than in the kernel". In practice this is the same
test as userspace already uses, but this makes it explicit.
2 will mean "HPT resize available and implemented in-kernel"
For now we always return 1, but the intention is to return 2 once HPT
resize is implemented for KVM HV.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/powerpc.c | 3 +++
include/uapi/linux/kvm.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 2f21ab7..a4250f1 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -572,6 +572,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_PPC_GET_SMMU_INFO:
r = 1;
break;
+ case KVM_CAP_SPAPR_RESIZE_HPT:
+ r = 1; /* resize allowed only if HPT is outside kernel */
+ break;
#endif
default:
r = 0;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 9da9051..7e7e0e3 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -850,6 +850,7 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
#define KVM_CAP_HYPERV_SYNIC 123
#define KVM_CAP_S390_RI 124
+#define KVM_CAP_SPAPR_RESIZE_HPT 125
#ifdef KVM_CAP_IRQ_ROUTING
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 11/25] powerpc/kvm: Rename kvm_alloc_hpt() for clarity
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (9 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 10/25] powerpc/kvm: Add capability flag for hashed page table resizing David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 12/25] powerpc/kvm: Gather HPT related variables into sub-structure David Gibson
` (13 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
The difference between kvm_alloc_hpt() and kvmppc_alloc_hpt() is not at
all obvious from the name. In practice kvmppc_alloc_hpt() allocates an HPT
by whatever means, and clals kvm_alloc_hpt() which will attempt to allocate
it with CMA only.
To make this less confusing, rename kvm_alloc_hpt() to kvm_alloc_hpt_cma().
Similarly, kvm_release_hpt() is renamed kvm_free_hpt_cma().
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_ppc.h | 4 ++--
arch/powerpc/kvm/book3s_64_mmu_hv.c | 8 ++++----
arch/powerpc/kvm/book3s_hv_builtin.c | 8 ++++----
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 2241d53..f25947a 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -170,8 +170,8 @@ extern long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
unsigned long ioba, unsigned long tce);
extern long kvmppc_h_get_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
unsigned long ioba);
-extern struct page *kvm_alloc_hpt(unsigned long nr_pages);
-extern void kvm_release_hpt(struct page *page, unsigned long nr_pages);
+extern struct page *kvm_alloc_hpt_cma(unsigned long nr_pages);
+extern void kvm_free_hpt_cma(struct page *page, unsigned long nr_pages);
extern int kvmppc_core_init_vm(struct kvm *kvm);
extern void kvmppc_core_destroy_vm(struct kvm *kvm);
extern void kvmppc_core_free_memslot(struct kvm *kvm,
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index fb37290..157285b0 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -62,7 +62,7 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
}
kvm->arch.hpt_cma_alloc = 0;
- page = kvm_alloc_hpt(1ul << (order - PAGE_SHIFT));
+ page = kvm_alloc_hpt_cma(1ul << (order - PAGE_SHIFT));
if (page) {
hpt = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
memset((void *)hpt, 0, (1ul << order));
@@ -106,7 +106,7 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
out_freehpt:
if (kvm->arch.hpt_cma_alloc)
- kvm_release_hpt(page, 1 << (order - PAGE_SHIFT));
+ kvm_free_hpt_cma(page, 1 << (order - PAGE_SHIFT));
else
free_pages(hpt, order - PAGE_SHIFT);
return -ENOMEM;
@@ -153,8 +153,8 @@ void kvmppc_free_hpt(struct kvm *kvm)
kvmppc_free_lpid(kvm->arch.lpid);
vfree(kvm->arch.revmap);
if (kvm->arch.hpt_cma_alloc)
- kvm_release_hpt(virt_to_page(kvm->arch.hpt_virt),
- 1 << (kvm->arch.hpt_order - PAGE_SHIFT));
+ kvm_free_hpt_cma(virt_to_page(kvm->arch.hpt_virt),
+ 1 << (kvm->arch.hpt_order - PAGE_SHIFT));
else
free_pages(kvm->arch.hpt_virt,
kvm->arch.hpt_order - PAGE_SHIFT);
diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c
index fd7006b..bcc00b7 100644
--- a/arch/powerpc/kvm/book3s_hv_builtin.c
+++ b/arch/powerpc/kvm/book3s_hv_builtin.c
@@ -49,19 +49,19 @@ static int __init early_parse_kvm_cma_resv(char *p)
}
early_param("kvm_cma_resv_ratio", early_parse_kvm_cma_resv);
-struct page *kvm_alloc_hpt(unsigned long nr_pages)
+struct page *kvm_alloc_hpt_cma(unsigned long nr_pages)
{
VM_BUG_ON(order_base_2(nr_pages) < KVM_CMA_CHUNK_ORDER - PAGE_SHIFT);
return cma_alloc(kvm_cma, nr_pages, order_base_2(HPT_ALIGN_PAGES));
}
-EXPORT_SYMBOL_GPL(kvm_alloc_hpt);
+EXPORT_SYMBOL_GPL(kvm_alloc_hpt_cma);
-void kvm_release_hpt(struct page *page, unsigned long nr_pages)
+void kvm_free_hpt_cma(struct page *page, unsigned long nr_pages)
{
cma_release(kvm_cma, page, nr_pages);
}
-EXPORT_SYMBOL_GPL(kvm_release_hpt);
+EXPORT_SYMBOL_GPL(kvm_free_hpt_cma);
/**
* kvm_cma_reserve() - reserve area for kvm hash pagetable
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 12/25] powerpc/kvm: Gather HPT related variables into sub-structure
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (10 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 11/25] powerpc/kvm: Rename kvm_alloc_hpt() for clarity David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 13/25] powerpc/kvm: Don't store values derivable from HPT order David Gibson
` (12 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
Currently, the powerpc kvm_arch structure contains a number of variables
tracking the state of the guest's hashed page table (HPT) in KVM HV. This
patch gathers them all together into a single kvm_hpt_info substructure.
This makes life more convenient for the upcoming HPT resizing
implementation.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
# Conflicts:
# arch/powerpc/kvm/book3s_64_mmu_hv.c
---
arch/powerpc/include/asm/kvm_host.h | 16 ++++---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 90 ++++++++++++++++++-------------------
arch/powerpc/kvm/book3s_hv.c | 2 +-
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 62 ++++++++++++-------------
4 files changed, 87 insertions(+), 83 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 9d08d8c..c32413a 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -223,11 +223,19 @@ struct kvm_arch_memory_slot {
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
};
+struct kvm_hpt_info {
+ unsigned long virt;
+ struct revmap_entry *rev;
+ unsigned long npte;
+ unsigned long mask;
+ u32 order;
+ int cma;
+};
+
struct kvm_arch {
unsigned int lpid;
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- unsigned long hpt_virt;
- struct revmap_entry *revmap;
+ struct kvm_hpt_info hpt;
unsigned int host_lpid;
unsigned long host_lpcr;
unsigned long sdr1;
@@ -236,14 +244,10 @@ struct kvm_arch {
unsigned long lpcr;
unsigned long vrma_slb_v;
int hpte_setup_done;
- u32 hpt_order;
atomic_t vcpus_running;
u32 online_vcores;
- unsigned long hpt_npte;
- unsigned long hpt_mask;
atomic_t hpte_mod_interest;
cpumask_t need_tlb_flush;
- int hpt_cma_alloc;
struct dentry *debugfs_dir;
struct dentry *htab_dentry;
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 157285b0..2ba9d99 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -61,12 +61,12 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
order = PPC_MIN_HPT_ORDER;
}
- kvm->arch.hpt_cma_alloc = 0;
+ kvm->arch.hpt.cma = 0;
page = kvm_alloc_hpt_cma(1ul << (order - PAGE_SHIFT));
if (page) {
hpt = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
memset((void *)hpt, 0, (1ul << order));
- kvm->arch.hpt_cma_alloc = 1;
+ kvm->arch.hpt.cma = 1;
}
/* Lastly try successively smaller sizes from the page allocator */
@@ -81,20 +81,20 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
if (!hpt)
return -ENOMEM;
- kvm->arch.hpt_virt = hpt;
- kvm->arch.hpt_order = order;
+ kvm->arch.hpt.virt = hpt;
+ kvm->arch.hpt.order = order;
/* HPTEs are 2**4 bytes long */
- kvm->arch.hpt_npte = 1ul << (order - 4);
+ kvm->arch.hpt.npte = 1ul << (order - 4);
/* 128 (2**7) bytes in each HPTEG */
- kvm->arch.hpt_mask = (1ul << (order - 7)) - 1;
+ kvm->arch.hpt.mask = (1ul << (order - 7)) - 1;
/* Allocate reverse map array */
- rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt_npte);
+ rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt.npte);
if (!rev) {
pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n");
goto out_freehpt;
}
- kvm->arch.revmap = rev;
+ kvm->arch.hpt.rev = rev;
kvm->arch.sdr1 = __pa(hpt) | (order - 18);
pr_info("KVM guest htab at %lx (order %ld), LPID %x\n",
@@ -105,7 +105,7 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
return 0;
out_freehpt:
- if (kvm->arch.hpt_cma_alloc)
+ if (kvm->arch.hpt.cma)
kvm_free_hpt_cma(page, 1 << (order - PAGE_SHIFT));
else
free_pages(hpt, order - PAGE_SHIFT);
@@ -127,10 +127,10 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
goto out;
}
}
- if (kvm->arch.hpt_virt) {
- order = kvm->arch.hpt_order;
+ if (kvm->arch.hpt.virt) {
+ order = kvm->arch.hpt.order;
/* Set the entire HPT to 0, i.e. invalid HPTEs */
- memset((void *)kvm->arch.hpt_virt, 0, 1ul << order);
+ memset((void *)kvm->arch.hpt.virt, 0, 1ul << order);
/*
* Reset all the reverse-mapping chains for all memslots
*/
@@ -151,13 +151,13 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
void kvmppc_free_hpt(struct kvm *kvm)
{
kvmppc_free_lpid(kvm->arch.lpid);
- vfree(kvm->arch.revmap);
- if (kvm->arch.hpt_cma_alloc)
- kvm_free_hpt_cma(virt_to_page(kvm->arch.hpt_virt),
- 1 << (kvm->arch.hpt_order - PAGE_SHIFT));
+ vfree(kvm->arch.hpt.rev);
+ if (kvm->arch.hpt.cma)
+ kvm_free_hpt_cma(virt_to_page(kvm->arch.hpt.virt),
+ 1 << (kvm->arch.hpt.order - PAGE_SHIFT));
else
- free_pages(kvm->arch.hpt_virt,
- kvm->arch.hpt_order - PAGE_SHIFT);
+ free_pages(kvm->arch.hpt.virt,
+ kvm->arch.hpt.order - PAGE_SHIFT);
}
/* Bits in first HPTE dword for pagesize 4k, 64k or 16M */
@@ -192,8 +192,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
if (npages > 1ul << (40 - porder))
npages = 1ul << (40 - porder);
/* Can't use more than 1 HPTE per HPTEG */
- if (npages > kvm->arch.hpt_mask + 1)
- npages = kvm->arch.hpt_mask + 1;
+ if (npages > kvm->arch.hpt.mask + 1)
+ npages = kvm->arch.hpt.mask + 1;
hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
HPTE_V_BOLTED | hpte0_pgsize_encoding(psize);
@@ -203,7 +203,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
for (i = 0; i < npages; ++i) {
addr = i << porder;
/* can't use hpt_hash since va > 64 bits */
- hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt_mask;
+ hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt.mask;
/*
* We assume that the hash table is empty and no
* vcpus are using it at this stage. Since we create
@@ -336,9 +336,9 @@ static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
preempt_enable();
return -ENOENT;
}
- hptep = (__be64 *)(kvm->arch.hpt_virt + (index << 4));
+ hptep = (__be64 *)(kvm->arch.hpt.virt + (index << 4));
v = be64_to_cpu(hptep[0]) & ~HPTE_V_HVLOCK;
- gr = kvm->arch.revmap[index].guest_rpte;
+ gr = kvm->arch.hpt.rev[index].guest_rpte;
unlock_hpte(hptep, v);
preempt_enable();
@@ -461,8 +461,8 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
if (ea != vcpu->arch.pgfault_addr)
return RESUME_GUEST;
index = vcpu->arch.pgfault_index;
- hptep = (__be64 *)(kvm->arch.hpt_virt + (index << 4));
- rev = &kvm->arch.revmap[index];
+ hptep = (__be64 *)(kvm->arch.hpt.virt + (index << 4));
+ rev = &kvm->arch.hpt.rev[index];
preempt_disable();
while (!try_lock_hpte(hptep, HPTE_V_HVLOCK))
cpu_relax();
@@ -713,7 +713,7 @@ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.revmap;
+ struct revmap_entry *rev = kvm->arch.hpt.rev;
unsigned long h, i, j;
__be64 *hptep;
unsigned long ptel, psize, rcbits;
@@ -731,7 +731,7 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
* rmap chain lock.
*/
i = *rmapp & KVMPPC_RMAP_INDEX;
- hptep = (__be64 *) (kvm->arch.hpt_virt + (i << 4));
+ hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
if (!try_lock_hpte(hptep, HPTE_V_HVLOCK)) {
/* unlock rmap before spinning on the HPTE lock */
unlock_rmap(rmapp);
@@ -813,7 +813,7 @@ void kvmppc_core_flush_memslot_hv(struct kvm *kvm,
static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.revmap;
+ struct revmap_entry *rev = kvm->arch.hpt.rev;
unsigned long head, i, j;
__be64 *hptep;
int ret = 0;
@@ -831,7 +831,7 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
i = head = *rmapp & KVMPPC_RMAP_INDEX;
do {
- hptep = (__be64 *) (kvm->arch.hpt_virt + (i << 4));
+ hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
j = rev[i].forw;
/* If this HPTE isn't referenced, ignore it */
@@ -871,7 +871,7 @@ int kvm_age_hva_hv(struct kvm *kvm, unsigned long start, unsigned long end)
static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.revmap;
+ struct revmap_entry *rev = kvm->arch.hpt.rev;
unsigned long head, i, j;
unsigned long *hp;
int ret = 1;
@@ -886,7 +886,7 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
if (*rmapp & KVMPPC_RMAP_PRESENT) {
i = head = *rmapp & KVMPPC_RMAP_INDEX;
do {
- hp = (unsigned long *)(kvm->arch.hpt_virt + (i << 4));
+ hp = (unsigned long *)(kvm->arch.hpt.virt + (i << 4));
j = rev[i].forw;
if (be64_to_cpu(hp[1]) & HPTE_R_R)
goto out;
@@ -920,7 +920,7 @@ static int vcpus_running(struct kvm *kvm)
*/
static int kvm_test_clear_dirty_npages(struct kvm *kvm, unsigned long *rmapp)
{
- struct revmap_entry *rev = kvm->arch.revmap;
+ struct revmap_entry *rev = kvm->arch.hpt.rev;
unsigned long head, i, j;
unsigned long n;
unsigned long v, r;
@@ -945,7 +945,7 @@ static int kvm_test_clear_dirty_npages(struct kvm *kvm, unsigned long *rmapp)
i = head = *rmapp & KVMPPC_RMAP_INDEX;
do {
unsigned long hptep1;
- hptep = (__be64 *) (kvm->arch.hpt_virt + (i << 4));
+ hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
j = rev[i].forw;
/*
@@ -1252,8 +1252,8 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
flags = ctx->flags;
i = ctx->index;
- hptp = (__be64 *)(kvm->arch.hpt_virt + (i * HPTE_SIZE));
- revp = kvm->arch.revmap + i;
+ hptp = (__be64 *)(kvm->arch.hpt.virt + (i * HPTE_SIZE));
+ revp = kvm->arch.hpt.rev + i;
lbuf = (unsigned long __user *)buf;
nb = 0;
@@ -1268,7 +1268,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
/* Skip uninteresting entries, i.e. clean on not-first pass */
if (!first_pass) {
- while (i < kvm->arch.hpt_npte &&
+ while (i < kvm->arch.hpt.npte &&
!hpte_dirty(revp, hptp)) {
++i;
hptp += 2;
@@ -1278,7 +1278,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
hdr.index = i;
/* Grab a series of valid entries */
- while (i < kvm->arch.hpt_npte &&
+ while (i < kvm->arch.hpt.npte &&
hdr.n_valid < 0xffff &&
nb + HPTE_SIZE < count &&
record_hpte(flags, hptp, hpte, revp, 1, first_pass)) {
@@ -1294,7 +1294,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
++revp;
}
/* Now skip invalid entries while we can */
- while (i < kvm->arch.hpt_npte &&
+ while (i < kvm->arch.hpt.npte &&
hdr.n_invalid < 0xffff &&
record_hpte(flags, hptp, hpte, revp, 0, first_pass)) {
/* found an invalid entry */
@@ -1315,7 +1315,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
}
/* Check if we've wrapped around the hash table */
- if (i >= kvm->arch.hpt_npte) {
+ if (i >= kvm->arch.hpt.npte) {
i = 0;
ctx->first_pass = 0;
break;
@@ -1374,11 +1374,11 @@ static ssize_t kvm_htab_write(struct file *file, const char __user *buf,
err = -EINVAL;
i = hdr.index;
- if (i >= kvm->arch.hpt_npte ||
- i + hdr.n_valid + hdr.n_invalid > kvm->arch.hpt_npte)
+ if (i >= kvm->arch.hpt.npte ||
+ i + hdr.n_valid + hdr.n_invalid > kvm->arch.hpt.npte)
break;
- hptp = (__be64 *)(kvm->arch.hpt_virt + (i * HPTE_SIZE));
+ hptp = (__be64 *)(kvm->arch.hpt.virt + (i * HPTE_SIZE));
lbuf = (unsigned long __user *)buf;
for (j = 0; j < hdr.n_valid; ++j) {
__be64 hpte_v;
@@ -1565,8 +1565,8 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
kvm = p->kvm;
i = p->hpt_index;
- hptp = (__be64 *)(kvm->arch.hpt_virt + (i * HPTE_SIZE));
- for (; len != 0 && i < kvm->arch.hpt_npte; ++i, hptp += 2) {
+ hptp = (__be64 *)(kvm->arch.hpt.virt + (i * HPTE_SIZE));
+ for (; len != 0 && i < kvm->arch.hpt.npte; ++i, hptp += 2) {
if (!(be64_to_cpu(hptp[0]) & (HPTE_V_VALID | HPTE_V_ABSENT)))
continue;
@@ -1576,7 +1576,7 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
cpu_relax();
v = be64_to_cpu(hptp[0]) & ~HPTE_V_HVLOCK;
hr = be64_to_cpu(hptp[1]);
- gr = kvm->arch.revmap[i].guest_rpte;
+ gr = kvm->arch.hpt.rev[i].guest_rpte;
unlock_hpte(hptp, v);
preempt_enable();
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index baeddb0..eea4dbd 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -2922,7 +2922,7 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
goto out; /* another vcpu beat us to it */
/* Allocate hashed page table (if not done already) and reset it */
- if (!kvm->arch.hpt_virt) {
+ if (!kvm->arch.hpt.virt) {
err = kvmppc_alloc_hpt(kvm, NULL);
if (err) {
pr_err("KVM: Couldn't alloc HPT\n");
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 9170051..a4641e4 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -79,10 +79,10 @@ void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
if (*rmap & KVMPPC_RMAP_PRESENT) {
i = *rmap & KVMPPC_RMAP_INDEX;
- head = &kvm->arch.revmap[i];
+ head = &kvm->arch.hpt.rev[i];
if (realmode)
head = real_vmalloc_addr(head);
- tail = &kvm->arch.revmap[head->back];
+ tail = &kvm->arch.hpt.rev[head->back];
if (realmode)
tail = real_vmalloc_addr(tail);
rev->forw = i;
@@ -147,8 +147,8 @@ static void remove_revmap_chain(struct kvm *kvm, long pte_index,
lock_rmap(rmap);
head = *rmap & KVMPPC_RMAP_INDEX;
- next = real_vmalloc_addr(&kvm->arch.revmap[rev->forw]);
- prev = real_vmalloc_addr(&kvm->arch.revmap[rev->back]);
+ next = real_vmalloc_addr(&kvm->arch.hpt.rev[rev->forw]);
+ prev = real_vmalloc_addr(&kvm->arch.hpt.rev[rev->back]);
next->back = rev->back;
prev->forw = rev->forw;
if (head == pte_index) {
@@ -281,11 +281,11 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
/* Find and lock the HPTEG slot to use */
do_insert:
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
if (likely((flags & H_EXACT) == 0)) {
pte_index &= ~7UL;
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
for (i = 0; i < 8; ++i) {
if ((be64_to_cpu(*hpte) & HPTE_V_VALID) == 0 &&
try_lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID |
@@ -316,7 +316,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
}
pte_index += i;
} else {
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
if (!try_lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID |
HPTE_V_ABSENT)) {
/* Lock the slot and check again */
@@ -333,7 +333,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
}
/* Save away the guest's idea of the second HPTE dword */
- rev = &kvm->arch.revmap[pte_index];
+ rev = &kvm->arch.hpt.rev[pte_index];
if (realmode)
rev = real_vmalloc_addr(rev);
if (rev) {
@@ -437,9 +437,9 @@ long kvmppc_do_h_remove(struct kvm *kvm, unsigned long flags,
struct revmap_entry *rev;
u64 pte;
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
cpu_relax();
pte = be64_to_cpu(hpte[0]);
@@ -450,7 +450,7 @@ long kvmppc_do_h_remove(struct kvm *kvm, unsigned long flags,
return H_NOT_FOUND;
}
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
v = pte & ~HPTE_V_HVLOCK;
if (v & HPTE_V_VALID) {
hpte[0] &= ~cpu_to_be64(HPTE_V_VALID);
@@ -515,13 +515,13 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
break;
}
if (req != 1 || flags == 3 ||
- pte_index >= kvm->arch.hpt_npte) {
+ pte_index >= kvm->arch.hpt.npte) {
/* parameter error */
args[j] = ((0xa0 | flags) << 56) + pte_index;
ret = H_PARAMETER;
break;
}
- hp = (__be64 *) (kvm->arch.hpt_virt + (pte_index << 4));
+ hp = (__be64 *) (kvm->arch.hpt.virt + (pte_index << 4));
/* to avoid deadlock, don't spin except for first */
if (!try_lock_hpte(hp, HPTE_V_HVLOCK)) {
if (n)
@@ -553,7 +553,7 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
}
args[j] = ((0x80 | flags) << 56) + pte_index;
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
note_hpte_modification(kvm, rev);
if (!(hp0 & HPTE_V_VALID)) {
@@ -607,10 +607,10 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long v, r, rb, mask, bits;
u64 pte;
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
cpu_relax();
pte = be64_to_cpu(hpte[0]);
@@ -628,7 +628,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
/* Update guest view of 2nd HPTE dword */
mask = HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
HPTE_R_KEY_HI | HPTE_R_KEY_LO;
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
if (rev) {
r = (rev->guest_rpte & ~mask) | bits;
rev->guest_rpte = r;
@@ -670,15 +670,15 @@ long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags,
int i, n = 1;
struct revmap_entry *rev = NULL;
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
if (flags & H_READ_4) {
pte_index &= ~3;
n = 4;
}
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
for (i = 0; i < n; ++i, ++pte_index) {
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
v = be64_to_cpu(hpte[0]) & ~HPTE_V_HVLOCK;
r = be64_to_cpu(hpte[1]);
if (v & HPTE_V_ABSENT) {
@@ -705,11 +705,11 @@ long kvmppc_h_clear_ref(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long *rmap;
long ret = H_NOT_FOUND;
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
cpu_relax();
v = be64_to_cpu(hpte[0]);
@@ -751,11 +751,11 @@ long kvmppc_h_clear_mod(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long *rmap;
long ret = H_NOT_FOUND;
- if (pte_index >= kvm->arch.hpt_npte)
+ if (pte_index >= kvm->arch.hpt.npte)
return H_PARAMETER;
- rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
- hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
cpu_relax();
v = be64_to_cpu(hpte[0]);
@@ -861,7 +861,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v,
somask = (1UL << 28) - 1;
vsid = (slb_v & ~SLB_VSID_B) >> SLB_VSID_SHIFT;
}
- hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvm->arch.hpt_mask;
+ hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvm->arch.hpt.mask;
avpn = slb_v & ~(somask >> 16); /* also includes B */
avpn |= (eaddr & somask) >> 16;
@@ -872,7 +872,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v,
val |= avpn;
for (;;) {
- hpte = (__be64 *)(kvm->arch.hpt_virt + (hash << 7));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (hash << 7));
for (i = 0; i < 16; i += 2) {
/* Read the PTE racily */
@@ -902,7 +902,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v,
if (val & HPTE_V_SECONDARY)
break;
val |= HPTE_V_SECONDARY;
- hash = hash ^ kvm->arch.hpt_mask;
+ hash = hash ^ kvm->arch.hpt.mask;
}
return -1;
}
@@ -941,10 +941,10 @@ long kvmppc_hpte_hv_fault(struct kvm_vcpu *vcpu, unsigned long addr,
return status; /* there really was no HPTE */
return 0; /* for prot fault, HPTE disappeared */
}
- hpte = (__be64 *)(kvm->arch.hpt_virt + (index << 4));
+ hpte = (__be64 *)(kvm->arch.hpt.virt + (index << 4));
v = be64_to_cpu(hpte[0]) & ~HPTE_V_HVLOCK;
r = be64_to_cpu(hpte[1]);
- rev = real_vmalloc_addr(&kvm->arch.revmap[index]);
+ rev = real_vmalloc_addr(&kvm->arch.hpt.rev[index]);
gr = rev->guest_rpte;
unlock_hpte(hpte, v);
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 13/25] powerpc/kvm: Don't store values derivable from HPT order
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (11 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 12/25] powerpc/kvm: Gather HPT related variables into sub-structure David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 14/25] powerpc/kvm: Split HPT allocation from activation David Gibson
` (11 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
Currently the kvm_hpt_info structure stores the hashed page table's order,
and also the number of HPTEs it contains and a mask for its size. The
last two can be easily derived from the order, so remove them and just
calculate them as necessary with a couple of helper inlines.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_book3s_64.h | 12 ++++++++++++
arch/powerpc/include/asm/kvm_host.h | 2 --
arch/powerpc/kvm/book3s_64_mmu_hv.c | 28 +++++++++++++---------------
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 18 +++++++++---------
4 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
index 2aa79c8..75b2dee 100644
--- a/arch/powerpc/include/asm/kvm_book3s_64.h
+++ b/arch/powerpc/include/asm/kvm_book3s_64.h
@@ -437,6 +437,18 @@ extern void kvmppc_mmu_debugfs_init(struct kvm *kvm);
extern void kvmhv_rm_send_ipi(int cpu);
+static inline unsigned long kvmppc_hpt_npte(struct kvm_hpt_info *hpt)
+{
+ /* HPTEs are 2**4 bytes long */
+ return 1UL << (hpt->order - 4);
+}
+
+static inline unsigned long kvmppc_hpt_mask(struct kvm_hpt_info *hpt)
+{
+ /* 128 (2**7) bytes in each HPTEG */
+ return (1UL << (hpt->order - 7)) - 1;
+}
+
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
#endif /* __ASM_KVM_BOOK3S_64_H__ */
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index c32413a..718dc56 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -226,8 +226,6 @@ struct kvm_arch_memory_slot {
struct kvm_hpt_info {
unsigned long virt;
struct revmap_entry *rev;
- unsigned long npte;
- unsigned long mask;
u32 order;
int cma;
};
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 2ba9d99..679c292 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -83,13 +83,9 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
kvm->arch.hpt.virt = hpt;
kvm->arch.hpt.order = order;
- /* HPTEs are 2**4 bytes long */
- kvm->arch.hpt.npte = 1ul << (order - 4);
- /* 128 (2**7) bytes in each HPTEG */
- kvm->arch.hpt.mask = (1ul << (order - 7)) - 1;
/* Allocate reverse map array */
- rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt.npte);
+ rev = vmalloc(sizeof(struct revmap_entry) * kvmppc_hpt_npte(&kvm->arch.hpt));
if (!rev) {
pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n");
goto out_freehpt;
@@ -192,8 +188,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
if (npages > 1ul << (40 - porder))
npages = 1ul << (40 - porder);
/* Can't use more than 1 HPTE per HPTEG */
- if (npages > kvm->arch.hpt.mask + 1)
- npages = kvm->arch.hpt.mask + 1;
+ if (npages > kvmppc_hpt_mask(&kvm->arch.hpt) + 1)
+ npages = kvmppc_hpt_mask(&kvm->arch.hpt) + 1;
hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
HPTE_V_BOLTED | hpte0_pgsize_encoding(psize);
@@ -203,7 +199,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
for (i = 0; i < npages; ++i) {
addr = i << porder;
/* can't use hpt_hash since va > 64 bits */
- hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt.mask;
+ hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25)))
+ & kvmppc_hpt_mask(&kvm->arch.hpt);
/*
* We assume that the hash table is empty and no
* vcpus are using it at this stage. Since we create
@@ -1268,7 +1265,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
/* Skip uninteresting entries, i.e. clean on not-first pass */
if (!first_pass) {
- while (i < kvm->arch.hpt.npte &&
+ while (i < kvmppc_hpt_npte(&kvm->arch.hpt) &&
!hpte_dirty(revp, hptp)) {
++i;
hptp += 2;
@@ -1278,7 +1275,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
hdr.index = i;
/* Grab a series of valid entries */
- while (i < kvm->arch.hpt.npte &&
+ while (i < kvmppc_hpt_npte(&kvm->arch.hpt) &&
hdr.n_valid < 0xffff &&
nb + HPTE_SIZE < count &&
record_hpte(flags, hptp, hpte, revp, 1, first_pass)) {
@@ -1294,7 +1291,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
++revp;
}
/* Now skip invalid entries while we can */
- while (i < kvm->arch.hpt.npte &&
+ while (i < kvmppc_hpt_npte(&kvm->arch.hpt) &&
hdr.n_invalid < 0xffff &&
record_hpte(flags, hptp, hpte, revp, 0, first_pass)) {
/* found an invalid entry */
@@ -1315,7 +1312,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
}
/* Check if we've wrapped around the hash table */
- if (i >= kvm->arch.hpt.npte) {
+ if (i >= kvmppc_hpt_npte(&kvm->arch.hpt)) {
i = 0;
ctx->first_pass = 0;
break;
@@ -1374,8 +1371,8 @@ static ssize_t kvm_htab_write(struct file *file, const char __user *buf,
err = -EINVAL;
i = hdr.index;
- if (i >= kvm->arch.hpt.npte ||
- i + hdr.n_valid + hdr.n_invalid > kvm->arch.hpt.npte)
+ if (i >= kvmppc_hpt_npte(&kvm->arch.hpt) ||
+ i + hdr.n_valid + hdr.n_invalid > kvmppc_hpt_npte(&kvm->arch.hpt))
break;
hptp = (__be64 *)(kvm->arch.hpt.virt + (i * HPTE_SIZE));
@@ -1566,7 +1563,8 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf,
kvm = p->kvm;
i = p->hpt_index;
hptp = (__be64 *)(kvm->arch.hpt.virt + (i * HPTE_SIZE));
- for (; len != 0 && i < kvm->arch.hpt.npte; ++i, hptp += 2) {
+ for (; len != 0 && i < kvmppc_hpt_npte(&kvm->arch.hpt);
+ ++i, hptp += 2) {
if (!(be64_to_cpu(hptp[0]) & (HPTE_V_VALID | HPTE_V_ABSENT)))
continue;
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index a4641e4..347ed0e 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -281,7 +281,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
/* Find and lock the HPTEG slot to use */
do_insert:
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
if (likely((flags & H_EXACT) == 0)) {
pte_index &= ~7UL;
@@ -437,7 +437,7 @@ long kvmppc_do_h_remove(struct kvm *kvm, unsigned long flags,
struct revmap_entry *rev;
u64 pte;
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
@@ -515,7 +515,7 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
break;
}
if (req != 1 || flags == 3 ||
- pte_index >= kvm->arch.hpt.npte) {
+ pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt)) {
/* parameter error */
args[j] = ((0xa0 | flags) << 56) + pte_index;
ret = H_PARAMETER;
@@ -607,7 +607,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long v, r, rb, mask, bits;
u64 pte;
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
hpte = (__be64 *)(kvm->arch.hpt.virt + (pte_index << 4));
@@ -670,7 +670,7 @@ long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags,
int i, n = 1;
struct revmap_entry *rev = NULL;
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
if (flags & H_READ_4) {
pte_index &= ~3;
@@ -705,7 +705,7 @@ long kvmppc_h_clear_ref(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long *rmap;
long ret = H_NOT_FOUND;
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
@@ -751,7 +751,7 @@ long kvmppc_h_clear_mod(struct kvm_vcpu *vcpu, unsigned long flags,
unsigned long *rmap;
long ret = H_NOT_FOUND;
- if (pte_index >= kvm->arch.hpt.npte)
+ if (pte_index >= kvmppc_hpt_npte(&kvm->arch.hpt))
return H_PARAMETER;
rev = real_vmalloc_addr(&kvm->arch.hpt.rev[pte_index]);
@@ -861,7 +861,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v,
somask = (1UL << 28) - 1;
vsid = (slb_v & ~SLB_VSID_B) >> SLB_VSID_SHIFT;
}
- hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvm->arch.hpt.mask;
+ hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvmppc_hpt_mask(&kvm->arch.hpt);
avpn = slb_v & ~(somask >> 16); /* also includes B */
avpn |= (eaddr & somask) >> 16;
@@ -902,7 +902,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v,
if (val & HPTE_V_SECONDARY)
break;
val |= HPTE_V_SECONDARY;
- hash = hash ^ kvm->arch.hpt.mask;
+ hash = hash ^ kvmppc_hpt_mask(&kvm->arch.hpt);
}
return -1;
}
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 14/25] powerpc/kvm: Split HPT allocation from activation
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (12 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 13/25] powerpc/kvm: Don't store values derivable from HPT order David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 15/25] powerpc/kvm: Allow KVM_PPC_ALLOCATE_HTAB ioctl() to change HPT size David Gibson
` (10 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
Currently, kvmppc_alloc_hpt() both allocates a new hashed page table (HPT)
and sets it up as the active page table for a VM. For the upcoming HPT
resize implementation we're going to want to allocate HPTs separately from
activating them.
So, split the allocation itself out into kvmppc_allocate_hpt() and perform
the activation with a new kvmppc_set_hpt() function. Likewise we split
kvmppc_free_hpt(), which just frees the HPT, from kvmppc_release_hpt()
which unsets it as an active HPT, then frees it.
We also move the logic to fall back to smaller HPT sizes if the first try
fails into the single caller which used that behaviour,
kvmppc_hv_setup_htab_rma(). This introduces a slight semantic change, in
that previously if the initial attempt at CMA allocation faile, we would
fall back to attempting smaller sizes with the page allocator. Now, we
try first CMA, then the page allocator at each size. As far as I can tell
this change should be harmless.
To match, we make kvmppc_free_hpt() just free the actual HPT itself. The
call to kvmppc_free_lpid() that was there, we move to the single caller.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
# Conflicts:
# arch/powerpc/kvm/book3s_64_mmu_hv.c
---
arch/powerpc/include/asm/kvm_book3s_64.h | 3 ++
arch/powerpc/include/asm/kvm_ppc.h | 5 +-
arch/powerpc/kvm/book3s_64_mmu_hv.c | 89 ++++++++++++++++----------------
arch/powerpc/kvm/book3s_hv.c | 18 +++++--
4 files changed, 65 insertions(+), 50 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
index 75b2dee..f1b832c 100644
--- a/arch/powerpc/include/asm/kvm_book3s_64.h
+++ b/arch/powerpc/include/asm/kvm_book3s_64.h
@@ -20,6 +20,9 @@
#ifndef __ASM_KVM_BOOK3S_64_H__
#define __ASM_KVM_BOOK3S_64_H__
+/* Power architecture requires HPT is at least 256kB */
+#define PPC_MIN_HPT_ORDER 18
+
#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
static inline struct kvmppc_book3s_shadow_vcpu *svcpu_get(struct kvm_vcpu *vcpu)
{
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index f25947a..f77d0a0 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -155,9 +155,10 @@ extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu);
extern int kvmppc_kvm_pv(struct kvm_vcpu *vcpu);
extern void kvmppc_map_magic(struct kvm_vcpu *vcpu);
-extern long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp);
+extern int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order);
+extern void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info);
extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp);
-extern void kvmppc_free_hpt(struct kvm *kvm);
+extern void kvmppc_free_hpt(struct kvm_hpt_info *info);
extern long kvmppc_prepare_vrma(struct kvm *kvm,
struct kvm_userspace_memory_region *mem);
extern void kvmppc_map_vrma(struct kvm_vcpu *vcpu,
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 679c292..eb1aa3a 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -40,74 +40,69 @@
#include "trace_hv.h"
-/* Power architecture requires HPT is at least 256kB */
-#define PPC_MIN_HPT_ORDER 18
-
static long kvmppc_virtmode_do_h_enter(struct kvm *kvm, unsigned long flags,
long pte_index, unsigned long pteh,
unsigned long ptel, unsigned long *pte_idx_ret);
static void kvmppc_rmap_reset(struct kvm *kvm);
-long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
+int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order)
{
- unsigned long hpt = 0;
- struct revmap_entry *rev;
+ unsigned long hpt;
+ int cma;
struct page *page = NULL;
- long order = KVM_DEFAULT_HPT_ORDER;
-
- if (htab_orderp) {
- order = *htab_orderp;
- if (order < PPC_MIN_HPT_ORDER)
- order = PPC_MIN_HPT_ORDER;
- }
+ struct revmap_entry *rev;
+ unsigned long npte;
- kvm->arch.hpt.cma = 0;
+ hpt = 0;
+ cma = 0;
page = kvm_alloc_hpt_cma(1ul << (order - PAGE_SHIFT));
if (page) {
hpt = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
memset((void *)hpt, 0, (1ul << order));
- kvm->arch.hpt.cma = 1;
+ cma = 1;
}
- /* Lastly try successively smaller sizes from the page allocator */
- /* Only do this if userspace didn't specify a size via ioctl */
- while (!hpt && order > PPC_MIN_HPT_ORDER && !htab_orderp) {
- hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT|
- __GFP_NOWARN, order - PAGE_SHIFT);
- if (!hpt)
- --order;
- }
+ if (!hpt)
+ hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT
+ |__GFP_NOWARN, order - PAGE_SHIFT);
if (!hpt)
return -ENOMEM;
- kvm->arch.hpt.virt = hpt;
- kvm->arch.hpt.order = order;
+ /* HPTEs are 2**4 bytes long */
+ npte = 1ul << (order - 4);
/* Allocate reverse map array */
- rev = vmalloc(sizeof(struct revmap_entry) * kvmppc_hpt_npte(&kvm->arch.hpt));
+ rev = vmalloc(sizeof(struct revmap_entry) * npte);
if (!rev) {
- pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n");
+ pr_err("kvmppc_allocate_hpt: Couldn't alloc reverse map array\n");
goto out_freehpt;
}
- kvm->arch.hpt.rev = rev;
- kvm->arch.sdr1 = __pa(hpt) | (order - 18);
- pr_info("KVM guest htab at %lx (order %ld), LPID %x\n",
- hpt, order, kvm->arch.lpid);
+ info->order = order;
+ info->virt = hpt;
+ info->cma = cma;
+ info->rev = rev;
- if (htab_orderp)
- *htab_orderp = order;
return 0;
out_freehpt:
- if (kvm->arch.hpt.cma)
+ if (info->cma)
kvm_free_hpt_cma(page, 1 << (order - PAGE_SHIFT));
else
- free_pages(hpt, order - PAGE_SHIFT);
+ free_pages(info->virt, order - PAGE_SHIFT);
return -ENOMEM;
}
+void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info)
+{
+ kvm->arch.hpt = *info;
+ kvm->arch.sdr1 = __pa(info->virt) | (info->order - 18);
+
+ pr_info("KVM guest htab at %lx (order %ld), LPID %x\n",
+ info->virt, (long)info->order, kvm->arch.lpid);
+}
+
long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
{
long err = -EBUSY;
@@ -136,24 +131,28 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
*htab_orderp = order;
err = 0;
} else {
- err = kvmppc_alloc_hpt(kvm, htab_orderp);
- order = *htab_orderp;
+ struct kvm_hpt_info info;
+
+ err = kvmppc_allocate_hpt(&info, *htab_orderp);
+ if (err < 0)
+ goto out;
+ kvmppc_set_hpt(kvm, &info);
}
out:
mutex_unlock(&kvm->lock);
return err;
}
-void kvmppc_free_hpt(struct kvm *kvm)
+void kvmppc_free_hpt(struct kvm_hpt_info *info)
{
- kvmppc_free_lpid(kvm->arch.lpid);
- vfree(kvm->arch.hpt.rev);
- if (kvm->arch.hpt.cma)
- kvm_free_hpt_cma(virt_to_page(kvm->arch.hpt.virt),
- 1 << (kvm->arch.hpt.order - PAGE_SHIFT));
+ vfree(info->rev);
+ if (info->cma)
+ kvm_free_hpt_cma(virt_to_page(info->virt),
+ 1 << (info->order - PAGE_SHIFT));
else
- free_pages(kvm->arch.hpt.virt,
- kvm->arch.hpt.order - PAGE_SHIFT);
+ free_pages(info->virt, info->order - PAGE_SHIFT);
+ info->virt = 0;
+ info->order = 0;
}
/* Bits in first HPTE dword for pagesize 4k, 64k or 16M */
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index eea4dbd..1199fb5 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -2923,11 +2923,22 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
/* Allocate hashed page table (if not done already) and reset it */
if (!kvm->arch.hpt.virt) {
- err = kvmppc_alloc_hpt(kvm, NULL);
- if (err) {
+ int order = KVM_DEFAULT_HPT_ORDER;
+ struct kvm_hpt_info info;
+
+ err = kvmppc_allocate_hpt(&info, order);
+ /* If we get here, it means userspace didn't specify a
+ * size explicitly. So, try successively smaller
+ * sizes if the default failed. */
+ while (err < 0 && --order > PPC_MIN_HPT_ORDER)
+ err = kvmppc_allocate_hpt(&info, order);
+
+ if (err < 0) {
pr_err("KVM: Couldn't alloc HPT\n");
goto out;
}
+
+ kvmppc_set_hpt(kvm, &info);
}
/* Look up the memslot for guest physical address 0 */
@@ -3056,7 +3067,8 @@ static void kvmppc_core_destroy_vm_hv(struct kvm *kvm)
kvmppc_free_vcores(kvm);
- kvmppc_free_hpt(kvm);
+ kvmppc_free_lpid(kvm->arch.lpid);
+ kvmppc_free_hpt(&kvm->arch.hpt);
}
/* We don't need to emulate any privileged instructions or dcbz */
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 15/25] powerpc/kvm: Allow KVM_PPC_ALLOCATE_HTAB ioctl() to change HPT size
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (13 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 14/25] powerpc/kvm: Split HPT allocation from activation David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 16/25] powerpc/kvm: HPT resizing stub implementation David Gibson
` (9 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
The KVM_PPC_ALLOCATE_HTAB ioctl() is used to set the size of hashed page
table (HPT) that userspace expects a guest VM to have, and is also used to
clear that HPT when necessary (e.g. guest reboot).
At present, once the ioctl() is called for the first time, the HPT size can
never be changed thereafter - it will be cleared but always sized as from
the first call.
With upcoming HPT resize implementation, we're going to need to allow
userspace to resize the HPT at reset (to change it back to the default size
if the guest changed it).
So, we need to allow this ioctl() to change the HPT size.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_ppc.h | 2 +-
arch/powerpc/kvm/book3s_64_mmu_hv.c | 52 ++++++++++++++++++++-----------------
arch/powerpc/kvm/book3s_hv.c | 5 +---
3 files changed, 30 insertions(+), 29 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index f77d0a0..bc7a104 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -157,7 +157,7 @@ extern void kvmppc_map_magic(struct kvm_vcpu *vcpu);
extern int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order);
extern void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info);
-extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp);
+extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order);
extern void kvmppc_free_hpt(struct kvm_hpt_info *info);
extern long kvmppc_prepare_vrma(struct kvm *kvm,
struct kvm_userspace_memory_region *mem);
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index eb1aa3a..4547b6e 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -103,10 +103,22 @@ void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info)
info->virt, (long)info->order, kvm->arch.lpid);
}
-long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
+void kvmppc_free_hpt(struct kvm_hpt_info *info)
+{
+ vfree(info->rev);
+ if (info->cma)
+ kvm_free_hpt_cma(virt_to_page(info->virt),
+ 1 << (info->order - PAGE_SHIFT));
+ else
+ free_pages(info->virt, info->order - PAGE_SHIFT);
+ info->virt = 0;
+ info->order = 0;
+}
+
+long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
{
long err = -EBUSY;
- long order;
+ struct kvm_hpt_info info;
mutex_lock(&kvm->lock);
if (kvm->arch.hpte_setup_done) {
@@ -118,8 +130,9 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
goto out;
}
}
- if (kvm->arch.hpt.virt) {
- order = kvm->arch.hpt.order;
+ if (kvm->arch.hpt.order == order) {
+ /* We already have a suitable HPT */
+
/* Set the entire HPT to 0, i.e. invalid HPTEs */
memset((void *)kvm->arch.hpt.virt, 0, 1ul << order);
/*
@@ -128,33 +141,24 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
kvmppc_rmap_reset(kvm);
/* Ensure that each vcpu will flush its TLB on next entry. */
cpumask_setall(&kvm->arch.need_tlb_flush);
- *htab_orderp = order;
err = 0;
- } else {
- struct kvm_hpt_info info;
-
- err = kvmppc_allocate_hpt(&info, *htab_orderp);
- if (err < 0)
- goto out;
- kvmppc_set_hpt(kvm, &info);
+ goto out;
}
+
+ if (kvm->arch.hpt.virt)
+ kvmppc_free_hpt(&kvm->arch.hpt);
+
+
+ err = kvmppc_allocate_hpt(&info, order);
+ if (err < 0)
+ goto out;
+ kvmppc_set_hpt(kvm, &info);
+
out:
mutex_unlock(&kvm->lock);
return err;
}
-void kvmppc_free_hpt(struct kvm_hpt_info *info)
-{
- vfree(info->rev);
- if (info->cma)
- kvm_free_hpt_cma(virt_to_page(info->virt),
- 1 << (info->order - PAGE_SHIFT));
- else
- free_pages(info->virt, info->order - PAGE_SHIFT);
- info->virt = 0;
- info->order = 0;
-}
-
/* Bits in first HPTE dword for pagesize 4k, 64k or 16M */
static inline unsigned long hpte0_pgsize_encoding(unsigned long pgsize)
{
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 1199fb5..a2730ca 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3113,12 +3113,9 @@ static long kvm_arch_vm_ioctl_hv(struct file *filp,
r = -EFAULT;
if (get_user(htab_order, (u32 __user *)argp))
break;
- r = kvmppc_alloc_reset_hpt(kvm, &htab_order);
+ r = kvmppc_alloc_reset_hpt(kvm, htab_order);
if (r)
break;
- r = -EFAULT;
- if (put_user(htab_order, (u32 __user *)argp))
- break;
r = 0;
break;
}
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 16/25] powerpc/kvm: HPT resizing stub implementation
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (14 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 15/25] powerpc/kvm: Allow KVM_PPC_ALLOCATE_HTAB ioctl() to change HPT size David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 17/25] powerpc/kvm: Advertise availablity of HPT resizing on KVM HV David Gibson
` (8 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This patch adds a stub (always failing) implementation of the hypercalls
for the HPT resizing PAPR extension.
For now we include a hack which makes it safe for qemu to call ENABLE_HCALL
on these hypercalls, although it will have no effect. That should go away
once the PAPR change is formalized and we can use "real" hcall numbers.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_book3s.h | 6 ++++++
arch/powerpc/kvm/book3s_64_mmu_hv.c | 19 +++++++++++++++++++
arch/powerpc/kvm/book3s_hv.c | 8 ++++++++
arch/powerpc/kvm/powerpc.c | 6 ++++++
4 files changed, 39 insertions(+)
diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h
index 8f39796..81f2b77 100644
--- a/arch/powerpc/include/asm/kvm_book3s.h
+++ b/arch/powerpc/include/asm/kvm_book3s.h
@@ -191,6 +191,12 @@ extern void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
struct kvm_vcpu *vcpu);
extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
struct kvmppc_book3s_shadow_vcpu *svcpu);
+extern unsigned long do_h_resize_hpt_prepare(struct kvm_vcpu *vcpu,
+ unsigned long flags,
+ unsigned long shift);
+extern unsigned long do_h_resize_hpt_commit(struct kvm_vcpu *vcpu,
+ unsigned long flags,
+ unsigned long shift);
static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
{
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 4547b6e..b92384f 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -1118,6 +1118,25 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va, unsigned long gpa,
}
/*
+ * HPT resizing
+ */
+
+unsigned long do_h_resize_hpt_prepare(struct kvm_vcpu *vcpu,
+ unsigned long flags,
+ unsigned long shift)
+{
+ return H_HARDWARE;
+}
+
+unsigned long do_h_resize_hpt_commit(struct kvm_vcpu *vcpu,
+ unsigned long flags,
+ unsigned long shift)
+{
+ return H_HARDWARE;
+}
+
+
+/*
* Functions for reading and writing the hash table via reads and
* writes on a file descriptor.
*
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index a2730ca..5a451f8 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -726,6 +726,14 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
kvmppc_get_gpr(vcpu, 5),
kvmppc_get_gpr(vcpu, 6));
break;
+ case H_RESIZE_HPT_PREPARE:
+ ret = do_h_resize_hpt_prepare(vcpu, kvmppc_get_gpr(vcpu, 4),
+ kvmppc_get_gpr(vcpu, 5));
+ break;
+ case H_RESIZE_HPT_COMMIT:
+ ret = do_h_resize_hpt_commit(vcpu, kvmppc_get_gpr(vcpu, 4),
+ kvmppc_get_gpr(vcpu, 5));
+ break;
case H_RTAS:
if (list_empty(&vcpu->kvm->arch.rtas_tokens))
return RESUME_HOST;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index a4250f1..eeda4a8 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -1287,6 +1287,12 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
unsigned long hcall = cap->args[0];
r = -EINVAL;
+ /* Hack: until we have proper hcall numbers allocated */
+ if ((hcall == H_RESIZE_HPT_PREPARE)
+ || (hcall == H_RESIZE_HPT_COMMIT)) {
+ r = 0;
+ break;
+ }
if (hcall > MAX_HCALL_OPCODE || (hcall & 3) ||
cap->args[1] > 1)
break;
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 17/25] powerpc/kvm: Advertise availablity of HPT resizing on KVM HV
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (15 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 16/25] powerpc/kvm: HPT resizing stub implementation David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 18/25] powerpc/kvm: Outline of HPT resizing implementation David Gibson
` (7 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This updates the KVM_CAP_SPAPR_RESIZE_HPT capability to advertise the
presence of in-kernel HPT resizing on KVM HV. In fact the HPT resizing
isn't fully implemented, but this allows us to experiment with what's
there.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/powerpc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index eeda4a8..2314059 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -573,7 +573,10 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
r = 1;
break;
case KVM_CAP_SPAPR_RESIZE_HPT:
- r = 1; /* resize allowed only if HPT is outside kernel */
+ if (hv_enabled)
+ r = 2; /* In-kernel resize implementation */
+ else
+ r = 1; /* outside kernel resize allowed */
break;
#endif
default:
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 18/25] powerpc/kvm: Outline of HPT resizing implementation
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (16 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 17/25] powerpc/kvm: Advertise availablity of HPT resizing on KVM HV David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 19/25] powerpc/kbm: Allocations for HPT resizing David Gibson
` (6 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This adds an outline (not yet working) of an implementation for the HPT
resizing PAPR extension. Specifically it adds the work function which will
see through the resizing workflow, and adds in the synchronization between
this and the HPT resizing hypercalls.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_host.h | 5 +
arch/powerpc/kvm/book3s_64_mmu_hv.c | 276 +++++++++++++++++++++++++++++++++++-
arch/powerpc/kvm/book3s_hv.c | 6 +
3 files changed, 285 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 718dc56..ef5b444 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -230,6 +230,8 @@ struct kvm_hpt_info {
int cma;
};
+struct kvm_resize_hpt;
+
struct kvm_arch {
unsigned int lpid;
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
@@ -248,6 +250,9 @@ struct kvm_arch {
cpumask_t need_tlb_flush;
struct dentry *debugfs_dir;
struct dentry *htab_dentry;
+ struct kvm_resize_hpt *resize_hpt; /* protected by kvm->mmu_lock */
+ struct mutex resize_hpt_mutex;
+ wait_queue_head_t resize_hpt_wq;
#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
struct mutex hpt_mutex;
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index b92384f..ee50e46 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -40,6 +40,56 @@
#include "trace_hv.h"
+#define DEBUG_RESIZE_HPT 1
+
+
+struct kvm_resize_hpt {
+ /* These fields are read-only after initialization */
+ struct kvm *kvm;
+ struct work_struct work;
+ u32 order;
+
+ /* These fields protected by kvm->mmu_lock */
+ unsigned long state;
+ /* Prepare completed, or failed */
+#define RESIZE_HPT_PREPARED (1UL << 1)
+ /* Something failed in work thread */
+#define RESIZE_HPT_FAILED (1UL << 2)
+ /* New HPT is active */
+#define RESIZE_HPT_COMMITTED (1UL << 3)
+
+ /* H_COMMIT hypercall has started */
+#define RESIZE_HPT_COMMIT (1UL << 16)
+ /* Cancelled */
+#define RESIZE_HPT_CANCEL (1UL << 17)
+ /* All done, state can be free()d */
+#define RESIZE_HPT_FREE (1UL << 18)
+
+ /* Private to the work thread, until RESIZE_HPT_FAILED is set,
+ * thereafter read-only */
+ int error;
+};
+
+#ifdef DEBUG_RESIZE_HPT
+#define resize_hpt_debug(resize, ...) \
+ do { \
+ printk(KERN_DEBUG "RESIZE HPT %p: ", resize); \
+ printk(__VA_ARGS__); \
+ } while (0)
+#else
+#define resize_hpt_debug(resize, ...) \
+ do { } while (0)
+#endif
+
+static void resize_hpt_set_state(struct kvm_resize_hpt *resize,
+ unsigned long newstate)
+{
+ struct kvm *kvm = resize->kvm;
+
+ resize->state |= newstate;
+ wake_up_all(&kvm->arch.resize_hpt_wq);
+}
+
static long kvmppc_virtmode_do_h_enter(struct kvm *kvm, unsigned long flags,
long pte_index, unsigned long pteh,
unsigned long ptel, unsigned long *pte_idx_ret);
@@ -1120,19 +1170,241 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va, unsigned long gpa,
/*
* HPT resizing
*/
+static int resize_hpt_allocate(struct kvm_resize_hpt *resize,
+ struct kvm_memslots *slots)
+{
+ return H_SUCCESS;
+}
+
+static int resize_hpt_rehash(struct kvm_resize_hpt *resize)
+{
+ return H_HARDWARE;
+}
+
+static void resize_hpt_pivot(struct kvm_resize_hpt *resize,
+ struct kvm_memslots *slots)
+{
+}
+
+static void resize_hpt_flush_rmaps(struct kvm_resize_hpt *resize,
+ struct kvm_memslots *slots)
+{
+}
+
+static void resize_hpt_free(struct kvm_resize_hpt *resize)
+{
+}
+
+static void resize_hpt_work(struct work_struct *work)
+{
+ struct kvm_resize_hpt *resize = container_of(work,
+ struct kvm_resize_hpt,
+ work);
+ struct kvm *kvm = resize->kvm;
+ struct kvm_memslots *slots;
+
+ resize_hpt_debug(resize, "Starting work, order = %d\n", resize->order);
+
+ mutex_lock(&kvm->arch.resize_hpt_mutex);
+
+ /* Don't want to have memslots change under us */
+ mutex_lock(&kvm->slots_lock);
+
+ slots = kvm_memslots(kvm);
+
+ resize->error = resize_hpt_allocate(resize, slots);
+ spin_lock(&kvm->mmu_lock);
+
+ if (resize->error || (resize->state & RESIZE_HPT_CANCEL))
+ goto out;
+
+ resize_hpt_set_state(resize, RESIZE_HPT_PREPARED);
+
+ spin_unlock(&kvm->mmu_lock);
+ /* Unlocked access to state is safe here, because the bit can
+ * only transition 0->1 */
+ wait_event(kvm->arch.resize_hpt_wq,
+ resize->state & (RESIZE_HPT_COMMIT | RESIZE_HPT_CANCEL));
+ spin_lock(&kvm->mmu_lock);
+
+ if (resize->state & RESIZE_HPT_CANCEL)
+ goto out;
+
+ spin_unlock(&kvm->mmu_lock);
+ resize->error = resize_hpt_rehash(resize);
+ spin_lock(&kvm->mmu_lock);
+
+ if (resize->error || (resize->state & RESIZE_HPT_CANCEL))
+ goto out;
+
+ resize_hpt_pivot(resize, slots);
+
+ resize_hpt_set_state(resize, RESIZE_HPT_COMMITTED);
+
+ BUG_ON((resize->state & RESIZE_HPT_CANCEL)
+ || (kvm->arch.resize_hpt != resize));
+
+ spin_unlock(&kvm->mmu_lock);
+ resize_hpt_flush_rmaps(resize, slots);
+ spin_lock(&kvm->mmu_lock);
+
+ BUG_ON((resize->state & RESIZE_HPT_CANCEL)
+ || (kvm->arch.resize_hpt != resize));
+
+ kvm->arch.resize_hpt = NULL;
+
+out:
+ if (resize->error != H_SUCCESS)
+ resize_hpt_set_state(resize, RESIZE_HPT_FAILED);
+
+ spin_unlock(&kvm->mmu_lock);
+
+ mutex_unlock(&kvm->slots_lock);
+
+ mutex_unlock(&kvm->arch.resize_hpt_mutex);
+
+ resize_hpt_free(resize);
+
+ /* Unlocked access to state is safe here, because the bit can
+ * only transition 0->1 */
+ wait_event(kvm->arch.resize_hpt_wq,
+ resize->state & RESIZE_HPT_FREE);
+
+ kfree(resize);
+}
unsigned long do_h_resize_hpt_prepare(struct kvm_vcpu *vcpu,
unsigned long flags,
unsigned long shift)
{
- return H_HARDWARE;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_resize_hpt *resize;
+ int ret;
+
+ if (flags != 0)
+ return H_PARAMETER;
+
+ if (shift && ((shift < 18) || (shift > 46)))
+ return H_PARAMETER;
+
+ // FIXME: resources limit of some sort
+
+ spin_lock(&kvm->mmu_lock);
+
+retry:
+ resize = kvm->arch.resize_hpt;
+
+ if (resize) {
+ if (resize->state & RESIZE_HPT_COMMITTED) {
+ /* Can't cancel a committed resize, have to
+ * wait for it to complete */
+ ret = H_BUSY;
+ goto out;
+ }
+
+ if (resize->order == shift) {
+ /* Suitable resize in progress */
+ if (resize->state & RESIZE_HPT_FAILED) {
+ ret = resize->error;
+ kvm->arch.resize_hpt = NULL;
+ resize_hpt_set_state(resize, RESIZE_HPT_FREE);
+ } else if (resize->state & RESIZE_HPT_PREPARED) {
+ ret = H_SUCCESS;
+ } else {
+ ret = H_LONG_BUSY_ORDER_100_MSEC;
+ }
+
+ goto out;
+ }
+
+ /* not suitable, cancel it */
+ kvm->arch.resize_hpt = NULL;
+ resize_hpt_set_state(resize,
+ RESIZE_HPT_CANCEL | RESIZE_HPT_FREE);
+ }
+
+ spin_unlock(&kvm->mmu_lock);
+
+ if (!shift)
+ return H_SUCCESS; /* nothing to do */
+
+ /* start new resize */
+
+ resize = kmalloc(sizeof(*resize), GFP_KERNEL);
+ resize->order = shift;
+ resize->kvm = kvm;
+ resize->state = 0;
+ INIT_WORK(&resize->work, resize_hpt_work);
+
+ schedule_work(&resize->work);
+
+ spin_lock(&kvm->mmu_lock);
+
+ if (kvm->arch.resize_hpt) {
+ /* Race with another H_PREPARE */
+ resize_hpt_set_state(resize,
+ RESIZE_HPT_CANCEL | RESIZE_HPT_FREE);
+ goto retry;
+ }
+
+ kvm->arch.resize_hpt = resize;
+
+ ret = H_LONG_BUSY_ORDER_100_MSEC;
+
+out:
+ spin_unlock(&kvm->mmu_lock);
+ return ret;
}
unsigned long do_h_resize_hpt_commit(struct kvm_vcpu *vcpu,
unsigned long flags,
unsigned long shift)
{
- return H_HARDWARE;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_resize_hpt *resize;
+ long ret;
+
+ if (flags != 0)
+ return H_PARAMETER;
+
+ if (shift && ((shift < 18) || (shift > 46)))
+ return H_PARAMETER;
+
+ spin_lock(&kvm->mmu_lock);
+
+ resize = kvm->arch.resize_hpt;
+
+ ret = H_NOT_ACTIVE;
+ if (!resize || (resize->order != shift))
+ goto out;
+
+ resize_hpt_set_state(resize, RESIZE_HPT_COMMIT);
+
+ spin_unlock(&kvm->mmu_lock);
+ /* Unlocked read of resize->state here is safe, because the
+ * bits can only ever transition 0->1 */
+ wait_event(kvm->arch.resize_hpt_wq,
+ (resize->state & (RESIZE_HPT_COMMITTED | RESIZE_HPT_FAILED
+ | RESIZE_HPT_CANCEL)));
+
+ spin_lock(&kvm->mmu_lock);
+
+ if (resize->state & RESIZE_HPT_CANCEL) {
+ BUG_ON(!(resize->state & RESIZE_HPT_FREE));
+ ret = H_CLOSED;
+ goto out;
+ }
+
+ if (resize->state & RESIZE_HPT_FAILED)
+ ret = resize->error;
+ else
+ ret = H_SUCCESS;
+
+ resize_hpt_set_state(resize, RESIZE_HPT_FREE);
+
+out:
+ spin_unlock(&kvm->mmu_lock);
+ return ret;
}
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 5a451f8..7fcd45d 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3041,6 +3041,12 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
lpcr |= LPCR_ONL;
kvm->arch.lpcr = lpcr;
+
+ /* Initialization for future HPT resizes */
+ kvm->arch.resize_hpt = NULL;
+ mutex_init(&kvm->arch.resize_hpt_mutex);
+ init_waitqueue_head(&kvm->arch.resize_hpt_wq);
+
/*
* Track that we now have a HV mode VM active. This blocks secondary
* CPU threads from coming online.
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 19/25] powerpc/kbm: Allocations for HPT resizing
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (17 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 18/25] powerpc/kvm: Outline of HPT resizing implementation David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 20/25] powerpc/kvm: Make MMU notifier handlers more flexible David Gibson
` (5 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This adds code to initialize an HPT resize operation, including allocating
a tentative new HPT and reverse maps. It also includes corresponding code
to free things afterwards.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 42 +++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index ee50e46..d2f04ee 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -68,6 +68,13 @@ struct kvm_resize_hpt {
/* Private to the work thread, until RESIZE_HPT_FAILED is set,
* thereafter read-only */
int error;
+
+ /* Private to the work thread, until RESIZE_HPT_PREPARED, then
+ * protected by kvm->mmu_lock until the resize struct is
+ * unlinked from struct kvm, then private to the work thread
+ * again */
+ struct kvm_hpt_info hpt;
+ unsigned long *rmap[KVM_USER_MEM_SLOTS];
};
#ifdef DEBUG_RESIZE_HPT
@@ -1173,6 +1180,31 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va, unsigned long gpa,
static int resize_hpt_allocate(struct kvm_resize_hpt *resize,
struct kvm_memslots *slots)
{
+ struct kvm_memory_slot *memslot;
+ int rc;
+
+ rc = kvmppc_allocate_hpt(&resize->hpt, resize->order);
+ if (rc == -ENOMEM)
+ return H_NO_MEM;
+ else if (rc < 0)
+ return H_HARDWARE;
+
+ resize_hpt_debug(resize, "HPT @ 0x%lx\n", resize->hpt.virt);
+
+ kvm_for_each_memslot(memslot, slots) {
+ unsigned long *rmap;
+
+ if (memslot->flags & KVM_MEMSLOT_INVALID)
+ continue;
+
+ rmap = vzalloc(memslot->npages * sizeof(*rmap));
+ if (!rmap)
+ return H_NO_MEM;
+ resize->rmap[memslot->id] = rmap;
+ resize_hpt_debug(resize, "Memslot %d (%lu pages): %p\n",
+ memslot->id, memslot->npages, rmap);
+ }
+
return H_SUCCESS;
}
@@ -1193,6 +1225,13 @@ static void resize_hpt_flush_rmaps(struct kvm_resize_hpt *resize,
static void resize_hpt_free(struct kvm_resize_hpt *resize)
{
+ int i;
+ if (resize->hpt.virt)
+ kvmppc_free_hpt(&resize->hpt);
+
+ for (i = 0; i < KVM_USER_MEM_SLOTS; i++)
+ if (resize->rmap[i])
+ vfree(resize->rmap[i]);
}
static void resize_hpt_work(struct work_struct *work)
@@ -1205,6 +1244,9 @@ static void resize_hpt_work(struct work_struct *work)
resize_hpt_debug(resize, "Starting work, order = %d\n", resize->order);
+ memset(&resize->hpt, 0, sizeof(resize->hpt));
+ memset(&resize->rmap, 0, sizeof(resize->rmap));
+
mutex_lock(&kvm->arch.resize_hpt_mutex);
/* Don't want to have memslots change under us */
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 20/25] powerpc/kvm: Make MMU notifier handlers more flexible
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (18 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 19/25] powerpc/kbm: Allocations for HPT resizing David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 21/25] powerpc/kvm: Make MMU notifiers HPT resize aware David Gibson
` (4 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
KVM on powerpc uses several MMU notifiers to update guest page tables and
reverse mappings based on host MM events. At these always act on the
guest's main active hash table and reverse mappings.
However, for HPT resizing we're going to need these to sometimes operate
on a tentative hash table or reverse mapping for an in-progress or
recently completed resize.
To allow that, extend the MMU notifier helper functions to take extra
parameters for the HPT to operate on.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 65 +++++++++++++++++++++++++------------
1 file changed, 44 insertions(+), 21 deletions(-)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index d2f04ee..db070ad 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -720,14 +720,38 @@ static void kvmppc_rmap_reset(struct kvm *kvm)
srcu_read_unlock(&kvm->srcu, srcu_idx);
}
+static int kvm_handle_hva_range_slot(struct kvm *kvm,
+ struct kvm_hpt_info *hpt,
+ struct kvm_memory_slot *memslot,
+ unsigned long *rmap,
+ gfn_t gfn_start, gfn_t gfn_end,
+ int (*handler)(struct kvm *kvm,
+ struct kvm_hpt_info *hpt,
+ unsigned long *rmapp,
+ unsigned long gfn))
+{
+ int ret;
+ int retval = 0;
+ gfn_t gfn;
+
+ for (gfn = gfn_start; gfn < gfn_end; ++gfn) {
+ gfn_t gfn_offset = gfn - memslot->base_gfn;
+
+ ret = handler(kvm, hpt, &rmap[gfn_offset], gfn);
+ retval |= ret;
+ }
+
+ return retval;
+}
+
static int kvm_handle_hva_range(struct kvm *kvm,
unsigned long start,
unsigned long end,
int (*handler)(struct kvm *kvm,
+ struct kvm_hpt_info *hpt,
unsigned long *rmapp,
unsigned long gfn))
{
- int ret;
int retval = 0;
struct kvm_memslots *slots;
struct kvm_memory_slot *memslot;
@@ -749,28 +773,27 @@ static int kvm_handle_hva_range(struct kvm *kvm,
gfn = hva_to_gfn_memslot(hva_start, memslot);
gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot);
- for (; gfn < gfn_end; ++gfn) {
- gfn_t gfn_offset = gfn - memslot->base_gfn;
-
- ret = handler(kvm, &memslot->arch.rmap[gfn_offset], gfn);
- retval |= ret;
- }
+ retval |= kvm_handle_hva_range_slot(kvm, &kvm->arch.hpt,
+ memslot, memslot->arch.rmap,
+ gfn, gfn_end, handler);
}
return retval;
}
static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
- int (*handler)(struct kvm *kvm, unsigned long *rmapp,
+ int (*handler)(struct kvm *kvm,
+ struct kvm_hpt_info *hpt,
+ unsigned long *rmapp,
unsigned long gfn))
{
return kvm_handle_hva_range(kvm, hva, hva + 1, handler);
}
-static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
- unsigned long gfn)
+static int kvm_unmap_rmapp(struct kvm *kvm, struct kvm_hpt_info *hpt,
+ unsigned long *rmapp, unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.hpt.rev;
+ struct revmap_entry *rev = hpt->rev;
unsigned long h, i, j;
__be64 *hptep;
unsigned long ptel, psize, rcbits;
@@ -788,7 +811,7 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
* rmap chain lock.
*/
i = *rmapp & KVMPPC_RMAP_INDEX;
- hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
+ hptep = (__be64 *) (hpt->virt + (i << 4));
if (!try_lock_hpte(hptep, HPTE_V_HVLOCK)) {
/* unlock rmap before spinning on the HPTE lock */
unlock_rmap(rmapp);
@@ -861,16 +884,16 @@ void kvmppc_core_flush_memslot_hv(struct kvm *kvm,
* thus the present bit can't go from 0 to 1.
*/
if (*rmapp & KVMPPC_RMAP_PRESENT)
- kvm_unmap_rmapp(kvm, rmapp, gfn);
+ kvm_unmap_rmapp(kvm, &kvm->arch.hpt, rmapp, gfn);
++rmapp;
++gfn;
}
}
-static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
- unsigned long gfn)
+static int kvm_age_rmapp(struct kvm *kvm, struct kvm_hpt_info *hpt,
+ unsigned long *rmapp, unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.hpt.rev;
+ struct revmap_entry *rev = hpt->rev;
unsigned long head, i, j;
__be64 *hptep;
int ret = 0;
@@ -888,7 +911,7 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
i = head = *rmapp & KVMPPC_RMAP_INDEX;
do {
- hptep = (__be64 *) (kvm->arch.hpt.virt + (i << 4));
+ hptep = (__be64 *) (hpt->virt + (i << 4));
j = rev[i].forw;
/* If this HPTE isn't referenced, ignore it */
@@ -925,10 +948,10 @@ int kvm_age_hva_hv(struct kvm *kvm, unsigned long start, unsigned long end)
return kvm_handle_hva_range(kvm, start, end, kvm_age_rmapp);
}
-static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
- unsigned long gfn)
+static int kvm_test_age_rmapp(struct kvm *kvm, struct kvm_hpt_info *hpt,
+ unsigned long *rmapp, unsigned long gfn)
{
- struct revmap_entry *rev = kvm->arch.hpt.rev;
+ struct revmap_entry *rev = hpt->rev;
unsigned long head, i, j;
unsigned long *hp;
int ret = 1;
@@ -943,7 +966,7 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
if (*rmapp & KVMPPC_RMAP_PRESENT) {
i = head = *rmapp & KVMPPC_RMAP_INDEX;
do {
- hp = (unsigned long *)(kvm->arch.hpt.virt + (i << 4));
+ hp = (unsigned long *)(hpt->virt + (i << 4));
j = rev[i].forw;
if (be64_to_cpu(hp[1]) & HPTE_R_R)
goto out;
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 21/25] powerpc/kvm: Make MMU notifiers HPT resize aware
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (19 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 20/25] powerpc/kvm: Make MMU notifier handlers more flexible David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:08 ` [RFCv2 22/25] powerpc/kvm: Exclude HPT resizes when collecting the dirty log David Gibson
` (3 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
While an HPT resize operation is in progress, specifically when a tentative
HPT has been allocated and we are possibly in the middle of populating it
various host side MM events need to be reflected in the tentative resized
HPT as well as the currently active one.
This extends the powerpc KVM MMU notifiers to act on both the active and
tentative HPTs (and reverse maps) when there is an active resize in
progress.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index db070ad..5b84347 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -97,6 +97,17 @@ static void resize_hpt_set_state(struct kvm_resize_hpt *resize,
wake_up_all(&kvm->arch.resize_hpt_wq);
}
+static struct kvm_resize_hpt *kvm_active_resize_hpt(struct kvm *kvm)
+{
+ struct kvm_resize_hpt *resize = kvm->arch.resize_hpt;
+
+ if (resize && (resize->state & RESIZE_HPT_PREPARED)
+ && !(resize->state & RESIZE_HPT_FAILED))
+ return resize;
+
+ return NULL;
+}
+
static long kvmppc_virtmode_do_h_enter(struct kvm *kvm, unsigned long flags,
long pte_index, unsigned long pteh,
unsigned long ptel, unsigned long *pte_idx_ret);
@@ -755,6 +766,7 @@ static int kvm_handle_hva_range(struct kvm *kvm,
int retval = 0;
struct kvm_memslots *slots;
struct kvm_memory_slot *memslot;
+ struct kvm_resize_hpt *resize = kvm_active_resize_hpt(kvm);
slots = kvm_memslots(kvm);
kvm_for_each_memslot(memslot, slots) {
@@ -776,6 +788,10 @@ static int kvm_handle_hva_range(struct kvm *kvm,
retval |= kvm_handle_hva_range_slot(kvm, &kvm->arch.hpt,
memslot, memslot->arch.rmap,
gfn, gfn_end, handler);
+ if (resize)
+ retval |= kvm_handle_hva_range_slot(kvm, &resize->hpt,
+ memslot, resize->rmap[memslot->id],
+ gfn, gfn_end, handler);
}
return retval;
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 22/25] powerpc/kvm: Exclude HPT resizes when collecting the dirty log
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (20 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 21/25] powerpc/kvm: Make MMU notifiers HPT resize aware David Gibson
@ 2016-03-08 3:08 ` David Gibson
2016-03-08 3:09 ` [RFCv2 23/25] powerpc/kvm: Rehashing for HPT resizing David Gibson
` (2 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:08 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
While there is an active HPT resize in progress, working out which guest
pages are dirty is rather more complicated, because depending on exactly
the phase of the resize the information could be in either the current,
tentative or previous HPT or reverse map of the guest.
To avoid this problem, for now we just exclude collecting the dirty map
while a resize is in progress, blocking the dirty map operation until the
resize is complete.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 5b84347..c4c1814 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -1128,6 +1128,7 @@ long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot,
unsigned long *rmapp;
struct kvm_vcpu *vcpu;
+ mutex_lock(&kvm->arch.resize_hpt_mutex); /* exclude a concurrent HPT resize */
preempt_disable();
rmapp = memslot->arch.rmap;
for (i = 0; i < memslot->npages; ++i) {
@@ -1152,6 +1153,7 @@ long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot,
spin_unlock(&vcpu->arch.vpa_update_lock);
}
preempt_enable();
+ mutex_unlock(&kvm->arch.resize_hpt_mutex);
return 0;
}
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 23/25] powerpc/kvm: Rehashing for HPT resizing
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (21 preceding siblings ...)
2016-03-08 3:08 ` [RFCv2 22/25] powerpc/kvm: Exclude HPT resizes when collecting the dirty log David Gibson
@ 2016-03-08 3:09 ` David Gibson
2016-03-08 3:09 ` [RFCv2 24/25] powerpc/kvm: HPT resize pivot David Gibson
2016-03-08 3:09 ` [RFCv2 25/25] powerpc/kvm: Harvest RC bits from old HPT after HPT resize David Gibson
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:09 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This adds code for the "guts" of an HPT resize operation: rehashing HPTEs
from the current HPT into the new resized HPT.
This is performed by the HPT resize work thread, but is gated to occur only
while the guest is executing the H_RESIZE_HPT_COMMIT hypercall. The guest
is expected not to modify or use the hash table during this period which
simplifies things somewhat (Linux guests do this with stop_machine()).
However, there are still host processes active which could affect the guest
so there's still some hairy synchronization.
To reduce the amount of stuff we need to do (and thus the latency of the
operation) we only rehash bolted entries, expecting the guest to refault
other HPTEs after the resize is complete.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/include/asm/kvm_book3s.h | 6 +-
arch/powerpc/kvm/book3s_64_mmu_hv.c | 166 +++++++++++++++++++++++++++++++++-
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 10 +-
3 files changed, 173 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h
index 81f2b77..935fbba 100644
--- a/arch/powerpc/include/asm/kvm_book3s.h
+++ b/arch/powerpc/include/asm/kvm_book3s.h
@@ -156,8 +156,10 @@ extern void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr);
extern int kvmppc_emulate_paired_single(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern kvm_pfn_t kvmppc_gpa_to_pfn(struct kvm_vcpu *vcpu, gpa_t gpa,
bool writing, bool *writable);
-extern void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
- unsigned long *rmap, long pte_index, int realmode);
+extern void kvmppc_add_revmap_chain(struct kvm_hpt_info *hpt,
+ struct revmap_entry *rev,
+ unsigned long *rmap,
+ long pte_index, int realmode);
extern void kvmppc_update_rmap_change(unsigned long *rmap, unsigned long psize);
extern void kvmppc_invalidate_hpte(struct kvm *kvm, __be64 *hptep,
unsigned long pte_index);
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index c4c1814..d06aef6 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -681,7 +681,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
/* don't lose previous R and C bits */
r |= be64_to_cpu(hptep[1]) & (HPTE_R_R | HPTE_R_C);
} else {
- kvmppc_add_revmap_chain(kvm, rev, rmap, index, 0);
+ kvmppc_add_revmap_chain(&kvm->arch.hpt, rev, rmap, index, 0);
}
hptep[1] = cpu_to_be64(r);
@@ -1249,9 +1249,171 @@ static int resize_hpt_allocate(struct kvm_resize_hpt *resize,
return H_SUCCESS;
}
+static unsigned long resize_hpt_rehash_hpte(struct kvm *kvm,
+ struct kvm_resize_hpt *resize,
+ unsigned long pteg, int slot)
+{
+
+ struct kvm_hpt_info *old = &kvm->arch.hpt;
+ struct kvm_hpt_info *new = &resize->hpt;
+ unsigned long old_idx = pteg * HPTES_PER_GROUP + slot;
+ unsigned long new_idx;
+ __be64 *hptep, *new_hptep;
+ unsigned long old_hash_mask = (1ULL << (old->order - 7)) - 1;
+ unsigned long new_hash_mask = (1ULL << (new->order - 7)) - 1;
+ unsigned long pte0, pte1, guest_pte1;
+ unsigned long avpn;
+ unsigned long psize, a_psize;
+ unsigned long hash, new_pteg, replace_pte0;
+ unsigned long gpa, gfn;
+ struct kvm_memory_slot *memslot;
+ struct revmap_entry *new_rev;
+ unsigned long mmu_seq;
+
+ mmu_seq = kvm->mmu_notifier_seq;
+ smp_rmb();
+
+ hptep = (__be64 *)(old->virt + (old_idx << 4));
+ if (!try_lock_hpte(hptep, HPTE_V_HVLOCK))
+ return H_HARDWARE;
+
+ pte0 = be64_to_cpu(hptep[0]);
+ pte1 = be64_to_cpu(hptep[1]);
+ guest_pte1 = old->rev[old_idx].guest_rpte;
+
+ unlock_hpte(hptep, pte0);
+
+ if (!(pte0 & HPTE_V_VALID) && !(pte0 & HPTE_V_ABSENT))
+ /* Nothing to do */
+ return H_SUCCESS;
+
+ if (!(pte0 & HPTE_V_BOLTED))
+ /* Don't bother rehashing non-bolted HPTEs */
+ return H_SUCCESS;
+
+ pte1 = be64_to_cpu(hptep[1]);
+ psize = hpte_base_page_size(pte0, pte1);
+ if (WARN_ON(!psize))
+ return H_HARDWARE;
+
+ avpn = HPTE_V_AVPN_VAL(pte0) & ~((psize - 1) >> 23);
+
+ if (pte0 & HPTE_V_SECONDARY)
+ pteg = ~pteg;
+
+ if (!(pte0 & HPTE_V_1TB_SEG)) {
+ unsigned long offset, vsid;
+
+ /* We only have 28 - 23 bits of offset in avpn */
+ offset = (avpn & 0x1f) << 23;
+ vsid = avpn >> 5;
+ /* We can find more bits from the pteg value */
+ if (psize < (1ULL << 23))
+ offset |= ((vsid ^ pteg) & old_hash_mask) * psize;
+
+ hash = vsid ^ (offset / psize);
+ } else {
+ unsigned long offset, vsid;
+
+ /* We only have 40 - 23 bits of seg_off in avpn */
+ offset = (avpn & 0x1ffff) << 23;
+ vsid = avpn >> 17;
+ if (psize < (1ULL << 23))
+ offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) * psize;
+
+ hash = vsid ^ (vsid << 25) ^ (offset / psize);
+ }
+
+ new_pteg = hash & new_hash_mask;
+ if (pte0 & HPTE_V_SECONDARY) {
+ BUG_ON(~pteg != (hash & old_hash_mask));
+ new_pteg = ~new_pteg;
+ } else {
+ BUG_ON(pteg != (hash & old_hash_mask));
+ }
+
+ new_idx = new_pteg * HPTES_PER_GROUP + slot;
+ new_hptep = (__be64 *)(new->virt + (new_idx << 4));
+ replace_pte0 = be64_to_cpu(new_hptep[0]);
+
+ if (replace_pte0 & HPTE_V_VALID) {
+ BUG_ON(new->order >= old->order);
+
+ if (replace_pte0 & HPTE_V_BOLTED) {
+ if (pte0 & HPTE_V_BOLTED)
+ /* Bolted collision, nothing we can do */
+ return H_PTEG_FULL;
+ else
+ /* Discard this hpte */
+ return H_SUCCESS;
+ }
+ // FIXME: clean up old HPTE
+ BUG();
+ }
+
+ /* Update the rmap */
+ new_rev = &new->rev[new_idx];
+ new_rev->guest_rpte = guest_pte1;
+
+ a_psize = hpte_page_size(pte0, pte1);
+ gpa = (guest_pte1 & HPTE_R_RPN) & ~(a_psize - 1);
+ gfn = gpa >> PAGE_SHIFT;
+ memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn);
+ if (memslot && !(memslot->flags & KVM_MEMSLOT_INVALID)) {
+ unsigned long *old_rmap =
+ &memslot->arch.rmap[gfn - memslot->base_gfn];
+ unsigned long *new_rmap =
+ &resize->rmap[memslot->id][gfn - memslot->base_gfn];
+
+ lock_rmap(old_rmap);
+ lock_rmap(new_rmap);
+ /* Check for pending invalidations under the rmap chain lock */
+ if (mmu_notifier_retry(kvm, mmu_seq)) {
+ /* inval in progress, write a non-present HPTE */
+ pte0 |= HPTE_V_ABSENT;
+ pte0 &= ~HPTE_V_VALID;
+ unlock_rmap(new_rmap);
+ unlock_rmap(old_rmap);
+ } else {
+ unsigned long rcbits;
+
+ kvmppc_add_revmap_chain(&resize->hpt, new_rev,
+ new_rmap, new_idx, false);
+ /* Only set R/C in real HPTE if already set in *rmap */
+ rcbits = *old_rmap >> KVMPPC_RMAP_RC_SHIFT;
+ rcbits |= *new_rmap >> KVMPPC_RMAP_RC_SHIFT;
+ unlock_rmap(old_rmap);
+ pte1 &= rcbits | ~(HPTE_R_R | HPTE_R_C);
+ }
+ } else {
+ /* Emulated MMIO, no rmap */
+ }
+
+ new_hptep[1] = cpu_to_be64(pte1);
+ /* Don't need a barrier here, because the hpt isn't in use yet */
+ new_hptep[0] = cpu_to_be64(replace_pte0);
+ unlock_hpte(new_hptep, pte0);
+
+ return H_SUCCESS;
+}
+
static int resize_hpt_rehash(struct kvm_resize_hpt *resize)
{
- return H_HARDWARE;
+ struct kvm *kvm = resize->kvm;
+ uint64_t n_ptegs = 1ULL << (kvm->arch.hpt.order - 7);
+ uint64_t pteg;
+ int slot;
+ int rc;
+
+ for (pteg = 0; pteg < n_ptegs; pteg++) {
+ for (slot = 0; slot < HPTES_PER_GROUP; slot++) {
+ rc = resize_hpt_rehash_hpte(kvm, resize, pteg, slot);
+ if (rc != H_SUCCESS)
+ return rc;
+ }
+ }
+
+ return H_SUCCESS;
}
static void resize_hpt_pivot(struct kvm_resize_hpt *resize,
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 347ed0e..48e74ac 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -71,7 +71,7 @@ static int global_invalidates(struct kvm *kvm, unsigned long flags)
* Add this HPTE into the chain for the real page.
* Must be called with the chain locked; it unlocks the chain.
*/
-void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
+void kvmppc_add_revmap_chain(struct kvm_hpt_info *hpt, struct revmap_entry *rev,
unsigned long *rmap, long pte_index, int realmode)
{
struct revmap_entry *head, *tail;
@@ -79,10 +79,10 @@ void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
if (*rmap & KVMPPC_RMAP_PRESENT) {
i = *rmap & KVMPPC_RMAP_INDEX;
- head = &kvm->arch.hpt.rev[i];
+ head = &hpt->rev[i];
if (realmode)
head = real_vmalloc_addr(head);
- tail = &kvm->arch.hpt.rev[head->back];
+ tail = &hpt->rev[head->back];
if (realmode)
tail = real_vmalloc_addr(tail);
rev->forw = i;
@@ -353,8 +353,8 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
pteh &= ~HPTE_V_VALID;
unlock_rmap(rmap);
} else {
- kvmppc_add_revmap_chain(kvm, rev, rmap, pte_index,
- realmode);
+ kvmppc_add_revmap_chain(&kvm->arch.hpt, rev, rmap,
+ pte_index, realmode);
/* Only set R/C in real HPTE if already set in *rmap */
rcbits = *rmap >> KVMPPC_RMAP_RC_SHIFT;
ptel &= rcbits | ~(HPTE_R_R | HPTE_R_C);
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 24/25] powerpc/kvm: HPT resize pivot
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (22 preceding siblings ...)
2016-03-08 3:09 ` [RFCv2 23/25] powerpc/kvm: Rehashing for HPT resizing David Gibson
@ 2016-03-08 3:09 ` David Gibson
2016-03-08 3:09 ` [RFCv2 25/25] powerpc/kvm: Harvest RC bits from old HPT after HPT resize David Gibson
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:09 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
This implements the code for HPT resizing to actually pivot from the
currently active HPT to the new HPT, which has previously been populated
by rehashing entries from the old HPT.
This only occurs while the guest is executing the H_RESIZE_HPT_COMMIT
hypercall, handling synchronization with the guest. On the host side this
is executed under the kvm->mmu_lock to prevent races with host side MMU
notifiers.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index d06aef6..45430fe 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -1416,9 +1416,45 @@ static int resize_hpt_rehash(struct kvm_resize_hpt *resize)
return H_SUCCESS;
}
+static void resize_hpt_pivot_cpu(void *opaque)
+{
+ /* Nothing to do, just force a KVM exit */
+}
+
static void resize_hpt_pivot(struct kvm_resize_hpt *resize,
struct kvm_memslots *slots)
{
+ struct kvm *kvm = resize->kvm;
+ struct kvm_memory_slot *memslot;
+ struct kvm_hpt_info hpt_tmp;
+
+ /* Exchange the pending tables in the resize structure with
+ * the active tables */
+
+ resize_hpt_debug(resize, "PIVOT!\n");
+
+ kvm_for_each_memslot(memslot, slots) {
+ unsigned long *tmp;
+
+ tmp = memslot->arch.rmap;
+ memslot->arch.rmap = resize->rmap[memslot->id];
+ resize->rmap[memslot->id] = tmp;
+ }
+
+ hpt_tmp = kvm->arch.hpt;
+ kvmppc_set_hpt(kvm, &resize->hpt);
+ resize->hpt = hpt_tmp;
+
+ spin_unlock(&kvm->mmu_lock);
+
+ synchronize_srcu_expedited(&kvm->srcu);
+
+ /* Force an exit on every vcpu, to make sure the real SDR1
+ * gets updated */
+
+ on_each_cpu(resize_hpt_pivot_cpu, NULL, 1);
+
+ spin_lock(&kvm->mmu_lock);
}
static void resize_hpt_flush_rmaps(struct kvm_resize_hpt *resize,
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFCv2 25/25] powerpc/kvm: Harvest RC bits from old HPT after HPT resize
2016-03-08 3:08 [RFCv2 00/25] PAPR HPT resizing, guest side & host side preliminaries David Gibson
` (23 preceding siblings ...)
2016-03-08 3:09 ` [RFCv2 24/25] powerpc/kvm: HPT resize pivot David Gibson
@ 2016-03-08 3:09 ` David Gibson
24 siblings, 0 replies; 26+ messages in thread
From: David Gibson @ 2016-03-08 3:09 UTC (permalink / raw)
To: paulus, aik, benh; +Cc: bharata, linuxppc-dev, David Gibson
During an HPT resize operation we have two HPTs and sets of reverse maps
for the guest: the active one, and the tentative resized one. This means
that information about a host page's referenced / dirty state as affected
by the guest could end up in either HPT depending on exactly what moment
it happens at.
During the transition we handle this by having things which need this
information consult both the new and old HPTs. However, in order to clean
things up after, we need to harvest any such information left over in the
old tables and store it in the new ones.
This implements this, first harvesting R & C bits from the old HPT into
the old rmaps, then folding that information into the new (now current)
rmaps.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
arch/powerpc/kvm/book3s_64_mmu_hv.c | 57 +++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 45430fe..f132f86 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -1457,9 +1457,66 @@ static void resize_hpt_pivot(struct kvm_resize_hpt *resize,
spin_lock(&kvm->mmu_lock);
}
+static void resize_hpt_harvest_rc(struct kvm_hpt_info *hpt,
+ unsigned long *rmapp)
+{
+ unsigned long idx;
+
+ if (!(*rmapp & KVMPPC_RMAP_PRESENT))
+ return;
+
+ idx = *rmapp & KVMPPC_RMAP_INDEX;
+ do {
+ struct revmap_entry *rev = &hpt->rev[idx];
+ __be64 *hptep = (__be64 *)(hpt->virt + (idx << 4));
+ unsigned long hpte0 = be64_to_cpu(hptep[0]);
+ unsigned long hpte1 = be64_to_cpu(hptep[1]);
+ unsigned long psize = hpte_page_size(hpte0, hpte1);
+ unsigned long rcbits = hpte1 & (HPTE_R_R | HPTE_R_C);
+
+ *rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT;
+ if (rcbits & HPTE_R_C)
+ kvmppc_update_rmap_change(rmapp, psize);
+
+ idx = rev->forw;
+ } while (idx != (*rmapp & KVMPPC_RMAP_INDEX));
+}
+
static void resize_hpt_flush_rmaps(struct kvm_resize_hpt *resize,
struct kvm_memslots *slots)
{
+ struct kvm_memory_slot *memslot;
+
+ kvm_for_each_memslot(memslot, slots) {
+ unsigned long *old_rmap = resize->rmap[memslot->id];
+ unsigned long *new_rmap = memslot->arch.rmap;
+ unsigned long i;
+
+ resize_hpt_debug(resize, "Flushing RMAPS for memslot %d\n", memslot->id);
+
+ for (i = 0; i < memslot->npages; i++) {
+ lock_rmap(old_rmap);
+
+ resize_hpt_harvest_rc(&resize->hpt, old_rmap);
+
+ lock_rmap(new_rmap);
+
+ *new_rmap |= *old_rmap & (KVMPPC_RMAP_REFERENCED
+ | KVMPPC_RMAP_CHANGED);
+ if ((*old_rmap & KVMPPC_RMAP_CHG_ORDER)
+ > (*new_rmap & KVMPPC_RMAP_CHG_ORDER)) {
+ *new_rmap &= ~KVMPPC_RMAP_CHG_ORDER;
+ *new_rmap |= *old_rmap & KVMPPC_RMAP_CHG_ORDER;
+ }
+ unlock_rmap(new_rmap);
+ unlock_rmap(old_rmap);
+
+ old_rmap++;
+ new_rmap++;
+ }
+
+ resize_hpt_debug(resize, "Flushed RMAPS for memslot %d\n", memslot->id);
+ }
}
static void resize_hpt_free(struct kvm_resize_hpt *resize)
--
2.5.0
^ permalink raw reply related [flat|nested] 26+ messages in thread