LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: Alexey Kardashevskiy @ 2012-11-27  4:06 UTC (permalink / raw)
  To: Alex Williamson
  Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1353954038.1809.114.camel@bling.home>

On 27/11/12 05:20, Alex Williamson wrote:
> On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
>> VFIO implements platform independent stuff such as
>> a PCI driver, BAR access (via read/write on a file descriptor
>> or direct mapping when possible) and IRQ signaling.
>>
>> The platform dependent part includes IOMMU initialization
>> and handling. This patch implements an IOMMU driver for VFIO
>> which does mapping/unmapping pages for the guest IO and
>> provides information about DMA window (required by a POWERPC
>> guest).
>>
>> The counterpart in QEMU is required to support this functionality.
>>
>> Cc: David Gibson <david@gibson.dropbear.id.au>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> ---
>>   drivers/vfio/Kconfig                |    6 +
>>   drivers/vfio/Makefile               |    1 +
>>   drivers/vfio/vfio_iommu_spapr_tce.c |  247 +++++++++++++++++++++++++++++++++++
>>   include/linux/vfio.h                |   20 +++
>>   4 files changed, 274 insertions(+)
>>   create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>>
>> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
>> index 7cd5dec..b464687 100644
>> --- a/drivers/vfio/Kconfig
>> +++ b/drivers/vfio/Kconfig
>> @@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
>>   	depends on VFIO
>>   	default n
>>
>> +config VFIO_IOMMU_SPAPR_TCE
>> +	tristate
>> +	depends on VFIO && SPAPR_TCE_IOMMU
>> +	default n
>> +
>>   menuconfig VFIO
>>   	tristate "VFIO Non-Privileged userspace driver framework"
>>   	depends on IOMMU_API
>>   	select VFIO_IOMMU_TYPE1 if X86
>> +	select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
>>   	help
>>   	  VFIO provides a framework for secure userspace device drivers.
>>   	  See Documentation/vfio.txt for more details.
>> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
>> index 2398d4a..72bfabc 100644
>> --- a/drivers/vfio/Makefile
>> +++ b/drivers/vfio/Makefile
>> @@ -1,3 +1,4 @@
>>   obj-$(CONFIG_VFIO) += vfio.o
>>   obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
>> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
>>   obj-$(CONFIG_VFIO_PCI) += pci/
>> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
>> new file mode 100644
>> index 0000000..46a6298
>> --- /dev/null
>> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
>> @@ -0,0 +1,247 @@
>> +/*
>> + * VFIO: IOMMU DMA mapping support for TCE on POWER
>> + *
>> + * Copyright (C) 2012 IBM Corp.  All rights reserved.
>> + *     Author: Alexey Kardashevskiy <aik@ozlabs.ru>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * Derived from original vfio_iommu_type1.c:
>> + * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
>> + *     Author: Alex Williamson <alex.williamson@redhat.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/err.h>
>> +#include <linux/vfio.h>
>> +#include <asm/iommu.h>
>> +
>> +#define DRIVER_VERSION  "0.1"
>> +#define DRIVER_AUTHOR   "aik@ozlabs.ru"
>> +#define DRIVER_DESC     "VFIO IOMMU SPAPR TCE"
>> +
>> +static void tce_iommu_detach_group(void *iommu_data,
>> +		struct iommu_group *iommu_group);
>> +
>> +/*
>> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
>> + */
>> +
>> +/*
>> + * The container descriptor supports only a single group per container.
>> + * Required by the API as the container is not supplied with the IOMMU group
>> + * at the moment of initialization.
>> + */
>> +struct tce_container {
>> +	struct mutex lock;
>> +	struct iommu_table *tbl;
>> +};
>> +
>> +static void *tce_iommu_open(unsigned long arg)
>> +{
>> +	struct tce_container *container;
>> +
>> +	if (arg != VFIO_SPAPR_TCE_IOMMU) {
>> +		printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	container = kzalloc(sizeof(*container), GFP_KERNEL);
>> +	if (!container)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	mutex_init(&container->lock);
>> +
>> +	return container;
>> +}
>> +
>> +static void tce_iommu_release(void *iommu_data)
>> +{
>> +	struct tce_container *container = iommu_data;
>> +
>> +	WARN_ON(container->tbl && !container->tbl->it_group);
>
> I think your patch ordering is backwards here.  it_group isn't added
> until 2/2.  I'd really like to see the arch/powerpc code approved and
> merged by the powerpc maintainer before we add the code that makes use
> of it into vfio.  Otherwise we just get lots of churn if interfaces
> change or they disapprove of it altogether.


Makes sense, thanks.


>> +	if (container->tbl && container->tbl->it_group)
>> +		tce_iommu_detach_group(iommu_data, container->tbl->it_group);
>> +
>> +	mutex_destroy(&container->lock);
>> +
>> +	kfree(container);
>> +}
>> +
>> +static long tce_iommu_ioctl(void *iommu_data,
>> +				 unsigned int cmd, unsigned long arg)
>> +{
>> +	struct tce_container *container = iommu_data;
>> +	unsigned long minsz;
>> +
>> +	switch (cmd) {
>> +	case VFIO_CHECK_EXTENSION: {
>> +		return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
>> +	}
>> +	case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
>> +		struct vfio_iommu_spapr_tce_info info;
>> +		struct iommu_table *tbl = container->tbl;
>> +
>> +		if (WARN_ON(!tbl))
>> +			return -ENXIO;
>> +
>> +		minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
>> +				dma64_window_size);
>> +
>> +		if (copy_from_user(&info, (void __user *)arg, minsz))
>> +			return -EFAULT;
>> +
>> +		if (info.argsz < minsz)
>> +			return -EINVAL;
>> +
>> +		info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
>> +		info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
>> +		info.dma64_window_start = 0;
>> +		info.dma64_window_size = 0;
>> +		info.flags = 0;
>> +
>> +		if (copy_to_user((void __user *)arg, &info, minsz))
>> +			return -EFAULT;
>> +
>> +		return 0;
>> +	}
>> +	case VFIO_IOMMU_MAP_DMA: {
>> +		vfio_iommu_spapr_tce_dma_map param;
>> +		struct iommu_table *tbl = container->tbl;
>> +		enum dma_data_direction direction = DMA_NONE;
>> +
>> +		if (WARN_ON(!tbl))
>> +			return -ENXIO;
>> +
>> +		minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
>> +
>> +		if (copy_from_user(&param, (void __user *)arg, minsz))
>> +			return -EFAULT;
>> +
>> +		if (param.argsz < minsz)
>> +			return -EINVAL;
>> +
>> +		if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
>> +				(param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
>> +			direction = DMA_BIDIRECTIONAL;
>> +		} else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
>> +			direction = DMA_TO_DEVICE;
>> +		} else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
>> +			direction = DMA_FROM_DEVICE;
>> +		}
>> +
>> +		param.size += param.iova & ~IOMMU_PAGE_MASK;
>> +		param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>
> On x86 we force iova, vaddr, and size to all be aligned to the smallest
> page granularity of the iommu and return -EINVAL if it doesn't fit.
> What does it imply to the user if they're always aligned to work here?
> Won't this interface happily map overlapping entries with no indication
> to the user that the previous mapping is no longer valid?
> Maybe another reason why a combined unmap/map makes me nervous, we have
> to assume the user knows what they're doing.


I got used to guests which do know what they are doing so I am pretty calm :)
but ok, I'll move alignment to the QEMU, it makes sense.


>> +
>> +		return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>> +				param.vaddr & IOMMU_PAGE_MASK, direction,
>> +				param.size >> IOMMU_PAGE_SHIFT);
>> +	}
>> +	case VFIO_IOMMU_UNMAP_DMA: {
>> +		vfio_iommu_spapr_tce_dma_unmap param;
>> +		struct iommu_table *tbl = container->tbl;
>> +
>> +		if (WARN_ON(!tbl))
>> +			return -ENXIO;
>> +
>> +		minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
>> +
>> +		if (copy_from_user(&param, (void __user *)arg, minsz))
>> +			return -EFAULT;
>> +
>> +		if (param.argsz < minsz)
>> +			return -EINVAL;
>> +
>> +		param.size += param.iova & ~IOMMU_PAGE_MASK;
>> +		param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>> +
>> +		return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>> +				0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
>> +	}
>> +	default:
>> +		printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
>
> pr_warn
>
>> +	}
>> +
>> +	return -ENOTTY;
>> +}
>> +
>> +static int tce_iommu_attach_group(void *iommu_data,
>> +		struct iommu_group *iommu_group)
>> +{
>> +	struct tce_container *container = iommu_data;
>> +	struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
>> +
>> +	BUG_ON(!tbl);
>> +	mutex_lock(&container->lock);
>> +	pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
>> +			iommu_group_id(iommu_group), iommu_group);
>> +	if (container->tbl) {
>> +		printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
>
> pr_warn
>
>> +				iommu_group_id(container->tbl->it_group),
>> +				iommu_group_id(iommu_group));
>> +		mutex_unlock(&container->lock);
>> +		return -EBUSY;
>> +	}
>> +
>> +	container->tbl = tbl;
>
> Would it be too much paranoia to clear all the tce here as you do below
> on detach?

Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e. 
unmaps) the whole DMA window at the boot time.


> ie. is there any risk that there's leftover programming?
> x86 allocates a new domain on open of the iommu, so we always start out
> clean.


>> +	mutex_unlock(&container->lock);
>> +
>> +	return 0;
>> +}
>> +
>> +static void tce_iommu_detach_group(void *iommu_data,
>> +		struct iommu_group *iommu_group)
>> +{
>> +	struct tce_container *container = iommu_data;
>> +	struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
>> +
>> +	BUG_ON(!tbl);
>> +	mutex_lock(&container->lock);
>> +	if (tbl != container->tbl) {
>> +		printk(KERN_WARNING "tce_vfio: detaching group #%u, expected group is #%u\n",
>
> pr_warn
>
>> +				iommu_group_id(iommu_group),
>> +				iommu_group_id(tbl->it_group));
>> +	} else {
>> +
>> +		pr_debug("tce_vfio: detaching group #%u from iommu %p\n",
>> +				iommu_group_id(iommu_group), iommu_group);
>> +
>> +		iommu_put_tces(tbl, tbl->it_offset, 0, DMA_NONE, tbl->it_size);
>
> So this cleans out any mappings when vfio is closed, good.
>
>> +		container->tbl = NULL;
>> +	}
>> +	mutex_unlock(&container->lock);
>> +}
>> +
>> +const struct vfio_iommu_driver_ops tce_iommu_driver_ops = {
>> +	.name		= "iommu-vfio-powerpc",
>> +	.owner		= THIS_MODULE,
>> +	.open		= tce_iommu_open,
>> +	.release	= tce_iommu_release,
>> +	.ioctl		= tce_iommu_ioctl,
>> +	.attach_group	= tce_iommu_attach_group,
>> +	.detach_group	= tce_iommu_detach_group,
>> +};
>> +
>> +static int __init tce_iommu_init(void)
>> +{
>> +	return vfio_register_iommu_driver(&tce_iommu_driver_ops);
>> +}
>> +
>> +static void __exit tce_iommu_cleanup(void)
>> +{
>> +	vfio_unregister_iommu_driver(&tce_iommu_driver_ops);
>> +}
>> +
>> +module_init(tce_iommu_init);
>> +module_exit(tce_iommu_cleanup);
>> +
>> +MODULE_VERSION(DRIVER_VERSION);
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR(DRIVER_AUTHOR);
>> +MODULE_DESCRIPTION(DRIVER_DESC);
>> +
>> diff --git a/include/linux/vfio.h b/include/linux/vfio.h
>> index 0a4f180..3ecd65c 100644
>> --- a/include/linux/vfio.h
>> +++ b/include/linux/vfio.h
>> @@ -99,6 +99,7 @@ extern void vfio_unregister_iommu_driver(
>>   /* Extensions */
>>
>>   #define VFIO_TYPE1_IOMMU		1
>> +#define VFIO_SPAPR_TCE_IOMMU		2
>>
>>   /*
>>    * The IOCTL interface is designed for extensibility by embedding the
>> @@ -442,4 +443,23 @@ struct vfio_iommu_type1_dma_unmap {
>>
>>   #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
>>
>> +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */
>> +
>> +struct vfio_iommu_spapr_tce_info {
>> +	__u32 argsz;
>> +	__u32 flags;
>> +	__u32 dma32_window_start;
>> +	__u32 dma32_window_size;
>> +	__u64 dma64_window_start;
>> +	__u64 dma64_window_size;
>> +};
>
> Is there anything we can document about this?

I'll put some.

> It should probably list that size is in bytes.  Is there any need to communicate the IOMMU page
> size here?

It is always 4k. I'll put it to comments.

>> +
>> +#define VFIO_IOMMU_SPAPR_TCE_GET_INFO	_IO(VFIO_TYPE, VFIO_BASE + 12)
>> +
>> +/* Reuse type1 map/unmap structs as they are the same at the moment */
>> +typedef struct vfio_iommu_type1_dma_map vfio_iommu_spapr_tce_dma_map;
>> +typedef struct vfio_iommu_type1_dma_unmap vfio_iommu_spapr_tce_dma_unmap;
>> +
>> +/* ***************************************************************** */
>> +
>>   #endif /* VFIO_H */
>
> Thanks,
>
> Alex
>
>
>


-- 
Alexey

^ permalink raw reply

* [PATCH 3/3] powerpc: Build kernel with -mcmodel=medium
From: Anton Blanchard @ 2012-11-27  3:41 UTC (permalink / raw)
  To: benh, paulus, amodra; +Cc: linuxppc-dev
In-Reply-To: <20121127143903.4b5cab17@kryten>


Finally remove the two level TOC and build with -mcmodel=medium.

Unfortunately we can't build modules with -mcmodel=medium due to
the tricks the kernel module loader plays with percpu data:

# -mcmodel=medium breaks modules because it uses 32bit offsets from
# the TOC pointer to create pointers where possible. Pointers into the
# percpu data area are created by this method.
#
# The kernel module loader relocates the percpu data section from the
# original location (starting with 0xd...) to somewhere in the base
# kernel percpu data space (starting with 0xc...). We need a full
# 64bit relocation for this to work, hence -mcmodel=large.

On older kernels we fall back to the two level TOC (-mminimal-toc)

Signed-off-by: Anton Blanchard <anton@samba.org>
--- 

Index: b/arch/powerpc/Makefile
===================================================================
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -67,7 +67,24 @@ LDFLAGS_vmlinux-y := -Bstatic
 LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie
 LDFLAGS_vmlinux	:= $(LDFLAGS_vmlinux-y)
 
-CFLAGS-$(CONFIG_PPC64)	:= -mminimal-toc -mtraceback=no -mcall-aixdesc
+ifeq ($(CONFIG_PPC64),y)
+ifeq ($(call cc-option-yn,-mcmodel=medium),y)
+	# -mcmodel=medium breaks modules because it uses 32bit offsets from
+	# the TOC pointer to create pointers where possible. Pointers into the
+	# percpu data area are created by this method.
+	#
+	# The kernel module loader relocates the percpu data section from the
+	# original location (starting with 0xd...) to somewhere in the base
+	# kernel percpu data space (starting with 0xc...). We need a full
+	# 64bit relocation for this to work, hence -mcmodel=large.
+	KBUILD_CFLAGS_MODULE += -mcmodel=large
+else
+	export NO_MINIMAL_TOC := -mno-minimal-toc
+endif
+endif
+
+CFLAGS-$(CONFIG_PPC64)	:= -mtraceback=no -mcall-aixdesc
+CFLAGS-$(CONFIG_PPC64)	+= $(call cc-option,-mcmodel=medium,-mminimal-toc)
 CFLAGS-$(CONFIG_PPC32)	:= -ffixed-r2 -mmultiple
 
 CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4)
Index: b/arch/powerpc/kernel/Makefile
===================================================================
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -7,7 +7,7 @@ CFLAGS_ptrace.o		+= -DUTS_MACHINE='"$(UT
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 ifeq ($(CONFIG_PPC64),y)
-CFLAGS_prom_init.o	+= -mno-minimal-toc
+CFLAGS_prom_init.o	+= $(NO_MINIMAL_TOC)
 endif
 ifeq ($(CONFIG_PPC32),y)
 CFLAGS_prom_init.o      += -fPIC
Index: b/arch/powerpc/lib/Makefile
===================================================================
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -4,7 +4,7 @@
 
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
-ccflags-$(CONFIG_PPC64)	:= -mno-minimal-toc
+ccflags-$(CONFIG_PPC64)	:= $(NO_MINIMAL_TOC)
 
 CFLAGS_REMOVE_code-patching.o = -pg
 CFLAGS_REMOVE_feature-fixups.o = -pg
Index: b/arch/powerpc/mm/Makefile
===================================================================
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -4,7 +4,7 @@
 
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
-ccflags-$(CONFIG_PPC64)	:= -mno-minimal-toc
+ccflags-$(CONFIG_PPC64)	:= $(NO_MINIMAL_TOC)
 
 obj-y				:= fault.o mem.o pgtable.o gup.o \
 				   init_$(CONFIG_WORD_SIZE).o \
Index: b/arch/powerpc/oprofile/Makefile
===================================================================
--- a/arch/powerpc/oprofile/Makefile
+++ b/arch/powerpc/oprofile/Makefile
@@ -1,6 +1,6 @@
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
-ccflags-$(CONFIG_PPC64)	:= -mno-minimal-toc
+ccflags-$(CONFIG_PPC64)	:= $(NO_MINIMAL_TOC)
 
 obj-$(CONFIG_OPROFILE) += oprofile.o
 
Index: b/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -1,4 +1,4 @@
-ccflags-$(CONFIG_PPC64)			:= -mno-minimal-toc
+ccflags-$(CONFIG_PPC64)			:= $(NO_MINIMAL_TOC)
 ccflags-$(CONFIG_PPC_PSERIES_DEBUG)	+= -DDEBUG
 
 obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
Index: b/arch/powerpc/sysdev/Makefile
===================================================================
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -1,6 +1,6 @@
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
-ccflags-$(CONFIG_PPC64)		:= -mno-minimal-toc
+ccflags-$(CONFIG_PPC64)		:= $(NO_MINIMAL_TOC)
 
 mpic-msi-obj-$(CONFIG_PCI_MSI)	+= mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
 obj-$(CONFIG_MPIC)		+= mpic.o $(mpic-msi-obj-y)
Index: b/arch/powerpc/xmon/Makefile
===================================================================
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -4,7 +4,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -
 
 GCOV_PROFILE := n
 
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
 
 obj-y			+= xmon.o nonstdio.o
 
Index: b/arch/powerpc/platforms/wsp/Makefile
===================================================================
--- a/arch/powerpc/platforms/wsp/Makefile
+++ b/arch/powerpc/platforms/wsp/Makefile
@@ -1,4 +1,4 @@
-ccflags-y			+= -mno-minimal-toc
+ccflags-y			+= $(NO_MINIMAL_TOC)
 
 obj-y				+= setup.o ics.o wsp.o
 obj-$(CONFIG_PPC_PSR2)		+= psr2.o
Index: b/arch/powerpc/kernel/head_64.S
===================================================================
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -169,6 +169,7 @@ _GLOBAL(generic_secondary_thread_init)
 
 	/* get a valid TOC pointer, wherever we're mapped at */
 	bl	.relative_toc
+	tovirt(r2,r2)
 
 #ifdef CONFIG_PPC_BOOK3E
 	/* Book3E initialization */
@@ -195,6 +196,7 @@ _GLOBAL(generic_secondary_smp_init)
 
 	/* get a valid TOC pointer, wherever we're mapped at */
 	bl	.relative_toc
+	tovirt(r2,r2)
 
 #ifdef CONFIG_PPC_BOOK3E
 	/* Book3E initialization */
@@ -531,6 +533,7 @@ _GLOBAL(pmac_secondary_start)
 
 	/* get TOC pointer (real address) */
 	bl	.relative_toc
+	tovirt(r2,r2)
 
 	/* Copy some CPU settings from CPU 0 */
 	bl	.__restore_cpu_ppc970
@@ -665,6 +668,13 @@ _GLOBAL(enable_64b_mode)
  * This puts the TOC pointer into r2, offset by 0x8000 (as expected
  * by the toolchain).  It computes the correct value for wherever we
  * are running at the moment, using position-independent code.
+ *
+ * Note: The compiler constructs pointers using offsets from the
+ * TOC in -mcmodel=medium mode. After we relocate to 0 but before
+ * the MMU is on we need our TOC to be a virtual address otherwise
+ * these pointers will be real addresses which may get stored and
+ * accessed later with the MMU on. We use tovirt() at the call
+ * sites to handle this.
  */
 _GLOBAL(relative_toc)
 	mflr	r0
@@ -681,8 +691,9 @@ p_toc:	.llong	__toc_start + 0x8000 - 0b
  * This is where the main kernel code starts.
  */
 _INIT_STATIC(start_here_multiplatform)
-	/* set up the TOC (real address) */
-	bl	.relative_toc
+	/* set up the TOC */
+	bl      .relative_toc
+	tovirt(r2,r2)
 
 	/* Clear out the BSS. It may have been done in prom_init,
 	 * already but that's irrelevant since prom_init will soon
Index: b/arch/powerpc/kernel/module_64.c
===================================================================
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -386,6 +386,14 @@ int apply_relocate_add(Elf64_Shdr *sechd
 				| (value & 0xffff);
 			break;
 
+		case R_PPC64_TOC16_LO:
+			/* Subtract TOC pointer */
+			value -= my_r2(sechdrs, me);
+			*((uint16_t *) location)
+				= (*((uint16_t *) location) & ~0xffff)
+				| (value & 0xffff);
+			break;
+
 		case R_PPC64_TOC16_DS:
 			/* Subtract TOC pointer */
 			value -= my_r2(sechdrs, me);
@@ -399,6 +407,28 @@ int apply_relocate_add(Elf64_Shdr *sechd
 				| (value & 0xfffc);
 			break;
 
+		case R_PPC64_TOC16_LO_DS:
+			/* Subtract TOC pointer */
+			value -= my_r2(sechdrs, me);
+			if ((value & 3) != 0) {
+				printk("%s: bad TOC16_LO_DS relocation (%lu)\n",
+				       me->name, value);
+				return -ENOEXEC;
+			}
+			*((uint16_t *) location)
+				= (*((uint16_t *) location) & ~0xfffc)
+				| (value & 0xfffc);
+			break;
+
+		case R_PPC64_TOC16_HA:
+			/* Subtract TOC pointer */
+			value -= my_r2(sechdrs, me);
+			value = ((value + 0x8000) >> 16);
+			*((uint16_t *) location)
+				= (*((uint16_t *) location) & ~0xffff)
+				| (value & 0xffff);
+			break;
+
 		case R_PPC_REL24:
 			/* FIXME: Handle weak symbols here --RR */
 			if (sym->st_shndx == SHN_UNDEF) {

^ permalink raw reply

* [PATCH 2/3] powerpc: Remove RELOC() macro
From: Anton Blanchard @ 2012-11-27  3:40 UTC (permalink / raw)
  To: benh, paulus, amodra; +Cc: linuxppc-dev
In-Reply-To: <20121127143903.4b5cab17@kryten>


Now we relocate prom_init.c on 64bit we can finally remove the
nasty RELOC() macro.

Finally a patch that I can claim has a net positive effect on
the kernel. It doesn't happen very often.

Signed-off-by: Anton Blanchard <anton@samba.org>
--- 

Index: b/arch/powerpc/kernel/prom_init.c
===================================================================
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -79,7 +79,6 @@
  * On ppc64, 64 bit values are truncated to 32 bits (and
  * fortunately don't get interpreted as two arguments).
  */
-#define RELOC(x)	(x)
 #define ADDR(x)		(u32)(unsigned long)(x)
 
 #ifdef CONFIG_PPC64
@@ -94,7 +93,7 @@ int of_workarounds;
 
 #define PROM_BUG() do {						\
         prom_printf("kernel BUG at %s line 0x%x!\n",		\
-		    RELOC(__FILE__), __LINE__);			\
+		    __FILE__, __LINE__);			\
         __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR);	\
 } while (0)
 
