Subject: Re: [PATCH 1/1] powerpc: Add page coalescing support

From: Benjamin Herrenschmidt <[email-masked]>
Date: Sat, 23 Apr 2011 08:59:38 +1000

On Fri, 2011-04-22 at 16:08 -0500, Brian King wrote:
> Adds support for page coalescing, which is a feature on IBM Power servers
> which allows for coalescing identical pages between logical partitions.
> Hint text pages as coalesce candidates, since they are the most likely
> pages to be able to be coalesced between partitions. This patch also
> exports some page coalescing statistics available from firmware via
> lparcfg.

Hi Brian !

Is this patch different from the version you posted previously which is
already on Patchwork ? I'm in the process of putting together
powerpc-next and your previous variant is in my queue.

Cheers,
Ben.
 
> Signed-off-by: Brian King <[email-masked]>
> ---
> 
>  arch/powerpc/include/asm/firmware.h         |    3 +-
>  arch/powerpc/include/asm/hvcall.h           |   12 ++++++++
>  arch/powerpc/include/asm/pSeries_reconfig.h |    6 ++++
>  arch/powerpc/kernel/lparcfg.c               |   40 ++++++++++++++++++++++++++++
>  arch/powerpc/kernel/prom_init.c             |    4 ++
>  arch/powerpc/kernel/rtas.c                  |    2 +
>  arch/powerpc/platforms/pseries/lpar.c       |    2 +
>  arch/powerpc/platforms/pseries/setup.c      |   11 +++++++
>  8 files changed, 78 insertions(+), 2 deletions(-)
> 
> diff -puN arch/powerpc/include/asm/hvcall.h~powerpc_coalesce arch/powerpc/include/asm/hvcall.h
> --- linux-2.6/arch/powerpc/include/asm/hvcall.h~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/include/asm/hvcall.h	2011-04-20 08:38:42.000000000 -0500
> @@ -102,6 +102,7 @@
>  #define H_ANDCOND		(1UL<<(63-33))
>  #define H_ICACHE_INVALIDATE	(1UL<<(63-40))	/* icbi, etc.  (ignored for IO pages) */
>  #define H_ICACHE_SYNCHRONIZE	(1UL<<(63-41))	/* dcbst, icbi, etc (ignored for IO pages */
> +#define H_COALESCE_CAND	(1UL<<(63-42))	/* page is a good candidate for coalescing */
>  #define H_ZERO_PAGE		(1UL<<(63-48))	/* zero the page before mapping (ignored for IO pages) */
>  #define H_COPY_PAGE		(1UL<<(63-49))
>  #define H_N			(1UL<<(63-61))
> @@ -234,6 +235,7 @@
>  #define H_GET_MPP		0x2D4
>  #define H_HOME_NODE_ASSOCIATIVITY 0x2EC
>  #define H_BEST_ENERGY		0x2F4
> +#define H_GET_MPP_X		0x314
>  #define MAX_HCALL_OPCODE	H_BEST_ENERGY
>  
>  #ifndef __ASSEMBLY__
> @@ -312,6 +314,16 @@ struct hvcall_mpp_data {
>  
>  int h_get_mpp(struct hvcall_mpp_data *);
>  
> +struct hvcall_mpp_x_data {
> +	unsigned long coalesced_bytes;
> +	unsigned long pool_coalesced_bytes;
> +	unsigned long pool_purr_cycles;
> +	unsigned long pool_spurr_cycles;
> +	unsigned long reserved[3];
> +};
> +
> +int h_get_mpp_x(struct hvcall_mpp_x_data *mpp_x_data);
> +
>  #ifdef CONFIG_PPC_PSERIES
>  extern int CMO_PrPSP;
>  extern int CMO_SecPSP;
> diff -puN arch/powerpc/platforms/pseries/setup.c~powerpc_coalesce arch/powerpc/platforms/pseries/setup.c
> --- linux-2.6/arch/powerpc/platforms/pseries/setup.c~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/platforms/pseries/setup.c	2011-04-20 08:38:42.000000000 -0500
> @@ -403,6 +403,16 @@ static int pseries_set_xdabr(unsigned lo
>  #define CMO_CHARACTERISTICS_TOKEN 44
>  #define CMO_MAXLENGTH 1026
>  
> +void pSeries_coalesce_init(void)
> +{
> +	struct hvcall_mpp_x_data mpp_x_data;
> +
> +	if (firmware_has_feature(FW_FEATURE_CMO) && !h_get_mpp_x(&mpp_x_data))
> +		powerpc_firmware_features |= FW_FEATURE_XCMO;
> +	else
> +		powerpc_firmware_features &= ~FW_FEATURE_XCMO;
> +}
> +
>  /**
>   * fw_cmo_feature_init - FW_FEATURE_CMO is not stored in ibm,hypertas-functions,
>   * handle that here. (Stolen from parse_system_parameter_string)
> @@ -472,6 +482,7 @@ void pSeries_cmo_feature_init(void)
>  		pr_debug("CMO enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP,
>  		         CMO_SecPSP);
>  		powerpc_firmware_features |= FW_FEATURE_CMO;
> +		pSeries_coalesce_init();
>  	} else
>  		pr_debug("CMO not enabled, PrPSP=%d, SecPSP=%d\n", CMO_PrPSP,
>  		         CMO_SecPSP);
> diff -puN arch/powerpc/kernel/prom_init.c~powerpc_coalesce arch/powerpc/kernel/prom_init.c
> --- linux-2.6/arch/powerpc/kernel/prom_init.c~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/kernel/prom_init.c	2011-04-20 08:38:42.000000000 -0500
> @@ -676,8 +676,10 @@ static void __init early_cmdline_parse(v
>  #endif /* CONFIG_PCI_MSI */
>  #ifdef CONFIG_PPC_SMLPAR
>  #define OV5_CMO			0x80	/* Cooperative Memory Overcommitment */
> +#define OV5_XCMO			0x40	/* Page Coalescing */
>  #else
>  #define OV5_CMO			0x00
> +#define OV5_XCMO			0x00
>  #endif
>  #define OV5_TYPE1_AFFINITY	0x80	/* Type 1 NUMA affinity */
>  
> @@ -732,7 +734,7 @@ static unsigned char ibm_architecture_ve
>  	OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY |
>  	OV5_DONATE_DEDICATE_CPU | OV5_MSI,
>  	0,
> -	OV5_CMO,
> +	OV5_CMO | OV5_XCMO,
>  	OV5_TYPE1_AFFINITY,
>  	0,
>  	0,
> diff -puN arch/powerpc/kernel/lparcfg.c~powerpc_coalesce arch/powerpc/kernel/lparcfg.c
> --- linux-2.6/arch/powerpc/kernel/lparcfg.c~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/kernel/lparcfg.c	2011-04-20 08:38:42.000000000 -0500
> @@ -161,6 +161,21 @@ int h_get_mpp(struct hvcall_mpp_data *mp
>  }
>  EXPORT_SYMBOL(h_get_mpp);
>  
> +int h_get_mpp_x(struct hvcall_mpp_x_data *mpp_x_data)
> +{
> +	int rc;
> +	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = { 0 };
> +
> +	rc = plpar_hcall9(H_GET_MPP_X, retbuf);
> +
> +	mpp_x_data->coalesced_bytes = retbuf[0];
> +	mpp_x_data->pool_coalesced_bytes = retbuf[1];
> +	mpp_x_data->pool_purr_cycles = retbuf[2];
> +	mpp_x_data->pool_spurr_cycles = retbuf[3];
> +
> +	return rc;
> +}
> +
>  struct hvcall_ppp_data {
>  	u64	entitlement;
>  	u64	unallocated_entitlement;
> @@ -345,6 +360,30 @@ static void parse_mpp_data(struct seq_fi
>  	seq_printf(m, "backing_memory=%ld bytes\n", mpp_data.backing_mem);
>  }
>  
> +/**
> + * parse_mpp_x_data
> + * Parse out data returned from h_get_mpp_x
> + */
> +static void parse_mpp_x_data(struct seq_file *m)
> +{
> +	struct hvcall_mpp_x_data mpp_x_data;
> +
> +	if (!firmware_has_feature(FW_FEATURE_XCMO))
> +		return;
> +	if (h_get_mpp_x(&mpp_x_data))
> +		return;
> +
> +	seq_printf(m, "coalesced_bytes=%ld\n", mpp_x_data.coalesced_bytes);
> +
> +	if (mpp_x_data.pool_coalesced_bytes)
> +		seq_printf(m, "pool_coalesced_bytes=%ld\n",
> +			   mpp_x_data.pool_coalesced_bytes);
> +	if (mpp_x_data.pool_purr_cycles)
> +		seq_printf(m, "coalesce_pool_purr=%ld\n", mpp_x_data.pool_purr_cycles);
> +	if (mpp_x_data.pool_spurr_cycles)
> +		seq_printf(m, "coalesce_pool_spurr=%ld\n", mpp_x_data.pool_spurr_cycles);
> +}
> +
>  #define SPLPAR_CHARACTERISTICS_TOKEN 20
>  #define SPLPAR_MAXLENGTH 1026*(sizeof(char))
>  
> @@ -520,6 +559,7 @@ static int pseries_lparcfg_data(struct s
>  		parse_system_parameter_string(m);
>  		parse_ppp_data(m);
>  		parse_mpp_data(m);
> +		parse_mpp_x_data(m);
>  		pseries_cmo_data(m);
>  		splpar_dispatch_data(m);
>  
> diff -puN arch/powerpc/include/asm/firmware.h~powerpc_coalesce arch/powerpc/include/asm/firmware.h
> --- linux-2.6/arch/powerpc/include/asm/firmware.h~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/include/asm/firmware.h	2011-04-20 08:38:42.000000000 -0500
> @@ -47,6 +47,7 @@
>  #define FW_FEATURE_BEAT		ASM_CONST(0x0000000001000000)
>  #define FW_FEATURE_CMO		ASM_CONST(0x0000000002000000)
>  #define FW_FEATURE_VPHN		ASM_CONST(0x0000000004000000)
> +#define FW_FEATURE_XCMO		ASM_CONST(0x0000000008000000)
>  
>  #ifndef __ASSEMBLY__
>  
> @@ -60,7 +61,7 @@ enum {
>  		FW_FEATURE_VIO | FW_FEATURE_RDMA | FW_FEATURE_LLAN |
>  		FW_FEATURE_BULK_REMOVE | FW_FEATURE_XDABR |
>  		FW_FEATURE_MULTITCE | FW_FEATURE_SPLPAR | FW_FEATURE_LPAR |
> -		FW_FEATURE_CMO | FW_FEATURE_VPHN,
> +		FW_FEATURE_CMO | FW_FEATURE_VPHN | FW_FEATURE_XCMO,
>  	FW_FEATURE_PSERIES_ALWAYS = 0,
>  	FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
>  	FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
> diff -puN arch/powerpc/platforms/pseries/lpar.c~powerpc_coalesce arch/powerpc/platforms/pseries/lpar.c
> --- linux-2.6/arch/powerpc/platforms/pseries/lpar.c~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/platforms/pseries/lpar.c	2011-04-20 08:38:42.000000000 -0500
> @@ -329,6 +329,8 @@ static long pSeries_lpar_hpte_insert(uns
>  	/* Make pHyp happy */
>  	if ((rflags & _PAGE_NO_CACHE) & !(rflags & _PAGE_WRITETHRU))
>  		hpte_r &= ~_PAGE_COHERENT;
> +	if (firmware_has_feature(FW_FEATURE_XCMO) && !(hpte_r & HPTE_R_N))
> +		flags |= H_COALESCE_CAND;
>  
>  	lpar_rc = plpar_pte_enter(flags, hpte_group, hpte_v, hpte_r, &slot);
>  	if (unlikely(lpar_rc == H_PTEG_FULL)) {
> diff -puN arch/powerpc/include/asm/pSeries_reconfig.h~powerpc_coalesce arch/powerpc/include/asm/pSeries_reconfig.h
> --- linux-2.6/arch/powerpc/include/asm/pSeries_reconfig.h~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/include/asm/pSeries_reconfig.h	2011-04-20 08:38:42.000000000 -0500
> @@ -14,6 +14,12 @@
>  #define PSERIES_DRCONF_MEM_ADD		0x0003
>  #define PSERIES_DRCONF_MEM_REMOVE	0x0004
>  
> +#ifdef CONFIG_PPC_SMLPAR
> +extern void pSeries_coalesce_init(void);
> +#else
> +static inline void pSeries_coalesce_init(void) { }
> +#endif
> +
>  #ifdef CONFIG_PPC_PSERIES
>  extern int pSeries_reconfig_notifier_register(struct notifier_block *);
>  extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
> diff -puN arch/powerpc/kernel/rtas.c~powerpc_coalesce arch/powerpc/kernel/rtas.c
> --- linux-2.6/arch/powerpc/kernel/rtas.c~powerpc_coalesce	2011-04-20 08:38:42.000000000 -0500
> +++ linux-2.6-bjking1/arch/powerpc/kernel/rtas.c	2011-04-20 08:38:42.000000000 -0500
> @@ -42,6 +42,7 @@
>  #include <asm/time.h>
>  #include <asm/mmu.h>
>  #include <asm/topology.h>
> +#include <asm/pSeries_reconfig.h>
>  
>  struct rtas_t rtas = {
>  	.lock = __ARCH_SPIN_LOCK_UNLOCKED
> @@ -731,6 +732,7 @@ static int __rtas_suspend_last_cpu(struc
>  
>  	atomic_set(&data->error, rc);
>  	start_topology_update();
> +	pSeries_coalesce_init();
>  
>  	if (wake_when_done) {
>  		atomic_set(&data->done, 1);
> _