@@ -232,7 +231,7 @@ static int __init call_prom(const char *
 	for (i = 0; i < nret; i++)
 		args.args[nargs+i] = 0;
 
-	if (enter_prom(&args, RELOC(prom_entry)) < 0)
+	if (enter_prom(&args, prom_entry) < 0)
 		return PROM_ERROR;
 
 	return (nret > 0) ? args.args[nargs] : 0;
@@ -257,7 +256,7 @@ static int __init call_prom_ret(const ch
 	for (i = 0; i < nret; i++)
 		args.args[nargs+i] = 0;
 
-	if (enter_prom(&args, RELOC(prom_entry)) < 0)
+	if (enter_prom(&args, prom_entry) < 0)
 		return PROM_ERROR;
 
 	if (rets != NULL)
@@ -271,20 +270,19 @@ static int __init call_prom_ret(const ch
 static void __init prom_print(const char *msg)
 {
 	const char *p, *q;
-	struct prom_t *_prom = &RELOC(prom);
 
-	if (_prom->stdout == 0)
+	if (prom.stdout == 0)
 		return;
 
 	for (p = msg; *p != 0; p = q) {
 		for (q = p; *q != 0 && *q != '\n'; ++q)
 			;
 		if (q > p)
-			call_prom("write", 3, 1, _prom->stdout, p, q - p);
+			call_prom("write", 3, 1, prom.stdout, p, q - p);
 		if (*q == 0)
 			break;
 		++q;
-		call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2);
+		call_prom("write", 3, 1, prom.stdout, ADDR("\r\n"), 2);
 	}
 }
 
@@ -293,7 +291,6 @@ static void __init prom_print_hex(unsign
 {
 	int i, nibbles = sizeof(val)*2;
 	char buf[sizeof(val)*2+1];
-	struct prom_t *_prom = &RELOC(prom);
 
 	for (i = nibbles-1;  i >= 0;  i--) {
 		buf[i] = (val & 0xf) + '0';
@@ -302,7 +299,7 @@ static void __init prom_print_hex(unsign
 		val >>= 4;
 	}
 	buf[nibbles] = '\0';
-	call_prom("write", 3, 1, _prom->stdout, buf, nibbles);
+	call_prom("write", 3, 1, prom.stdout, buf, nibbles);
 }
 
 /* max number of decimal digits in an unsigned long */
@@ -311,7 +308,6 @@ static void __init prom_print_dec(unsign
 {
 	int i, size;
 	char buf[UL_DIGITS+1];
-	struct prom_t *_prom = &RELOC(prom);
 
 	for (i = UL_DIGITS-1; i >= 0;  i--) {
 		buf[i] = (val % 10) + '0';
@@ -321,7 +317,7 @@ static void __init prom_print_dec(unsign
 	}
 	/* shift stuff down */
 	size = UL_DIGITS - i;
-	call_prom("write", 3, 1, _prom->stdout, buf+i, size);
+	call_prom("write", 3, 1, prom.stdout, buf+i, size);
 }
 
 static void __init prom_printf(const char *format, ...)
@@ -330,19 +326,18 @@ static void __init prom_printf(const cha
 	va_list args;
 	unsigned long v;
 	long vs;
-	struct prom_t *_prom = &RELOC(prom);
 
 	va_start(args, format);
 	for (p = format; *p != 0; p = q) {
 		for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
 			;
 		if (q > p)
-			call_prom("write", 3, 1, _prom->stdout, p, q - p);
+			call_prom("write", 3, 1, prom.stdout, p, q - p);
 		if (*q == 0)
 			break;
 		if (*q == '\n') {
 			++q;
-			call_prom("write", 3, 1, _prom->stdout,
+			call_prom("write", 3, 1, prom.stdout,
 				  ADDR("\r\n"), 2);
 			continue;
 		}
@@ -364,7 +359,7 @@ static void __init prom_printf(const cha
 			++q;
 			vs = va_arg(args, int);
 			if (vs < 0) {
-				prom_print(RELOC("-"));
+				prom_print("-");
 				vs = -vs;
 			}
 			prom_print_dec(vs);
@@ -385,7 +380,7 @@ static void __init prom_printf(const cha
 				++q;
 				vs = va_arg(args, long);
 				if (vs < 0) {
-					prom_print(RELOC("-"));
+					prom_print("-");
 					vs = -vs;
 				}
 				prom_print_dec(vs);
@@ -399,7 +394,6 @@ static void __init prom_printf(const cha
 static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
 				unsigned long align)
 {
-	struct prom_t *_prom = &RELOC(prom);
 
 	if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) {
 		/*
@@ -410,21 +404,21 @@ static unsigned int __init prom_claim(un
 		prom_arg_t result;
 
 		ret = call_prom_ret("call-method", 5, 2, &result,
-				    ADDR("claim"), _prom->memory,
+				    ADDR("claim"), prom.memory,
 				    align, size, virt);
 		if (ret != 0 || result == -1)
 			return -1;
 		ret = call_prom_ret("call-method", 5, 2, &result,
-				    ADDR("claim"), _prom->mmumap,
+				    ADDR("claim"), prom.mmumap,
 				    align, size, virt);
 		if (ret != 0) {
 			call_prom("call-method", 4, 1, ADDR("release"),
-				  _prom->memory, size, virt);
+				  prom.memory, size, virt);
 			return -1;
 		}
 		/* the 0x12 is M (coherence) + PP == read/write */
 		call_prom("call-method", 6, 1,
-			  ADDR("map"), _prom->mmumap, 0x12, size, virt, virt);
+			  ADDR("map"), prom.mmumap, 0x12, size, virt, virt);
 		return virt;
 	}
 	return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size,
@@ -436,7 +430,7 @@ static void __init __attribute__((noretu
 	prom_print(reason);
 	/* Do not call exit because it clears the screen on pmac
 	 * it also causes some sort of double-fault on early pmacs */
-	if (RELOC(of_platform) == PLATFORM_POWERMAC)
+	if (of_platform == PLATFORM_POWERMAC)
 		asm("trap\n");
 
 	/* ToDo: should put up an SRC here on pSeries */
@@ -518,13 +512,13 @@ static int __init prom_setprop(phandle n
 	add_string(&p, tohex((u32)(unsigned long) value));
 	add_string(&p, tohex(valuelen));
 	add_string(&p, tohex(ADDR(pname)));
-	add_string(&p, tohex(strlen(RELOC(pname))));
+	add_string(&p, tohex(strlen(pname)));
 	add_string(&p, "property");
 	*p = 0;
 	return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);
 }
 
-/* We can't use the standard versions because of RELOC headaches. */
+/* We can't use the standard versions because of relocation headaches. */
 #define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
 			 || ('a' <= (c) && (c) <= 'f') \
 			 || ('A' <= (c) && (c) <= 'F'))
@@ -591,43 +585,42 @@ static unsigned long prom_memparse(const
  */
 static void __init early_cmdline_parse(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
 	const char *opt;
 
 	char *p;
 	int l = 0;
 
-	RELOC(prom_cmd_line[0]) = 0;
-	p = RELOC(prom_cmd_line);
-	if ((long)_prom->chosen > 0)
-		l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
+	prom_cmd_line[0] = 0;
+	p = prom_cmd_line;
+	if ((long)prom.chosen > 0)
+		l = prom_getprop(prom.chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
 #ifdef CONFIG_CMDLINE
 	if (l <= 0 || p[0] == '\0') /* dbl check */
-		strlcpy(RELOC(prom_cmd_line),
-			RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line));
+		strlcpy(prom_cmd_line,
+			CONFIG_CMDLINE, sizeof(prom_cmd_line));
 #endif /* CONFIG_CMDLINE */
-	prom_printf("command line: %s\n", RELOC(prom_cmd_line));
+	prom_printf("command line: %s\n", prom_cmd_line);
 
 #ifdef CONFIG_PPC64
-	opt = strstr(RELOC(prom_cmd_line), RELOC("iommu="));
+	opt = strstr(prom_cmd_line, "iommu=");
 	if (opt) {
 		prom_printf("iommu opt is: %s\n", opt);
 		opt += 6;
 		while (*opt && *opt == ' ')
 			opt++;
-		if (!strncmp(opt, RELOC("off"), 3))
-			RELOC(prom_iommu_off) = 1;
-		else if (!strncmp(opt, RELOC("force"), 5))
-			RELOC(prom_iommu_force_on) = 1;
+		if (!strncmp(opt, "off", 3))
+			prom_iommu_off = 1;
+		else if (!strncmp(opt, "force", 5))
+			prom_iommu_force_on = 1;
 	}
 #endif
-	opt = strstr(RELOC(prom_cmd_line), RELOC("mem="));
+	opt = strstr(prom_cmd_line, "mem=");
 	if (opt) {
 		opt += 4;
-		RELOC(prom_memory_limit) = prom_memparse(opt, (const char **)&opt);
+		prom_memory_limit = prom_memparse(opt, (const char **)&opt);
 #ifdef CONFIG_PPC64
 		/* Align to 16 MB == size of ppc64 large page */
-		RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000);
+		prom_memory_limit = ALIGN(prom_memory_limit, 0x1000000);
 #endif
 	}
 }
@@ -880,7 +873,7 @@ static int __init prom_count_smt_threads
 		type[0] = 0;
 		prom_getprop(node, "device_type", type, sizeof(type));
 
-		if (strcmp(type, RELOC("cpu")))
+		if (strcmp(type, "cpu"))
 			continue;
 		/*
 		 * There is an entry for each smt thread, each entry being
@@ -998,21 +991,21 @@ static void __init prom_send_capabilitie
  */
 static unsigned long __init alloc_up(unsigned long size, unsigned long align)
 {
-	unsigned long base = RELOC(alloc_bottom);
+	unsigned long base = alloc_bottom;
 	unsigned long addr = 0;
 
 	if (align)
 		base = _ALIGN_UP(base, align);
 	prom_debug("alloc_up(%x, %x)\n", size, align);
-	if (RELOC(ram_top) == 0)
+	if (ram_top == 0)
 		prom_panic("alloc_up() called with mem not initialized\n");
 
 	if (align)
-		base = _ALIGN_UP(RELOC(alloc_bottom), align);
+		base = _ALIGN_UP(alloc_bottom, align);
 	else
-		base = RELOC(alloc_bottom);
+		base = alloc_bottom;
 
-	for(; (base + size) <= RELOC(alloc_top); 
+	for(; (base + size) <= alloc_top; 
 	    base = _ALIGN_UP(base + 0x100000, align)) {
 		prom_debug("    trying: 0x%x\n\r", base);
 		addr = (unsigned long)prom_claim(base, size, 0);
@@ -1024,14 +1017,14 @@ static unsigned long __init alloc_up(uns
 	}
 	if (addr == 0)
 		return 0;
-	RELOC(alloc_bottom) = addr + size;
+	alloc_bottom = addr + size;
 
 	prom_debug(" -> %x\n", addr);
-	prom_debug("  alloc_bottom : %x\n", RELOC(alloc_bottom));
-	prom_debug("  alloc_top    : %x\n", RELOC(alloc_top));
-	prom_debug("  alloc_top_hi : %x\n", RELOC(alloc_top_high));
-	prom_debug("  rmo_top      : %x\n", RELOC(rmo_top));
-	prom_debug("  ram_top      : %x\n", RELOC(ram_top));
+	prom_debug("  alloc_bottom : %x\n", alloc_bottom);
+	prom_debug("  alloc_top    : %x\n", alloc_top);
+	prom_debug("  alloc_top_hi : %x\n", alloc_top_high);
+	prom_debug("  rmo_top      : %x\n", rmo_top);
+	prom_debug("  ram_top      : %x\n", ram_top);
 
 	return addr;
 }
@@ -1047,32 +1040,32 @@ static unsigned long __init alloc_down(u
 	unsigned long base, addr = 0;
 
 	prom_debug("alloc_down(%x, %x, %s)\n", size, align,
-		   highmem ? RELOC("(high)") : RELOC("(low)"));
-	if (RELOC(ram_top) == 0)
+		   highmem ? "(high)" : "(low)");
+	if (ram_top == 0)
 		prom_panic("alloc_down() called with mem not initialized\n");
 
 	if (highmem) {
 		/* Carve out storage for the TCE table. */
-		addr = _ALIGN_DOWN(RELOC(alloc_top_high) - size, align);
-		if (addr <= RELOC(alloc_bottom))
+		addr = _ALIGN_DOWN(alloc_top_high - size, align);
+		if (addr <= alloc_bottom)
 			return 0;
 		/* Will we bump into the RMO ? If yes, check out that we
 		 * didn't overlap existing allocations there, if we did,
 		 * we are dead, we must be the first in town !
 		 */
-		if (addr < RELOC(rmo_top)) {
+		if (addr < rmo_top) {
 			/* Good, we are first */
-			if (RELOC(alloc_top) == RELOC(rmo_top))
-				RELOC(alloc_top) = RELOC(rmo_top) = addr;
+			if (alloc_top == rmo_top)
+				alloc_top = rmo_top = addr;
 			else
 				return 0;
 		}
-		RELOC(alloc_top_high) = addr;
+		alloc_top_high = addr;
 		goto bail;
 	}
 
-	base = _ALIGN_DOWN(RELOC(alloc_top) - size, align);
-	for (; base > RELOC(alloc_bottom);
+	base = _ALIGN_DOWN(alloc_top - size, align);
+	for (; base > alloc_bottom;
 	     base = _ALIGN_DOWN(base - 0x100000, align))  {
 		prom_debug("    trying: 0x%x\n\r", base);
 		addr = (unsigned long)prom_claim(base, size, 0);
@@ -1082,15 +1075,15 @@ static unsigned long __init alloc_down(u
 	}
 	if (addr == 0)
 		return 0;
-	RELOC(alloc_top) = addr;
+	alloc_top = addr;
 
  bail:
 	prom_debug(" -> %x\n", addr);
-	prom_debug("  alloc_bottom : %x\n", RELOC(alloc_bottom));
-	prom_debug("  alloc_top    : %x\n", RELOC(alloc_top));
-	prom_debug("  alloc_top_hi : %x\n", RELOC(alloc_top_high));
-	prom_debug("  rmo_top      : %x\n", RELOC(rmo_top));
-	prom_debug("  ram_top      : %x\n", RELOC(ram_top));
+	prom_debug("  alloc_bottom : %x\n", alloc_bottom);
+	prom_debug("  alloc_top    : %x\n", alloc_top);
+	prom_debug("  alloc_top_hi : %x\n", alloc_top_high);
+	prom_debug("  rmo_top      : %x\n", rmo_top);
+	prom_debug("  ram_top      : %x\n", ram_top);
 
 	return addr;
 }
@@ -1130,7 +1123,7 @@ static unsigned long __init prom_next_ce
 static void __init reserve_mem(u64 base, u64 size)
 {
 	u64 top = base + size;
-	unsigned long cnt = RELOC(mem_reserve_cnt);
+	unsigned long cnt = mem_reserve_cnt;
 
 	if (size == 0)
 		return;
@@ -1145,9 +1138,9 @@ static void __init reserve_mem(u64 base,
 
 	if (cnt >= (MEM_RESERVE_MAP_SIZE - 1))
 		prom_panic("Memory reserve map exhausted !\n");
-	RELOC(mem_reserve_map)[cnt].base = base;
-	RELOC(mem_reserve_map)[cnt].size = size;
-	RELOC(mem_reserve_cnt) = cnt + 1;
+	mem_reserve_map[cnt].base = base;
+	mem_reserve_map[cnt].size = size;
+	mem_reserve_cnt = cnt + 1;
 }
 
 /*
@@ -1160,7 +1153,6 @@ static void __init prom_init_mem(void)
 	char *path, type[64];
 	unsigned int plen;
 	cell_t *p, *endp;
-	struct prom_t *_prom = &RELOC(prom);
 	u32 rac, rsc;
 
 	/*
@@ -1169,14 +1161,14 @@ static void __init prom_init_mem(void)
 	 * 2) top of memory
 	 */
 	rac = 2;
-	prom_getprop(_prom->root, "#address-cells", &rac, sizeof(rac));
+	prom_getprop(prom.root, "#address-cells", &rac, sizeof(rac));
 	rsc = 1;
-	prom_getprop(_prom->root, "#size-cells", &rsc, sizeof(rsc));
+	prom_getprop(prom.root, "#size-cells", &rsc, sizeof(rsc));
 	prom_debug("root_addr_cells: %x\n", (unsigned long) rac);
 	prom_debug("root_size_cells: %x\n", (unsigned long) rsc);
 
 	prom_debug("scanning memory:\n");
-	path = RELOC(prom_scratch);
+	path = prom_scratch;
 
 	for (node = 0; prom_next_node(&node); ) {
 		type[0] = 0;
@@ -1189,15 +1181,15 @@ static void __init prom_init_mem(void)
 			 */
 			prom_getprop(node, "name", type, sizeof(type));
 		}
-		if (strcmp(type, RELOC("memory")))
+		if (strcmp(type, "memory"))
 			continue;
 
-		plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf));
+		plen = prom_getprop(node, "reg", regbuf, sizeof(regbuf));
 		if (plen > sizeof(regbuf)) {
 			prom_printf("memory node too large for buffer !\n");
 			plen = sizeof(regbuf);
 		}
-		p = RELOC(regbuf);
+		p = regbuf;
 		endp = p + (plen / sizeof(cell_t));
 
 #ifdef DEBUG_PROM
@@ -1215,14 +1207,14 @@ static void __init prom_init_mem(void)
 			if (size == 0)
 				continue;
 			prom_debug("    %x %x\n", base, size);
-			if (base == 0 && (RELOC(of_platform) & PLATFORM_LPAR))
-				RELOC(rmo_top) = size;
-			if ((base + size) > RELOC(ram_top))
-				RELOC(ram_top) = base + size;
+			if (base == 0 && (of_platform & PLATFORM_LPAR))
+				rmo_top = size;
+			if ((base + size) > ram_top)
+				ram_top = base + size;
 		}
 	}
 
-	RELOC(alloc_bottom) = PAGE_ALIGN((unsigned long)&RELOC(_end) + 0x4000);
+	alloc_bottom = PAGE_ALIGN((unsigned long)&_end + 0x4000);
 
 	/*
 	 * If prom_memory_limit is set we reduce the upper limits *except* for
@@ -1230,20 +1222,20 @@ static void __init prom_init_mem(void)
 	 * TCE's up there.
 	 */
 
-	RELOC(alloc_top_high) = RELOC(ram_top);
+	alloc_top_high = ram_top;
 
-	if (RELOC(prom_memory_limit)) {
-		if (RELOC(prom_memory_limit) <= RELOC(alloc_bottom)) {
+	if (prom_memory_limit) {
+		if (prom_memory_limit <= alloc_bottom) {
 			prom_printf("Ignoring mem=%x <= alloc_bottom.\n",
-				RELOC(prom_memory_limit));
-			RELOC(prom_memory_limit) = 0;
-		} else if (RELOC(prom_memory_limit) >= RELOC(ram_top)) {
+				prom_memory_limit);
+			prom_memory_limit = 0;
+		} else if (prom_memory_limit >= ram_top) {
 			prom_printf("Ignoring mem=%x >= ram_top.\n",
-				RELOC(prom_memory_limit));
-			RELOC(prom_memory_limit) = 0;
+				prom_memory_limit);
+			prom_memory_limit = 0;
 		} else {
-			RELOC(ram_top) = RELOC(prom_memory_limit);
-			RELOC(rmo_top) = min(RELOC(rmo_top), RELOC(prom_memory_limit));
+			ram_top = prom_memory_limit;
+			rmo_top = min(rmo_top, prom_memory_limit);
 		}
 	}
 
@@ -1255,36 +1247,35 @@ static void __init prom_init_mem(void)
 	 * Since 768MB is plenty of room, and we need to cap to something
 	 * reasonable on 32-bit, cap at 768MB on all machines.
 	 */
-	if (!RELOC(rmo_top))
-		RELOC(rmo_top) = RELOC(ram_top);
-	RELOC(rmo_top) = min(0x30000000ul, RELOC(rmo_top));
-	RELOC(alloc_top) = RELOC(rmo_top);
-	RELOC(alloc_top_high) = RELOC(ram_top);
+	if (!rmo_top)
+		rmo_top = ram_top;
+	rmo_top = min(0x30000000ul, rmo_top);
+	alloc_top = rmo_top;
+	alloc_top_high = ram_top;
 
 	/*
 	 * Check if we have an initrd after the kernel but still inside
 	 * the RMO.  If we do move our bottom point to after it.
 	 */
-	if (RELOC(prom_initrd_start) &&
-	    RELOC(prom_initrd_start) < RELOC(rmo_top) &&
-	    RELOC(prom_initrd_end) > RELOC(alloc_bottom))
-		RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end));
+	if (prom_initrd_start &&
+	    prom_initrd_start < rmo_top &&
+	    prom_initrd_end > alloc_bottom)
+		alloc_bottom = PAGE_ALIGN(prom_initrd_end);
 
 	prom_printf("memory layout at init:\n");
-	prom_printf("  memory_limit : %x (16 MB aligned)\n", RELOC(prom_memory_limit));
-	prom_printf("  alloc_bottom : %x\n", RELOC(alloc_bottom));
-	prom_printf("  alloc_top    : %x\n", RELOC(alloc_top));
-	prom_printf("  alloc_top_hi : %x\n", RELOC(alloc_top_high));
-	prom_printf("  rmo_top      : %x\n", RELOC(rmo_top));
-	prom_printf("  ram_top      : %x\n", RELOC(ram_top));
+	prom_printf("  memory_limit : %x (16 MB aligned)\n", prom_memory_limit);
+	prom_printf("  alloc_bottom : %x\n", alloc_bottom);
+	prom_printf("  alloc_top    : %x\n", alloc_top);
+	prom_printf("  alloc_top_hi : %x\n", alloc_top_high);
+	prom_printf("  rmo_top      : %x\n", rmo_top);
+	prom_printf("  ram_top      : %x\n", ram_top);
 }
 
 static void __init prom_close_stdin(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
 	ihandle val;
 
-	if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0)
+	if (prom_getprop(prom.chosen, "stdin", &val, sizeof(val)) > 0)
 		call_prom("close", 1, 0, val);
 }
 
@@ -1325,19 +1316,19 @@ static void __init prom_query_opal(void)
 	}
 
 	prom_printf("Querying for OPAL presence... ");
-	rc = opal_query_takeover(&RELOC(prom_opal_size),
-				 &RELOC(prom_opal_align));
+	rc = opal_query_takeover(&prom_opal_size,
+				 &prom_opal_align);
 	prom_debug("(rc = %ld) ", rc);
 	if (rc != 0) {
 		prom_printf("not there.\n");
 		return;
 	}
-	RELOC(of_platform) = PLATFORM_OPAL;
+	of_platform = PLATFORM_OPAL;
 	prom_printf(" there !\n");
-	prom_debug("  opal_size  = 0x%lx\n", RELOC(prom_opal_size));
-	prom_debug("  opal_align = 0x%lx\n", RELOC(prom_opal_align));
-	if (RELOC(prom_opal_align) < 0x10000)
-		RELOC(prom_opal_align) = 0x10000;
+	prom_debug("  opal_size  = 0x%lx\n", prom_opal_size);
+	prom_debug("  opal_align = 0x%lx\n", prom_opal_align);
+	if (prom_opal_align < 0x10000)
+		prom_opal_align = 0x10000;
 }
 
 static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...)
@@ -1358,8 +1349,8 @@ static int prom_rtas_call(int token, int
 	for (i = 0; i < nret; ++i)
 		rtas_args.rets[i] = 0;
 
-	opal_enter_rtas(&rtas_args, RELOC(prom_rtas_data),
-			RELOC(prom_rtas_entry));
+	opal_enter_rtas(&rtas_args, prom_rtas_data,
+			prom_rtas_entry);
 
 	if (nret > 1 && outputs != NULL)
 		for (i = 0; i < nret-1; ++i)
@@ -1374,9 +1365,8 @@ static void __init prom_opal_hold_cpus(v
 	phandle node;
 	char type[64];
 	u32 servers[8];
-	struct prom_t *_prom = &RELOC(prom);
-	void *entry = (unsigned long *)&RELOC(opal_secondary_entry);
-	struct opal_secondary_data *data = &RELOC(opal_secondary_data);
+	void *entry = (unsigned long *)&opal_secondary_entry;
+	struct opal_secondary_data *data = &opal_secondary_data;
 
 	prom_debug("prom_opal_hold_cpus: start...\n");
 	prom_debug("    - entry       = 0x%x\n", entry);
@@ -1389,12 +1379,12 @@ static void __init prom_opal_hold_cpus(v
 	for (node = 0; prom_next_node(&node); ) {
 		type[0] = 0;
 		prom_getprop(node, "device_type", type, sizeof(type));
-		if (strcmp(type, RELOC("cpu")) != 0)
+		if (strcmp(type, "cpu") != 0)
 			continue;
 
 		/* Skip non-configured cpus. */
 		if (prom_getprop(node, "status", type, sizeof(type)) > 0)
-			if (strcmp(type, RELOC("okay")) != 0)
+			if (strcmp(type, "okay") != 0)
 				continue;
 
 		cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers,
@@ -1405,7 +1395,7 @@ static void __init prom_opal_hold_cpus(v
 		for (i = 0; i < cnt; i++) {
 			cpu = servers[i];
 			prom_debug("CPU %d ... ", cpu);
-			if (cpu == _prom->cpu) {
+			if (cpu == prom.cpu) {
 				prom_debug("booted !\n");
 				continue;
 			}
@@ -1416,7 +1406,7 @@ static void __init prom_opal_hold_cpus(v
 			 * spinloop.
 			 */
 			data->ack = -1;
-			rc = prom_rtas_call(RELOC(prom_rtas_start_cpu), 3, 1,
+			rc = prom_rtas_call(prom_rtas_start_cpu, 3, 1,
 					    NULL, cpu, entry, data);
 			prom_debug("rtas rc=%d ...", rc);
 
@@ -1436,21 +1426,21 @@ static void __init prom_opal_hold_cpus(v
 
 static void __init prom_opal_takeover(void)
 {
-	struct opal_secondary_data *data = &RELOC(opal_secondary_data);
+	struct opal_secondary_data *data = &opal_secondary_data;
 	struct opal_takeover_args *args = &data->args;
-	u64 align = RELOC(prom_opal_align);
+	u64 align = prom_opal_align;
 	u64 top_addr, opal_addr;
 
-	args->k_image	= (u64)RELOC(_stext);
+	args->k_image	= (u64)_stext;
 	args->k_size	= _end - _stext;
 	args->k_entry	= 0;
 	args->k_entry2	= 0x60;
 
 	top_addr = _ALIGN_UP(args->k_size, align);
 
-	if (RELOC(prom_initrd_start) != 0) {
-		args->rd_image = RELOC(prom_initrd_start);
-		args->rd_size = RELOC(prom_initrd_end) - args->rd_image;
+	if (prom_initrd_start != 0) {
+		args->rd_image = prom_initrd_start;
+		args->rd_size = prom_initrd_end - args->rd_image;
 		args->rd_loc = top_addr;
 		top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align);
 	}
@@ -1462,13 +1452,13 @@ static void __init prom_opal_takeover(vo
 	 * has plenty of memory, and we ask for the HAL for now to
 	 * be just below the 1G point, or above the initrd
 	 */
-	opal_addr = _ALIGN_DOWN(0x40000000 - RELOC(prom_opal_size), align);
+	opal_addr = _ALIGN_DOWN(0x40000000 - prom_opal_size, align);
 	if (opal_addr < top_addr)
 		opal_addr = top_addr;
 	args->hal_addr = opal_addr;
 
 	/* Copy the command line to the kernel image */
-	strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line),
+	strlcpy(boot_command_line, prom_cmd_line,
 		COMMAND_LINE_SIZE);
 
 	prom_debug("  k_image    = 0x%lx\n", args->k_image);
@@ -1550,8 +1540,8 @@ static void __init prom_instantiate_opal
 		     &entry, sizeof(entry));
 
 #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
-	RELOC(prom_opal_base) = base;
-	RELOC(prom_opal_entry) = entry;
+	prom_opal_base = base;
+	prom_opal_entry = entry;
 #endif
 	prom_debug("prom_instantiate_opal: end...\n");
 }
@@ -1609,9 +1599,9 @@ static void __init prom_instantiate_rtas
 
 #ifdef CONFIG_PPC_POWERNV
 	/* PowerVN takeover hack */
-	RELOC(prom_rtas_data) = base;
-	RELOC(prom_rtas_entry) = entry;
-	prom_getprop(rtas_node, "start-cpu", &RELOC(prom_rtas_start_cpu), 4);
+	prom_rtas_data = base;
+	prom_rtas_entry = entry;
+	prom_getprop(rtas_node, "start-cpu", &prom_rtas_start_cpu, 4);
 #endif
 	prom_debug("rtas base     = 0x%x\n", base);
 	prom_debug("rtas entry    = 0x%x\n", entry);
@@ -1686,20 +1676,20 @@ static void __init prom_initialize_tce_t
 	phandle node;
 	ihandle phb_node;
 	char compatible[64], type[64], model[64];
-	char *path = RELOC(prom_scratch);
+	char *path = prom_scratch;
 	u64 base, align;
 	u32 minalign, minsize;
 	u64 tce_entry, *tce_entryp;
 	u64 local_alloc_top, local_alloc_bottom;
 	u64 i;
 
-	if (RELOC(prom_iommu_off))
+	if (prom_iommu_off)
 		return;
 
 	prom_debug("starting prom_initialize_tce_table\n");
 
 	/* Cache current top of allocs so we reserve a single block */
-	local_alloc_top = RELOC(alloc_top_high);
+	local_alloc_top = alloc_top_high;
 	local_alloc_bottom = local_alloc_top;
 
 	/* Search all nodes looking for PHBs. */
@@ -1712,19 +1702,19 @@ static void __init prom_initialize_tce_t
 		prom_getprop(node, "device_type", type, sizeof(type));
 		prom_getprop(node, "model", model, sizeof(model));
 
-		if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL))
+		if ((type[0] == 0) || (strstr(type, "pci") == NULL))
 			continue;
 
 		/* Keep the old logic intact to avoid regression. */
 		if (compatible[0] != 0) {
-			if ((strstr(compatible, RELOC("python")) == NULL) &&
-			    (strstr(compatible, RELOC("Speedwagon")) == NULL) &&
-			    (strstr(compatible, RELOC("Winnipeg")) == NULL))
+			if ((strstr(compatible, "python") == NULL) &&
+			    (strstr(compatible, "Speedwagon") == NULL) &&
+			    (strstr(compatible, "Winnipeg") == NULL))
 				continue;
 		} else if (model[0] != 0) {
-			if ((strstr(model, RELOC("ython")) == NULL) &&
-			    (strstr(model, RELOC("peedwagon")) == NULL) &&
-			    (strstr(model, RELOC("innipeg")) == NULL))
+			if ((strstr(model, "ython") == NULL) &&
+			    (strstr(model, "peedwagon") == NULL) &&
+			    (strstr(model, "innipeg") == NULL))
 				continue;
 		}
 
@@ -1803,8 +1793,8 @@ static void __init prom_initialize_tce_t
 
 	/* These are only really needed if there is a memory limit in
 	 * effect, but we don't know so export them always. */
-	RELOC(prom_tce_alloc_start) = local_alloc_bottom;
-	RELOC(prom_tce_alloc_end) = local_alloc_top;
+	prom_tce_alloc_start = local_alloc_bottom;
+	prom_tce_alloc_end = local_alloc_top;
 
 	/* Flag the first invalid entry */
 	prom_debug("ending prom_initialize_tce_table\n");
@@ -1841,7 +1831,6 @@ static void __init prom_hold_cpus(void)
 	unsigned int reg;
 	phandle node;
 	char type[64];
-	struct prom_t *_prom = &RELOC(prom);
 	unsigned long *spinloop
 		= (void *) LOW_ADDR(__secondary_hold_spinloop);
 	unsigned long *acknowledge
@@ -1867,12 +1856,12 @@ static void __init prom_hold_cpus(void)
 	for (node = 0; prom_next_node(&node); ) {
 		type[0] = 0;
 		prom_getprop(node, "device_type", type, sizeof(type));
-		if (strcmp(type, RELOC("cpu")) != 0)
+		if (strcmp(type, "cpu") != 0)
 			continue;
 
 		/* Skip non-configured cpus. */
 		if (prom_getprop(node, "status", type, sizeof(type)) > 0)
-			if (strcmp(type, RELOC("okay")) != 0)
+			if (strcmp(type, "okay") != 0)
 				continue;
 
 		reg = -1;
@@ -1886,7 +1875,7 @@ static void __init prom_hold_cpus(void)
 		 */
 		*acknowledge = (unsigned long)-1;
 
-		if (reg != _prom->cpu) {
+		if (reg != prom.cpu) {
 			/* Primary Thread of non-boot cpu or any thread */
 			prom_printf("starting cpu hw idx %lu... ", reg);
 			call_prom("start-cpu", 3, 0, node,
@@ -1913,22 +1902,20 @@ static void __init prom_hold_cpus(void)
 
 static void __init prom_init_client_services(unsigned long pp)
 {
-	struct prom_t *_prom = &RELOC(prom);
-
 	/* Get a handle to the prom entry point before anything else */
-	RELOC(prom_entry) = pp;
+	prom_entry = pp;
 
 	/* get a handle for the stdout device */
-	_prom->chosen = call_prom("finddevice", 1, 1, ADDR("/chosen"));
-	if (!PHANDLE_VALID(_prom->chosen))
+	prom.chosen = call_prom("finddevice", 1, 1, ADDR("/chosen"));
+	if (!PHANDLE_VALID(prom.chosen))
 		prom_panic("cannot find chosen"); /* msg won't be printed :( */
 
 	/* get device tree root */
-	_prom->root = call_prom("finddevice", 1, 1, ADDR("/"));
-	if (!PHANDLE_VALID(_prom->root))
+	prom.root = call_prom("finddevice", 1, 1, ADDR("/"));
+	if (!PHANDLE_VALID(prom.root))
 		prom_panic("cannot find device tree root"); /* msg won't be printed :( */
 
-	_prom->mmumap = 0;
+	prom.mmumap = 0;
 }
 
 #ifdef CONFIG_PPC32
@@ -1939,7 +1926,6 @@ static void __init prom_init_client_serv
  */
 static void __init prom_find_mmu(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
 	phandle oprom;
 	char version[64];
 
@@ -1957,10 +1943,10 @@ static void __init prom_find_mmu(void)
 		call_prom("interpret", 1, 1, "dev /memory 0 to allow-reclaim");
 	} else
 		return;
-	_prom->memory = call_prom("open", 1, 1, ADDR("/memory"));
-	prom_getprop(_prom->chosen, "mmu", &_prom->mmumap,
-		     sizeof(_prom->mmumap));
-	if (!IHANDLE_VALID(_prom->memory) || !IHANDLE_VALID(_prom->mmumap))
+	prom.memory = call_prom("open", 1, 1, ADDR("/memory"));
+	prom_getprop(prom.chosen, "mmu", &prom.mmumap,
+		     sizeof(prom.mmumap));
+	if (!IHANDLE_VALID(prom.memory) || !IHANDLE_VALID(prom.mmumap))
 		of_workarounds &= ~OF_WA_CLAIM;		/* hmmm */
 }
 #else
@@ -1969,36 +1955,34 @@ static void __init prom_find_mmu(void)
 
 static void __init prom_init_stdout(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
-	char *path = RELOC(of_stdout_device);
+	char *path = of_stdout_device;
 	char type[16];
 	u32 val;
 
-	if (prom_getprop(_prom->chosen, "stdout", &val, sizeof(val)) <= 0)
+	if (prom_getprop(prom.chosen, "stdout", &val, sizeof(val)) <= 0)
 		prom_panic("cannot find stdout");
 
-	_prom->stdout = val;
+	prom.stdout = val;
 
 	/* Get the full OF pathname of the stdout device */
 	memset(path, 0, 256);
-	call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255);
-	val = call_prom("instance-to-package", 1, 1, _prom->stdout);
-	prom_setprop(_prom->chosen, "/chosen", "linux,stdout-package",
+	call_prom("instance-to-path", 3, 1, prom.stdout, path, 255);
+	val = call_prom("instance-to-package", 1, 1, prom.stdout);
+	prom_setprop(prom.chosen, "/chosen", "linux,stdout-package",
 		     &val, sizeof(val));
-	prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device));
-	prom_setprop(_prom->chosen, "/chosen", "linux,stdout-path",
+	prom_printf("OF stdout device is: %s\n", of_stdout_device);
+	prom_setprop(prom.chosen, "/chosen", "linux,stdout-path",
 		     path, strlen(path) + 1);
 
 	/* If it's a display, note it */
 	memset(type, 0, sizeof(type));
 	prom_getprop(val, "device_type", type, sizeof(type));
-	if (strcmp(type, RELOC("display")) == 0)
+	if (strcmp(type, "display") == 0)
 		prom_setprop(val, path, "linux,boot-display", NULL, 0);
 }
 
 static int __init prom_find_machine_type(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
 	char compat[256];
 	int len, i = 0;
 #ifdef CONFIG_PPC64
@@ -2007,7 +1991,7 @@ static int __init prom_find_machine_type
 #endif
 
 	/* Look for a PowerMac or a Cell */
-	len = prom_getprop(_prom->root, "compatible",
+	len = prom_getprop(prom.root, "compatible",
 			   compat, sizeof(compat)-1);
 	if (len > 0) {
 		compat[len] = 0;
@@ -2016,16 +2000,16 @@ static int __init prom_find_machine_type
 			int sl = strlen(p);
 			if (sl == 0)
 				break;
-			if (strstr(p, RELOC("Power Macintosh")) ||
-			    strstr(p, RELOC("MacRISC")))
+			if (strstr(p, "Power Macintosh") ||
+			    strstr(p, "MacRISC"))
 				return PLATFORM_POWERMAC;
 #ifdef CONFIG_PPC64
 			/* We must make sure we don't detect the IBM Cell
 			 * blades as pSeries due to some firmware issues,
 			 * so we do it here.
 			 */
-			if (strstr(p, RELOC("IBM,CBEA")) ||
-			    strstr(p, RELOC("IBM,CPBW-1.0")))
+			if (strstr(p, "IBM,CBEA") ||
+			    strstr(p, "IBM,CPBW-1.0"))
 				return PLATFORM_GENERIC;
 #endif /* CONFIG_PPC64 */
 			i += sl + 1;
@@ -2042,11 +2026,11 @@ static int __init prom_find_machine_type
 	 *    non-IBM designs !
 	 *  - it has /rtas
 	 */
-	len = prom_getprop(_prom->root, "device_type",
+	len = prom_getprop(prom.root, "device_type",
 			   compat, sizeof(compat)-1);
 	if (len <= 0)
 		return PLATFORM_GENERIC;
-	if (strcmp(compat, RELOC("chrp")))
+	if (strcmp(compat, "chrp"))
 		return PLATFORM_GENERIC;
 
 	/* Default to pSeries. We need to know if we are running LPAR */
@@ -2108,11 +2092,11 @@ static void __init prom_check_displays(v
 	for (node = 0; prom_next_node(&node); ) {
 		memset(type, 0, sizeof(type));
 		prom_getprop(node, "device_type", type, sizeof(type));
-		if (strcmp(type, RELOC("display")) != 0)
+		if (strcmp(type, "display") != 0)
 			continue;
 
 		/* It seems OF doesn't null-terminate the path :-( */
-		path = RELOC(prom_scratch);
+		path = prom_scratch;
 		memset(path, 0, PROM_SCRATCH_SIZE);
 
 		/*
@@ -2136,15 +2120,15 @@ static void __init prom_check_displays(v
 
 		/* Setup a usable color table when the appropriate
 		 * method is available. Should update this to set-colors */
-		clut = RELOC(default_colors);
+		clut = default_colors;
 		for (i = 0; i < 16; i++, clut += 3)
 			if (prom_set_color(ih, i, clut[0], clut[1],
 					   clut[2]) != 0)
 				break;
 
 #ifdef CONFIG_LOGO_LINUX_CLUT224
-		clut = PTRRELOC(RELOC(logo_linux_clut224.clut));
-		for (i = 0; i < RELOC(logo_linux_clut224.clutsize); i++, clut += 3)
+		clut = PTRRELOC(logo_linux_clut224.clut);
+		for (i = 0; i < logo_linux_clut224.clutsize; i++, clut += 3)
 			if (prom_set_color(ih, i + 32, clut[0], clut[1],
 					   clut[2]) != 0)
 				break;
@@ -2164,8 +2148,8 @@ static void __init *make_room(unsigned l
 		unsigned long room, chunk;
 
 		prom_debug("Chunk exhausted, claiming more at %x...\n",
-			   RELOC(alloc_bottom));
-		room = RELOC(alloc_top) - RELOC(alloc_bottom);
+			   alloc_bottom);
+		room = alloc_top - alloc_bottom;
 		if (room > DEVTREE_CHUNK_SIZE)
 			room = DEVTREE_CHUNK_SIZE;
 		if (room < PAGE_SIZE)
@@ -2191,9 +2175,9 @@ static unsigned long __init dt_find_stri
 {
 	char *s, *os;
 
-	s = os = (char *)RELOC(dt_string_start);
+	s = os = (char *)dt_string_start;
 	s += 4;
-	while (s <  (char *)RELOC(dt_string_end)) {
+	while (s <  (char *)dt_string_end) {
 		if (strcmp(s, str) == 0)
 			return s - os;
 		s += strlen(s) + 1;
@@ -2215,10 +2199,10 @@ static void __init scan_dt_build_strings
 	unsigned long soff;
 	phandle child;
 
-	sstart =  (char *)RELOC(dt_string_start);
+	sstart =  (char *)dt_string_start;
 
 	/* get and store all property names */
-	prev_name = RELOC("");
+	prev_name = "";
 	for (;;) {
 		/* 64 is max len of name including nul. */
 		namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1);
@@ -2229,9 +2213,9 @@ static void __init scan_dt_build_strings
 		}
 
  		/* skip "name" */
- 		if (strcmp(namep, RELOC("name")) == 0) {
+ 		if (strcmp(namep, "name") == 0) {
  			*mem_start = (unsigned long)namep;
- 			prev_name = RELOC("name");
+ 			prev_name = "name";
  			continue;
  		}
 		/* get/create string entry */
@@ -2242,7 +2226,7 @@ static void __init scan_dt_build_strings
 		} else {
 			/* Trim off some if we can */
 			*mem_start = (unsigned long)namep + strlen(namep) + 1;
-			RELOC(dt_string_end) = *mem_start;
+			dt_string_end = *mem_start;
 		}
 		prev_name = namep;
 	}
@@ -2297,35 +2281,35 @@ static void __init scan_dt_build_struct(
 	}
 
 	/* get it again for debugging */
-	path = RELOC(prom_scratch);
+	path = prom_scratch;
 	memset(path, 0, PROM_SCRATCH_SIZE);
 	call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1);
 
 	/* get and store all properties */
-	prev_name = RELOC("");
-	sstart = (char *)RELOC(dt_string_start);
+	prev_name = "";
+	sstart = (char *)dt_string_start;
 	for (;;) {
 		if (call_prom("nextprop", 3, 1, node, prev_name,
-			      RELOC(pname)) != 1)
+			      pname) != 1)
 			break;
 
  		/* skip "name" */
- 		if (strcmp(RELOC(pname), RELOC("name")) == 0) {
- 			prev_name = RELOC("name");
+ 		if (strcmp(pname, "name") == 0) {
+ 			prev_name = "name";
  			continue;
  		}
 
 		/* find string offset */
-		soff = dt_find_string(RELOC(pname));
+		soff = dt_find_string(pname);
 		if (soff == 0) {
 			prom_printf("WARNING: Can't find string index for"
-				    " <%s>, node %s\n", RELOC(pname), path);
+				    " <%s>, node %s\n", pname, path);
 			break;
 		}
 		prev_name = sstart + soff;
 
 		/* get length */
-		l = call_prom("getproplen", 2, 1, node, RELOC(pname));
+		l = call_prom("getproplen", 2, 1, node, pname);
 
 		/* sanity checks */
 		if (l == PROM_ERROR)
@@ -2338,10 +2322,10 @@ static void __init scan_dt_build_struct(
 
 		/* push property content */
 		valp = make_room(mem_start, mem_end, l, 4);
-		call_prom("getprop", 4, 1, node, RELOC(pname), valp, l);
+		call_prom("getprop", 4, 1, node, pname, valp, l);
 		*mem_start = _ALIGN(*mem_start, 4);
 
-		if (!strcmp(RELOC(pname), RELOC("phandle")))
+		if (!strcmp(pname, "phandle"))
 			has_phandle = 1;
 	}
 
@@ -2349,7 +2333,7 @@ static void __init scan_dt_build_struct(
 	 * existed (can happen with OPAL)
 	 */
 	if (!has_phandle) {
-		soff = dt_find_string(RELOC("linux,phandle"));
+		soff = dt_find_string("linux,phandle");
 		if (soff == 0)
 			prom_printf("WARNING: Can't find string index for"
 				    " <linux-phandle> node %s\n", path);
@@ -2377,7 +2361,6 @@ static void __init flatten_device_tree(v
 	phandle root;
 	unsigned long mem_start, mem_end, room;
 	struct boot_param_header *hdr;
-	struct prom_t *_prom = &RELOC(prom);
 	char *namep;
 	u64 *rsvmap;
 
@@ -2385,10 +2368,10 @@ static void __init flatten_device_tree(v
 	 * Check how much room we have between alloc top & bottom (+/- a
 	 * few pages), crop to 1MB, as this is our "chunk" size
 	 */
-	room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000;
+	room = alloc_top - alloc_bottom - 0x4000;
 	if (room > DEVTREE_CHUNK_SIZE)
 		room = DEVTREE_CHUNK_SIZE;
-	prom_debug("starting device tree allocs at %x\n", RELOC(alloc_bottom));
+	prom_debug("starting device tree allocs at %x\n", alloc_bottom);
 
 	/* Now try to claim that */
 	mem_start = (unsigned long)alloc_up(room, PAGE_SIZE);
@@ -2405,66 +2388,66 @@ static void __init flatten_device_tree(v
 	mem_start = _ALIGN(mem_start, 4);
 	hdr = make_room(&mem_start, &mem_end,
 			sizeof(struct boot_param_header), 4);
-	RELOC(dt_header_start) = (unsigned long)hdr;
+	dt_header_start = (unsigned long)hdr;
 	rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8);
 
 	/* Start of strings */
 	mem_start = PAGE_ALIGN(mem_start);
-	RELOC(dt_string_start) = mem_start;
+	dt_string_start = mem_start;
 	mem_start += 4; /* hole */
 
 	/* Add "linux,phandle" in there, we'll need it */
 	namep = make_room(&mem_start, &mem_end, 16, 1);
-	strcpy(namep, RELOC("linux,phandle"));
+	strcpy(namep, "linux,phandle");
 	mem_start = (unsigned long)namep + strlen(namep) + 1;
 
 	/* Build string array */
 	prom_printf("Building dt strings...\n"); 
 	scan_dt_build_strings(root, &mem_start, &mem_end);
-	RELOC(dt_string_end) = mem_start;
+	dt_string_end = mem_start;
 
 	/* Build structure */
 	mem_start = PAGE_ALIGN(mem_start);
-	RELOC(dt_struct_start) = mem_start;
+	dt_struct_start = mem_start;
 	prom_printf("Building dt structure...\n"); 
 	scan_dt_build_struct(root, &mem_start, &mem_end);
 	dt_push_token(OF_DT_END, &mem_start, &mem_end);
-	RELOC(dt_struct_end) = PAGE_ALIGN(mem_start);
+	dt_struct_end = PAGE_ALIGN(mem_start);
 
 	/* Finish header */
-	hdr->boot_cpuid_phys = _prom->cpu;
+	hdr->boot_cpuid_phys = prom.cpu;
 	hdr->magic = OF_DT_HEADER;
-	hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start);
-	hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start);
-	hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start);
-	hdr->dt_strings_size = RELOC(dt_string_end) - RELOC(dt_string_start);
-	hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start);
+	hdr->totalsize = dt_struct_end - dt_header_start;
+	hdr->off_dt_struct = dt_struct_start - dt_header_start;
+	hdr->off_dt_strings = dt_string_start - dt_header_start;
+	hdr->dt_strings_size = dt_string_end - dt_string_start;
+	hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - dt_header_start;
 	hdr->version = OF_DT_VERSION;
 	/* Version 16 is not backward compatible */
 	hdr->last_comp_version = 0x10;
 
 	/* Copy the reserve map in */
-	memcpy(rsvmap, RELOC(mem_reserve_map), sizeof(mem_reserve_map));
+	memcpy(rsvmap, mem_reserve_map, sizeof(mem_reserve_map));
 
 #ifdef DEBUG_PROM
 	{
 		int i;
 		prom_printf("reserved memory map:\n");
-		for (i = 0; i < RELOC(mem_reserve_cnt); i++)
+		for (i = 0; i < mem_reserve_cnt; i++)
 			prom_printf("  %x - %x\n",
-				    RELOC(mem_reserve_map)[i].base,
-				    RELOC(mem_reserve_map)[i].size);
+				    mem_reserve_map[i].base,
+				    mem_reserve_map[i].size);
 	}
 #endif
 	/* Bump mem_reserve_cnt to cause further reservations to fail
 	 * since it's too late.
 	 */
-	RELOC(mem_reserve_cnt) = MEM_RESERVE_MAP_SIZE;
+	mem_reserve_cnt = MEM_RESERVE_MAP_SIZE;
 
 	prom_printf("Device tree strings 0x%x -> 0x%x\n",
-		    RELOC(dt_string_start), RELOC(dt_string_end)); 
+		    dt_string_start, dt_string_end);
 	prom_printf("Device tree struct  0x%x -> 0x%x\n",
-		    RELOC(dt_struct_start), RELOC(dt_struct_end));
+		    dt_struct_start, dt_struct_end);
 
 }
 
@@ -2519,7 +2502,6 @@ static void __init fixup_device_tree_map
 	phandle mc;
 	u32 mc_reg[4];
 	char *name = "/hostbridge@f8000000";
-	struct prom_t *_prom = &RELOC(prom);
 	u32 ac, sc;
 
 	mc = call_prom("finddevice", 1, 1, ADDR(name));
@@ -2529,8 +2511,8 @@ static void __init fixup_device_tree_map
 	if (prom_getproplen(mc, "reg") != 8)
 		return;
 
-	prom_getprop(_prom->root, "#address-cells", &ac, sizeof(ac));
-	prom_getprop(_prom->root, "#size-cells", &sc, sizeof(sc));
+	prom_getprop(prom.root, "#address-cells", &ac, sizeof(ac));
+	prom_getprop(prom.root, "#size-cells", &sc, sizeof(sc));
 	if ((ac != 2) || (sc != 2))
 		return;
 
@@ -2799,46 +2781,43 @@ static void __init fixup_device_tree(voi
 
 static void __init prom_find_boot_cpu(void)
 {
-	struct prom_t *_prom = &RELOC(prom);
 	u32 getprop_rval;
 	ihandle prom_cpu;
 	phandle cpu_pkg;
 
-	_prom->cpu = 0;
-	if (prom_getprop(_prom->chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0)
+	prom.cpu = 0;
+	if (prom_getprop(prom.chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0)
 		return;
 
 	cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu);
 
 	prom_getprop(cpu_pkg, "reg", &getprop_rval, sizeof(getprop_rval));
-	_prom->cpu = getprop_rval;
+	prom.cpu = getprop_rval;
 
-	prom_debug("Booting CPU hw index = %lu\n", _prom->cpu);
+	prom_debug("Booting CPU hw index = %lu\n", prom.cpu);
 }
 
 static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
-	struct prom_t *_prom = &RELOC(prom);
-
 	if (r3 && r4 && r4 != 0xdeadbeef) {
 		unsigned long val;
 
-		RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3;
-		RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4;
+		prom_initrd_start = is_kernel_addr(r3) ? __pa(r3) : r3;
+		prom_initrd_end = prom_initrd_start + r4;
 
-		val = RELOC(prom_initrd_start);
-		prom_setprop(_prom->chosen, "/chosen", "linux,initrd-start",
+		val = prom_initrd_start;
+		prom_setprop(prom.chosen, "/chosen", "linux,initrd-start",
 			     &val, sizeof(val));
-		val = RELOC(prom_initrd_end);
-		prom_setprop(_prom->chosen, "/chosen", "linux,initrd-end",
+		val = prom_initrd_end;
+		prom_setprop(prom.chosen, "/chosen", "linux,initrd-end",
 			     &val, sizeof(val));
 
-		reserve_mem(RELOC(prom_initrd_start),
-			    RELOC(prom_initrd_end) - RELOC(prom_initrd_start));
+		reserve_mem(prom_initrd_start,
+			    prom_initrd_end - prom_initrd_start);
 
-		prom_debug("initrd_start=0x%x\n", RELOC(prom_initrd_start));
-		prom_debug("initrd_end=0x%x\n", RELOC(prom_initrd_end));
+		prom_debug("initrd_start=0x%x\n", prom_initrd_start);
+		prom_debug("initrd_end=0x%x\n", prom_initrd_end);
 	}
 #endif /* CONFIG_BLK_DEV_INITRD */
 }
@@ -2901,7 +2880,6 @@ unsigned long __init prom_init(unsigned
 			       unsigned long r6, unsigned long r7,
 			       unsigned long kbase)
 {	
-	struct prom_t *_prom;
 	unsigned long hdr;
 
 #ifdef CONFIG_PPC32
@@ -2911,12 +2889,10 @@ unsigned long __init prom_init(unsigned
 	reloc_toc();
 #endif
 
-	_prom = &RELOC(prom);
-
 	/*
 	 * First zero the BSS
 	 */
-	memset(&RELOC(__bss_start), 0, __bss_stop - __bss_start);
+	memset(&__bss_start, 0, __bss_stop - __bss_start);
 
 	/*
 	 * Init interface to Open Firmware, get some node references,
@@ -2935,14 +2911,14 @@ unsigned long __init prom_init(unsigned
 	 */
 	prom_init_stdout();
 
-	prom_printf("Preparing to boot %s", RELOC(linux_banner));
+	prom_printf("Preparing to boot %s", linux_banner);
 
 	/*
 	 * Get default machine type. At this point, we do not differentiate
 	 * between pSeries SMP and pSeries LPAR
 	 */
-	RELOC(of_platform) = prom_find_machine_type();
-	prom_printf("Detected machine type: %x\n", RELOC(of_platform));
+	of_platform = prom_find_machine_type();
+	prom_printf("Detected machine type: %x\n", of_platform);
 
 #ifndef CONFIG_NONSTATIC_KERNEL
 	/* Bail if this is a kdump kernel. */
@@ -2959,15 +2935,15 @@ unsigned long __init prom_init(unsigned
 	/*
 	 * On pSeries, inform the firmware about our capabilities
 	 */
-	if (RELOC(of_platform) == PLATFORM_PSERIES ||
-	    RELOC(of_platform) == PLATFORM_PSERIES_LPAR)
+	if (of_platform == PLATFORM_PSERIES ||
+	    of_platform == PLATFORM_PSERIES_LPAR)
 		prom_send_capabilities();
 #endif
 
 	/*
 	 * Copy the CPU hold code
 	 */
-	if (RELOC(of_platform) != PLATFORM_POWERMAC)
+	if (of_platform != PLATFORM_POWERMAC)
 		copy_and_flush(0, kbase, 0x100, 0);
 
 	/*
@@ -2996,7 +2972,7 @@ unsigned long __init prom_init(unsigned
 	 * that uses the allocator, we need to make sure we get the top of memory
 	 * available for us here...
 	 */
-	if (RELOC(of_platform) == PLATFORM_PSERIES)
+	if (of_platform == PLATFORM_PSERIES)
 		prom_initialize_tce_table();
 #endif
 
@@ -3004,19 +2980,19 @@ unsigned long __init prom_init(unsigned
 	 * On non-powermacs, try to instantiate RTAS. PowerMacs don't
 	 * have a usable RTAS implementation.
 	 */
-	if (RELOC(of_platform) != PLATFORM_POWERMAC &&
-	    RELOC(of_platform) != PLATFORM_OPAL)
+	if (of_platform != PLATFORM_POWERMAC &&
+	    of_platform != PLATFORM_OPAL)
 		prom_instantiate_rtas();
 
 #ifdef CONFIG_PPC_POWERNV
 	/* Detect HAL and try instanciating it & doing takeover */
-	if (RELOC(of_platform) == PLATFORM_PSERIES_LPAR) {
+	if (of_platform == PLATFORM_PSERIES_LPAR) {
 		prom_query_opal();
-		if (RELOC(of_platform) == PLATFORM_OPAL) {
+		if (of_platform == PLATFORM_OPAL) {
 			prom_opal_hold_cpus();
 			prom_opal_takeover();
 		}
-	} else if (RELOC(of_platform) == PLATFORM_OPAL)
+	} else if (of_platform == PLATFORM_OPAL)
 		prom_instantiate_opal();
 #endif
 
@@ -3030,32 +3006,32 @@ unsigned long __init prom_init(unsigned
 	 *
 	 * PowerMacs use a different mechanism to spin CPUs
 	 */
-	if (RELOC(of_platform) != PLATFORM_POWERMAC &&
-	    RELOC(of_platform) != PLATFORM_OPAL)
+	if (of_platform != PLATFORM_POWERMAC &&
+	    of_platform != PLATFORM_OPAL)
 		prom_hold_cpus();
 
 	/*
 	 * Fill in some infos for use by the kernel later on
 	 */
-	if (RELOC(prom_memory_limit))
-		prom_setprop(_prom->chosen, "/chosen", "linux,memory-limit",
-			     &RELOC(prom_memory_limit),
+	if (prom_memory_limit)
+		prom_setprop(prom.chosen, "/chosen", "linux,memory-limit",
+			     &prom_memory_limit,
 			     sizeof(prom_memory_limit));
 #ifdef CONFIG_PPC64
-	if (RELOC(prom_iommu_off))
-		prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
+	if (prom_iommu_off)
+		prom_setprop(prom.chosen, "/chosen", "linux,iommu-off",
 			     NULL, 0);
 
-	if (RELOC(prom_iommu_force_on))
-		prom_setprop(_prom->chosen, "/chosen", "linux,iommu-force-on",
+	if (prom_iommu_force_on)
+		prom_setprop(prom.chosen, "/chosen", "linux,iommu-force-on",
 			     NULL, 0);
 
-	if (RELOC(prom_tce_alloc_start)) {
-		prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-start",
-			     &RELOC(prom_tce_alloc_start),
+	if (prom_tce_alloc_start) {
+		prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-start",
+			     &prom_tce_alloc_start,
 			     sizeof(prom_tce_alloc_start));
-		prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-end",
-			     &RELOC(prom_tce_alloc_end),
+		prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-end",
+			     &prom_tce_alloc_end,
 			     sizeof(prom_tce_alloc_end));
 	}
 #endif
@@ -3077,8 +3053,8 @@ unsigned long __init prom_init(unsigned
 	 * closed stdin already (in particular the powerbook 101). It
 	 * appears that the OPAL version of OFW doesn't like it either.
 	 */
-	if (RELOC(of_platform) != PLATFORM_POWERMAC &&
-	    RELOC(of_platform) != PLATFORM_OPAL)
+	if (of_platform != PLATFORM_POWERMAC &&
+	    of_platform != PLATFORM_OPAL)
 		prom_close_stdin();
 
 	/*
@@ -3093,10 +3069,10 @@ unsigned long __init prom_init(unsigned
 	 * tree and NULL as r5, thus triggering the new entry point which
 	 * is common to us and kexec
 	 */
-	hdr = RELOC(dt_header_start);
+	hdr = dt_header_start;
 
 	/* Don't print anything after quiesce under OPAL, it crashes OFW */
-	if (RELOC(of_platform) != PLATFORM_OPAL) {
+	if (of_platform != PLATFORM_OPAL) {
 		prom_printf("returning from prom_init\n");
 		prom_debug("->dt_header_start=0x%x\n", hdr);
 	}
@@ -3110,7 +3086,7 @@ unsigned long __init prom_init(unsigned
 #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
 	/* OPAL early debug gets the OPAL base & entry in r8 and r9 */
 	__start(hdr, kbase, 0, 0, 0,
-		RELOC(prom_opal_base), RELOC(prom_opal_entry));
+		prom_opal_base, prom_opal_entry);
 #else
 	__start(hdr, kbase, 0, 0, 0, 0, 0);
 #endif

^ permalink raw reply

* [PATCH 1/3] powerpc: Relocate prom_init.c on 64bit
From: Anton Blanchard @ 2012-11-27  3:39 UTC (permalink / raw)
  To: benh, paulus, amodra; +Cc: linuxppc-dev


The ppc64 kernel can get loaded at any address which means
our very early init code in prom_init.c must be relocatable. We do
this with a pretty nasty RELOC() macro that we wrap accesses of
variables with. It is very fragile and sometimes we forget to add a
RELOC() to an uncommon path or sometimes a compiler change breaks it.

32bit has a much more elegant solution where we build prom_init.c
with -mrelocatable and then process the relocations manually.
Unfortunately we can't do the equivalent on 64bit and we would
have to build the entire kernel relocatable (-pie), resulting in a
large increase in kernel footprint (megabytes of relocation data).
The relocation data will be marked __initdata but it still creates
more pressure on our already tight memory layout at boot.

Alan Modra pointed out that the 64bit ABI is relocatable even
if we don't build with -pie, we just need to relocate the TOC.
This patch implements that idea and relocates the TOC entries of
prom_init.c. An added bonus is there are very few relocations to
process which helps keep boot times on simulators down.

gcc does not put 64bit integer constants into the TOC but to be
safe we may want a build time script which passes through the
prom_init.c TOC entries to make sure everything looks reasonable.

Signed-off-by: Anton Blanchard <anton@samba.org>
--- 

To keep the patch small and reviewable, I separated the removal
of the RELOC macro into a follow up patch.

For simplicity I do the relocation in C but if self brain surgery
keeps people up at night we can move it into assembly.

Index: b/arch/powerpc/kernel/prom_init.c
===================================================================
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -66,8 +66,8 @@
  * is running at whatever address it has been loaded at.
  * On ppc32 we compile with -mrelocatable, which means that references
  * to extern and static variables get relocated automatically.
- * On ppc64 we have to relocate the references explicitly with
- * RELOC.  (Note that strings count as static variables.)
+ * ppc64 objects are always relocatable, we just need to relocate the
+ * TOC.
  *
  * Because OF may have mapped I/O devices into the area starting at
  * KERNELBASE, particularly on CHRP machines, we can't safely call
@@ -79,13 +79,12 @@
  * On ppc64, 64 bit values are truncated to 32 bits (and
  * fortunately don't get interpreted as two arguments).
  */
+#define RELOC(x)	(x)
+#define ADDR(x)		(u32)(unsigned long)(x)
+
 #ifdef CONFIG_PPC64
-#define RELOC(x)        (*PTRRELOC(&(x)))
-#define ADDR(x)		(u32) add_reloc_offset((unsigned long)(x))
 #define OF_WORKAROUNDS	0
 #else
-#define RELOC(x)	(x)
-#define ADDR(x)		(u32) (x)
 #define OF_WORKAROUNDS	of_workarounds
 int of_workarounds;
 #endif
@@ -334,9 +333,6 @@ static void __init prom_printf(const cha
 	struct prom_t *_prom = &RELOC(prom);
 
 	va_start(args, format);
-#ifdef CONFIG_PPC64
-	format = PTRRELOC(format);
-#endif
 	for (p = format; *p != 0; p = q) {
 		for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
 			;
@@ -437,9 +433,6 @@ static unsigned int __init prom_claim(un
 
 static void __init __attribute__((noreturn)) prom_panic(const char *reason)
 {
-#ifdef CONFIG_PPC64
-	reason = PTRRELOC(reason);
-#endif
 	prom_print(reason);
 	/* Do not call exit because it clears the screen on pmac
 	 * it also causes some sort of double-fault on early pmacs */
@@ -929,7 +922,7 @@ static void __init prom_send_capabilitie
 		 * (we assume this is the same for all cores) and use it to
 		 * divide NR_CPUS.
 		 */
-		cores = (u32 *)PTRRELOC(&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]);
+		cores = (u32 *)&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET];
 		if (*cores != NR_CPUS) {
 			prom_printf("WARNING ! "
 				    "ibm_architecture_vec structure inconsistent: %lu!\n",
@@ -2850,6 +2843,53 @@ static void __init prom_check_initrd(uns
 #endif /* CONFIG_BLK_DEV_INITRD */
 }
 
+#ifdef CONFIG_PPC64
+#ifdef CONFIG_RELOCATABLE
+static void reloc_toc(void)
+{
+}
+
+static void unreloc_toc(void)
+{
+}
+#else
+static void __reloc_toc(void *tocstart, unsigned long offset,
+			unsigned long nr_entries)
+{
+	unsigned long i;
+	unsigned long *toc_entry = (unsigned long *)tocstart;
+
+	for (i = 0; i < nr_entries; i++) {
+		*toc_entry = *toc_entry + offset;
+		toc_entry++;
+	}
+}
+
+static void reloc_toc(void)
+{
+	unsigned long offset = reloc_offset();
+	unsigned long nr_entries =
+		(__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
+
+	/* Need to add offset to get at __prom_init_toc_start */
+	__reloc_toc(__prom_init_toc_start + offset, offset, nr_entries);
+
+	mb();
+}
+
+static void unreloc_toc(void)
+{
+	unsigned long offset = reloc_offset();
+	unsigned long nr_entries =
+		(__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
+
+	mb();
+
+	/* __prom_init_toc_start has been relocated, no need to add offset */
+	__reloc_toc(__prom_init_toc_start, -offset, nr_entries);
+}
+#endif
+#endif
 
 /*
  * We enter here early on, when the Open Firmware prom is still
@@ -2867,6 +2907,8 @@ unsigned long __init prom_init(unsigned
 #ifdef CONFIG_PPC32
 	unsigned long offset = reloc_offset();
 	reloc_got2(offset);
+#else
+	reloc_toc();
 #endif
 
 	_prom = &RELOC(prom);
@@ -3061,6 +3103,8 @@ unsigned long __init prom_init(unsigned
 
 #ifdef CONFIG_PPC32
 	reloc_got2(-offset);
+#else
+	unreloc_toc();
 #endif
 
 #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
Index: b/arch/powerpc/kernel/prom_init_check.sh
===================================================================
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -22,7 +22,7 @@ __secondary_hold_acknowledge __secondary
 strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
 reloc_got2 kernstart_addr memstart_addr linux_banner _stext
 opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry
-boot_command_line"
+boot_command_line __prom_init_toc_start __prom_init_toc_end"
 
 NM="$1"
 OBJ="$2"
Index: b/arch/powerpc/kernel/vmlinux.lds.S
===================================================================
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -218,6 +218,11 @@ SECTIONS
 
 	.got : AT(ADDR(.got) - LOAD_OFFSET) {
 		__toc_start = .;
+#ifndef CONFIG_RELOCATABLE
+		__prom_init_toc_start = .;
+		arch/powerpc/kernel/prom_init.o*(.toc .got)
+		__prom_init_toc_end = .;
+#endif
 		*(.got)
 		*(.toc)
 	}
Index: b/arch/powerpc/Makefile
===================================================================
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -136,6 +136,7 @@ head-$(CONFIG_FSL_BOOKE)	:= arch/powerpc
 head-$(CONFIG_PPC64)		+= arch/powerpc/kernel/entry_64.o
 head-$(CONFIG_PPC_FPU)		+= arch/powerpc/kernel/fpu.o
 head-$(CONFIG_ALTIVEC)		+= arch/powerpc/kernel/vector.o
+head-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE)  += arch/powerpc/kernel/prom_init.o
 
 core-y				+= arch/powerpc/kernel/ \
 				   arch/powerpc/mm/ \
Index: b/arch/powerpc/kernel/Makefile
===================================================================
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -91,7 +91,6 @@ obj-$(CONFIG_RELOCATABLE_PPC32)	+= reloc
 obj-$(CONFIG_PPC32)		+= entry_32.o setup_32.o
 obj-$(CONFIG_PPC64)		+= dma-iommu.o iommu.o
 obj-$(CONFIG_KGDB)		+= kgdb.o
-obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE)	+= prom_init.o
 obj-$(CONFIG_MODULES)		+= ppc_ksyms.o
 obj-$(CONFIG_BOOTX_TEXT)	+= btext.o
 obj-$(CONFIG_SMP)		+= smp.o
@@ -142,6 +141,7 @@ GCOV_PROFILE_kprobes.o := n
 extra-$(CONFIG_PPC_FPU)		+= fpu.o
 extra-$(CONFIG_ALTIVEC)		+= vector.o
 extra-$(CONFIG_PPC64)		+= entry_64.o
+extra-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE)	+= prom_init.o
 
 extra-y				+= systbl_chk.i
 $(obj)/systbl.o:		systbl_chk
Index: b/arch/powerpc/include/asm/sections.h
===================================================================
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -10,6 +10,9 @@
 
 extern char __end_interrupts[];
 
+extern char __prom_init_toc_start[];
+extern char __prom_init_toc_end[];
+
 static inline int in_kernel_text(unsigned long addr)
 {
 	if (addr >= (unsigned long)_stext && addr < (unsigned long)__init_end)

^ permalink raw reply

* Re: [PATCH] vfio powerpc: enabled and supported on powernv platform
From: Alexey Kardashevskiy @ 2012-11-27  3:28 UTC (permalink / raw)
  To: Alex Williamson
  Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Paul Mackerras,
	Sethi Varun-B16395, linuxppc-dev@lists.ozlabs.org, David Gibson
In-Reply-To: <1353953070.1809.103.camel@bling.home>

On 27/11/12 05:04, Alex Williamson wrote:
> On Mon, 2012-11-26 at 08:18 -0700, Alex Williamson wrote:
>> On Fri, 2012-11-23 at 13:02 +1100, Alexey Kardashevskiy wrote:
>>> On 22/11/12 22:56, Sethi Varun-B16395 wrote:
>>>>
>>>>
>>>>> -----Original Message-----
>>>>> From: linux-kernel-owner@vger.kernel.org [mailto:linux-kernel-
>>>>> owner@vger.kernel.org] On Behalf Of Alex Williamson
>>>>> Sent: Tuesday, November 20, 2012 11:50 PM
>>>>> To: Alexey Kardashevskiy
>>>>> Cc: Benjamin Herrenschmidt; Paul Mackerras; linuxppc-
>>>>> dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; kvm@vger.kernel.org;
>>>>> David Gibson
>>>>> Subject: Re: [PATCH] vfio powerpc: enabled and supported on powernv
>>>>> platform
>>>>>
>>>>> On Tue, 2012-11-20 at 11:48 +1100, Alexey Kardashevskiy wrote:
>>>>>> VFIO implements platform independent stuff such as a PCI driver, BAR
>>>>>> access (via read/write on a file descriptor or direct mapping when
>>>>>> possible) and IRQ signaling.
>>>>>> The platform dependent part includes IOMMU initialization and
>>>>>> handling.
>>>>>>
>>>>>> This patch initializes IOMMU groups based on the IOMMU configuration
>>>>>> discovered during the PCI scan, only POWERNV platform is supported at
>>>>>> the moment.
>>>>>>
>>>>>> Also the patch implements an VFIO-IOMMU driver which manages DMA
>>>>>> mapping/unmapping requests coming from the client (now QEMU). It also
>>>>>> returns a DMA window information to let the guest initialize the
>>>>>> device tree for a guest OS properly. Although this driver has been
>>>>>> tested only on POWERNV, it should work on any platform supporting TCE
>>>>>> tables.
>>>>>>
>>>>>> To enable VFIO on POWER, enable SPAPR_TCE_IOMMU config option.
>>>>>>
>>>>>> Cc: David Gibson <david@gibson.dropbear.id.au>
>>>>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>>>>> ---
>>>>>>    arch/powerpc/include/asm/iommu.h     |    6 +
>>>>>>    arch/powerpc/kernel/iommu.c          |  140 +++++++++++++++++++
>>>>>>    arch/powerpc/platforms/powernv/pci.c |  135 +++++++++++++++++++
>>>>>>    drivers/iommu/Kconfig                |    8 ++
>>>>>>    drivers/vfio/Kconfig                 |    6 +
>>>>>>    drivers/vfio/Makefile                |    1 +
>>>>>>    drivers/vfio/vfio_iommu_spapr_tce.c  |  247
>>>>> ++++++++++++++++++++++++++++++++++
>>>>>>    include/linux/vfio.h                 |   20 +++
>>>>>>    8 files changed, 563 insertions(+)
>>>>>>    create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>>>>>>
>>>>>> diff --git a/arch/powerpc/include/asm/iommu.h
>>>>>> b/arch/powerpc/include/asm/iommu.h
>>>>>> index cbfe678..5ba66cb 100644
>>>>>> --- a/arch/powerpc/include/asm/iommu.h
>>>>>> +++ b/arch/powerpc/include/asm/iommu.h
>>>>>> @@ -64,30 +64,33 @@ struct iommu_pool {  }
>>>>>> ____cacheline_aligned_in_smp;
>>>>>>
>>>>>>    struct iommu_table {
>>>>>>    	unsigned long  it_busno;     /* Bus number this table belongs to */
>>>>>>    	unsigned long  it_size;      /* Size of iommu table in entries */
>>>>>>    	unsigned long  it_offset;    /* Offset into global table */
>>>>>>    	unsigned long  it_base;      /* mapped address of tce table */
>>>>>>    	unsigned long  it_index;     /* which iommu table this is */
>>>>>>    	unsigned long  it_type;      /* type: PCI or Virtual Bus */
>>>>>>    	unsigned long  it_blocksize; /* Entries in each block (cacheline)
>>>>> */
>>>>>>    	unsigned long  poolsize;
>>>>>>    	unsigned long  nr_pools;
>>>>>>    	struct iommu_pool large_pool;
>>>>>>    	struct iommu_pool pools[IOMMU_NR_POOLS];
>>>>>>    	unsigned long *it_map;       /* A simple allocation bitmap for now
>>>>> */
>>>>>> +#ifdef CONFIG_IOMMU_API
>>>>>> +	struct iommu_group *it_group;
>>>>>> +#endif
>>>>>>    };
>>>>>>
>>>>>>    struct scatterlist;
>>>>>>
>>>>>>    static inline void set_iommu_table_base(struct device *dev, void
>>>>>> *base)  {
>>>>>>    	dev->archdata.dma_data.iommu_table_base = base;  }
>>>>>>
>>>>>>    static inline void *get_iommu_table_base(struct device *dev)  {
>>>>>>    	return dev->archdata.dma_data.iommu_table_base;
>>>>>>    }
>>>>>>
>>>>>>    /* Frees table for an individual device node */ @@ -135,17 +138,20 @@
>>>>>> static inline void pci_iommu_init(void) { }  extern void
>>>>>> alloc_dart_table(void);  #if defined(CONFIG_PPC64) &&
>>>>>> defined(CONFIG_PM)  static inline void iommu_save(void)  {
>>>>>>    	if (ppc_md.iommu_save)
>>>>>>    		ppc_md.iommu_save();
>>>>>>    }
>>>>>>
>>>>>>    static inline void iommu_restore(void)  {
>>>>>>    	if (ppc_md.iommu_restore)
>>>>>>    		ppc_md.iommu_restore();
>>>>>>    }
>>>>>>    #endif
>>>>>>
>>>>>> +extern long iommu_put_tces(struct iommu_table *tbl, unsigned long
>>>>> entry, uint64_t tce,
>>>>>> +		enum dma_data_direction direction, unsigned long pages);
>>>>>> +
>>>>>>    #endif /* __KERNEL__ */
>>>>>>    #endif /* _ASM_IOMMU_H */
>>>>>> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
>>>>>> index ff5a6ce..94f614b 100644
>>>>>> --- a/arch/powerpc/kernel/iommu.c
>>>>>> +++ b/arch/powerpc/kernel/iommu.c
>>>>>> @@ -32,30 +32,31 @@
>>>>>>    #include <linux/dma-mapping.h>
>>>>>>    #include <linux/bitmap.h>
>>>>>>    #include <linux/iommu-helper.h>
>>>>>>    #include <linux/crash_dump.h>
>>>>>>    #include <linux/hash.h>
>>>>>>    #include <linux/fault-inject.h>
>>>>>>    #include <linux/pci.h>
>>>>>>    #include <asm/io.h>
>>>>>>    #include <asm/prom.h>
>>>>>>    #include <asm/iommu.h>
>>>>>>    #include <asm/pci-bridge.h>
>>>>>>    #include <asm/machdep.h>
>>>>>>    #include <asm/kdump.h>
>>>>>>    #include <asm/fadump.h>
>>>>>>    #include <asm/vio.h>
>>>>>> +#include <asm/tce.h>
>>>>>>
>>>>>>    #define DBG(...)
>>>>>>
>>>>>>    static int novmerge;
>>>>>>
>>>>>>    static void __iommu_free(struct iommu_table *, dma_addr_t, unsigned
>>>>>> int);
>>>>>>
>>>>>>    static int __init setup_iommu(char *str)  {
>>>>>>    	if (!strcmp(str, "novmerge"))
>>>>>>    		novmerge = 1;
>>>>>>    	else if (!strcmp(str, "vmerge"))
>>>>>>    		novmerge = 0;
>>>>>>    	return 1;
>>>>>>    }
>>>>>> @@ -844,15 +845,154 @@ void *iommu_alloc_coherent(struct device *dev,
>>>>>> struct iommu_table *tbl,  }
>>>>>>
>>>>>>    void iommu_free_coherent(struct iommu_table *tbl, size_t size,
>>>>>>    			 void *vaddr, dma_addr_t dma_handle)  {
>>>>>>    	if (tbl) {
>>>>>>    		unsigned int nio_pages;
>>>>>>
>>>>>>    		size = PAGE_ALIGN(size);
>>>>>>    		nio_pages = size >> IOMMU_PAGE_SHIFT;
>>>>>>    		iommu_free(tbl, dma_handle, nio_pages);
>>>>>>    		size = PAGE_ALIGN(size);
>>>>>>    		free_pages((unsigned long)vaddr, get_order(size));
>>>>>>    	}
>>>>>>    }
>>>>>> +
>>>>>> +#ifdef CONFIG_IOMMU_API
>>>>>> +/*
>>>>>> + * SPAPR TCE API
>>>>>> + */
>>>>>> +static struct page *free_tce(struct iommu_table *tbl, unsigned long
>>>>>> +entry) {
>>>>>> +	struct page *page = NULL;
>>>>>
>>>>> NULL initialization doesn't appear to be necessary
>>>>>
>>>>>> +	unsigned long oldtce;
>>>>>> +
>>>>>> +	oldtce = ppc_md.tce_get(tbl, entry);
>>>>>> +
>>>>>> +	if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
>>>>>> +		return NULL;
>>>>>> +
>>>>>> +	page = pfn_to_page(oldtce >> PAGE_SHIFT);
>>>>>> +
>>>>>> +	WARN_ON(!page);
>>>>>> +	if (page && (oldtce & TCE_PCI_WRITE))
>>>>>> +		SetPageDirty(page);
>>>>>> +	ppc_md.tce_free(tbl, entry, 1);
>>>>>> +
>>>>>> +	return page;
>>>>>> +}
>>>>>> +
>>>>>> +static int put_tce(struct iommu_table *tbl, unsigned long entry,
>>>>>> +		uint64_t tce, enum dma_data_direction direction) {
>>>>>> +	int ret;
>>>>>> +	struct page *page = NULL;
>>>>>> +	unsigned long kva, offset;
>>>>>> +
>>>>>> +	/* Map new TCE */
>>>>>> +	offset = (tce & IOMMU_PAGE_MASK) - (tce & PAGE_MASK);
>>>>>> +	ret = get_user_pages_fast(tce & PAGE_MASK, 1,
>>>>>> +			direction != DMA_TO_DEVICE, &page);
>>>>>> +	if (ret < 1) {
>>>>>> +		printk(KERN_ERR "tce_vfio: get_user_pages_fast failed
>>>>> tce=%llx ioba=%lx ret=%d\n",
>>>>>> +				tce, entry << IOMMU_PAGE_SHIFT, ret);
>>>>>> +		if (!ret)
>>>>>> +			ret = -EFAULT;
>>>>>
>>>>> Missing return ret?  Otherwise we've got some bogus uses of page below
>>>>> and we're setting ret for no reason here.
>>>>>
>>>>>> +	}
>>>>>> +
>>>>>> +	kva = (unsigned long) page_address(page);
>>>>>> +	kva += offset;
>>>>>> +
>>>>>> +	/* tce_build receives a virtual address */
>>>>>> +	entry += tbl->it_offset; /* Offset into real TCE table */
>>>>>> +	ret = ppc_md.tce_build(tbl, entry, 1, kva, direction, NULL);
>>>>>> +
>>>>>> +	/* tce_build() only returns non-zero for transient errors */
>>>>>> +	if (unlikely(ret)) {
>>>>>> +		printk(KERN_ERR "tce_vfio: tce_put failed on tce=%llx
>>>>> ioba=%lx kva=%lx ret=%d\n",
>>>>>> +				tce, entry << IOMMU_PAGE_SHIFT, kva, ret);
>>>>>> +		put_page(page);
>>>>>> +		return -EIO;
>>>>>> +	}
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void tce_flush(struct iommu_table *tbl) {
>>>>>> +	/* Flush/invalidate TLB caches if necessary */
>>>>>> +	if (ppc_md.tce_flush)
>>>>>> +		ppc_md.tce_flush(tbl);
>>>>>> +
>>>>>> +	/* Make sure updates are seen by hardware */
>>>>>> +	mb();
>>>>>> +}
>>>>>> +
>>>>>> +long iommu_put_tces(struct iommu_table *tbl, unsigned long entry,
>>>>> uint64_t tce,
>>>>>> +		enum dma_data_direction direction, unsigned long pages) {
>>>>>> +	int i, ret = 0, pages_to_put = 0;
>>>>>> +	struct page *page;
>>>>>> +	struct iommu_pool *pool = get_pool(tbl, entry);
>>>>>> +	struct page **oldpages;
>>>>>> +	const int oldpagesnum = PAGE_SIZE/sizeof(*oldpages);
>>>>>> +
>>>>>> +	BUILD_BUG_ON(PAGE_SIZE < IOMMU_PAGE_SIZE);
>>>>>> +
>>>>>> +	/* Handle a single page request without allocation
>>>>>> +	   of pages-to-release array */
>>>>>> +	if (pages == 1) {
>>>>>> +		spin_lock(&(pool->lock));
>>>>>> +		page = free_tce(tbl, entry);
>>>>>> +
>>>>>> +		if (direction != DMA_NONE)
>>>>>> +			ret = put_tce(tbl, entry, tce, direction);
>>>>>> +
>>>>>> +		tce_flush(tbl);
>>>>>> +
>>>>>> +		if (page)
>>>>>> +			put_page(page);
>>>>>> +
>>>>>> +		spin_unlock(&(pool->lock));
>>>>>> +		return ret;
>>>>>> +	}
>>>>>> +
>>>>>> +	/* Releasing multiple pages */
>>>>>> +	/* Allocate an array for pages to be released after TCE table
>>>>>> +	   is updated */
>>>>>> +	oldpages = kmalloc(PAGE_SIZE, GFP_KERNEL);
>>>>>> +	if (!oldpages)
>>>>>> +		return -ENOMEM;
>>>>>> +
>>>>>> +	spin_lock(&(pool->lock));
>>>>>> +
>>>>>> +	for (i = 0; (i < pages) && !ret; ++i, ++entry, tce +=
>>>>> IOMMU_PAGE_SIZE) {
>>>>>> +		page = free_tce(tbl, entry);
>>>>>> +		if (page) {
>>>>>> +			oldpages[pages_to_put] = page;
>>>>>> +			++pages_to_put;
>>>>>> +		}
>>>>>> +
>>>>>> +		if (direction != DMA_NONE)
>>>>>> +			ret = put_tce(tbl, entry, tce, direction);
>>>>>> +
>>>>>> +		/* Release old pages if we reached the end of oldpages[] or
>>>>>> +		   it is the last page or we are about to exit the loop */
>>>>>> +		if ((pages_to_put == oldpagesnum) || (i == pages - 1) || ret)
>>>>> {
>>>>>> +			tce_flush(tbl);
>>>>>
>>>>> Avoiding tce_flush() is the reason for all this extra overhead, right?
>>>>> I wonder if it'd be cleaner separating map vs unmap, where the map case
>>>>> can avoid the oldpages array... but that means inserting new mappings on
>>>>> top of old ones wouldn't put the pages.
>>>
>>>
>>> Yes, we do not want to loose pages if the guest forgot to unmap them.
>>
>> Hmm, does that mean we're not actively clearing tce entries or somehow
>> disabling the iommu window when the iommu is released through vfio?
>
> Ok, I see tces are put on shutdown via tce_iommu_detach_group, so you're
> more concerned about the guest simply mapping over top of it's own
> mappings.  Is that common?  Is it common enough for every multi-page
> mapping to assume it will happen?  I know this is a performance
> sensitive path for you and it seems like a map-only w/ fallback to
> unmap, remap would be better in the general case.


I do not get it. Where exactly does the performance suffer?
iommu_put_tces() with non zero "tce" (i.e. "map") has to check if the entry 
is not used, at least to return EBUSY when it is, and this check is 
performed. If it is zero, there is no overhead at all. And it is going to 
be the 99.(9)% case as the guest (un)maps one page per call.

Generally speaking we want to move "put tce" completely to the kernel for 
the (much) better performance and vfio won't be dealing with it all.

We already agreed that SPAPR TCE driver uses x86 (aka type1) API but I do 
not see why the powerpc implementation should look x86 alike as it still 
operates with powerpc machine dependent callbacks so the reader has to have 
some powerpc knowledge.


> On x86 we do exactly that, but we do the unmap, remap from userspace
> when we get an EBUSY.  Thanks,



-- 
Alexey

^ permalink raw reply

* [PATCH 16/16] powerpc: Documentation for transactional memory on powerpc
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 Documentation/powerpc/transactional_memory.txt |  119 ++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 Documentation/powerpc/transactional_memory.txt

diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt
new file mode 100644
index 0000000..881df6b
--- /dev/null
+++ b/Documentation/powerpc/transactional_memory.txt
@@ -0,0 +1,119 @@
+Transactional Memory support
+============================
+
+POWER kernel support for this feature is currently limited to supporting
+its use by user programs.  It is not currently used by the kernel itself.
+
+This file aims to sum up how it is supported by Linux and what behaviour you
+can expect from your user programs.
+
+
+Basic overview
+==============
+
+Hardware Transactional Memory is supported on POWER8 processors, and is a
+feature that enables a different form of atomic memory access.  Several new
+instructions are presented to delimit transactions; transactions are
+guaranteed to either complete atomically or roll back and undo any partial
+changes.
+
+A simple transaction looks like this:
+
+begin_move_money:
+  tbegin
+  beq   abort_handler
+
+  ld    r4, SAVINGS_ACCT(r3)
+  ld    r5, CURRENT_ACCT(r3)
+  subi  r5, r5, 1
+  addi  r4, r4, 1
+  std   r4, SAVINGS_ACCT(r3)
+  std   r5, CURRENT_ACCT(r3)
+
+  tend
+
+  b     continue
+
+abort_handler:
+  ... test for odd failures ...
+
+  /* Retry the transaction if it failed because it conflicted with
+   * someone else: */
+  b     begin_move_money
+
+
+The 'tbegin' instruction denotes the start point, and 'tend' the end point.
+Between these points the processor is in 'Transactional' state; any memory
+references will complete in one go if there are no conflicts with other
+transactional or non-transactional accesses within the system.  In this
+example, the transaction completes as though it were normal straight-line code
+IF no other processor has touched SAVINGS_ACCT(r3) or CURRENT_ACCT(r3); an
+atomic move of money from the current account to the savings account has been
+performed.  Even though the normal ld/std instructions are used (note no
+lwarx/stwcx), either *both* SAVINGS_ACCT(r3) and CURRENT_ACCT(r3) will be
+updated, or neither will be updated.
+
+If, in the meantime, there is a conflict with the locations accessed by the
+transaction, the transaction will be aborted by the CPU.  Register and memory
+state will roll back to that at the 'tbegin', and control will continue from
+'tbegin+4'.  The branch to abort_handler will be taken this second time; the
+abort handler can check the cause of the failure, and retry.
+
+Checkpointed registers include all GPRs, FPRs, VRs/VSRs, LR, CCR/CR, CTR, FPCSR
+and a few other status/flag regs; see the ISA for details.
+
+Causes of transaction aborts
+============================
+
+- Conflicts with cache lines used by other processors
+- Signals
+- Context switches
+- See the ISA for full documentation of everything that will abort transactions.
+
+
+Syscalls
+========
+
+Performing syscalls from within transaction is not recommended, and can lead
+to unpredictable results.
+
+Syscalls do not by design abort transactions, but beware: The kernel code will
+not be running in transactional state.  The effect of syscalls will always
+remain visible, but depending on the call they may abort your transaction as a
+side-effect, read soon-to-be-aborted transactional data that should not remain
+invisible, etc.  If you constantly retry a transaction that constantly aborts
+itself by calling a syscall, you'll have a livelock & make no progress.
+
+Simple syscalls (e.g. sigprocmask()) "could" be OK.  Even things like write()
+from, say, printf() should be OK as long as the kernel does not access any
+memory that was accessed transactionally.
+
+Consider any syscalls that happen to work as debug-only -- not recommended for
+production use.  Best to queue them up till after the transaction is over.
+
+
+Failure cause codes used by kernel
+==================================
+
+These are defined in <asm/reg.h>, and distinguish different reasons why the
+kernel aborted a transaction:
+
+ TM_CAUSE_RESCHED       Thread was rescheduled.
+ TM_CAUSE_FAC_UNAV      FP/VEC/VSX unavailable trap.
+ TM_CAUSE_SYSCALL       Currently unused; future syscalls that must abort
+                        transactions for consistency will use this.
+ TM_CAUSE_SIGNAL        Signal delivered.
+ TM_CAUSE_MISC          Currently unused.
+
+These can be checked by the user program's abort handler as TEXASR[0:7].
+
+
+GDB
+===
+
+GDB and ptrace are not currently TM-aware.  If one stops during a transaction,
+it looks like the transaction has just started (the checkpointed state is
+presented).  The transaction cannot then be continued and will take the failure
+handler route.  Furthermore, the transactional 2nd register state will be
+inaccessible.  GDB can currently be used on programs using TM, but not sensibly
+in parts within transactions.
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 15/16] powerpc: Add transactional memory to pseries and ppc64 defconfigs
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/configs/ppc64_defconfig   |    1 +
 arch/powerpc/configs/pseries_defconfig |    1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index 6d03530..26000f6 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -46,6 +46,7 @@ CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_HZ_100=y
 CONFIG_BINFMT_MISC=m
+CONFIG_TRANSACTIONAL_MEM=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_KEXEC=y
 CONFIG_IRQ_ALL_CPUS=y
diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig
index 5b8e1e5..b5f94b7 100644
--- a/arch/powerpc/configs/pseries_defconfig
+++ b/arch/powerpc/configs/pseries_defconfig
@@ -40,6 +40,7 @@ CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_HZ_100=y
 CONFIG_BINFMT_MISC=m
+CONFIG_TRANSACTIONAL_MEM=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_KEXEC=y
 CONFIG_IRQ_ALL_CPUS=y
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 14/16] powerpc: Add config option for transactional memory
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Kconfig option for transactional memory on powerpc.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/Kconfig |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index a902a5c..ece67ca 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -308,6 +308,14 @@ config MATH_EMULATION
 	  unit, which will allow programs that use floating-point
 	  instructions to run.
 
+config TRANSACTIONAL_MEM
+       bool "Transactional Memory support"
+       depends on PPC64
+       depends on SMP
+       default n
+       ---help---
+         Support user-mode Transactional Memory.
+
 config 8XX_MINIMAL_FPEMU
 	bool "Minimal math emulation for 8xx"
 	depends on 8xx && !MATH_EMULATION
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 13/16] powerpc: Add transactional memory to POWER8 cpu features
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/cputable.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index f2163da..74458e69 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -417,7 +417,7 @@ extern const char *powerpc_base_platform;
 	    CPU_FTR_DSCR | CPU_FTR_SAO  | \
 	    CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \
 	    CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \
-	    CPU_FTR_DBELL)
+	    CPU_FTR_DBELL | CPU_FTR_TM_COMP)
 #define CPU_FTRS_CELL	(CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
 	    CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
 	    CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 12/16] powerpc: Hook in new transactional memory code
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

This hooks the new transactional memory code into context switching, FP/VMX/VMX
unavailable and exception return.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/kernel/entry_64.S       |   22 ++++++++++++++++
 arch/powerpc/kernel/exceptions-64s.S |   48 ++++++++++++++++++++++++++++++++--
 arch/powerpc/kernel/fpu.S            |    1 -
 arch/powerpc/kernel/process.c        |   15 +++++++++--
 arch/powerpc/kernel/traps.c          |   32 +++++++++++++++++++++++
 arch/powerpc/kernel/vector.S         |    1 -
 6 files changed, 113 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 5ae8e51..b3590c3 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -602,7 +602,29 @@ _GLOBAL(ret_from_except_lite)
 	beq	1f
 	bl	.restore_interrupts
 	bl	.schedule
+#ifdef CONFIG_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+	/* If TIF_RESTOREALL was set by switch_to, we MUST clear it before
+	 * any return to userspace -- no one else is going to.  TRAP.0 has been
+	 * cleared to flag full regs to ret_from_except.
+	 * Try to avoid the slow atomic clear if the flag isn't set.
+	 * (This is OK as no one else will be clearing this flag.)
+         */
+	clrrdi	r9,r1,THREAD_SHIFT	/* current_thread_info() */
+	li	r4,_TIF_RESTOREALL
+	addi	r9, r9, TI_FLAGS
+	ld	r3, 0(r9)		/* Test TIF_RESTOREALL first! */
+	and.	r0, r3, r4
+	beq	.ret_from_except
+3:	ldarx	r10, 0, r9		/* If set, clear. */
+	andc	r10, r10, r4
+	stdcx.	r10, 0, r9
+	bne	3b
+	b	.ret_from_except	/* Not _lite; we may have full regs! */
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
+#else
 	b	.ret_from_except_lite
+#endif
 
 1:	bl	.save_nvgprs
 	bl	.restore_interrupts
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index d97cea4..220b896 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -1144,9 +1144,24 @@ fp_unavailable_common:
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	bl	.kernel_fp_unavailable_exception
 	BUG_OPCODE
-1:	bl	.load_up_fpu
+1:
+#ifdef CONFIG_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+	srdi	r0, r12, MSR_TS_LG
+	andi.	r0, r0, 3
+	bne-	2f
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
+#endif
+	bl	.load_up_fpu
+	std     r12,_MSR(r1)
 	b	fast_exception_return
-
+#ifdef CONFIG_TRANSACTIONAL_MEM
+2:	/* User process was in a transaction */
+	bl	.save_nvgprs
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.fp_unavailable_tm
+	b	.ret_from_except
+#endif
 	.align	7
 	.globl altivec_unavailable_common
 altivec_unavailable_common:
@@ -1154,8 +1169,23 @@ altivec_unavailable_common:
 #ifdef CONFIG_ALTIVEC
 BEGIN_FTR_SECTION
 	beq	1f
+#ifdef CONFIG_TRANSACTIONAL_MEM
+  BEGIN_FTR_SECTION_NESTED(69)
+	srdi	r0, r12, MSR_TS_LG
+	andi.	r0, r0, 3
+	bne-	2f
+  END_FTR_SECTION_NESTED(CPU_FTR_TM, CPU_FTR_TM, 69)
+#endif
 	bl	.load_up_altivec
+	std	r12,_MSR(r1)
 	b	fast_exception_return
+#ifdef CONFIG_TRANSACTIONAL_MEM
+2:	/* User process was in a transaction */
+	bl	.save_nvgprs
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.altivec_unavailable_tm
+	b	.ret_from_except
+#endif
 1:
 END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 #endif
@@ -1172,7 +1202,21 @@ vsx_unavailable_common:
 #ifdef CONFIG_VSX
 BEGIN_FTR_SECTION
 	beq	1f
+#ifdef CONFIG_TRANSACTIONAL_MEM
+  BEGIN_FTR_SECTION_NESTED(69)
+	srdi	r0, r12, MSR_TS_LG
+	andi.	r0, r0, 3
+	bne-	2f
+  END_FTR_SECTION_NESTED(CPU_FTR_TM, CPU_FTR_TM, 69)
+#endif
 	b	.load_up_vsx
+#ifdef CONFIG_TRANSACTIONAL_MEM
+2:	/* User process was in a transaction */
+	bl	.save_nvgprs
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.vsx_unavailable_tm
+	b	.ret_from_except
+#endif
 1:
 END_FTR_SECTION_IFSET(CPU_FTR_VSX)
 #endif
diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S
index 6ab0e87..08b6a12f 100644
--- a/arch/powerpc/kernel/fpu.S
+++ b/arch/powerpc/kernel/fpu.S
@@ -170,7 +170,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX)
 	lwz	r4,THREAD_FPEXC_MODE(r5)
 	ori	r12,r12,MSR_FP
 	or	r12,r12,r4
-	std	r12,_MSR(r1)
 #endif
 	lfd	fr0,THREAD_FPSCR(r5)
 	MTFSF_L(fr0)
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 1bf2c6c7..a0bfd97 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -455,7 +455,7 @@ out_and_saveregs:
 	tm_save_sprs(thr);
 }
 
-static inline void __maybe_unused tm_recheckpoint_new_task(struct task_struct *new)
+static inline void tm_recheckpoint_new_task(struct task_struct *new)
 {
 	unsigned long msr;
 
@@ -530,6 +530,8 @@ struct task_struct *__switch_to(struct task_struct *prev,
 	struct ppc64_tlb_batch *batch;
 #endif
 
+	__switch_to_tm(prev);
+
 #ifdef CONFIG_SMP
 	/* avoid complexity of lazy save/restore of fpu
 	 * by just saving it every time we switch out if
@@ -645,6 +647,9 @@ struct task_struct *__switch_to(struct task_struct *prev,
 	 * of sync. Hard disable here.
 	 */
 	hard_irq_disable();
+
+	tm_recheckpoint_new_task(new);
+
 	last = _switch(old_thread, new_thread);
 
 #ifdef CONFIG_PPC_BOOK3S_64
@@ -1019,7 +1024,6 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
 		regs->msr = MSR_USER32;
 	}
 #endif
-
 	discard_lazy_cpu_state();
 #ifdef CONFIG_VSX
 	current->thread.used_vsr = 0;
@@ -1039,6 +1043,13 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
 	current->thread.spefscr = 0;
 	current->thread.used_spe = 0;
 #endif /* CONFIG_SPE */
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	if (cpu_has_feature(CPU_FTR_TM))
+		regs->msr |= MSR_TM;
+	current->thread.tm_tfhar = 0;
+	current->thread.tm_texasr = 0;
+	current->thread.tm_tfiar = 0;
+#endif /* CONFIG_TRANSACTIONAL_MEM */
 }
 
 #define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 7b9f160..cb04fc7 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -1032,6 +1032,38 @@ void __kprobes program_check_exception(struct pt_regs *regs)
 		_exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
 		return;
 	}
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	if (reason & REASON_TM) {
+		/* This is a TM "Bad Thing Exception" program check.
+		 * This occurs when:
+		 * -  An rfid/hrfid/mtmsrd attempts to cause an illegal
+		 *    transition in TM states.
+		 * -  A trechkpt is attempted when transactional.
+		 * -  A treclaim is attempted when non transactional.
+		 * -  A tend is illegally attempted.
+		 * -  writing a TM SPR when transactional.
+		 */
+		if (!user_mode(regs) &&
+		    report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
+			regs->nip += 4;
+			return;
+		}
+		/* If usermode caused this, it's done something illegal and
+		 * gets a SIGILL slap on the wrist.  We call it an illegal
+		 * operand to distinguish from the instruction just being bad
+		 * (e.g. executing a 'tend' on a CPU without TM!); it's an
+		 * illegal /placement/ of a valid instruction.
+		 */
+		if (user_mode(regs)) {
+			_exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
+			return;
+		} else {
+			printk(KERN_EMERG "Unexpected TM Bad Thing exception "
+			       "at %lx (msr 0x%x)\n", regs->nip, reason);
+			die("Unrecoverable exception", regs, SIGABRT);
+		}
+	}
+#endif
 
 	/* We restore the interrupt state now */
 	if (!arch_irq_disabled_regs(regs))
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S
index 330fc8c..9b47306 100644
--- a/arch/powerpc/kernel/vector.S
+++ b/arch/powerpc/kernel/vector.S
@@ -123,7 +123,6 @@ _GLOBAL(load_up_altivec)
 	ld	r4,PACACURRENT(r13)
 	addi	r5,r4,THREAD		/* Get THREAD */
 	oris	r12,r12,MSR_VEC@h
-	std	r12,_MSR(r1)
 #endif
 	li	r4,1
 	li	r10,THREAD_VSCR
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 11/16] powerpc: Assembler routines for FP/VSX/VMX unavailable during a transaction
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

We do lazy FP but not lazy TM (ie. userspace starts with MSR TM=1 FP=0).  Hence
if userspace does an FP instruction during a transaction, we'll take an
fp unavailable exception.

This adds functions needed to handle this case.  We have to inject the current
FP state into the checkpoint so that the hardware can decide what to do with
the transaction.  We can't inject only the FP so we have to do a full treclaim
and recheckpoint to inject just the FP state.  This will cause the transaction
to be marked as aborted by the hardware.

This just add the routines needed to do this for FP, VMX and VSX.  It doesn't
hook them into the rest of the code yet.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/kernel/traps.c |   95 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 4acd98d..7b9f160 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -58,6 +58,7 @@
 #include <asm/rio.h>
 #include <asm/fadump.h>
 #include <asm/switch_to.h>
+#include <asm/tm.h>
 #include <asm/debug.h>
 
 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
@@ -1192,6 +1193,100 @@ void tm_unavailable_exception(struct pt_regs *regs)
 	die("Unexpected TM unavailable exception", regs, SIGABRT);
 }
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+
+extern void do_load_up_fpu(struct pt_regs *regs);
+
+void fp_unavailable_tm(struct pt_regs *regs)
+{
+	/* Note:  This does not handle any kind of FP laziness. */
+
+	/* We restore the interrupt state now */
+	if (!arch_irq_disabled_regs(regs))
+		local_irq_enable();
+
+	TM_DEBUG("FP Unavailable trap whilst transactional at 0x%lx, MSR=%lx\n",
+		 regs->nip, regs->msr);
+	tm_enable();
+
+        /* We can only have got here if the task started using FP after
+         * beginning the transaction.  So, the transactional regs are just a
+         * copy of the checkpointed ones.  But, we still need to recheckpoint
+         * as we're enabling FP for the process; it will return, abort the
+         * transaction, and probably retry but now with FP enabled.  So the
+         * checkpointed FP registers need to be loaded.
+	 */
+	tm_reclaim(&current->thread, current->thread.regs->msr,
+		   TM_CAUSE_FAC_UNAV);
+	/* Reclaim didn't save out any FPRs to transact_fprs. */
+
+	/* Enable FP for the task: */
+	regs->msr |= (MSR_FP | current->thread.fpexc_mode);
+
+	/* This loads and recheckpoints the FP registers from
+	 * thread.fpr[].  They will remain in registers after the
+	 * checkpoint so we don't need to reload them after.
+	 */
+	tm_recheckpoint(&current->thread, regs->msr);
+}
+
+#ifdef CONFIG_ALTIVEC
+extern void do_load_up_altivec(struct pt_regs *regs);
+
+void altivec_unavailable_tm(struct pt_regs *regs)
+{
+	/* See the comments in fp_unavailable_tm().  This function operates
+	 * the same way.
+	 */
+
+	/* We restore the interrupt state now */
+	if (!arch_irq_disabled_regs(regs))
+		local_irq_enable();
+
+	TM_DEBUG("Vector Unavailable trap whilst transactional at 0x%lx,"
+		 "MSR=%lx\n",
+		 regs->nip, regs->msr);
+	tm_enable();
+	tm_reclaim(&current->thread, current->thread.regs->msr,
+		   TM_CAUSE_FAC_UNAV);
+	regs->msr |= MSR_VEC;
+	tm_recheckpoint(&current->thread, regs->msr);
+	current->thread.used_vr = 1;
+}
+#endif
+
+#ifdef CONFIG_VSX
+void vsx_unavailable_tm(struct pt_regs *regs)
+{
+	/* See the comments in fp_unavailable_tm().  This works similarly,
+	 * though we're loading both FP and VEC registers in here.
+	 *
+	 * If FP isn't in use, load FP regs.  If VEC isn't in use, load VEC
+	 * regs.  Either way, set MSR_VSX.
+	 */
+
+	/* We restore the interrupt state now */
+	if (!arch_irq_disabled_regs(regs))
+		local_irq_enable();
+
+	TM_DEBUG("VSX Unavailable trap whilst transactional at 0x%lx,"
+		 "MSR=%lx\n",
+		 regs->nip, regs->msr);
+
+	tm_enable();
+	/* This reclaims FP and/or VR regs if they're already enabled */
+	tm_reclaim(&current->thread, current->thread.regs->msr,
+		   TM_CAUSE_FAC_UNAV);
+
+	regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode |
+		MSR_VSX;
+	/* This loads & recheckpoints FP and VRs. */
+	tm_recheckpoint(&current->thread, regs->msr);
+	current->thread.used_vsr = 1;
+}
+#endif
+#endif /* CONFIG_TRANSACTIONAL_MEM */
+
 void performance_monitor_exception(struct pt_regs *regs)
 {
 	__get_cpu_var(irq_stat).pmu_irqs++;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 10/16] powerpc: Add transactional memory unavaliable execption handler
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

These should never happen since we always turn on MSR TM when in userspace. We
don't do lazy TM.

Hence if we hit this, we barf and kill the task as something's gone horribly
wrong.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/kernel/exceptions-64s.S |   19 +++++++++++++++++++
 arch/powerpc/kernel/traps.c          |   21 +++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 32fc04f..d97cea4 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -313,6 +313,9 @@ vsx_unavailable_pSeries_1:
 	. = 0xf40
 	b	vsx_unavailable_pSeries
 
+	. = 0xf60
+	b	tm_unavailable_pSeries
+
 #ifdef CONFIG_CBE_RAS
 	STD_EXCEPTION_HV(0x1200, 0x1202, cbe_system_error)
 	KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1202)
@@ -526,6 +529,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206)
 	KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf20)
 	STD_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable)
 	KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40)
+	STD_EXCEPTION_PSERIES(., 0xf60, tm_unavailable)
+	KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf60)
 
 /*
  * An interrupt came in while soft-disabled. We set paca->irq_happened, then:
@@ -815,6 +820,10 @@ vsx_unavailable_relon_pSeries_1:
 	. = 0x4f40
 	b	vsx_unavailable_relon_pSeries
 
+tm_unavailable_relon_pSeries_1:
+	. = 0x4f60
+	b	tm_unavailable_relon_pSeries
+
 #ifdef CONFIG_CBE_RAS
 	STD_RELON_EXCEPTION_HV(0x5200, 0x1202, cbe_system_error)
 #endif /* CONFIG_CBE_RAS */
@@ -1174,6 +1183,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX)
 	b	.ret_from_except
 
 	.align	7
+	.globl tm_unavailable_common
+tm_unavailable_common:
+	EXCEPTION_PROLOG_COMMON(0xf60, PACA_EXGEN)
+	bl	.save_nvgprs
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.tm_unavailable_exception
+	b	.ret_from_except
+
+	.align	7
 	.globl	__end_handlers
 __end_handlers:
 
@@ -1387,6 +1405,7 @@ _GLOBAL(do_stab_bolted)
 	STD_RELON_EXCEPTION_PSERIES(., 0xf00, performance_monitor)
 	STD_RELON_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable)
 	STD_RELON_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable)
+	STD_RELON_EXCEPTION_PSERIES(., 0xf60, tm_unavailable)
 
 #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
 /*
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 8fed874..4acd98d 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -1171,6 +1171,27 @@ void vsx_unavailable_exception(struct pt_regs *regs)
 	die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT);
 }
 
+void tm_unavailable_exception(struct pt_regs *regs)
+{
+	/* We restore the interrupt state now */
+	if (!arch_irq_disabled_regs(regs))
+		local_irq_enable();
+
+	/* Currently we never expect a TMU exception.  Catch
+	 * this and kill the process!
+	 */
+	printk(KERN_EMERG "Unexpected TM unavailable exception at %lx "
+	       "(msr %lx)\n",
+	       regs->nip, regs->msr);
+
+	if (user_mode(regs)) {
+		_exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+		return;
+	}
+
+	die("Unexpected TM unavailable exception", regs, SIGABRT);
+}
+
 void performance_monitor_exception(struct pt_regs *regs)
 {
 	__get_cpu_var(irq_stat).pmu_irqs++;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 09/16] powerpc: Add reclaim and recheckpoint functions for context switching transactional memory processes
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

When we switch out a task, we need to save both the checkpointed and the
speculated state into the thread struct.

Similarly when we are switching in a task we need to load both the checkpointed
and speculated state.  If the task was using FP, we non-lazily reload both the
original and the speculative FP register states.  This is because the kernel
doesn't see if/when a TM rollback occurs, so if we take an FP unavoidable
later, we are unable to determine which set of FP regs need to be restored.

This simply adds these functions.  It doesn't hook them into the existing code
yet.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/kernel/process.c |  113 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index fd5ce1b..1bf2c6c7 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -50,6 +50,7 @@
 #include <asm/runlatch.h>
 #include <asm/syscalls.h>
 #include <asm/switch_to.h>
+#include <asm/tm.h>
 #include <asm/debug.h>
 #ifdef CONFIG_PPC64
 #include <asm/firmware.h>
@@ -407,6 +408,118 @@ int set_dabr(unsigned long dabr, unsigned long dabrx)
 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array);
 #endif
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+static inline void tm_reclaim_task(struct task_struct *tsk)
+{
+	/* We have to work out if we're switching from/to a task that's in the
+	 * middle of a transaction.
+	 *
+	 * In switching we need to maintain a 2nd register state as
+	 * oldtask->thread.ckpt_regs.  We tm_reclaim(oldproc); this saves the
+	 * checkpointed (tbegin) state in ckpt_regs and saves the transactional
+	 * (current) FPRs into oldtask->thread.transact_fpr[].
+	 *
+	 * We also context switch (save) TFHAR/TEXASR/TFIAR in here.
+	 */
+	struct thread_struct *thr = &tsk->thread;
+
+	if (!thr->regs)
+		return;
+
+	if (!MSR_TM_ACTIVE(thr->regs->msr))
+		goto out_and_saveregs;
+
+	/* Stash the original thread MSR, as giveup_fpu et al will
+	 * modify it.  We hold onto it to see whether the task used
+	 * FP & vector regs.
+	 */
+	thr->tm_orig_msr = thr->regs->msr;
+
+	TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, "
+		 "ccr=%lx, msr=%lx, trap=%lx)\n",
+		 tsk->pid, thr->regs->nip,
+		 thr->regs->ccr, thr->regs->msr,
+		 thr->regs->trap);
+
+	tm_reclaim(thr, thr->regs->msr, TM_CAUSE_RESCHED);
+
+	TM_DEBUG("--- tm_reclaim on pid %d complete\n",
+		 tsk->pid);
+
+out_and_saveregs:
+	/* Always save the regs here, even if a transaction's not active.
+	 * This context-switches a thread's TM info SPRs.  We do it here to
+	 * be consistent with the restore path (in recheckpoint) which
+	 * cannot happen later in _switch().
+	 */
+	tm_save_sprs(thr);
+}
+
+static inline void __maybe_unused tm_recheckpoint_new_task(struct task_struct *new)
+{
+	unsigned long msr;
+
+	if (!cpu_has_feature(CPU_FTR_TM))
+		return;
+
+	/* Recheckpoint the registers of the thread we're about to switch to.
+	 *
+	 * If the task was using FP, we non-lazily reload both the original and
+	 * the speculative FP register states.  This is because the kernel
+	 * doesn't see if/when a TM rollback occurs, so if we take an FP
+	 * unavoidable later, we are unable to determine which set of FP regs
+	 * need to be restored.
+	 */
+	if (!new->thread.regs)
+		return;
+
+	/* The TM SPRs are restored here, so that TEXASR.FS can be set
+	 * before the trecheckpoint and no explosion occurs.
+	 */
+	tm_restore_sprs(&new->thread);
+
+	if (!MSR_TM_ACTIVE(new->thread.regs->msr))
+		return;
+	msr = new->thread.tm_orig_msr;
+	/* Recheckpoint to restore original checkpointed register state. */
+	TM_DEBUG("*** tm_recheckpoint of pid %d "
+		 "(new->msr 0x%lx, new->origmsr 0x%lx)\n",
+		 new->pid, new->thread.regs->msr, msr);
+
+	/* This loads the checkpointed FP/VEC state, if used */
+	tm_recheckpoint(&new->thread, msr);
+
+	/* This loads the speculative FP/VEC state, if used */
+	if (msr & MSR_FP) {
+		do_load_up_transact_fpu(&new->thread);
+		new->thread.regs->msr |=
+			(MSR_FP | new->thread.fpexc_mode);
+	}
+	if (msr & MSR_VEC) {
+		do_load_up_transact_altivec(&new->thread);
+		new->thread.regs->msr |= MSR_VEC;
+	}
+	/* We may as well turn on VSX too since all the state is restored now */
+	if (msr & MSR_VSX)
+		new->thread.regs->msr |= MSR_VSX;
+
+	TM_DEBUG("*** tm_recheckpoint of pid %d complete "
+		 "(kernel msr 0x%lx)\n",
+		 new->pid, mfmsr());
+}
+
+static inline void __switch_to_tm(struct task_struct *prev)
+{
+	if (cpu_has_feature(CPU_FTR_TM)) {
+		tm_enable();
+		tm_reclaim_task(prev);
+	}
+}
+#else
+#define tm_recheckpoint_new_task(new)
+#define __switch_to_tm(prev)
+#endif /* CONFIG_TRANSACTIONAL_MEM */
+
 struct task_struct *__switch_to(struct task_struct *prev,
 	struct task_struct *new)
 {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 08/16] powerpc: Add FP/VSX and VMX register load functions for transactional memory
From: Michael Neuling @ 2012-11-27  2:48 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

This adds functions to restore the state of the FP/VSX registers from
what's stored in the thread_struct.  Two version for FP/VSX are required
since one restores them from transactional/checkpoint side of the
thread_struct and the other from the speculated side.

Similar functions are added for VMX registers.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/kernel/fpu.S    |   54 ++++++++++++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/vector.S |   51 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S
index adb1551..6ab0e87 100644
--- a/arch/powerpc/kernel/fpu.S
+++ b/arch/powerpc/kernel/fpu.S
@@ -62,6 +62,60 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX);					\
 	__REST_32FPVSRS_TRANSACT(n,__REG_##c,__REG_##base)
 #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base)
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+/*
+ * Wrapper to call load_up_fpu from C.
+ * void do_load_up_fpu(struct pt_regs *regs);
+ */
+_GLOBAL(do_load_up_fpu)
+	mflr	r0
+	std	r0, 16(r1)
+	stdu	r1, -112(r1)
+
+	subi	r6, r3, STACK_FRAME_OVERHEAD
+	/* load_up_fpu expects r12=MSR, r13=PACA, and returns
+	 * with r12 = new MSR.
+	 */
+	ld	r12,_MSR(r6)
+	GET_PACA(r13)
+
+	bl	load_up_fpu
+	std	r12,_MSR(r6)
+
+	ld	r0, 112+16(r1)
+	addi	r1, r1, 112
+	mtlr	r0
+	blr
+
+
+/* void do_load_up_fpu(struct thread_struct *thread)
+ *
+ * This is similar to load_up_fpu but for the transactional version of the FP
+ * register set.  It doesn't mess with the task MSR or valid flags.
+ * Furthermore, we don't do lazy FP with TM currently.
+ */
+_GLOBAL(do_load_up_transact_fpu)
+	mfmsr	r6
+	ori	r5,r6,MSR_FP
+#ifdef CONFIG_VSX
+BEGIN_FTR_SECTION
+	oris	r5,r5,MSR_VSX@h
+END_FTR_SECTION_IFSET(CPU_FTR_VSX)
+#endif
+	SYNC
+	MTMSRD(r5)
+
+	lfd	fr0,THREAD_TRANSACT_FPSCR(r3)
+	MTFSF_L(fr0)
+	REST_32FPVSRS_TRANSACT(0, R4, R3)
+
+	/* FP/VSX off again */
+	MTMSRD(r6)
+	SYNC
+
+	blr
+#endif /* CONFIG_TRANSACTIONAL_MEM */
+
 /*
  * This task wants to use the FPU now.
  * On UP, disable FP for the task which had the FPU previously,
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S
index e830289..330fc8c 100644
--- a/arch/powerpc/kernel/vector.S
+++ b/arch/powerpc/kernel/vector.S
@@ -7,6 +7,57 @@
 #include <asm/page.h>
 #include <asm/ptrace.h>
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+/*
+ * Wrapper to call load_up_altivec from C.
+ * void do_load_up_altivec(struct pt_regs *regs);
+ */
+_GLOBAL(do_load_up_altivec)
+	mflr	r0
+	std	r0, 16(r1)
+	stdu	r1, -112(r1)
+
+	subi	r6, r3, STACK_FRAME_OVERHEAD
+	/* load_up_altivec expects r12=MSR, r13=PACA, and returns
+	 * with r12 = new MSR.
+	 */
+	ld	r12,_MSR(r6)
+	GET_PACA(r13)
+	bl	load_up_altivec
+	std	r12,_MSR(r6)
+
+	ld	r0, 112+16(r1)
+	addi	r1, r1, 112
+	mtlr	r0
+	blr
+
+/* void do_load_up_altivec(struct thread_struct *thread)
+ *
+ * This is similar to load_up_altivec but for the transactional version of the
+ * vector regs.  It doesn't mess with the task MSR or valid flags.
+ * Furthermore, VEC laziness is not supported with TM currently.
+ */
+_GLOBAL(do_load_up_transact_altivec)
+	mfmsr	r6
+	oris	r5,r6,MSR_VEC@h
+	MTMSRD(r5)
+	isync
+
+	li	r4,1
+	stw	r4,THREAD_USED_VR(r3)
+
+	li	r10,THREAD_TRANSACT_VSCR
+	lvx	vr0,r10,r3
+	mtvscr	vr0
+	REST_32VRS_TRANSACT(0,r4,r3)
+
+	/* Disable VEC again. */
+	MTMSRD(r6)
+	isync
+
+	blr
+#endif
+
 /*
  * load_up_altivec(unused, unused, tsk)
  * Disable VMX for the task which had it previously,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 07/16] powerpc: Add helper functions for transactional memory context switching
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Here we add the helper functions to be used when context switching.  These
allow us to fully reclaim and recheckpoint a transaction.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/reg.h |    2 +-
 arch/powerpc/include/asm/tm.h  |   19 ++
 arch/powerpc/kernel/Makefile   |    2 +
 arch/powerpc/kernel/tm.S       |  378 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 400 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/include/asm/tm.h
 create mode 100644 arch/powerpc/kernel/tm.S

diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index bea823f..895020f 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -784,7 +784,7 @@
  *        HV mode in which case it is HSPRG0
  *
  * 64-bit server:
- *	- SPRG0 unused (reserved for HV on Power4)
+ *	- SPRG0 scratch for TM recheckpoint/reclaim (reserved for HV on Power4)
  *	- SPRG2 scratch for exception vectors
  *	- SPRG3 CPU and NUMA node for VDSO getcpu (user visible)
  *      - HSPRG0 stores PACA in HV mode
diff --git a/arch/powerpc/include/asm/tm.h b/arch/powerpc/include/asm/tm.h
new file mode 100644
index 0000000..18f6bf7
--- /dev/null
+++ b/arch/powerpc/include/asm/tm.h
@@ -0,0 +1,19 @@
+/*
+ * Transactional memory support routines to reclaim and recheckpoint
+ * transactional process state.
+ *
+ * Copyright 2012 Matt Evans & Michael Neuling, IBM Corporation.
+ */
+
+#ifdef CONFIG_TRANSACTIONAL_MEM
+extern void do_load_up_transact_fpu(struct thread_struct *thread);
+extern void do_load_up_transact_altivec(struct thread_struct *thread);
+#endif
+
+extern void tm_enable(void);
+extern void tm_reclaim(struct thread_struct *thread,
+		       unsigned long orig_msr, uint8_t cause);
+extern void tm_recheckpoint(struct thread_struct *thread,
+			    unsigned long orig_msr);
+extern void tm_save_sprs(struct thread_struct *thread);
+extern void tm_restore_sprs(struct thread_struct *thread);
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 44fbbea..abf8469 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -122,6 +122,8 @@ ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
 obj-y				+= iomap.o
 endif
 
+obj64-$(CONFIG_TRANSACTIONAL_MEM)	+= tm.o
+
 obj-$(CONFIG_PPC64)		+= $(obj64-y)
 obj-$(CONFIG_PPC32)		+= $(obj32-y)
 
diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S
new file mode 100644
index 0000000..b368e4a
--- /dev/null
+++ b/arch/powerpc/kernel/tm.S
@@ -0,0 +1,378 @@
+/*
+ * Transactional memory support routines to reclaim and recheckpoint
+ * transactional process state.
+ *
+ * Copyright 2012 Matt Evans & Michael Neuling, IBM Corporation.
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/ppc_asm.h>
+#include <asm/ppc-opcode.h>
+#include <asm/ptrace.h>
+#include <asm/reg.h>
+
+#ifdef CONFIG_VSX
+/* See fpu.S, this is very similar but to save/restore checkpointed FPRs/VSRs */
+#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base)	\
+BEGIN_FTR_SECTION				\
+	b	2f;				\
+END_FTR_SECTION_IFSET(CPU_FTR_VSX);		\
+	SAVE_32FPRS_TRANSACT(n,base);		\
+	b	3f;				\
+2:	SAVE_32VSRS_TRANSACT(n,c,base);		\
+3:
+/* ...and this is just plain borrowed from there. */
+#define __REST_32FPRS_VSRS(n,c,base)		\
+BEGIN_FTR_SECTION				\
+	b	2f;				\
+END_FTR_SECTION_IFSET(CPU_FTR_VSX);		\
+	REST_32FPRS(n,base);			\
+	b	3f;				\
+2:	REST_32VSRS(n,c,base);			\
+3:
+#else
+#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base) SAVE_32FPRS_TRANSACT(n, base)
+#define __REST_32FPRS_VSRS(n,c,base)	      REST_32FPRS(n, base)
+#endif
+#define SAVE_32FPRS_VSRS_TRANSACT(n,c,base) \
+	__SAVE_32FPRS_VSRS_TRANSACT(n,__REG_##c,__REG_##base)
+#define REST_32FPRS_VSRS(n,c,base) \
+	__REST_32FPRS_VSRS(n,__REG_##c,__REG_##base)
+
+/* Stack frame offsets for local variables. */
+#define TM_FRAME_L0	TM_FRAME_SIZE-16
+#define TM_FRAME_L1	TM_FRAME_SIZE-8
+#define STACK_PARAM(x)	(48+((x)*8))
+
+
+/* In order to access the TM SPRs, TM must be enabled.  So, do so: */
+_GLOBAL(tm_enable)
+	mfmsr	r4
+	li	r3, MSR_TM >> 32
+	sldi	r3, r3, 32
+	and.	r0, r4, r3
+	bne	1f
+	or	r4, r4, r3
+	mtmsrd	r4
+1:	blr
+
+_GLOBAL(tm_save_sprs)
+	mfspr	r0, SPRN_TFHAR
+	std	r0, THREAD_TM_TFHAR(r3)
+	mfspr	r0, SPRN_TEXASR
+	std	r0, THREAD_TM_TEXASR(r3)
+	mfspr	r0, SPRN_TFIAR
+	std	r0, THREAD_TM_TFIAR(r3)
+	blr
+
+_GLOBAL(tm_restore_sprs)
+	ld	r0, THREAD_TM_TFHAR(r3)
+	mtspr	SPRN_TFHAR, r0
+	ld	r0, THREAD_TM_TEXASR(r3)
+	mtspr	SPRN_TEXASR, r0
+	ld	r0, THREAD_TM_TFIAR(r3)
+	mtspr	SPRN_TFIAR, r0
+	blr
+
+
+/* void tm_reclaim(struct thread_struct *thread,
+ *                 unsigned long orig_msr,
+ *		   uint8_t cause)
+ *
+ *	- Performs a full reclaim.  This destroys outstanding
+ *	  transactions and updates thread->regs.tm_ckpt_* with the
+ *	  original checkpointed state.  Note that thread->regs is
+ *	  unchanged.
+ *	- FP regs are written back to thread->transact_fpr before
+ *	  reclaiming.  These are the transactional (current) versions.
+ *
+ * Purpose is to both abort transactions of, and preserve the state of,
+ * a transactions at a context switch. We preserve/restore both sets of process
+ * state to restore them when the thread's scheduled again.  We continue in
+ * userland as though nothing happened, but when the transaction is resumed
+ * they will abort back to the checkpointed state we save out here.
+ */
+_GLOBAL(tm_reclaim)
+	mfcr	r6
+	mflr	r0
+	std	r6, 8(r1)
+	std	r0, 16(r1)
+	std	r2, 40(r1)
+	stdu	r1, -TM_FRAME_SIZE(r1)
+
+	/* We've a struct pt_regs at [r1+STACK_FRAME_OVERHEAD]. */
+
+	std	r3, STACK_PARAM(0)(r1)
+	SAVE_NVGPRS(r1)
+
+	/* hard_irq_disable */
+	mfmsr	r14
+	li	r6, MSR_EE-1
+	addi	r6, r6, 1			/* Get around sxt on 0x8000 */
+	andc	r15, r14, r6
+	ori	r15, r15, MSR_FP
+	oris	r15, r15, MSR_VEC@h
+#ifdef CONFIG_VSX
+	BEGIN_FTR_SECTION
+	oris	r15,r15, MSR_VSX@h
+	END_FTR_SECTION_IFSET(CPU_FTR_VSX)
+#endif
+	mtmsrd	r15
+	std	r14, TM_FRAME_L0(r1)
+
+	std	r1, PACAR1(r13)
+
+	/* ******************** FPR/VR/VSRs ************
+	 * Before reclaiming, capture the current/transactional FPR/VR
+	* versions /if used/.
+	 *
+	 * (If VSX used, FP and VMX are implied.  Or, we don't need to look
+	 * at MSR.VSX as copying FP regs if .FP, vector regs if .VMX covers it.)
+	 *
+	 * We're passed the thread's MSR as parameter 2.
+	 *
+	 * We enabled VEC/FP/VSX in the msr above, so we can execute these
+	 * instructions!
+	 */
+	andis.		r0, r4, MSR_VEC@h
+	beq	dont_backup_vec
+
+	SAVE_32VRS_TRANSACT(0, r6, r3)	/* r6 scratch, r3 thread */
+	mfvscr	vr0
+	li	r6, THREAD_TRANSACT_VSCR
+	stvx	vr0, r3, r6
+	mfspr	r0, SPRN_VRSAVE
+	std	r0, THREAD_TRANSACT_VRSAVE(r3)
+
+dont_backup_vec:
+	andi.	r0, r4, MSR_FP
+	beq	dont_backup_fp
+
+	SAVE_32FPRS_VSRS_TRANSACT(0, R6, R3)	/* r6 scratch, r3 thread */
+
+	mffs    fr0
+	stfd    fr0,THREAD_TRANSACT_FPSCR(r3)
+
+dont_backup_fp:
+	/* The moment we treclaim, ALL of our GPRs will switch
+	 * to user register state.  (FPRs, CCR etc. also!)
+	 * Use an sprg and a scratch dword in the PACA to shuffle.
+	 */
+	TRECLAIM(R5)				/* Cause in r5 */
+
+	/* ******************** GPRs ******************** */
+	SET_SCRATCH0(r13)
+	GET_PACA(r13)
+	std	r1, PACATMSCRATCH(r13)
+
+	ld	r1, PACAR1(r13)		/* Get our stack back */
+	/* Temporarily stash checkpointed r0 here to get another
+	 * scratch reg for our userland pt_regs pointer:
+	 */
+	std	r7, GPR7(r1)			/* Temporary stash */
+	std	r12, GPR12(r1)			/* ''   ''    ''   */
+	ld	r12, STACK_PARAM(0)(r1)		/* Param 0, thread_struct * */
+
+	addi	r7, r12, PT_CKPT_REGS		/* Thread's ckpt_regs */
+
+	/* Make r7 look like an exception frame so that we
+	 * can use the neat GPRx(n) macros.  r7 is NOT a pt_regs ptr!
+	 */
+	subi	r7, r7, STACK_FRAME_OVERHEAD
+
+	/* Sync the userland GPRs 2-12, 14-31 to thread->regs: */
+	SAVE_GPR(0, r7)				/* user r0 */
+	SAVE_GPR(2, r7)			/* user r2 */
+	SAVE_4GPRS(3, r7)			/* user r3-r6 */
+	SAVE_4GPRS(8, r7)			/* user r8-r11 */
+	ld	r3, PACATMSCRATCH(r13)		/* user r1 */
+	ld	r4, GPR7(r1)			/* user r7 */
+	ld	r5, GPR12(r1)			/* user r12 */
+	GET_SCRATCH0(6)				/* user r13 */
+	std	r3, GPR1(r7)
+	std	r4, GPR7(r7)
+	std	r5, GPR12(r7)
+	std	r6, GPR13(r7)
+
+	SAVE_NVGPRS(r7)				/* user r14-r31 */
+
+	/* ******************** NIP ******************** */
+	mfspr	r3, SPRN_TFHAR
+	std	r3, _NIP(r7)			/* Returns to failhandler */
+	/* The checkpointed NIP is ignored when rescheduling/rechkpting,
+	 * but is used in signal return to 'wind back' to the abort handler.
+	 */
+
+	/* ******************** CR,LR,CCR,MSR ********** */
+	mfctr	r3
+	mflr	r4
+	mfcr	r5
+	mfxer	r6
+
+	std	r3, _CTR(r7)
+	std	r4, _LINK(r7)
+	std	r5, _CCR(r7)
+	std	r6, _XER(r7)
+
+	/* MSR and flags:  We don't change CRs, and we don't need to alter
+	 * MSR.
+	 */
+
+	/* TM regs, incl TEXASR -- these live in thread_struct.  Note they've
+	 * been updated by the treclaim, to explain to userland the failure
+	 * cause (aborted).
+	 */
+	mfspr	r0, SPRN_TEXASR
+	mfspr	r3, SPRN_TFHAR
+	mfspr	r4, SPRN_TFIAR
+	std	r0, THREAD_TM_TEXASR(r12)
+	std	r3, THREAD_TM_TFHAR(r12)
+	std	r4, THREAD_TM_TFIAR(r12)
+
+	/* AMR and PPR are checkpointed too, but are unsupported by Linux. */
+
+	/* Restore original MSR/IRQ state & clear TM mode */
+	ld	r14, TM_FRAME_L0(r1)		/* Orig MSR */
+	li	r15, 0
+	rldimi  r14, r15, MSR_TS_LG, (63-MSR_TS_LG)-1
+	mtmsrd  r14
+
+	REST_NVGPRS(r1)
+
+	addi    r1, r1, TM_FRAME_SIZE
+	ld	r4, 8(r1)
+	ld	r0, 16(r1)
+	mtcr	r4
+	mtlr	r0
+	ld	r2, 40(r1)
+	blr
+
+
+	/* void tm_recheckpoint(struct thread_struct *thread,
+	 *			unsigned long orig_msr)
+	 *	- Restore the checkpointed register state saved by tm_reclaim
+	 *	  when we switch_to a process.
+	 *
+	 *	Call with IRQs off, stacks get all out of sync for
+	 *	some periods in here!
+	 */
+_GLOBAL(tm_recheckpoint)
+	mfcr	r5
+	mflr	r0
+	std	r5, 8(r1)
+	std	r0, 16(r1)
+	std	r2, 40(r1)
+	stdu	r1, -TM_FRAME_SIZE(r1)
+
+	/* We've a struct pt_regs at [r1+STACK_FRAME_OVERHEAD].
+	 * This is used for backing up the NVGPRs:
+	 */
+	SAVE_NVGPRS(r1)
+
+	std	r1, PACAR1(r13)
+
+	/* Load complete register state from ts_ckpt* registers */
+
+	addi	r7, r3, PT_CKPT_REGS		/* Thread's ckpt_regs */
+
+	/* Make r7 look like an exception frame so that we
+	 * can use the neat GPRx(n) macros.  r7 is now NOT a pt_regs ptr!
+	 */
+	subi	r7, r7, STACK_FRAME_OVERHEAD
+
+	SET_SCRATCH0(r1)
+
+	mfmsr	r6
+	/* R4 = original MSR to indicate whether thread used FP/Vector etc. */
+
+	/* Enable FP/vec in MSR if necessary! */
+	lis	r5, MSR_VEC@h
+	ori	r5, r5, MSR_FP
+	and.	r5, r4, r5
+	beq	restore_gprs			/* if neither, skip both */
+
+#ifdef CONFIG_VSX
+	BEGIN_FTR_SECTION
+	oris	r5, r5, MSR_VSX@h
+	END_FTR_SECTION_IFSET(CPU_FTR_VSX)
+#endif
+	or	r5, r6, r5			/* Set MSR.FP+.VSX/.VEC */
+	mtmsr	r5
+
+	/* FP and VEC registers:  These are recheckpointed from thread.fpr[]
+	 * and thread.vr[] respectively.  The thread.transact_fpr[] version
+	 * is more modern, and will be loaded subsequently by any FPUnavailable
+	 * trap.
+	 */
+	andis.	r0, r4, MSR_VEC@h
+	beq	dont_restore_vec
+
+	li	r5, THREAD_VSCR
+	lvx	vr0, r3, r5
+	mtvscr	vr0
+	REST_32VRS(0, r5, r3)			/* r5 scratch, r3 THREAD ptr */
+	ld	r5, THREAD_VRSAVE(r3)
+	mtspr	SPRN_VRSAVE, r5
+
+dont_restore_vec:
+	andi.	r0, r4, MSR_FP
+	beq	dont_restore_fp
+
+	lfd	fr0, THREAD_FPSCR(r3)
+	MTFSF_L(fr0)
+	REST_32FPRS_VSRS(0, R4, R3)
+
+dont_restore_fp:
+	mtmsr	r6				/* FP/Vec off again! */
+
+restore_gprs:
+	/* ******************** CR,LR,CCR,MSR ********** */
+	ld	r3, _CTR(r7)
+	ld	r4, _LINK(r7)
+	ld	r5, _CCR(r7)
+	ld	r6, _XER(r7)
+
+	mtctr	r3
+	mtlr	r4
+	mtcr	r5
+	mtxer	r6
+
+	/* MSR and flags:  We don't change CRs, and we don't need to alter
+	 * MSR.
+	 */
+
+	REST_4GPRS(0, r7)			/* GPR0-3 */
+	REST_GPR(4, r7)				/* GPR4-6 */
+	REST_GPR(5, r7)
+	REST_GPR(6, r7)
+	REST_4GPRS(8, r7)			/* GPR8-11 */
+	REST_2GPRS(12, r7)			/* GPR12-13 */
+
+	REST_NVGPRS(r7)				/* GPR14-31 */
+
+	ld	r7, GPR7(r7)			/* GPR7 */
+
+	/* Commit register state as checkpointed state: */
+	TRECHKPT
+
+	/* Our transactional state has now changed.
+	 *
+	 * Now just get out of here.  Transactional (current) state will be
+	 * updated once restore is called on the return path in the _switch-ed
+	 * -to process.
+	 */
+
+	GET_PACA(r13)
+	GET_SCRATCH0(r1)
+
+	REST_NVGPRS(r1)
+
+	addi    r1, r1, TM_FRAME_SIZE
+	ld	r4, 8(r1)
+	ld	r0, 16(r1)
+	mtcr	r4
+	mtlr	r0
+	ld	r2, 40(r1)
+	blr
+
+	/* ****************************************************************** */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 06/16] powerpc: Add transactional memory paca scratch register to show_regs
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Add transactional memory paca scratch register to show_regs.  This is useful
for debugging.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/paca.h   |    1 +
 arch/powerpc/kernel/asm-offsets.c |    1 +
 arch/powerpc/kernel/entry_64.S    |    4 ++++
 arch/powerpc/kernel/process.c     |    3 +++
 4 files changed, 9 insertions(+)

diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index e9e7a69..0168516 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -137,6 +137,7 @@ struct paca_struct {
 	u8 irq_work_pending;		/* IRQ_WORK interrupt while soft-disable */
 	u8 nap_state_lost;		/* NV GPR values lost in power7_idle */
 	u64 sprg3;			/* Saved user-visible sprg */
+	u64 tm_scratch;                 /* TM scratch area for reclaim */
 
 #ifdef CONFIG_PPC_POWERNV
 	/* Pointer to OPAL machine check event structure set by the
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 1a70f02..42a4243 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -122,6 +122,7 @@ int main(void)
 #endif
 
 #ifdef CONFIG_TRANSACTIONAL_MEM
+	DEFINE(PACATMSCRATCH, offsetof(struct paca_struct, tm_scratch));
 	DEFINE(THREAD_TM_TFHAR, offsetof(struct thread_struct, tm_tfhar));
 	DEFINE(THREAD_TM_TEXASR, offsetof(struct thread_struct, tm_texasr));
 	DEFINE(THREAD_TM_TFIAR, offsetof(struct thread_struct, tm_tfiar));
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index df6857f..5ae8e51 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -755,6 +755,10 @@ fast_exception_return:
 	andc	r4,r4,r0	 /* r0 contains MSR_RI here */
 	mtmsrd	r4,1
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	/* TM debug */
+	std	r3, PACATMSCRATCH(r13) /* Stash returned-to MSR */
+#endif
 	/*
 	 * r13 is our per cpu area, only restore it if we are returning to
 	 * userspace the value stored in the stack frame may belong to
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 6d66a68..fd5ce1b 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -693,6 +693,9 @@ void show_regs(struct pt_regs * regs)
 	printk("NIP ["REG"] %pS\n", regs->nip, (void *)regs->nip);
 	printk("LR ["REG"] %pS\n", regs->link, (void *)regs->link);
 #endif
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	printk("PACATMSCRATCH [%llx]\n", get_paca()->tm_scratch);
+#endif
 	show_stack(current, (unsigned long *) regs->gpr[1]);
 	if (!user_mode(regs))
 		show_instructions(regs);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 05/16] powerpc: Register defines for various transactional memory registers
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Defines for MSR bits and transactional memory related SPRs TFIAR, TEXASR and
TEXASRU.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/reg.h |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 736c6af..bea823f 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -29,6 +29,8 @@
 #define MSR_SF_LG	63              /* Enable 64 bit mode */
 #define MSR_ISF_LG	61              /* Interrupt 64b mode valid on 630 */
 #define MSR_HV_LG 	60              /* Hypervisor state */
+#define MSR_TS_LG	33		/* Transactional Mem State (2 bits) */
+#define MSR_TM_LG	32		/* Transactional Mem Available */
 #define MSR_VEC_LG	25	        /* Enable AltiVec */
 #define MSR_VSX_LG	23		/* Enable VSX */
 #define MSR_POW_LG	18		/* Enable Power Management */
@@ -98,6 +100,21 @@
 #define MSR_RI		__MASK(MSR_RI_LG)	/* Recoverable Exception */
 #define MSR_LE		__MASK(MSR_LE_LG)	/* Little Endian */
 
+#define MSR_TM	        __MASK(MSR_TM_LG)	/* Transactional Mem Available */
+#define MSR_TS_MASK     (__MASK(MSR_TS_LG) | \
+		         __MASK(MSR_TS_LG+1))   /* Transaction State bits */
+#define MSR_TM_ACTIVE(x) (((x) & MSR_TS_MASK) != 0) /* Transaction active? */
+
+/* Reason codes describing kernel causes for transaction aborts.  By
+   convention, bit0 is copied to TEXASR[56] (IBM bit 7) which is set if
+   the failure is persistent.
+*/
+#define TM_CAUSE_RESCHED	0xfe
+#define TM_CAUSE_TLBI		0xfc
+#define TM_CAUSE_FAC_UNAV	0xfa
+#define TM_CAUSE_SYSCALL	0xf9 /* Persistent */
+#define TM_CAUSE_MISC		0xf6
+
 #if defined(CONFIG_PPC_BOOK3S_64)
 #define MSR_64BIT	MSR_SF
 
@@ -193,6 +210,10 @@
 #define SPRN_UAMOR	0x9d	/* User Authority Mask Override Register */
 #define SPRN_AMOR	0x15d	/* Authority Mask Override Register */
 #define SPRN_ACOP	0x1F	/* Available Coprocessor Register */
+#define SPRN_TFIAR	0x81	/* Transaction Failure Inst Addr   */
+#define SPRN_TEXASR	0x82	/* Transaction EXception & Summary */
+#define SPRN_TEXASRU	0x83	/* ''	   ''	   ''	 Upper 32  */
+#define SPRN_TFHAR	0x80	/* Transaction Failure Handler Addr */
 #define SPRN_CTRLF	0x088
 #define SPRN_CTRLT	0x098
 #define   CTRL_CT	0xc0000000	/* current thread */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 04/16] powerpc: New macros for transactional memory support
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

This adds new macros for saving and restoring checkpointed architected state
from and to the thread_struct.

It also adds some debugging macros for when your brain explodes trying to debug
your transactional memory enabled kernel.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/ppc_asm.h   |   83 ++++++++++++++++++++++++++++++++++
 arch/powerpc/include/asm/processor.h |    1 +
 arch/powerpc/kernel/asm-offsets.c    |   24 ++++++++++
 arch/powerpc/kernel/fpu.S            |   12 +++++
 arch/powerpc/kernel/process.c        |   10 ++++
 arch/powerpc/kernel/traps.c          |   11 +++++
 6 files changed, 141 insertions(+)

diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index ea2a86e..a17a598 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -125,6 +125,89 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR)
 #define REST_16VRS(n,b,base)	REST_8VRS(n,b,base); REST_8VRS(n+8,b,base)
 #define REST_32VRS(n,b,base)	REST_16VRS(n,b,base); REST_16VRS(n+16,b,base)
 
+/* Save/restore FPRs, VRs and VSRs from their checkpointed backups in
+ * thread_struct:
+ */
+#define SAVE_FPR_TRANSACT(n, base)	stfd n,THREAD_TRANSACT_FPR0+	\
+					8*TS_FPRWIDTH*(n)(base)
+#define SAVE_2FPRS_TRANSACT(n, base)	SAVE_FPR_TRANSACT(n, base);	\
+					SAVE_FPR_TRANSACT(n+1, base)
+#define SAVE_4FPRS_TRANSACT(n, base)	SAVE_2FPRS_TRANSACT(n, base);	\
+					SAVE_2FPRS_TRANSACT(n+2, base)
+#define SAVE_8FPRS_TRANSACT(n, base)	SAVE_4FPRS_TRANSACT(n, base);	\
+					SAVE_4FPRS_TRANSACT(n+4, base)
+#define SAVE_16FPRS_TRANSACT(n, base)	SAVE_8FPRS_TRANSACT(n, base);	\
+					SAVE_8FPRS_TRANSACT(n+8, base)
+#define SAVE_32FPRS_TRANSACT(n, base)	SAVE_16FPRS_TRANSACT(n, base);	\
+					SAVE_16FPRS_TRANSACT(n+16, base)
+
+#define REST_FPR_TRANSACT(n, base)	lfd	n,THREAD_TRANSACT_FPR0+	\
+					8*TS_FPRWIDTH*(n)(base)
+#define REST_2FPRS_TRANSACT(n, base)	REST_FPR_TRANSACT(n, base);	\
+					REST_FPR_TRANSACT(n+1, base)
+#define REST_4FPRS_TRANSACT(n, base)	REST_2FPRS_TRANSACT(n, base);	\
+					REST_2FPRS_TRANSACT(n+2, base)
+#define REST_8FPRS_TRANSACT(n, base)	REST_4FPRS_TRANSACT(n, base);	\
+					REST_4FPRS_TRANSACT(n+4, base)
+#define REST_16FPRS_TRANSACT(n, base)	REST_8FPRS_TRANSACT(n, base);	\
+					REST_8FPRS_TRANSACT(n+8, base)
+#define REST_32FPRS_TRANSACT(n, base)	REST_16FPRS_TRANSACT(n, base);	\
+					REST_16FPRS_TRANSACT(n+16, base)
+
+
+#define SAVE_VR_TRANSACT(n,b,base)	li b,THREAD_TRANSACT_VR0+(16*(n)); \
+					stvx n,b,base
+#define SAVE_2VRS_TRANSACT(n,b,base)	SAVE_VR_TRANSACT(n,b,base);	\
+					SAVE_VR_TRANSACT(n+1,b,base)
+#define SAVE_4VRS_TRANSACT(n,b,base)	SAVE_2VRS_TRANSACT(n,b,base);	\
+					SAVE_2VRS_TRANSACT(n+2,b,base)
+#define SAVE_8VRS_TRANSACT(n,b,base)	SAVE_4VRS_TRANSACT(n,b,base);	\
+					SAVE_4VRS_TRANSACT(n+4,b,base)
+#define SAVE_16VRS_TRANSACT(n,b,base)	SAVE_8VRS_TRANSACT(n,b,base);	\
+					SAVE_8VRS_TRANSACT(n+8,b,base)
+#define SAVE_32VRS_TRANSACT(n,b,base)	SAVE_16VRS_TRANSACT(n,b,base);	\
+					SAVE_16VRS_TRANSACT(n+16,b,base)
+
+#define REST_VR_TRANSACT(n,b,base)	li b,THREAD_TRANSACT_VR0+(16*(n)); \
+					lvx n,b,base
+#define REST_2VRS_TRANSACT(n,b,base)	REST_VR_TRANSACT(n,b,base);	\
+					REST_VR_TRANSACT(n+1,b,base)
+#define REST_4VRS_TRANSACT(n,b,base)	REST_2VRS_TRANSACT(n,b,base);	\
+					REST_2VRS_TRANSACT(n+2,b,base)
+#define REST_8VRS_TRANSACT(n,b,base)	REST_4VRS_TRANSACT(n,b,base);	\
+					REST_4VRS_TRANSACT(n+4,b,base)
+#define REST_16VRS_TRANSACT(n,b,base)	REST_8VRS_TRANSACT(n,b,base);	\
+					REST_8VRS_TRANSACT(n+8,b,base)
+#define REST_32VRS_TRANSACT(n,b,base)	REST_16VRS_TRANSACT(n,b,base);	\
+					REST_16VRS_TRANSACT(n+16,b,base)
+
+
+#define SAVE_VSR_TRANSACT(n,b,base)	li b,THREAD_TRANSACT_VSR0+(16*(n)); \
+					STXVD2X(n,R##base,R##b)
+#define SAVE_2VSRS_TRANSACT(n,b,base)	SAVE_VSR_TRANSACT(n,b,base);	\
+	                                SAVE_VSR_TRANSACT(n+1,b,base)
+#define SAVE_4VSRS_TRANSACT(n,b,base)	SAVE_2VSRS_TRANSACT(n,b,base);	\
+	                                SAVE_2VSRS_TRANSACT(n+2,b,base)
+#define SAVE_8VSRS_TRANSACT(n,b,base)	SAVE_4VSRS_TRANSACT(n,b,base);	\
+	                                SAVE_4VSRS_TRANSACT(n+4,b,base)
+#define SAVE_16VSRS_TRANSACT(n,b,base)	SAVE_8VSRS_TRANSACT(n,b,base);	\
+	                                SAVE_8VSRS_TRANSACT(n+8,b,base)
+#define SAVE_32VSRS_TRANSACT(n,b,base)	SAVE_16VSRS_TRANSACT(n,b,base);	\
+	                                SAVE_16VSRS_TRANSACT(n+16,b,base)
+
+#define REST_VSR_TRANSACT(n,b,base)	li b,THREAD_TRANSACT_VSR0+(16*(n)); \
+					LXVD2X(n,R##base,R##b)
+#define REST_2VSRS_TRANSACT(n,b,base)	REST_VSR_TRANSACT(n,b,base);    \
+	                                REST_VSR_TRANSACT(n+1,b,base)
+#define REST_4VSRS_TRANSACT(n,b,base)	REST_2VSRS_TRANSACT(n,b,base);	\
+	                                REST_2VSRS_TRANSACT(n+2,b,base)
+#define REST_8VSRS_TRANSACT(n,b,base)	REST_4VSRS_TRANSACT(n,b,base);	\
+	                                REST_4VSRS_TRANSACT(n+4,b,base)
+#define REST_16VSRS_TRANSACT(n,b,base)	REST_8VSRS_TRANSACT(n,b,base);	\
+	                                REST_8VSRS_TRANSACT(n+8,b,base)
+#define REST_32VSRS_TRANSACT(n,b,base)	REST_16VSRS_TRANSACT(n,b,base);	\
+	                                REST_16VSRS_TRANSACT(n+16,b,base)
+
 /* Save the lower 32 VSRs in the thread VSR region */
 #define SAVE_VSR(n,b,base)	li b,THREAD_VSR0+(16*(n));  STXVD2X(n,R##base,R##b)
 #define SAVE_2VSRS(n,b,base)	SAVE_VSR(n,b,base); SAVE_VSR(n+1,b,base)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 0d1c188..8b2bf7a 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -141,6 +141,7 @@ typedef struct {
 #define TS_FPROFFSET 0
 #define TS_VSRLOWOFFSET 1
 #define TS_FPR(i) fpr[i][TS_FPROFFSET]
+#define TS_TRANS_FPR(i) transact_fpr[i][TS_FPROFFSET]
 
 struct thread_struct {
 	unsigned long	ksp;		/* Kernel stack pointer */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 7523539..1a70f02 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -121,6 +121,30 @@ int main(void)
 	DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu));
 #endif
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	DEFINE(THREAD_TM_TFHAR, offsetof(struct thread_struct, tm_tfhar));
+	DEFINE(THREAD_TM_TEXASR, offsetof(struct thread_struct, tm_texasr));
+	DEFINE(THREAD_TM_TFIAR, offsetof(struct thread_struct, tm_tfiar));
+	DEFINE(PT_CKPT_REGS, offsetof(struct thread_struct, ckpt_regs));
+	DEFINE(THREAD_TRANSACT_VR0, offsetof(struct thread_struct,
+					 transact_vr[0]));
+	DEFINE(THREAD_TRANSACT_VSCR, offsetof(struct thread_struct,
+					  transact_vscr));
+	DEFINE(THREAD_TRANSACT_VRSAVE, offsetof(struct thread_struct,
+					    transact_vrsave));
+	DEFINE(THREAD_TRANSACT_FPR0, offsetof(struct thread_struct,
+					  transact_fpr[0]));
+	DEFINE(THREAD_TRANSACT_FPSCR, offsetof(struct thread_struct,
+					   transact_fpscr));
+#ifdef CONFIG_VSX
+	DEFINE(THREAD_TRANSACT_VSR0, offsetof(struct thread_struct,
+					  transact_fpr[0]));
+#endif
+	/* Local pt_regs on stack for Transactional Memory funcs. */
+	DEFINE(TM_FRAME_SIZE, STACK_FRAME_OVERHEAD +
+	       sizeof(struct pt_regs) + 16);
+#endif /* CONFIG_TRANSACTIONAL_MEM */
+
 	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
 	DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
 	DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S
index e0ada05..adb1551 100644
--- a/arch/powerpc/kernel/fpu.S
+++ b/arch/powerpc/kernel/fpu.S
@@ -35,6 +35,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX);					\
 2:	REST_32VSRS(n,c,base);						\
 3:
 
+#define __REST_32FPVSRS_TRANSACT(n,c,base)				\
+BEGIN_FTR_SECTION							\
+	b	2f;							\
+END_FTR_SECTION_IFSET(CPU_FTR_VSX);					\
+	REST_32FPRS_TRANSACT(n,base);					\
+	b	3f;							\
+2:	REST_32VSRS_TRANSACT(n,c,base);					\
+3:
+
 #define __SAVE_32FPVSRS(n,c,base)					\
 BEGIN_FTR_SECTION							\
 	b	2f;							\
@@ -45,9 +54,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX);					\
 3:
 #else
 #define __REST_32FPVSRS(n,b,base)	REST_32FPRS(n, base)
+#define __REST_32FPVSRS_TRANSACT(n,b,base)	REST_32FPRS(n, base)
 #define __SAVE_32FPVSRS(n,b,base)	SAVE_32FPRS(n, base)
 #endif
 #define REST_32FPVSRS(n,c,base) __REST_32FPVSRS(n,__REG_##c,__REG_##base)
+#define REST_32FPVSRS_TRANSACT(n,c,base) \
+	__REST_32FPVSRS_TRANSACT(n,__REG_##c,__REG_##base)
 #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base)
 
 /*
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index ba48233..6d66a68 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -57,6 +57,16 @@
 #include <linux/kprobes.h>
 #include <linux/kdebug.h>
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+/* Transactional Memory debug */
+// #define TM_DEBUG_SW
+#ifdef TM_DEBUG_SW
+#define TM_DEBUG(x...) printk(KERN_INFO x)
+#else
+#define TM_DEBUG(x...) do { } while(0)
+#endif
+#endif
+
 extern unsigned long _get_SP(void);
 
 #ifndef CONFIG_SMP
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 3251840..8fed874 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -78,6 +78,16 @@ EXPORT_SYMBOL(__debugger_dabr_match);
 EXPORT_SYMBOL(__debugger_fault_handler);
 #endif
 
+#ifdef CONFIG_TRANSACTIONAL_MEM
+/* Transactional Memory trap debug */
+// #define TM_DEBUG_SW
+#ifdef TM_DEBUG_SW
+#define TM_DEBUG(x...) printk(KERN_INFO x)
+#else
+#define TM_DEBUG(x...) do { } while(0)
+#endif
+#endif
+
 /*
  * Trap & Exception support
  */
@@ -350,6 +360,7 @@ static inline int check_io_access(struct pt_regs *regs)
    exception is in the MSR. */
 #define get_reason(regs)	((regs)->msr)
 #define get_mc_reason(regs)	((regs)->msr)
+#define REASON_TM		0x200000
 #define REASON_FP		0x100000
 #define REASON_ILLEGAL		0x80000
 #define REASON_PRIVILEGED	0x40000
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 03/16] powerpc: Add additional state needed for transactional memory to thread struct
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Set of new archtected state for saving away on context switch.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/processor.h |   28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 8750204..0d1c188 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -236,6 +236,34 @@ struct thread_struct {
 	unsigned long	spefscr;	/* SPE & eFP status */
 	int		used_spe;	/* set if process has used spe */
 #endif /* CONFIG_SPE */
+#ifdef CONFIG_TRANSACTIONAL_MEM
+	u64		tm_tfhar;	/* Transaction fail handler addr */
+	u64		tm_texasr;	/* Transaction exception & summary */
+	u64		tm_tfiar;	/* Transaction fail instr address reg */
+	unsigned long	tm_orig_msr;	/* Thread's MSR on ctx switch */
+	struct pt_regs	ckpt_regs;	/* Checkpointed registers */
+
+	/*
+	 * Transactional FP and VSX 0-31 register set.
+	 * NOTE: the sense of these is the opposite of the integer ckpt_regs!
+	 *
+	 * When a transaction is active/signalled/scheduled etc., *regs is the
+	 * most recent set of/speculated GPRs with ckpt_regs being the older
+	 * checkpointed regs to which we roll back if transaction aborts.
+	 *
+	 * However, fpr[] is the checkpointed 'base state' of FP regs, and
+	 * transact_fpr[] is the new set of transactional values.
+	 * VRs work the same way.
+	 */
+	double		transact_fpr[32][TS_FPRWIDTH];
+	struct {
+		unsigned int pad;
+		unsigned int val;	/* Floating point status */
+	} transact_fpscr;
+	vector128	transact_vr[32] __attribute__((aligned(16)));
+	vector128	transact_vscr __attribute__((aligned(16)));
+	unsigned long	transact_vrsave;
+#endif /* CONFIG_TRANSACTIONAL_MEM */
 #ifdef CONFIG_KVM_BOOK3S_32_HANDLER
 	void*		kvm_shadow_vcpu; /* KVM internal data */
 #endif /* CONFIG_KVM_BOOK3S_32_HANDLER */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 02/16] powerpc: Add new instructions for transactional memory
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Here we define the new instructions we need for transactional memory in the
kernel.  This is so we can support compiling with binutils that don't support
the new transactional memory instructions.

Transactional memory results in two sets of architected state (GPRs/VSRs
etc).

treclaim allows us to read the checkpointed state (from the tbegin) so that we
can store it away on a context switch.  It does this by overwriting the exiting
architected state, so you have to save that away before you treclaim.  treclaim
will also abort a transaction, so you can give a register value which contains
an abort reason.

trecheckpoint allows us to inject into the checkpointed state as if it were at
the tbegin.  It does this by copying the current architected state into the
checkpointed state.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/ppc-opcode.h |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index 45fd394..3674ffc 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -129,6 +129,8 @@
 #define PPC_INST_TLBSRX_DOT		0x7c0006a5
 #define PPC_INST_XXLOR			0xf0000510
 #define PPC_INST_XVCPSGNDP		0xf0000780
+#define PPC_INST_TRECHKPT		0x7c0007dd
+#define PPC_INST_TRECLAIM		0x7c00075d
 
 #define PPC_INST_NAP			0x4c000364
 #define PPC_INST_SLEEP			0x4c0003a4
@@ -291,4 +293,9 @@
 #define PPC_NAP			stringify_in_c(.long PPC_INST_NAP)
 #define PPC_SLEEP		stringify_in_c(.long PPC_INST_SLEEP)
 
+/* Transactional memory instructions */
+#define TRECHKPT		stringify_in_c(.long PPC_INST_TRECHKPT)
+#define TRECLAIM(r)		stringify_in_c(.long PPC_INST_TRECLAIM \
+					       | __PPC_RA(r))
+
 #endif /* _ASM_POWERPC_PPC_OPCODE_H */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 01/16] powerpc: Add new CPU feature bit for transactional memory
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
 arch/powerpc/include/asm/cputable.h |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index fc4d2c5..f2163da 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -171,6 +171,7 @@ extern const char *powerpc_base_platform;
 #define CPU_FTR_POPCNTD			LONG_ASM_CONST(0x0800000000000000)
 #define CPU_FTR_ICSWX			LONG_ASM_CONST(0x1000000000000000)
 #define CPU_FTR_VMX_COPY		LONG_ASM_CONST(0x2000000000000000)
+#define CPU_FTR_TM			LONG_ASM_CONST(0x4000000000000000)
 
 #ifndef __ASSEMBLY__
 
@@ -216,6 +217,13 @@ extern const char *powerpc_base_platform;
 #define PPC_FEATURE_HAS_EFP_DOUBLE_COMP 0
 #endif
 
+/* We only set the TM feature if the kernel was compiled with TM supprt */
+#ifdef CONFIG_TRANSACTIONAL_MEM
+#define CPU_FTR_TM_COMP		CPU_FTR_TM
+#else
+#define CPU_FTR_TM_COMP		0
+#endif
+
 /* We need to mark all pages as being coherent if we're SMP or we have a
  * 74[45]x and an MPC107 host bridge. Also 83xx and PowerQUICC II
  * require it for PCI "streaming/prefetch" to work properly.
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 00/16] powerpc: Hardware transactional memory support for POWER8
From: Michael Neuling @ 2012-11-27  2:47 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev

POWER8 implements hardware transactional memory support.  This patch series
adds kernel support so that user programs can use this hardware transactional
memory and the new state is properly context switched.  It is not currently
used by the kernel itself.

This patch series was originally developed by Matt Evans.

Basic overview of a POWER8 hardware transaction memory
=====================================================
Hardware transactional memory is a feature that enables a different form of
atomic memory access.  Several new instructions are presented to delimit
transactions; transactions are guaranteed to either complete atomically or roll
back and undo any partial changes.

A simple transaction looks like this:

begin_move_money:
  tbegin
  beq   abort_handler

  ld    r4, SAVINGS_ACCT(r3)
  ld    r5, CURRENT_ACCT(r3)
  subi  r5, r5, 1
  addi  r4, r4, 1
  std   r4, SAVINGS_ACCT(r3)
  std   r5, CURRENT_ACCT(r3)

  tend

  b     continue

abort_handler:
  ... test for odd failures ...

  /* Retry the transaction if it failed because it conflicted with
   * someone else: */
  b     begin_move_money


The 'tbegin' instruction denotes the start point, and 'tend' the end point.
Between these points the processor is in 'Transactional' state; any memory
references will complete in one go if there are no conflicts with other
transactional or non-transactional accesses within the system.  In this
example, the transaction completes as though it were normal straight-line code
IF no other processor has touched SAVINGS_ACCT(r3) or CURRENT_ACCT(r3); an
atomic move of money from the current account to the savings account has been
performed.  Even though the normal ld/std instructions are used (note no
lwarx/stwcx), either *both* SAVINGS_ACCT(r3) and CURRENT_ACCT(r3) will be
updated, or neither will be updated.

If, in the meantime, there is a conflict with the locations accessed by the
transaction, the transaction will be aborted by the CPU.  Register and memory
state will roll back to that at the 'tbegin', and control will continue from
'tbegin+4'.  The branch to abort_handler will be taken this second time; the
abort handler can check the cause of the failure, and retry.

Checkpointed registers include all GPRs, FPRs, VRs/VSRs, LR, CCR/CR, CTR, FPCSR
and a few other status/flag regs; 

-- 
1.7.9.5

^ permalink raw reply

* Re: [PATCH v3 11/12] memory-hotplug: remove sysfs file of node
From: Wen Congyang @ 2012-11-27  2:24 UTC (permalink / raw)
  To: Jianguo Wu
  Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
	linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
	paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
	Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50B37C52.2060301@gmail.com>

At 11/26/2012 10:27 PM, Jianguo Wu Wrote:
> On 2012/11/1 17:44, Wen Congyang wrote:
>> This patch introduces a new function try_offline_node() to
>> remove sysfs file of node when all memory sections of this
>> node are removed. If some memory sections of this node are
>> not removed, this function does nothing.
>>
>> CC: David Rientjes <rientjes@google.com>
>> CC: Jiang Liu <liuj97@gmail.com>
>> CC: Len Brown <len.brown@intel.com>
>> CC: Christoph Lameter <cl@linux.com>
>> Cc: Minchan Kim <minchan.kim@gmail.com>
>> CC: Andrew Morton <akpm@linux-foundation.org>
>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>> CC: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
>> ---
>>  drivers/acpi/acpi_memhotplug.c |  8 +++++-
>>  include/linux/memory_hotplug.h |  2 +-
>>  mm/memory_hotplug.c            | 58 ++++++++++++++++++++++++++++++++++++++++--
>>  3 files changed, 64 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
>> index 24c807f..0780f99 100644
>> --- a/drivers/acpi/acpi_memhotplug.c
>> +++ b/drivers/acpi/acpi_memhotplug.c
>> @@ -310,7 +310,9 @@ static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
>>  {
>>  	int result;
>>  	struct acpi_memory_info *info, *n;
>> +	int node;
>>  
>> +	node = acpi_get_node(mem_device->device->handle);
>>  
>>  	/*
>>  	 * Ask the VM to offline this memory range.
>> @@ -318,7 +320,11 @@ static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
>>  	 */
>>  	list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
>>  		if (info->enabled) {
>> -			result = remove_memory(info->start_addr, info->length);
>> +			if (node < 0)
>> +				node = memory_add_physaddr_to_nid(
>> +					info->start_addr);
>> +			result = remove_memory(node, info->start_addr,
>> +				info->length);
>>  			if (result)
>>  				return result;
>>  		}
>> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
>> index d4c4402..7b4cfe6 100644
>> --- a/include/linux/memory_hotplug.h
>> +++ b/include/linux/memory_hotplug.h
>> @@ -231,7 +231,7 @@ extern int arch_add_memory(int nid, u64 start, u64 size);
>>  extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
>>  extern int offline_memory_block(struct memory_block *mem);
>>  extern bool is_memblock_offlined(struct memory_block *mem);
>> -extern int remove_memory(u64 start, u64 size);
>> +extern int remove_memory(int node, u64 start, u64 size);
>>  extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
>>  								int nr_pages);
>>  extern void sparse_remove_one_section(struct zone *zone, struct mem_section *ms);
>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>> index 7bcced0..d965da3 100644
>> --- a/mm/memory_hotplug.c
>> +++ b/mm/memory_hotplug.c
>> @@ -29,6 +29,7 @@
>>  #include <linux/suspend.h>
>>  #include <linux/mm_inline.h>
>>  #include <linux/firmware-map.h>
>> +#include <linux/stop_machine.h>
>>  
>>  #include <asm/tlbflush.h>
>>  
>> @@ -1299,7 +1300,58 @@ static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)
>>  	return ret;
>>  }
>>  
>> -int __ref remove_memory(u64 start, u64 size)
>> +static int check_cpu_on_node(void *data)
>> +{
>> +	struct pglist_data *pgdat = data;
>> +	int cpu;
>> +
>> +	for_each_present_cpu(cpu) {
>> +		if (cpu_to_node(cpu) == pgdat->node_id)
>> +			/*
>> +			 * the cpu on this node isn't removed, and we can't
>> +			 * offline this node.
>> +			 */
>> +			return -EBUSY;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/* offline the node if all memory sections of this node are removed */
>> +static void try_offline_node(int nid)
>> +{
>> +	unsigned long start_pfn = NODE_DATA(nid)->node_start_pfn;
>> +	unsigned long end_pfn = start_pfn + NODE_DATA(nid)->node_spanned_pages;
>> +	unsigned long pfn;
>> +
>> +	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
>> +		unsigned long section_nr = pfn_to_section_nr(pfn);
>> +
>> +		if (!present_section_nr(section_nr))
>> +			continue;
>> +
>> +		if (pfn_to_nid(pfn) != nid)
>> +			continue;
>> +
>> +		/*
>> +		 * some memory sections of this node are not removed, and we
>> +		 * can't offline node now.
>> +		 */
>> +		return;
>> +	}
>> +
>> +	if (stop_machine(check_cpu_on_node, NODE_DATA(nid), NULL))
>> +		return;
> 
> how about:
> 	if (nr_cpus_node(nid))

If all cpus on the node is offlined, but not removed, nr_cpus_node(nid) will
return 0. In this case, we still can't offline the node.

Another purpose to use stop_machine() is to prevent cpu hotplug. We can't lock
cpuhotplug here.

Thanks
Wen Congyang

> 		return;
>> +
>> +	/*
>> +	 * all memory/cpu of this node are removed, we can offline this
>> +	 * node now.
>> +	 */
>> +	node_set_offline(nid);
>> +	unregister_one_node(nid);
>> +}
>> +
>> +int __ref remove_memory(int nid, u64 start, u64 size)
>>  {
>>  	unsigned long start_pfn, end_pfn;
>>  	int ret = 0;
>> @@ -1346,6 +1398,8 @@ repeat:
>>  
>>  	arch_remove_memory(start, size);
>>  
>> +	try_offline_node(nid);
>> +
>>  	unlock_memory_hotplug();
>>  
>>  	return 0;
>> @@ -1355,7 +1409,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
>>  {
>>  	return -EINVAL;
>>  }
>> -int remove_memory(u64 start, u64 size)
>> +int remove_memory(int nid, u64 start, u64 size)
>>  {
>>  	return -EINVAL;
>>  }
>>
> 
> 

^ permalink raw reply

* Re: [PATCH 5/6] powerpc: Macros for saving/restore PPR
From: Haren Myneni @ 2012-11-27  1:49 UTC (permalink / raw)
  To: Michael Neuling; +Cc: anton, paulus, linuxppc-dev
In-Reply-To: <27433.1353641997@neuling.org>

On 11/22/2012 07:39 PM, Michael Neuling wrote:
> Haren Myneni <haren@linux.vnet.ibm.com> wrote:
> 
>> [PATCH 5/6] powerpc: Macros for saving/restore PPR
>>
>> Several macros are defined for saving and restore user defined PPR value.
>>
>> Signed-off-by: Haren Myneni <haren@us.ibm.com>
>> ---
>>  arch/powerpc/include/asm/exception-64s.h |   29 +++++++++++++++++++++++++++++
>>  arch/powerpc/include/asm/ppc_asm.h       |   25 +++++++++++++++++++++++++
>>  arch/powerpc/include/asm/reg.h           |    1 +
>>  3 files changed, 55 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h
>> index bfd3f1f..880ef7d 100644
>> --- a/arch/powerpc/include/asm/exception-64s.h
>> +++ b/arch/powerpc/include/asm/exception-64s.h
>> @@ -62,6 +62,35 @@
>>  #define EXC_HV	H
>>  #define EXC_STD
>>  
>> +/*
>> + * PPR save/restore macros used in exceptions_64s.S  
>> + * Used for P7 or later processors
>> + */
>> +#define SAVE_PPR(area, ra, rb)						\
>> +BEGIN_FTR_SECTION_NESTED(940)						\
>> +	ld	ra,PACACURRENT(r13);					\
>> +	ld	rb,area+EX_PPR(r13);	/* Read PPR from paca */	\
>> +	std	rb,TASKTHREADPPR(ra);					\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,940)
>> +
>> +#define RESTORE_PPR_PACA(area, ra)					\
>> +BEGIN_FTR_SECTION_NESTED(941)						\
>> +	ld	ra,area+EX_PPR(r13);					\
>> +	mtspr	SPRN_PPR,ra;						\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,941)
>> +
> 
> Can you add some documentation here on why we should use the different
> versions.
> 
> I'm trying to read the next patch and it's not clear to my why some have
> HMT_MEDIUM_NO_PPR and other times HMT_MEDIUM and others
> HTM_MEDIUM_HAS_PPR.
> 
> Looks like HTM_MEDIUM_NO_PPR sets the priority to medium on systems
> where we can't save/restore the PPR, hence it can be called earlier in
> the exception handler before we have free GPRs.  HTM_MEDIUM_HAS_PPR
> saves the priority on systems where it can, and then sets the priority
> to medium.
> 
> Maybe we should change the names 
>   HTM_MEDIUM_NO_PPR  => HTM_MEDIUM_PPR_DISCARD   and
>   HTM_MEDIUM_HAS_PPR => HTM_MEDIUM_PPR_SAVE
> But now I'm heading into bike shedding territory... plus I think I
> suggested the names you have currently, so I'm feeling a bit dumb now
> :-)

No problem, We can change these macro names if HTM_MEDIUM_PPR_DISCARD/
HTM_MEDIUM_PPR_SAVE gives better description.

Right, HTM_MEDIUM_NO_PPR is used on systems where we do not save/restore
PPR. So the behaviour is same as before - just increases the priority.
HTM_MEDIUM_HAS_PPR will be executed on systems where CPU_FTR_HAS_PPR is
enabled. I will write some comments around these macros to make it clear.

We can also name them HMT_MEDIUM_CPU_NO_PPR_SAVE and
HMT_MEDIUM_CPU_HAS_PPR_SAVE since we are enabling PPR save/restore using
CPU_FTR macro.

Otherwise I will follow with your suggestions.


Thanks
Haren

> Mikey
> 
>> +#define HMT_MEDIUM_NO_PPR						\
>> +BEGIN_FTR_SECTION_NESTED(942)						\
>> +	HMT_MEDIUM;							\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,0,942)  /*non P7*/		
>> +
>> +#define HMT_MEDIUM_HAS_PPR(area, ra)					\
>> +BEGIN_FTR_SECTION_NESTED(943)						\
>> +	mfspr	ra,SPRN_PPR;						\
>> +	std	ra,area+EX_PPR(r13);					\
>> +	HMT_MEDIUM;							\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,943) 
>> +
>>  #define __EXCEPTION_PROLOG_1(area, extra, vec)				\
>>  	GET_PACA(r13);							\
>>  	std	r9,area+EX_R9(r13);	/* save r9 - r12 */		\
> 
> 
>> diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
>> index 376e36d..52649cc 100644
>> --- a/arch/powerpc/include/asm/ppc_asm.h
>> +++ b/arch/powerpc/include/asm/ppc_asm.h
>> @@ -389,6 +389,31 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
>>  	FTR_SECTION_ELSE_NESTED(848);	\
>>  	mtocrf (FXM), RS;		\
>>  	ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_NOEXECUTE, 848)
>> +
>> +/*
>> + * PPR restore macros used in entry_64.S
>> + * Used for P7 or later processors
>> + */
>> +#define HMT_MEDIUM_LOW_HAS_PPR						\
>> +BEGIN_FTR_SECTION_NESTED(944)						\
>> +	HMT_MEDIUM_LOW;							\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,944)
>> +
>> +#define SET_DEFAULT_THREAD_PPR(ra, rb)					\
>> +BEGIN_FTR_SECTION_NESTED(945)						\
>> +	lis	ra,0xc;		/* default ppr=3 */			\
>> +	ld	rb,PACACURRENT(r13);					\
>> +	sldi	ra,ra,32;	/* 11- 13 bits are used for ppr */	\
>> +	std	ra,TASKTHREADPPR(rb);					\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,945)
>> +
>> +#define RESTORE_PPR(ra, rb)						\
>> +BEGIN_FTR_SECTION_NESTED(946)						\
>> +	ld	ra,PACACURRENT(r13);					\
>> +	ld	rb,TASKTHREADPPR(ra);					\
>> +	mtspr	SPRN_PPR,rb;	/* Restore PPR */			\
>> +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,946)
>> +
>>  #endif
>>  
>>  /*
>> diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
>> index d24c141..1723be3 100644
>> --- a/arch/powerpc/include/asm/reg.h
>> +++ b/arch/powerpc/include/asm/reg.h
>> @@ -287,6 +287,7 @@
>>  #define SPRN_DBAT6U	0x23C	/* Data BAT 6 Upper Register */
>>  #define SPRN_DBAT7L	0x23F	/* Data BAT 7 Lower Register */
>>  #define SPRN_DBAT7U	0x23E	/* Data BAT 7 Upper Register */
>> +#define SPRN_PPR	0x380	/* SMT Thread status Register */
>>  
>>  #define SPRN_DEC	0x016		/* Decrement Register */
>>  #define SPRN_DER	0x095		/* Debug Enable Regsiter */
>> -- 
>> 1.7.1
>>
>>
>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>>

^ permalink raw reply

* Re: [PATCH 2/6] powerpc: Define CPU_FTR_HAS_PPR
From: Haren Myneni @ 2012-11-27  0:52 UTC (permalink / raw)
  To: Michael Neuling; +Cc: anton, paulus, linuxppc-dev
In-Reply-To: <28905.1353643302@neuling.org>

On 11/22/2012 08:01 PM, Michael Neuling wrote:
> Heaven Myneni <haren@linux.vnet.ibm.com> wrote:
> 
>> [PATCH 2/6] powerpc: Define CPU_FTR_HAS_PPR
>>
>> CPU_FTR_HAS_PPR is defined for POWER7.
>>
>> Signed-off-by: Haren Myneni <haren@us.ibm.com>
>> ---
>>  arch/powerpc/include/asm/cputable.h |    6 ++++--
>>  1 files changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
>> index 21a0687..12e3a1d 100644
>> --- a/arch/powerpc/include/asm/cputable.h
>> +++ b/arch/powerpc/include/asm/cputable.h
>> @@ -171,6 +171,7 @@ extern const char *powerpc_base_platform;
>>  #define CPU_FTR_POPCNTD			LONG_ASM_CONST(0x0800000000000000)
>>  #define CPU_FTR_ICSWX			LONG_ASM_CONST(0x1000000000000000)
>>  #define CPU_FTR_VMX_COPY		LONG_ASM_CONST(0x2000000000000000)
>> +#define	CPU_FTR_HAS_PPR			LONG_ASM_CONST(0x4000000000000000)
>>  
>>  #ifndef __ASSEMBLY__
>>  
>> @@ -400,7 +401,8 @@ extern const char *powerpc_base_platform;
>>  	    CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \
>>  	    CPU_FTR_DSCR | CPU_FTR_SAO  | CPU_FTR_ASYM_SMT | \
>>  	    CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \
>> -	    CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY)
>> +	    CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | \
>> +	    CPU_FTR_VMX_COPY | CPU_FTR_HAS_PPR)
>>  #define CPU_FTRS_CELL	(CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
>>  	    CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
>>  	    CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \
>> @@ -422,7 +424,7 @@ extern const char *powerpc_base_platform;
>>  	    (CPU_FTRS_POWER3 | CPU_FTRS_RS64 | CPU_FTRS_POWER4 |	\
>>  	    CPU_FTRS_PPC970 | CPU_FTRS_POWER5 | CPU_FTRS_POWER6 |	\
>>  	    CPU_FTRS_POWER7 | CPU_FTRS_CELL | CPU_FTRS_PA6T |		\
>> -	    CPU_FTR_VSX)
>> +	    CPU_FTR_VSX | CPU_FTR_HAS_PPR)
> 
> FYI, there is no need to add this to POSSIBLE, since you are adding it
> to POWER7 anyway.

Will remove CPU_FTR_HAS_PPR for POSSIBLE macro. Added this in second
version for enabling this feature with command parameter, but forgot to
remove it.

Thanks
Haren


> 
> Mikey
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox