LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 02/10] ARM: disable CONFIG_IDE in footbridge_defconfig
From: Cye Borg @ 2021-03-22 15:44 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jens Axboe, Thomas Bogendoerfer, linux-doc,
	Russell King - ARM Linux admin, linux-kernel, linux-ide,
	linux-m68k, Ivan Kokshaysky, linux-alpha, Geert Uytterhoeven,
	Matt Turner, linux-mips, linuxppc-dev, David S. Miller,
	linux-arm-kernel, Richard Henderson
In-Reply-To: <20210322151823.GA2764@lst.de>

[-- Attachment #1: Type: text/plain, Size: 1717 bytes --]

I use pata_cypress on Alpha, and for the above mentioned problems,
it always restricted me to use only one IDE channel out of the two
available. Also, not forcing it to use PIO mode, it always failed to
operate. I would love to test on Alpha, and give you feedback about
a fix/finished implementation of the driver. I have both Alpha Server DS20E
and Alpha Personal Workstation 500 including that freak chipset.

Let me know if/when I can help with my limited capabilities.

Thanks, best regards,
Barnabas Viragh

On Mon, Mar 22, 2021 at 4:19 PM Christoph Hellwig <hch@lst.de> wrote:

> On Mon, Mar 22, 2021 at 03:15:03PM +0000, Russell King - ARM Linux admin
> wrote:
> > It gets worse than that though - due to a change to remove
> > pcibios_min_io from the generic code, moving it into the ARM
> > architecture code, this has caused a regression that prevents the
> > legacy resources being registered against the bus resource. So even
> > if they are there, they cause probe failures. I haven't found a
> > reasonable way to solve this yet, but until there is, there is no
> > way that the PATA driver can be used as the "legacy mode" support
> > is effectively done via the PCI code assigning virtual IO port
> > resources.
> >
> > I'm quite surprised that the CY82C693 even works on Alpha - I've
> > asked for a lspci for that last week but nothing has yet been
> > forthcoming from whoever responded to your patch for Alpha - so I
> > can't compare what I'm seeing with what's happening with Alpha.
>
> That sounds like something we could fix with a quirk for function 2
> in the PCI resource assignment code.  Can you show what vendor and
> device ID function 2 has so that I could try to come up with one?
>

[-- Attachment #2: Type: text/html, Size: 2247 bytes --]

^ permalink raw reply

* Re: [PATCH] xsysace: Remove SYSACE driver
From: Davidlohr Bueso @ 2021-03-23  0:04 UTC (permalink / raw)
  To: Michal Simek
  Cc: Jens Axboe, devicetree, monstr, YueHaibing, linux-kernel,
	Rob Herring, linux-block, Chris Packham, Paul Mackerras, git,
	linuxppc-dev
In-Reply-To: <ee1fe969905f641f5f97d812ee0cac44c12fe0f6.1604919578.git.michal.simek@xilinx.com>

Hi,

On Mon, 09 Nov 2020, Michal Simek wrote:

>Sysace IP is no longer used on Xilinx PowerPC 405/440 and Microblaze
>systems. The driver is not regularly tested and very likely not working for
>quite a long time that's why remove it.

Is there a reason this patch was never merged? can the driver be removed? I ran
into this as a potential tasklet user that can be replaced/removed.

Thanks,
Davidlohr

>
>Signed-off-by: Michal Simek <michal.simek@xilinx.com>
>---
>
>Based on discussion
>https://lore.kernel.org/linux-arm-kernel/5ab9a2a1-20e3-c7b2-f666-2034df436e74@kernel.dk/
>
>I have grepped the kernel and found any old ppc platform. I have included
>it in this patch to have a discussion about it.
>
>---
> MAINTAINERS                             |    1 -
> arch/microblaze/boot/dts/system.dts     |    8 -
> arch/powerpc/boot/dts/icon.dts          |    7 -
> arch/powerpc/configs/44x/icon_defconfig |    1 -
> drivers/block/Kconfig                   |    6 -
> drivers/block/Makefile                  |    1 -
> drivers/block/xsysace.c                 | 1273 -----------------------
> 7 files changed, 1297 deletions(-)
> delete mode 100644 drivers/block/xsysace.c
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index cba8ddf87a08..38556c009758 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -2741,7 +2741,6 @@ T:	git https://github.com/Xilinx/linux-xlnx.git
> F:	Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml
> F:	Documentation/devicetree/bindings/i2c/xlnx,xps-iic-2.00.a.yaml
> F:	arch/arm/mach-zynq/
>-F:	drivers/block/xsysace.c
> F:	drivers/clocksource/timer-cadence-ttc.c
> F:	drivers/cpuidle/cpuidle-zynq.c
> F:	drivers/edac/synopsys_edac.c
>diff --git a/arch/microblaze/boot/dts/system.dts b/arch/microblaze/boot/dts/system.dts
>index 5b236527176e..b7ee1056779e 100644
>--- a/arch/microblaze/boot/dts/system.dts
>+++ b/arch/microblaze/boot/dts/system.dts
>@@ -310,14 +310,6 @@ RS232_Uart_1: serial@84000000 {
> 			xlnx,odd-parity = <0x0>;
> 			xlnx,use-parity = <0x0>;
> 		} ;
>-		SysACE_CompactFlash: sysace@83600000 {
>-			compatible = "xlnx,xps-sysace-1.00.a";
>-			interrupt-parent = <&xps_intc_0>;
>-			interrupts = < 4 2 >;
>-			reg = < 0x83600000 0x10000 >;
>-			xlnx,family = "virtex5";
>-			xlnx,mem-width = <0x10>;
>-		} ;
> 		debug_module: debug@84400000 {
> 			compatible = "xlnx,mdm-1.00.d";
> 			reg = < 0x84400000 0x10000 >;
>diff --git a/arch/powerpc/boot/dts/icon.dts b/arch/powerpc/boot/dts/icon.dts
>index fbaa60b8f87a..4fd7a4fbb4fb 100644
>--- a/arch/powerpc/boot/dts/icon.dts
>+++ b/arch/powerpc/boot/dts/icon.dts
>@@ -197,13 +197,6 @@ partition@fa0000 {
> 						reg = <0x00fa0000 0x00060000>;
> 					};
> 				};
>-
>-				SysACE_CompactFlash: sysace@1,0 {
>-					compatible = "xlnx,sysace";
>-					interrupt-parent = <&UIC2>;
>-					interrupts = <24 0x4>;
>-					reg = <0x00000001 0x00000000 0x10000>;
>-				};
> 			};
>
> 			UART0: serial@f0000200 {
>diff --git a/arch/powerpc/configs/44x/icon_defconfig b/arch/powerpc/configs/44x/icon_defconfig
>index 930948a1da76..fb9a15573546 100644
>--- a/arch/powerpc/configs/44x/icon_defconfig
>+++ b/arch/powerpc/configs/44x/icon_defconfig
>@@ -28,7 +28,6 @@ CONFIG_MTD_CFI_AMDSTD=y
> CONFIG_MTD_PHYSMAP_OF=y
> CONFIG_BLK_DEV_RAM=y
> CONFIG_BLK_DEV_RAM_SIZE=35000
>-CONFIG_XILINX_SYSACE=y
> CONFIG_SCSI=y
> CONFIG_BLK_DEV_SD=y
> CONFIG_SCSI_CONSTANTS=y
>diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
>index ecceaaa1a66f..9cb02861298d 100644
>--- a/drivers/block/Kconfig
>+++ b/drivers/block/Kconfig
>@@ -388,12 +388,6 @@ config SUNVDC
>
> source "drivers/s390/block/Kconfig"
>
>-config XILINX_SYSACE
>-	tristate "Xilinx SystemACE support"
>-	depends on 4xx || MICROBLAZE
>-	help
>-	  Include support for the Xilinx SystemACE CompactFlash interface
>-
> config XEN_BLKDEV_FRONTEND
> 	tristate "Xen virtual block device support"
> 	depends on XEN
>diff --git a/drivers/block/Makefile b/drivers/block/Makefile
>index e1f63117ee94..5ddd9370972a 100644
>--- a/drivers/block/Makefile
>+++ b/drivers/block/Makefile
>@@ -19,7 +19,6 @@ obj-$(CONFIG_ATARI_FLOPPY)	+= ataflop.o
> obj-$(CONFIG_AMIGA_Z2RAM)	+= z2ram.o
> obj-$(CONFIG_BLK_DEV_RAM)	+= brd.o
> obj-$(CONFIG_BLK_DEV_LOOP)	+= loop.o
>-obj-$(CONFIG_XILINX_SYSACE)	+= xsysace.o
> obj-$(CONFIG_CDROM_PKTCDVD)	+= pktcdvd.o
> obj-$(CONFIG_SUNVDC)		+= sunvdc.o
> obj-$(CONFIG_BLK_DEV_SKD)	+= skd.o
>diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
>deleted file mode 100644
>index eb8ef65778c3..000000000000
>--- a/drivers/block/xsysace.c
>+++ /dev/null
>@@ -1,1273 +0,0 @@
>-// SPDX-License-Identifier: GPL-2.0-only
>-/*
>- * Xilinx SystemACE device driver
>- *
>- * Copyright 2007 Secret Lab Technologies Ltd.
>- */
>-
>-/*
>- * The SystemACE chip is designed to configure FPGAs by loading an FPGA
>- * bitstream from a file on a CF card and squirting it into FPGAs connected
>- * to the SystemACE JTAG chain.  It also has the advantage of providing an
>- * MPU interface which can be used to control the FPGA configuration process
>- * and to use the attached CF card for general purpose storage.
>- *
>- * This driver is a block device driver for the SystemACE.
>- *
>- * Initialization:
>- *    The driver registers itself as a platform_device driver at module
>- *    load time.  The platform bus will take care of calling the
>- *    ace_probe() method for all SystemACE instances in the system.  Any
>- *    number of SystemACE instances are supported.  ace_probe() calls
>- *    ace_setup() which initialized all data structures, reads the CF
>- *    id structure and registers the device.
>- *
>- * Processing:
>- *    Just about all of the heavy lifting in this driver is performed by
>- *    a Finite State Machine (FSM).  The driver needs to wait on a number
>- *    of events; some raised by interrupts, some which need to be polled
>- *    for.  Describing all of the behaviour in a FSM seems to be the
>- *    easiest way to keep the complexity low and make it easy to
>- *    understand what the driver is doing.  If the block ops or the
>- *    request function need to interact with the hardware, then they
>- *    simply need to flag the request and kick of FSM processing.
>- *
>- *    The FSM itself is atomic-safe code which can be run from any
>- *    context.  The general process flow is:
>- *    1. obtain the ace->lock spinlock.
>- *    2. loop on ace_fsm_dostate() until the ace->fsm_continue flag is
>- *       cleared.
>- *    3. release the lock.
>- *
>- *    Individual states do not sleep in any way.  If a condition needs to
>- *    be waited for then the state much clear the fsm_continue flag and
>- *    either schedule the FSM to be run again at a later time, or expect
>- *    an interrupt to call the FSM when the desired condition is met.
>- *
>- *    In normal operation, the FSM is processed at interrupt context
>- *    either when the driver's tasklet is scheduled, or when an irq is
>- *    raised by the hardware.  The tasklet can be scheduled at any time.
>- *    The request method in particular schedules the tasklet when a new
>- *    request has been indicated by the block layer.  Once started, the
>- *    FSM proceeds as far as it can processing the request until it
>- *    needs on a hardware event.  At this point, it must yield execution.
>- *
>- *    A state has two options when yielding execution:
>- *    1. ace_fsm_yield()
>- *       - Call if need to poll for event.
>- *       - clears the fsm_continue flag to exit the processing loop
>- *       - reschedules the tasklet to run again as soon as possible
>- *    2. ace_fsm_yieldirq()
>- *       - Call if an irq is expected from the HW
>- *       - clears the fsm_continue flag to exit the processing loop
>- *       - does not reschedule the tasklet so the FSM will not be processed
>- *         again until an irq is received.
>- *    After calling a yield function, the state must return control back
>- *    to the FSM main loop.
>- *
>- *    Additionally, the driver maintains a kernel timer which can process
>- *    the FSM.  If the FSM gets stalled, typically due to a missed
>- *    interrupt, then the kernel timer will expire and the driver can
>- *    continue where it left off.
>- *
>- * To Do:
>- *    - Add FPGA configuration control interface.
>- *    - Request major number from lanana
>- */
>-
>-#undef DEBUG
>-
>-#include <linux/module.h>
>-#include <linux/ctype.h>
>-#include <linux/init.h>
>-#include <linux/interrupt.h>
>-#include <linux/errno.h>
>-#include <linux/kernel.h>
>-#include <linux/delay.h>
>-#include <linux/slab.h>
>-#include <linux/blk-mq.h>
>-#include <linux/mutex.h>
>-#include <linux/ata.h>
>-#include <linux/hdreg.h>
>-#include <linux/platform_device.h>
>-#if defined(CONFIG_OF)
>-#include <linux/of_address.h>
>-#include <linux/of_device.h>
>-#include <linux/of_platform.h>
>-#endif
>-
>-MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
>-MODULE_DESCRIPTION("Xilinx SystemACE device driver");
>-MODULE_LICENSE("GPL");
>-
>-/* SystemACE register definitions */
>-#define ACE_BUSMODE (0x00)
>-
>-#define ACE_STATUS (0x04)
>-#define ACE_STATUS_CFGLOCK      (0x00000001)
>-#define ACE_STATUS_MPULOCK      (0x00000002)
>-#define ACE_STATUS_CFGERROR     (0x00000004)	/* config controller error */
>-#define ACE_STATUS_CFCERROR     (0x00000008)	/* CF controller error */
>-#define ACE_STATUS_CFDETECT     (0x00000010)
>-#define ACE_STATUS_DATABUFRDY   (0x00000020)
>-#define ACE_STATUS_DATABUFMODE  (0x00000040)
>-#define ACE_STATUS_CFGDONE      (0x00000080)
>-#define ACE_STATUS_RDYFORCFCMD  (0x00000100)
>-#define ACE_STATUS_CFGMODEPIN   (0x00000200)
>-#define ACE_STATUS_CFGADDR_MASK (0x0000e000)
>-#define ACE_STATUS_CFBSY        (0x00020000)
>-#define ACE_STATUS_CFRDY        (0x00040000)
>-#define ACE_STATUS_CFDWF        (0x00080000)
>-#define ACE_STATUS_CFDSC        (0x00100000)
>-#define ACE_STATUS_CFDRQ        (0x00200000)
>-#define ACE_STATUS_CFCORR       (0x00400000)
>-#define ACE_STATUS_CFERR        (0x00800000)
>-
>-#define ACE_ERROR (0x08)
>-#define ACE_CFGLBA (0x0c)
>-#define ACE_MPULBA (0x10)
>-
>-#define ACE_SECCNTCMD (0x14)
>-#define ACE_SECCNTCMD_RESET      (0x0100)
>-#define ACE_SECCNTCMD_IDENTIFY   (0x0200)
>-#define ACE_SECCNTCMD_READ_DATA  (0x0300)
>-#define ACE_SECCNTCMD_WRITE_DATA (0x0400)
>-#define ACE_SECCNTCMD_ABORT      (0x0600)
>-
>-#define ACE_VERSION (0x16)
>-#define ACE_VERSION_REVISION_MASK (0x00FF)
>-#define ACE_VERSION_MINOR_MASK    (0x0F00)
>-#define ACE_VERSION_MAJOR_MASK    (0xF000)
>-
>-#define ACE_CTRL (0x18)
>-#define ACE_CTRL_FORCELOCKREQ   (0x0001)
>-#define ACE_CTRL_LOCKREQ        (0x0002)
>-#define ACE_CTRL_FORCECFGADDR   (0x0004)
>-#define ACE_CTRL_FORCECFGMODE   (0x0008)
>-#define ACE_CTRL_CFGMODE        (0x0010)
>-#define ACE_CTRL_CFGSTART       (0x0020)
>-#define ACE_CTRL_CFGSEL         (0x0040)
>-#define ACE_CTRL_CFGRESET       (0x0080)
>-#define ACE_CTRL_DATABUFRDYIRQ  (0x0100)
>-#define ACE_CTRL_ERRORIRQ       (0x0200)
>-#define ACE_CTRL_CFGDONEIRQ     (0x0400)
>-#define ACE_CTRL_RESETIRQ       (0x0800)
>-#define ACE_CTRL_CFGPROG        (0x1000)
>-#define ACE_CTRL_CFGADDR_MASK   (0xe000)
>-
>-#define ACE_FATSTAT (0x1c)
>-
>-#define ACE_NUM_MINORS 16
>-#define ACE_SECTOR_SIZE (512)
>-#define ACE_FIFO_SIZE (32)
>-#define ACE_BUF_PER_SECTOR (ACE_SECTOR_SIZE / ACE_FIFO_SIZE)
>-
>-#define ACE_BUS_WIDTH_8  0
>-#define ACE_BUS_WIDTH_16 1
>-
>-struct ace_reg_ops;
>-
>-struct ace_device {
>-	/* driver state data */
>-	int id;
>-	int media_change;
>-	int users;
>-	struct list_head list;
>-
>-	/* finite state machine data */
>-	struct tasklet_struct fsm_tasklet;
>-	uint fsm_task;		/* Current activity (ACE_TASK_*) */
>-	uint fsm_state;		/* Current state (ACE_FSM_STATE_*) */
>-	uint fsm_continue_flag;	/* cleared to exit FSM mainloop */
>-	uint fsm_iter_num;
>-	struct timer_list stall_timer;
>-
>-	/* Transfer state/result, use for both id and block request */
>-	struct request *req;	/* request being processed */
>-	void *data_ptr;		/* pointer to I/O buffer */
>-	int data_count;		/* number of buffers remaining */
>-	int data_result;	/* Result of transfer; 0 := success */
>-
>-	int id_req_count;	/* count of id requests */
>-	int id_result;
>-	struct completion id_completion;	/* used when id req finishes */
>-	int in_irq;
>-
>-	/* Details of hardware device */
>-	resource_size_t physaddr;
>-	void __iomem *baseaddr;
>-	int irq;
>-	int bus_width;		/* 0 := 8 bit; 1 := 16 bit */
>-	struct ace_reg_ops *reg_ops;
>-	int lock_count;
>-
>-	/* Block device data structures */
>-	spinlock_t lock;
>-	struct device *dev;
>-	struct request_queue *queue;
>-	struct gendisk *gd;
>-	struct blk_mq_tag_set tag_set;
>-	struct list_head rq_list;
>-
>-	/* Inserted CF card parameters */
>-	u16 cf_id[ATA_ID_WORDS];
>-};
>-
>-static DEFINE_MUTEX(xsysace_mutex);
>-static int ace_major;
>-
>-/* ---------------------------------------------------------------------
>- * Low level register access
>- */
>-
>-struct ace_reg_ops {
>-	u16(*in) (struct ace_device * ace, int reg);
>-	void (*out) (struct ace_device * ace, int reg, u16 val);
>-	void (*datain) (struct ace_device * ace);
>-	void (*dataout) (struct ace_device * ace);
>-};
>-
>-/* 8 Bit bus width */
>-static u16 ace_in_8(struct ace_device *ace, int reg)
>-{
>-	void __iomem *r = ace->baseaddr + reg;
>-	return in_8(r) | (in_8(r + 1) << 8);
>-}
>-
>-static void ace_out_8(struct ace_device *ace, int reg, u16 val)
>-{
>-	void __iomem *r = ace->baseaddr + reg;
>-	out_8(r, val);
>-	out_8(r + 1, val >> 8);
>-}
>-
>-static void ace_datain_8(struct ace_device *ace)
>-{
>-	void __iomem *r = ace->baseaddr + 0x40;
>-	u8 *dst = ace->data_ptr;
>-	int i = ACE_FIFO_SIZE;
>-	while (i--)
>-		*dst++ = in_8(r++);
>-	ace->data_ptr = dst;
>-}
>-
>-static void ace_dataout_8(struct ace_device *ace)
>-{
>-	void __iomem *r = ace->baseaddr + 0x40;
>-	u8 *src = ace->data_ptr;
>-	int i = ACE_FIFO_SIZE;
>-	while (i--)
>-		out_8(r++, *src++);
>-	ace->data_ptr = src;
>-}
>-
>-static struct ace_reg_ops ace_reg_8_ops = {
>-	.in = ace_in_8,
>-	.out = ace_out_8,
>-	.datain = ace_datain_8,
>-	.dataout = ace_dataout_8,
>-};
>-
>-/* 16 bit big endian bus attachment */
>-static u16 ace_in_be16(struct ace_device *ace, int reg)
>-{
>-	return in_be16(ace->baseaddr + reg);
>-}
>-
>-static void ace_out_be16(struct ace_device *ace, int reg, u16 val)
>-{
>-	out_be16(ace->baseaddr + reg, val);
>-}
>-
>-static void ace_datain_be16(struct ace_device *ace)
>-{
>-	int i = ACE_FIFO_SIZE / 2;
>-	u16 *dst = ace->data_ptr;
>-	while (i--)
>-		*dst++ = in_le16(ace->baseaddr + 0x40);
>-	ace->data_ptr = dst;
>-}
>-
>-static void ace_dataout_be16(struct ace_device *ace)
>-{
>-	int i = ACE_FIFO_SIZE / 2;
>-	u16 *src = ace->data_ptr;
>-	while (i--)
>-		out_le16(ace->baseaddr + 0x40, *src++);
>-	ace->data_ptr = src;
>-}
>-
>-/* 16 bit little endian bus attachment */
>-static u16 ace_in_le16(struct ace_device *ace, int reg)
>-{
>-	return in_le16(ace->baseaddr + reg);
>-}
>-
>-static void ace_out_le16(struct ace_device *ace, int reg, u16 val)
>-{
>-	out_le16(ace->baseaddr + reg, val);
>-}
>-
>-static void ace_datain_le16(struct ace_device *ace)
>-{
>-	int i = ACE_FIFO_SIZE / 2;
>-	u16 *dst = ace->data_ptr;
>-	while (i--)
>-		*dst++ = in_be16(ace->baseaddr + 0x40);
>-	ace->data_ptr = dst;
>-}
>-
>-static void ace_dataout_le16(struct ace_device *ace)
>-{
>-	int i = ACE_FIFO_SIZE / 2;
>-	u16 *src = ace->data_ptr;
>-	while (i--)
>-		out_be16(ace->baseaddr + 0x40, *src++);
>-	ace->data_ptr = src;
>-}
>-
>-static struct ace_reg_ops ace_reg_be16_ops = {
>-	.in = ace_in_be16,
>-	.out = ace_out_be16,
>-	.datain = ace_datain_be16,
>-	.dataout = ace_dataout_be16,
>-};
>-
>-static struct ace_reg_ops ace_reg_le16_ops = {
>-	.in = ace_in_le16,
>-	.out = ace_out_le16,
>-	.datain = ace_datain_le16,
>-	.dataout = ace_dataout_le16,
>-};
>-
>-static inline u16 ace_in(struct ace_device *ace, int reg)
>-{
>-	return ace->reg_ops->in(ace, reg);
>-}
>-
>-static inline u32 ace_in32(struct ace_device *ace, int reg)
>-{
>-	return ace_in(ace, reg) | (ace_in(ace, reg + 2) << 16);
>-}
>-
>-static inline void ace_out(struct ace_device *ace, int reg, u16 val)
>-{
>-	ace->reg_ops->out(ace, reg, val);
>-}
>-
>-static inline void ace_out32(struct ace_device *ace, int reg, u32 val)
>-{
>-	ace_out(ace, reg, val);
>-	ace_out(ace, reg + 2, val >> 16);
>-}
>-
>-/* ---------------------------------------------------------------------
>- * Debug support functions
>- */
>-
>-#if defined(DEBUG)
>-static void ace_dump_mem(void *base, int len)
>-{
>-	const char *ptr = base;
>-	int i, j;
>-
>-	for (i = 0; i < len; i += 16) {
>-		printk(KERN_INFO "%.8x:", i);
>-		for (j = 0; j < 16; j++) {
>-			if (!(j % 4))
>-				printk(" ");
>-			printk("%.2x", ptr[i + j]);
>-		}
>-		printk(" ");
>-		for (j = 0; j < 16; j++)
>-			printk("%c", isprint(ptr[i + j]) ? ptr[i + j] : '.');
>-		printk("\n");
>-	}
>-}
>-#else
>-static inline void ace_dump_mem(void *base, int len)
>-{
>-}
>-#endif
>-
>-static void ace_dump_regs(struct ace_device *ace)
>-{
>-	dev_info(ace->dev,
>-		 "    ctrl:  %.8x  seccnt/cmd: %.4x      ver:%.4x\n"
>-		 "    status:%.8x  mpu_lba:%.8x  busmode:%4x\n"
>-		 "    error: %.8x  cfg_lba:%.8x  fatstat:%.4x\n",
>-		 ace_in32(ace, ACE_CTRL),
>-		 ace_in(ace, ACE_SECCNTCMD),
>-		 ace_in(ace, ACE_VERSION),
>-		 ace_in32(ace, ACE_STATUS),
>-		 ace_in32(ace, ACE_MPULBA),
>-		 ace_in(ace, ACE_BUSMODE),
>-		 ace_in32(ace, ACE_ERROR),
>-		 ace_in32(ace, ACE_CFGLBA), ace_in(ace, ACE_FATSTAT));
>-}
>-
>-static void ace_fix_driveid(u16 *id)
>-{
>-#if defined(__BIG_ENDIAN)
>-	int i;
>-
>-	/* All half words have wrong byte order; swap the bytes */
>-	for (i = 0; i < ATA_ID_WORDS; i++, id++)
>-		*id = le16_to_cpu(*id);
>-#endif
>-}
>-
>-/* ---------------------------------------------------------------------
>- * Finite State Machine (FSM) implementation
>- */
>-
>-/* FSM tasks; used to direct state transitions */
>-#define ACE_TASK_IDLE      0
>-#define ACE_TASK_IDENTIFY  1
>-#define ACE_TASK_READ      2
>-#define ACE_TASK_WRITE     3
>-#define ACE_FSM_NUM_TASKS  4
>-
>-/* FSM state definitions */
>-#define ACE_FSM_STATE_IDLE               0
>-#define ACE_FSM_STATE_REQ_LOCK           1
>-#define ACE_FSM_STATE_WAIT_LOCK          2
>-#define ACE_FSM_STATE_WAIT_CFREADY       3
>-#define ACE_FSM_STATE_IDENTIFY_PREPARE   4
>-#define ACE_FSM_STATE_IDENTIFY_TRANSFER  5
>-#define ACE_FSM_STATE_IDENTIFY_COMPLETE  6
>-#define ACE_FSM_STATE_REQ_PREPARE        7
>-#define ACE_FSM_STATE_REQ_TRANSFER       8
>-#define ACE_FSM_STATE_REQ_COMPLETE       9
>-#define ACE_FSM_STATE_ERROR             10
>-#define ACE_FSM_NUM_STATES              11
>-
>-/* Set flag to exit FSM loop and reschedule tasklet */
>-static inline void ace_fsm_yieldpoll(struct ace_device *ace)
>-{
>-	tasklet_schedule(&ace->fsm_tasklet);
>-	ace->fsm_continue_flag = 0;
>-}
>-
>-static inline void ace_fsm_yield(struct ace_device *ace)
>-{
>-	dev_dbg(ace->dev, "%s()\n", __func__);
>-	ace_fsm_yieldpoll(ace);
>-}
>-
>-/* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */
>-static inline void ace_fsm_yieldirq(struct ace_device *ace)
>-{
>-	dev_dbg(ace->dev, "ace_fsm_yieldirq()\n");
>-
>-	if (ace->irq > 0)
>-		ace->fsm_continue_flag = 0;
>-	else
>-		ace_fsm_yieldpoll(ace);
>-}
>-
>-static bool ace_has_next_request(struct request_queue *q)
>-{
>-	struct ace_device *ace = q->queuedata;
>-
>-	return !list_empty(&ace->rq_list);
>-}
>-
>-/* Get the next read/write request; ending requests that we don't handle */
>-static struct request *ace_get_next_request(struct request_queue *q)
>-{
>-	struct ace_device *ace = q->queuedata;
>-	struct request *rq;
>-
>-	rq = list_first_entry_or_null(&ace->rq_list, struct request, queuelist);
>-	if (rq) {
>-		list_del_init(&rq->queuelist);
>-		blk_mq_start_request(rq);
>-	}
>-
>-	return NULL;
>-}
>-
>-static void ace_fsm_dostate(struct ace_device *ace)
>-{
>-	struct request *req;
>-	u32 status;
>-	u16 val;
>-	int count;
>-
>-#if defined(DEBUG)
>-	dev_dbg(ace->dev, "fsm_state=%i, id_req_count=%i\n",
>-		ace->fsm_state, ace->id_req_count);
>-#endif
>-
>-	/* Verify that there is actually a CF in the slot. If not, then
>-	 * bail out back to the idle state and wake up all the waiters */
>-	status = ace_in32(ace, ACE_STATUS);
>-	if ((status & ACE_STATUS_CFDETECT) == 0) {
>-		ace->fsm_state = ACE_FSM_STATE_IDLE;
>-		ace->media_change = 1;
>-		set_capacity(ace->gd, 0);
>-		dev_info(ace->dev, "No CF in slot\n");
>-
>-		/* Drop all in-flight and pending requests */
>-		if (ace->req) {
>-			blk_mq_end_request(ace->req, BLK_STS_IOERR);
>-			ace->req = NULL;
>-		}
>-		while ((req = ace_get_next_request(ace->queue)) != NULL)
>-			blk_mq_end_request(req, BLK_STS_IOERR);
>-
>-		/* Drop back to IDLE state and notify waiters */
>-		ace->fsm_state = ACE_FSM_STATE_IDLE;
>-		ace->id_result = -EIO;
>-		while (ace->id_req_count) {
>-			complete(&ace->id_completion);
>-			ace->id_req_count--;
>-		}
>-	}
>-
>-	switch (ace->fsm_state) {
>-	case ACE_FSM_STATE_IDLE:
>-		/* See if there is anything to do */
>-		if (ace->id_req_count || ace_has_next_request(ace->queue)) {
>-			ace->fsm_iter_num++;
>-			ace->fsm_state = ACE_FSM_STATE_REQ_LOCK;
>-			mod_timer(&ace->stall_timer, jiffies + HZ);
>-			if (!timer_pending(&ace->stall_timer))
>-				add_timer(&ace->stall_timer);
>-			break;
>-		}
>-		del_timer(&ace->stall_timer);
>-		ace->fsm_continue_flag = 0;
>-		break;
>-
>-	case ACE_FSM_STATE_REQ_LOCK:
>-		if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {
>-			/* Already have the lock, jump to next state */
>-			ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;
>-			break;
>-		}
>-
>-		/* Request the lock */
>-		val = ace_in(ace, ACE_CTRL);
>-		ace_out(ace, ACE_CTRL, val | ACE_CTRL_LOCKREQ);
>-		ace->fsm_state = ACE_FSM_STATE_WAIT_LOCK;
>-		break;
>-
>-	case ACE_FSM_STATE_WAIT_LOCK:
>-		if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {
>-			/* got the lock; move to next state */
>-			ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;
>-			break;
>-		}
>-
>-		/* wait a bit for the lock */
>-		ace_fsm_yield(ace);
>-		break;
>-
>-	case ACE_FSM_STATE_WAIT_CFREADY:
>-		status = ace_in32(ace, ACE_STATUS);
>-		if (!(status & ACE_STATUS_RDYFORCFCMD) ||
>-		    (status & ACE_STATUS_CFBSY)) {
>-			/* CF card isn't ready; it needs to be polled */
>-			ace_fsm_yield(ace);
>-			break;
>-		}
>-
>-		/* Device is ready for command; determine what to do next */
>-		if (ace->id_req_count)
>-			ace->fsm_state = ACE_FSM_STATE_IDENTIFY_PREPARE;
>-		else
>-			ace->fsm_state = ACE_FSM_STATE_REQ_PREPARE;
>-		break;
>-
>-	case ACE_FSM_STATE_IDENTIFY_PREPARE:
>-		/* Send identify command */
>-		ace->fsm_task = ACE_TASK_IDENTIFY;
>-		ace->data_ptr = ace->cf_id;
>-		ace->data_count = ACE_BUF_PER_SECTOR;
>-		ace_out(ace, ACE_SECCNTCMD, ACE_SECCNTCMD_IDENTIFY);
>-
>-		/* As per datasheet, put config controller in reset */
>-		val = ace_in(ace, ACE_CTRL);
>-		ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);
>-
>-		/* irq handler takes over from this point; wait for the
>-		 * transfer to complete */
>-		ace->fsm_state = ACE_FSM_STATE_IDENTIFY_TRANSFER;
>-		ace_fsm_yieldirq(ace);
>-		break;
>-
>-	case ACE_FSM_STATE_IDENTIFY_TRANSFER:
>-		/* Check that the sysace is ready to receive data */
>-		status = ace_in32(ace, ACE_STATUS);
>-		if (status & ACE_STATUS_CFBSY) {
>-			dev_dbg(ace->dev, "CFBSY set; t=%i iter=%i dc=%i\n",
>-				ace->fsm_task, ace->fsm_iter_num,
>-				ace->data_count);
>-			ace_fsm_yield(ace);
>-			break;
>-		}
>-		if (!(status & ACE_STATUS_DATABUFRDY)) {
>-			ace_fsm_yield(ace);
>-			break;
>-		}
>-
>-		/* Transfer the next buffer */
>-		ace->reg_ops->datain(ace);
>-		ace->data_count--;
>-
>-		/* If there are still buffers to be transfers; jump out here */
>-		if (ace->data_count != 0) {
>-			ace_fsm_yieldirq(ace);
>-			break;
>-		}
>-
>-		/* transfer finished; kick state machine */
>-		dev_dbg(ace->dev, "identify finished\n");
>-		ace->fsm_state = ACE_FSM_STATE_IDENTIFY_COMPLETE;
>-		break;
>-
>-	case ACE_FSM_STATE_IDENTIFY_COMPLETE:
>-		ace_fix_driveid(ace->cf_id);
>-		ace_dump_mem(ace->cf_id, 512);	/* Debug: Dump out disk ID */
>-
>-		if (ace->data_result) {
>-			/* Error occurred, disable the disk */
>-			ace->media_change = 1;
>-			set_capacity(ace->gd, 0);
>-			dev_err(ace->dev, "error fetching CF id (%i)\n",
>-				ace->data_result);
>-		} else {
>-			ace->media_change = 0;
>-
>-			/* Record disk parameters */
>-			set_capacity(ace->gd,
>-				ata_id_u32(ace->cf_id, ATA_ID_LBA_CAPACITY));
>-			dev_info(ace->dev, "capacity: %i sectors\n",
>-				ata_id_u32(ace->cf_id, ATA_ID_LBA_CAPACITY));
>-		}
>-
>-		/* We're done, drop to IDLE state and notify waiters */
>-		ace->fsm_state = ACE_FSM_STATE_IDLE;
>-		ace->id_result = ace->data_result;
>-		while (ace->id_req_count) {
>-			complete(&ace->id_completion);
>-			ace->id_req_count--;
>-		}
>-		break;
>-
>-	case ACE_FSM_STATE_REQ_PREPARE:
>-		req = ace_get_next_request(ace->queue);
>-		if (!req) {
>-			ace->fsm_state = ACE_FSM_STATE_IDLE;
>-			break;
>-		}
>-
>-		/* Okay, it's a data request, set it up for transfer */
>-		dev_dbg(ace->dev,
>-			"request: sec=%llx hcnt=%x, ccnt=%x, dir=%i\n",
>-			(unsigned long long)blk_rq_pos(req),
>-			blk_rq_sectors(req), blk_rq_cur_sectors(req),
>-			rq_data_dir(req));
>-
>-		ace->req = req;
>-		ace->data_ptr = bio_data(req->bio);
>-		ace->data_count = blk_rq_cur_sectors(req) * ACE_BUF_PER_SECTOR;
>-		ace_out32(ace, ACE_MPULBA, blk_rq_pos(req) & 0x0FFFFFFF);
>-
>-		count = blk_rq_sectors(req);
>-		if (rq_data_dir(req)) {
>-			/* Kick off write request */
>-			dev_dbg(ace->dev, "write data\n");
>-			ace->fsm_task = ACE_TASK_WRITE;
>-			ace_out(ace, ACE_SECCNTCMD,
>-				count | ACE_SECCNTCMD_WRITE_DATA);
>-		} else {
>-			/* Kick off read request */
>-			dev_dbg(ace->dev, "read data\n");
>-			ace->fsm_task = ACE_TASK_READ;
>-			ace_out(ace, ACE_SECCNTCMD,
>-				count | ACE_SECCNTCMD_READ_DATA);
>-		}
>-
>-		/* As per datasheet, put config controller in reset */
>-		val = ace_in(ace, ACE_CTRL);
>-		ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);
>-
>-		/* Move to the transfer state.  The systemace will raise
>-		 * an interrupt once there is something to do
>-		 */
>-		ace->fsm_state = ACE_FSM_STATE_REQ_TRANSFER;
>-		if (ace->fsm_task == ACE_TASK_READ)
>-			ace_fsm_yieldirq(ace);	/* wait for data ready */
>-		break;
>-
>-	case ACE_FSM_STATE_REQ_TRANSFER:
>-		/* Check that the sysace is ready to receive data */
>-		status = ace_in32(ace, ACE_STATUS);
>-		if (status & ACE_STATUS_CFBSY) {
>-			dev_dbg(ace->dev,
>-				"CFBSY set; t=%i iter=%i c=%i dc=%i irq=%i\n",
>-				ace->fsm_task, ace->fsm_iter_num,
>-				blk_rq_cur_sectors(ace->req) * 16,
>-				ace->data_count, ace->in_irq);
>-			ace_fsm_yield(ace);	/* need to poll CFBSY bit */
>-			break;
>-		}
>-		if (!(status & ACE_STATUS_DATABUFRDY)) {
>-			dev_dbg(ace->dev,
>-				"DATABUF not set; t=%i iter=%i c=%i dc=%i irq=%i\n",
>-				ace->fsm_task, ace->fsm_iter_num,
>-				blk_rq_cur_sectors(ace->req) * 16,
>-				ace->data_count, ace->in_irq);
>-			ace_fsm_yieldirq(ace);
>-			break;
>-		}
>-
>-		/* Transfer the next buffer */
>-		if (ace->fsm_task == ACE_TASK_WRITE)
>-			ace->reg_ops->dataout(ace);
>-		else
>-			ace->reg_ops->datain(ace);
>-		ace->data_count--;
>-
>-		/* If there are still buffers to be transfers; jump out here */
>-		if (ace->data_count != 0) {
>-			ace_fsm_yieldirq(ace);
>-			break;
>-		}
>-
>-		/* bio finished; is there another one? */
>-		if (blk_update_request(ace->req, BLK_STS_OK,
>-		    blk_rq_cur_bytes(ace->req))) {
>-			/* dev_dbg(ace->dev, "next block; h=%u c=%u\n",
>-			 *      blk_rq_sectors(ace->req),
>-			 *      blk_rq_cur_sectors(ace->req));
>-			 */
>-			ace->data_ptr = bio_data(ace->req->bio);
>-			ace->data_count = blk_rq_cur_sectors(ace->req) * 16;
>-			ace_fsm_yieldirq(ace);
>-			break;
>-		}
>-
>-		ace->fsm_state = ACE_FSM_STATE_REQ_COMPLETE;
>-		break;
>-
>-	case ACE_FSM_STATE_REQ_COMPLETE:
>-		ace->req = NULL;
>-
>-		/* Finished request; go to idle state */
>-		ace->fsm_state = ACE_FSM_STATE_IDLE;
>-		break;
>-
>-	default:
>-		ace->fsm_state = ACE_FSM_STATE_IDLE;
>-		break;
>-	}
>-}
>-
>-static void ace_fsm_tasklet(unsigned long data)
>-{
>-	struct ace_device *ace = (void *)data;
>-	unsigned long flags;
>-
>-	spin_lock_irqsave(&ace->lock, flags);
>-
>-	/* Loop over state machine until told to stop */
>-	ace->fsm_continue_flag = 1;
>-	while (ace->fsm_continue_flag)
>-		ace_fsm_dostate(ace);
>-
>-	spin_unlock_irqrestore(&ace->lock, flags);
>-}
>-
>-static void ace_stall_timer(struct timer_list *t)
>-{
>-	struct ace_device *ace = from_timer(ace, t, stall_timer);
>-	unsigned long flags;
>-
>-	dev_warn(ace->dev,
>-		 "kicking stalled fsm; state=%i task=%i iter=%i dc=%i\n",
>-		 ace->fsm_state, ace->fsm_task, ace->fsm_iter_num,
>-		 ace->data_count);
>-	spin_lock_irqsave(&ace->lock, flags);
>-
>-	/* Rearm the stall timer *before* entering FSM (which may then
>-	 * delete the timer) */
>-	mod_timer(&ace->stall_timer, jiffies + HZ);
>-
>-	/* Loop over state machine until told to stop */
>-	ace->fsm_continue_flag = 1;
>-	while (ace->fsm_continue_flag)
>-		ace_fsm_dostate(ace);
>-
>-	spin_unlock_irqrestore(&ace->lock, flags);
>-}
>-
>-/* ---------------------------------------------------------------------
>- * Interrupt handling routines
>- */
>-static int ace_interrupt_checkstate(struct ace_device *ace)
>-{
>-	u32 sreg = ace_in32(ace, ACE_STATUS);
>-	u16 creg = ace_in(ace, ACE_CTRL);
>-
>-	/* Check for error occurrence */
>-	if ((sreg & (ACE_STATUS_CFGERROR | ACE_STATUS_CFCERROR)) &&
>-	    (creg & ACE_CTRL_ERRORIRQ)) {
>-		dev_err(ace->dev, "transfer failure\n");
>-		ace_dump_regs(ace);
>-		return -EIO;
>-	}
>-
>-	return 0;
>-}
>-
>-static irqreturn_t ace_interrupt(int irq, void *dev_id)
>-{
>-	u16 creg;
>-	struct ace_device *ace = dev_id;
>-
>-	/* be safe and get the lock */
>-	spin_lock(&ace->lock);
>-	ace->in_irq = 1;
>-
>-	/* clear the interrupt */
>-	creg = ace_in(ace, ACE_CTRL);
>-	ace_out(ace, ACE_CTRL, creg | ACE_CTRL_RESETIRQ);
>-	ace_out(ace, ACE_CTRL, creg);
>-
>-	/* check for IO failures */
>-	if (ace_interrupt_checkstate(ace))
>-		ace->data_result = -EIO;
>-
>-	if (ace->fsm_task == 0) {
>-		dev_err(ace->dev,
>-			"spurious irq; stat=%.8x ctrl=%.8x cmd=%.4x\n",
>-			ace_in32(ace, ACE_STATUS), ace_in32(ace, ACE_CTRL),
>-			ace_in(ace, ACE_SECCNTCMD));
>-		dev_err(ace->dev, "fsm_task=%i fsm_state=%i data_count=%i\n",
>-			ace->fsm_task, ace->fsm_state, ace->data_count);
>-	}
>-
>-	/* Loop over state machine until told to stop */
>-	ace->fsm_continue_flag = 1;
>-	while (ace->fsm_continue_flag)
>-		ace_fsm_dostate(ace);
>-
>-	/* done with interrupt; drop the lock */
>-	ace->in_irq = 0;
>-	spin_unlock(&ace->lock);
>-
>-	return IRQ_HANDLED;
>-}
>-
>-/* ---------------------------------------------------------------------
>- * Block ops
>- */
>-static blk_status_t ace_queue_rq(struct blk_mq_hw_ctx *hctx,
>-				 const struct blk_mq_queue_data *bd)
>-{
>-	struct ace_device *ace = hctx->queue->queuedata;
>-	struct request *req = bd->rq;
>-
>-	if (blk_rq_is_passthrough(req)) {
>-		blk_mq_start_request(req);
>-		return BLK_STS_IOERR;
>-	}
>-
>-	spin_lock_irq(&ace->lock);
>-	list_add_tail(&req->queuelist, &ace->rq_list);
>-	spin_unlock_irq(&ace->lock);
>-
>-	tasklet_schedule(&ace->fsm_tasklet);
>-	return BLK_STS_OK;
>-}
>-
>-static unsigned int ace_check_events(struct gendisk *gd, unsigned int clearing)
>-{
>-	struct ace_device *ace = gd->private_data;
>-	dev_dbg(ace->dev, "ace_check_events(): %i\n", ace->media_change);
>-
>-	return ace->media_change ? DISK_EVENT_MEDIA_CHANGE : 0;
>-}
>-
>-static void ace_media_changed(struct ace_device *ace)
>-{
>-	unsigned long flags;
>-
>-	dev_dbg(ace->dev, "requesting cf id and scheduling tasklet\n");
>-
>-	spin_lock_irqsave(&ace->lock, flags);
>-	ace->id_req_count++;
>-	spin_unlock_irqrestore(&ace->lock, flags);
>-
>-	tasklet_schedule(&ace->fsm_tasklet);
>-	wait_for_completion(&ace->id_completion);
>-
>-	dev_dbg(ace->dev, "revalidate complete\n");
>-}
>-
>-static int ace_open(struct block_device *bdev, fmode_t mode)
>-{
>-	struct ace_device *ace = bdev->bd_disk->private_data;
>-	unsigned long flags;
>-
>-	dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1);
>-
>-	mutex_lock(&xsysace_mutex);
>-	spin_lock_irqsave(&ace->lock, flags);
>-	ace->users++;
>-	spin_unlock_irqrestore(&ace->lock, flags);
>-
>-	if (bdev_check_media_change(bdev) && ace->media_change)
>-		ace_media_changed(ace);
>-	mutex_unlock(&xsysace_mutex);
>-
>-	return 0;
>-}
>-
>-static void ace_release(struct gendisk *disk, fmode_t mode)
>-{
>-	struct ace_device *ace = disk->private_data;
>-	unsigned long flags;
>-	u16 val;
>-
>-	dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1);
>-
>-	mutex_lock(&xsysace_mutex);
>-	spin_lock_irqsave(&ace->lock, flags);
>-	ace->users--;
>-	if (ace->users == 0) {
>-		val = ace_in(ace, ACE_CTRL);
>-		ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ);
>-	}
>-	spin_unlock_irqrestore(&ace->lock, flags);
>-	mutex_unlock(&xsysace_mutex);
>-}
>-
>-static int ace_getgeo(struct block_device *bdev, struct hd_geometry *geo)
>-{
>-	struct ace_device *ace = bdev->bd_disk->private_data;
>-	u16 *cf_id = ace->cf_id;
>-
>-	dev_dbg(ace->dev, "ace_getgeo()\n");
>-
>-	geo->heads	= cf_id[ATA_ID_HEADS];
>-	geo->sectors	= cf_id[ATA_ID_SECTORS];
>-	geo->cylinders	= cf_id[ATA_ID_CYLS];
>-
>-	return 0;
>-}
>-
>-static const struct block_device_operations ace_fops = {
>-	.owner = THIS_MODULE,
>-	.open = ace_open,
>-	.release = ace_release,
>-	.check_events = ace_check_events,
>-	.getgeo = ace_getgeo,
>-};
>-
>-static const struct blk_mq_ops ace_mq_ops = {
>-	.queue_rq	= ace_queue_rq,
>-};
>-
>-/* --------------------------------------------------------------------
>- * SystemACE device setup/teardown code
>- */
>-static int ace_setup(struct ace_device *ace)
>-{
>-	u16 version;
>-	u16 val;
>-	int rc;
>-
>-	dev_dbg(ace->dev, "ace_setup(ace=0x%p)\n", ace);
>-	dev_dbg(ace->dev, "physaddr=0x%llx irq=%i\n",
>-		(unsigned long long)ace->physaddr, ace->irq);
>-
>-	spin_lock_init(&ace->lock);
>-	init_completion(&ace->id_completion);
>-	INIT_LIST_HEAD(&ace->rq_list);
>-
>-	/*
>-	 * Map the device
>-	 */
>-	ace->baseaddr = ioremap(ace->physaddr, 0x80);
>-	if (!ace->baseaddr)
>-		goto err_ioremap;
>-
>-	/*
>-	 * Initialize the state machine tasklet and stall timer
>-	 */
>-	tasklet_init(&ace->fsm_tasklet, ace_fsm_tasklet, (unsigned long)ace);
>-	timer_setup(&ace->stall_timer, ace_stall_timer, 0);
>-
>-	/*
>-	 * Initialize the request queue
>-	 */
>-	ace->queue = blk_mq_init_sq_queue(&ace->tag_set, &ace_mq_ops, 2,
>-						BLK_MQ_F_SHOULD_MERGE);
>-	if (IS_ERR(ace->queue)) {
>-		rc = PTR_ERR(ace->queue);
>-		ace->queue = NULL;
>-		goto err_blk_initq;
>-	}
>-	ace->queue->queuedata = ace;
>-
>-	blk_queue_logical_block_size(ace->queue, 512);
>-	blk_queue_bounce_limit(ace->queue, BLK_BOUNCE_HIGH);
>-
>-	/*
>-	 * Allocate and initialize GD structure
>-	 */
>-	ace->gd = alloc_disk(ACE_NUM_MINORS);
>-	if (!ace->gd)
>-		goto err_alloc_disk;
>-
>-	ace->gd->major = ace_major;
>-	ace->gd->first_minor = ace->id * ACE_NUM_MINORS;
>-	ace->gd->fops = &ace_fops;
>-	ace->gd->events = DISK_EVENT_MEDIA_CHANGE;
>-	ace->gd->queue = ace->queue;
>-	ace->gd->private_data = ace;
>-	snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a');
>-
>-	/* set bus width */
>-	if (ace->bus_width == ACE_BUS_WIDTH_16) {
>-		/* 0x0101 should work regardless of endianess */
>-		ace_out_le16(ace, ACE_BUSMODE, 0x0101);
>-
>-		/* read it back to determine endianess */
>-		if (ace_in_le16(ace, ACE_BUSMODE) == 0x0001)
>-			ace->reg_ops = &ace_reg_le16_ops;
>-		else
>-			ace->reg_ops = &ace_reg_be16_ops;
>-	} else {
>-		ace_out_8(ace, ACE_BUSMODE, 0x00);
>-		ace->reg_ops = &ace_reg_8_ops;
>-	}
>-
>-	/* Make sure version register is sane */
>-	version = ace_in(ace, ACE_VERSION);
>-	if ((version == 0) || (version == 0xFFFF))
>-		goto err_read;
>-
>-	/* Put sysace in a sane state by clearing most control reg bits */
>-	ace_out(ace, ACE_CTRL, ACE_CTRL_FORCECFGMODE |
>-		ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ);
>-
>-	/* Now we can hook up the irq handler */
>-	if (ace->irq > 0) {
>-		rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace);
>-		if (rc) {
>-			/* Failure - fall back to polled mode */
>-			dev_err(ace->dev, "request_irq failed\n");
>-			ace->irq = rc;
>-		}
>-	}
>-
>-	/* Enable interrupts */
>-	val = ace_in(ace, ACE_CTRL);
>-	val |= ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ;
>-	ace_out(ace, ACE_CTRL, val);
>-
>-	/* Print the identification */
>-	dev_info(ace->dev, "Xilinx SystemACE revision %i.%i.%i\n",
>-		 (version >> 12) & 0xf, (version >> 8) & 0x0f, version & 0xff);
>-	dev_dbg(ace->dev, "physaddr 0x%llx, mapped to 0x%p, irq=%i\n",
>-		(unsigned long long) ace->physaddr, ace->baseaddr, ace->irq);
>-
>-	ace->media_change = 1;
>-	ace_media_changed(ace);
>-
>-	/* Make the sysace device 'live' */
>-	add_disk(ace->gd);
>-
>-	return 0;
>-
>-err_read:
>-	/* prevent double queue cleanup */
>-	ace->gd->queue = NULL;
>-	put_disk(ace->gd);
>-err_alloc_disk:
>-	blk_cleanup_queue(ace->queue);
>-	blk_mq_free_tag_set(&ace->tag_set);
>-err_blk_initq:
>-	iounmap(ace->baseaddr);
>-err_ioremap:
>-	dev_info(ace->dev, "xsysace: error initializing device at 0x%llx\n",
>-		 (unsigned long long) ace->physaddr);
>-	return -ENOMEM;
>-}
>-
>-static void ace_teardown(struct ace_device *ace)
>-{
>-	if (ace->gd) {
>-		del_gendisk(ace->gd);
>-		put_disk(ace->gd);
>-	}
>-
>-	if (ace->queue) {
>-		blk_cleanup_queue(ace->queue);
>-		blk_mq_free_tag_set(&ace->tag_set);
>-	}
>-
>-	tasklet_kill(&ace->fsm_tasklet);
>-
>-	if (ace->irq > 0)
>-		free_irq(ace->irq, ace);
>-
>-	iounmap(ace->baseaddr);
>-}
>-
>-static int ace_alloc(struct device *dev, int id, resource_size_t physaddr,
>-		     int irq, int bus_width)
>-{
>-	struct ace_device *ace;
>-	int rc;
>-	dev_dbg(dev, "ace_alloc(%p)\n", dev);
>-
>-	/* Allocate and initialize the ace device structure */
>-	ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL);
>-	if (!ace) {
>-		rc = -ENOMEM;
>-		goto err_alloc;
>-	}
>-
>-	ace->dev = dev;
>-	ace->id = id;
>-	ace->physaddr = physaddr;
>-	ace->irq = irq;
>-	ace->bus_width = bus_width;
>-
>-	/* Call the setup code */
>-	rc = ace_setup(ace);
>-	if (rc)
>-		goto err_setup;
>-
>-	dev_set_drvdata(dev, ace);
>-	return 0;
>-
>-err_setup:
>-	dev_set_drvdata(dev, NULL);
>-	kfree(ace);
>-err_alloc:
>-	dev_err(dev, "could not initialize device, err=%i\n", rc);
>-	return rc;
>-}
>-
>-static void ace_free(struct device *dev)
>-{
>-	struct ace_device *ace = dev_get_drvdata(dev);
>-	dev_dbg(dev, "ace_free(%p)\n", dev);
>-
>-	if (ace) {
>-		ace_teardown(ace);
>-		dev_set_drvdata(dev, NULL);
>-		kfree(ace);
>-	}
>-}
>-
>-/* ---------------------------------------------------------------------
>- * Platform Bus Support
>- */
>-
>-static int ace_probe(struct platform_device *dev)
>-{
>-	int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */
>-	resource_size_t physaddr;
>-	struct resource *res;
>-	u32 id = dev->id;
>-	int irq;
>-	int i;
>-
>-	dev_dbg(&dev->dev, "ace_probe(%p)\n", dev);
>-
>-	/* device id and bus width */
>-	if (of_property_read_u32(dev->dev.of_node, "port-number", &id))
>-		id = 0;
>-	if (of_find_property(dev->dev.of_node, "8-bit", NULL))
>-		bus_width = ACE_BUS_WIDTH_8;
>-
>-	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
>-	if (!res)
>-		return -EINVAL;
>-
>-	physaddr = res->start;
>-	if (!physaddr)
>-		return -ENODEV;
>-
>-	irq = platform_get_irq_optional(dev, 0);
>-
>-	/* Call the bus-independent setup code */
>-	return ace_alloc(&dev->dev, id, physaddr, irq, bus_width);
>-}
>-
>-/*
>- * Platform bus remove() method
>- */
>-static int ace_remove(struct platform_device *dev)
>-{
>-	ace_free(&dev->dev);
>-	return 0;
>-}
>-
>-#if defined(CONFIG_OF)
>-/* Match table for of_platform binding */
>-static const struct of_device_id ace_of_match[] = {
>-	{ .compatible = "xlnx,opb-sysace-1.00.b", },
>-	{ .compatible = "xlnx,opb-sysace-1.00.c", },
>-	{ .compatible = "xlnx,xps-sysace-1.00.a", },
>-	{ .compatible = "xlnx,sysace", },
>-	{},
>-};
>-MODULE_DEVICE_TABLE(of, ace_of_match);
>-#else /* CONFIG_OF */
>-#define ace_of_match NULL
>-#endif /* CONFIG_OF */
>-
>-static struct platform_driver ace_platform_driver = {
>-	.probe = ace_probe,
>-	.remove = ace_remove,
>-	.driver = {
>-		.name = "xsysace",
>-		.of_match_table = ace_of_match,
>-	},
>-};
>-
>-/* ---------------------------------------------------------------------
>- * Module init/exit routines
>- */
>-static int __init ace_init(void)
>-{
>-	int rc;
>-
>-	ace_major = register_blkdev(ace_major, "xsysace");
>-	if (ace_major <= 0) {
>-		rc = -ENOMEM;
>-		goto err_blk;
>-	}
>-
>-	rc = platform_driver_register(&ace_platform_driver);
>-	if (rc)
>-		goto err_plat;
>-
>-	pr_info("Xilinx SystemACE device driver, major=%i\n", ace_major);
>-	return 0;
>-
>-err_plat:
>-	unregister_blkdev(ace_major, "xsysace");
>-err_blk:
>-	printk(KERN_ERR "xsysace: registration failed; err=%i\n", rc);
>-	return rc;
>-}
>-module_init(ace_init);
>-
>-static void __exit ace_exit(void)
>-{
>-	pr_debug("Unregistering Xilinx SystemACE driver\n");
>-	platform_driver_unregister(&ace_platform_driver);
>-	unregister_blkdev(ace_major, "xsysace");
>-}
>-module_exit(ace_exit);
>-- 
>2.29.2
>

^ permalink raw reply

* Re: [PATCH 3/3] powerpc/mm/hash: Avoid multiple HPT resize-downs on memory hotunplug
From: David Gibson @ 2021-03-22 23:45 UTC (permalink / raw)
  To: Leonardo Bras
  Cc: Nathan Lynch, David Hildenbrand, Scott Cheloha, linux-kernel,
	linuxppc-dev, Nicholas Piggin, Bharata B Rao, Paul Mackerras,
	Sandipan Das, Aneesh Kumar K.V, Andrew Morton, Laurent Dufour,
	Logan Gunthorpe, Dan Williams, Mike Rapoport
In-Reply-To: <20210312072940.598696-4-leobras.c@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 9150 bytes --]

On Fri, Mar 12, 2021 at 04:29:41AM -0300, Leonardo Bras wrote:
> During memory hotunplug, after each LMB is removed, the HPT may be
> resized-down if it would map a max of 4 times the current amount of memory.
> (2 shifts, due to introduced histeresis)
> 
> It usually is not an issue, but it can take a lot of time if HPT
> resizing-down fails. This happens  because resize-down failures
> usually repeat at each LMB removal, until there are no more bolted entries
> conflict, which can take a while to happen.
> 
> This can be solved by doing a single HPT resize at the end of memory
> hotunplug, after all requested entries are removed.
> 
> To make this happen, it's necessary to temporarily disable all HPT
> resize-downs before hotunplug, re-enable them after hotunplug ends,
> and then resize-down HPT to the current memory size.
> 
> As an example, hotunplugging 256GB from a 385GB guest took 621s without
> this patch, and 100s after applied.
> 
> Signed-off-by: Leonardo Bras <leobras.c@gmail.com>
> ---
>  arch/powerpc/include/asm/book3s/64/hash.h     |  2 ++
>  arch/powerpc/include/asm/sparsemem.h          |  2 ++
>  arch/powerpc/mm/book3s64/hash_utils.c         | 28 +++++++++++++++++++
>  arch/powerpc/mm/book3s64/pgtable.c            | 12 ++++++++
>  .../platforms/pseries/hotplug-memory.c        | 16 +++++++++++
>  5 files changed, 60 insertions(+)
> 
> diff --git a/arch/powerpc/include/asm/book3s/64/hash.h b/arch/powerpc/include/asm/book3s/64/hash.h
> index 843b0a178590..f92697c107f7 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash.h
> @@ -256,6 +256,8 @@ int hash__create_section_mapping(unsigned long start, unsigned long end,
>  int hash__remove_section_mapping(unsigned long start, unsigned long end);
>  
>  void hash_memory_batch_expand_prepare(unsigned long newsize);
> +void hash_memory_batch_shrink_begin(void);
> +void hash_memory_batch_shrink_end(void);
>  
>  #endif /* !__ASSEMBLY__ */
>  #endif /* __KERNEL__ */
> diff --git a/arch/powerpc/include/asm/sparsemem.h b/arch/powerpc/include/asm/sparsemem.h
> index 16b5f5300c84..a7a8a0d070fc 100644
> --- a/arch/powerpc/include/asm/sparsemem.h
> +++ b/arch/powerpc/include/asm/sparsemem.h
> @@ -18,6 +18,8 @@ extern int memory_add_physaddr_to_nid(u64 start);
>  #define memory_add_physaddr_to_nid memory_add_physaddr_to_nid
>  
>  void memory_batch_expand_prepare(unsigned long newsize);
> +void memory_batch_shrink_begin(void);
> +void memory_batch_shrink_end(void);
>  
>  #ifdef CONFIG_NUMA
>  extern int hot_add_scn_to_nid(unsigned long scn_addr);
> diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c
> index 1f6aa0bf27e7..e16f207de8e4 100644
> --- a/arch/powerpc/mm/book3s64/hash_utils.c
> +++ b/arch/powerpc/mm/book3s64/hash_utils.c
> @@ -794,6 +794,9 @@ static unsigned long __init htab_get_table_size(void)
>  }
>  
>  #ifdef CONFIG_MEMORY_HOTPLUG
> +
> +atomic_t hpt_resize_disable = ATOMIC_INIT(0);
> +
>  static int resize_hpt_for_hotplug(unsigned long new_mem_size, bool shrinking)
>  {
>  	unsigned target_hpt_shift;
> @@ -805,6 +808,10 @@ static int resize_hpt_for_hotplug(unsigned long new_mem_size, bool shrinking)
>  
>  	if (shrinking) {
>  
> +		/* When batch removing entries, only resizes HPT at the end. */
> +		if (atomic_read_acquire(&hpt_resize_disable))
> +			return 0;
> +

I'm not quite convinced by this locking.  Couldn't hpt_resize_disable
be set after this point, but while you're still inside
resize_hpt_for_hotplug()?  Probably better to use an explicit mutex
(and mutex_trylock()) to make the critical sections clearer.

Except... do we even need the fancy mechanics to suppress the resizes
in one place to do them elswhere.  Couldn't we just replace the
existing resize calls with the batched ones?

>  		/*
>  		 * To avoid lots of HPT resizes if memory size is fluctuating
>  		 * across a boundary, we deliberately have some hysterisis
> @@ -872,6 +879,27 @@ void hash_memory_batch_expand_prepare(unsigned long newsize)
>  		pr_warn("Hash collision while resizing HPT\n");
>  	}
>  }
> +
> +void hash_memory_batch_shrink_begin(void)
> +{
> +	/* Disable HPT resize-down during hot-unplug */
> +	atomic_set_release(&hpt_resize_disable, 1);
> +}
> +
> +void hash_memory_batch_shrink_end(void)
> +{
> +	unsigned long newsize;
> +
> +	/* Re-enables HPT resize-down after hot-unplug */
> +	atomic_set_release(&hpt_resize_disable, 0);
> +
> +	newsize = memblock_phys_mem_size();
> +	/* Resize to smallest SHIFT possible */
> +	while (resize_hpt_for_hotplug(newsize, true) == -ENOSPC) {
> +		newsize *= 2;

As noted earlier, doing this without an explicit cap on the new hpt
size (of the existing size) this makes me nervous.  Less so, but doing
the calculations on memory size, rather than explictly on HPT size /
HPT order also seems kinda clunky.

> +		pr_warn("Hash collision while resizing HPT\n");
> +	}
> +}
>  #endif /* CONFIG_MEMORY_HOTPLUG */
>  
>  static void __init hash_init_partition_table(phys_addr_t hash_table,
> diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c
> index f1cd8af0f67f..e01681e22e00 100644
> --- a/arch/powerpc/mm/book3s64/pgtable.c
> +++ b/arch/powerpc/mm/book3s64/pgtable.c
> @@ -199,6 +199,18 @@ void memory_batch_expand_prepare(unsigned long newsize)
>  	if (!radix_enabled())
>  		hash_memory_batch_expand_prepare(newsize);
>  }
> +
> +void memory_batch_shrink_begin(void)
> +{
> +	if (!radix_enabled())
> +		hash_memory_batch_shrink_begin();
> +}
> +
> +void memory_batch_shrink_end(void)
> +{
> +	if (!radix_enabled())
> +		hash_memory_batch_shrink_end();
> +}

Again, these wrappers don't seem particularly useful to me.

>  #endif /* CONFIG_MEMORY_HOTPLUG */
>  
>  void __init mmu_partition_table_init(void)
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index 353c71249214..9182fb5b5c01 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -425,6 +425,8 @@ static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
>  		return -EINVAL;
>  	}
>  
> +	memory_batch_shrink_begin();
> +
>  	for_each_drmem_lmb(lmb) {
>  		rc = dlpar_remove_lmb(lmb);
>  		if (rc)
> @@ -470,6 +472,8 @@ static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
>  		rc = 0;
>  	}
>  
> +	memory_batch_shrink_end();
> +
>  	return rc;
>  }
>  
> @@ -481,6 +485,8 @@ static int dlpar_memory_remove_by_index(u32 drc_index)
>  
>  	pr_debug("Attempting to hot-remove LMB, drc index %x\n", drc_index);
>  
> +	memory_batch_shrink_begin();
> +
>  	lmb_found = 0;
>  	for_each_drmem_lmb(lmb) {
>  		if (lmb->drc_index == drc_index) {
> @@ -502,6 +508,8 @@ static int dlpar_memory_remove_by_index(u32 drc_index)
>  	else
>  		pr_debug("Memory at %llx was hot-removed\n", lmb->base_addr);
>  
> +	memory_batch_shrink_end();

remove_by_index only removes a single LMB, so there's no real point to
batching here.

>  	return rc;
>  }
>  
> @@ -532,6 +540,8 @@ static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
>  	if (lmbs_available < lmbs_to_remove)
>  		return -EINVAL;
>  
> +	memory_batch_shrink_begin();
> +
>  	for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
>  		if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
>  			continue;
> @@ -572,6 +582,8 @@ static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
>  		}
>  	}
>  
> +	memory_batch_shrink_end();
> +
>  	return rc;
>  }
>  
> @@ -700,6 +712,7 @@ static int dlpar_memory_add_by_count(u32 lmbs_to_add)
>  	if (lmbs_added != lmbs_to_add) {
>  		pr_err("Memory hot-add failed, removing any added LMBs\n");
>  
> +		memory_batch_shrink_begin();


The effect of these on the memory grow path is far from clear.

>  		for_each_drmem_lmb(lmb) {
>  			if (!drmem_lmb_reserved(lmb))
>  				continue;
> @@ -713,6 +726,7 @@ static int dlpar_memory_add_by_count(u32 lmbs_to_add)
>  
>  			drmem_remove_lmb_reservation(lmb);
>  		}
> +		memory_batch_shrink_end();
>  		rc = -EINVAL;
>  	} else {
>  		for_each_drmem_lmb(lmb) {
> @@ -814,6 +828,7 @@ static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index)
>  	if (rc) {
>  		pr_err("Memory indexed-count-add failed, removing any added LMBs\n");
>  
> +		memory_batch_shrink_begin();
>  		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
>  			if (!drmem_lmb_reserved(lmb))
>  				continue;
> @@ -827,6 +842,7 @@ static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index)
>  
>  			drmem_remove_lmb_reservation(lmb);
>  		}
> +		memory_batch_shrink_end();
>  		rc = -EINVAL;
>  	} else {
>  		for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH] powerpc/asm: Fix a typo
From: Michael Ellerman @ 2021-03-22 23:48 UTC (permalink / raw)
  To: Randy Dunlap, Bhaskar Chowdhury, benh, paulus, linuxppc-dev,
	linux-kernel
In-Reply-To: <96eda8ce-1d89-1491-51a2-92ba9883912b@infradead.org>

Randy Dunlap <rdunlap@infradead.org> writes:
> On 3/22/21 4:32 AM, Bhaskar Chowdhury wrote:
>> 
>> s/poiner/pointer/
>> 
>> Signed-off-by: Bhaskar Chowdhury <unixbhaskar@gmail.com>
>
> Acked-by: Randy Dunlap <rdunlap@infradead.org>
>
> However, it would be a GOOD THING to collect multiple similar
> patches that are in e.g. arch/powerpc/ and send them as one patch
> instead of many little patches.

Yes.

Please send me one patch for all of the spelling issues you can
currently find in arch/powerpc.

cheers

^ permalink raw reply

* Re: [PATCH 00/10] Move 64e to new interrupt return code
From: Daniel Axtens @ 2021-03-22 23:45 UTC (permalink / raw)
  To: Nicholas Piggin, linuxppc-dev; +Cc: Scott Wood, Nicholas Piggin
In-Reply-To: <20210315031716.3940350-1-npiggin@gmail.com>

Hi Nick,

> Since RFC this is rebased on Christophe's v3 ppc32 conversion, and
> has fixed up small details, and then adds some powerpc-wide
> cleanups at the end.
>
> Tested on qemu only (QEMU e500), which is not ideal for interrupt
> handling particularly the critical interrupts which I don't know
> whether it can generate.

I tested this on a T4240RDB with:

stress-ng --class interrupts --seq 0 -t 5

There are some problems that occur only when testing with your series. I
haven't made any attempt to debug them yet.

stress-ng: info:  [3101] unsuccessful run completed in 6352.60s (1 hour, 45 mins, 52.60 secs)
stress-ng: fail:  [3101] aio instance 0 corrupted bogo-ops counter, 7542705 vs 0
stress-ng: fail:  [3101] aio instance 0 hash error in bogo-ops counter and run flag, 359866039 vs 0
stress-ng: fail:  [3101] aio instance 17 corrupted bogo-ops counter, 7638823 vs 0
stress-ng: fail:  [3101] aio instance 17 hash error in bogo-ops counter and run flag, 2001558423 vs 0
stress-ng: fail:  [3101] aio instance 30 corrupted bogo-ops counter, 8192545 vs 0
info: 5 failures reached, aborting stress process
stress-ng: fail:  [3101] aio instance 30 hash error in bogo-ops counter and run flag, 3023200976 vs 0
stress-ng: fail:  [3101] pidfd instance 25 corrupted bogo-ops counter, 116476 vs 0
stress-ng: fail:  [3101] pidfd instance 25 hash error in bogo-ops counter and run flag, 1964630417 vs 0
stress-ng: fail:  [3101] sigabrt instance 3 corrupted bogo-ops counter, 95662 vs 0
stress-ng: fail:  [3101] sigabrt instance 3 hash error in bogo-ops counter and run flag, 1321243721 vs 0
stress-ng: fail:  [3101] sigabrt instance 9 corrupted bogo-ops counter, 92858 vs 0
stress-ng: fail:  [3101] sigabrt instance 9 hash error in bogo-ops counter and run flag, 3835381330 vs 0
stress-ng: fail:  [3101] sigabrt instance 11 corrupted bogo-ops counter, 98333 vs 0
stress-ng: fail:  [3101] sigabrt instance 11 hash error in bogo-ops counter and run flag, 3447969030 vs 0
stress-ng: fail:  [3101] sigabrt instance 14 corrupted bogo-ops counter, 96995 vs 0
stress-ng: fail:  [3101] sigabrt instance 14 hash error in bogo-ops counter and run flag, 2621581502 vs 0
stress-ng: fail:  [3101] sigabrt instance 16 corrupted bogo-ops counter, 97464 vs 0
stress-ng: fail:  [3101] sigabrt instance 16 hash error in bogo-ops counter and run flag, 3422440538 vs 0
stress-ng: fail:  [3101] sigabrt instance 19 corrupted bogo-ops counter, 96044 vs 0
stress-ng: fail:  [3101] sigabrt instance 19 hash error in bogo-ops counter and run flag, 511989935 vs 0
stress-ng: fail:  [3101] sigabrt instance 20 corrupted bogo-ops counter, 96018 vs 0
stress-ng: fail:  [3101] sigabrt instance 20 hash error in bogo-ops counter and run flag, 2348631606 vs 0
stress-ng: fail:  [3101] sigabrt instance 23 corrupted bogo-ops counter, 95252 vs 0
stress-ng: fail:  [3101] sigabrt instance 23 hash error in bogo-ops counter and run flag, 2302430489 vs 0
stress-ng: fail:  [3101] sigabrt instance 26 corrupted bogo-ops counter, 99151 vs 0
stress-ng: fail:  [3101] sigabrt instance 26 hash error in bogo-ops counter and run flag, 2882282932 vs 0
stress-ng: fail:  [3101] sigabrt instance 27 corrupted bogo-ops counter, 95434 vs 0
stress-ng: fail:  [3101] sigabrt instance 27 hash error in bogo-ops counter and run flag, 260112434 vs 0
stress-ng: fail:  [3101] sigabrt instance 28 corrupted bogo-ops counter, 97138 vs 0
stress-ng: fail:  [3101] sigabrt instance 28 hash error in bogo-ops counter and run flag, 2822283734 vs 0
stress-ng: fail:  [3101] sigabrt instance 30 corrupted bogo-ops counter, 97728 vs 0
stress-ng: fail:  [3101] sigabrt instance 30 hash error in bogo-ops counter and run flag, 738567801 vs 0
stress-ng: fail:  [3101] sigabrt instance 31 corrupted bogo-ops counter, 96368 vs 0
stress-ng: fail:  [3101] sigabrt instance 31 hash error in bogo-ops counter and run flag, 1663873592 vs 0
stress-ng: fail:  [3101] sigio instance 0 corrupted bogo-ops counter, 1141 vs 0
stress-ng: fail:  [3101] sigio instance 0 hash error in bogo-ops counter and run flag, 3981634025 vs 0
stress-ng: fail:  [3101] sigio instance 1 corrupted bogo-ops counter, 1323 vs 0
stress-ng: fail:  [3101] sigio instance 1 hash error in bogo-ops counter and run flag, 2384922462 vs 0
stress-ng: fail:  [3101] sigio instance 2 corrupted bogo-ops counter, 876 vs 0
stress-ng: fail:  [3101] sigio instance 2 hash error in bogo-ops counter and run flag, 2730635354 vs 0
stress-ng: fail:  [3101] sigio instance 3 corrupted bogo-ops counter, 3391 vs 0
stress-ng: fail:  [3101] sigio instance 3 hash error in bogo-ops counter and run flag, 3893594528 vs 0
stress-ng: fail:  [3101] sigio instance 4 corrupted bogo-ops counter, 988 vs 0
stress-ng: fail:  [3101] sigio instance 4 hash error in bogo-ops counter and run flag, 2252189661 vs 0
stress-ng: fail:  [3101] sigio instance 5 corrupted bogo-ops counter, 4158 vs 0
stress-ng: fail:  [3101] sigio instance 5 hash error in bogo-ops counter and run flag, 908770141 vs 0
stress-ng: fail:  [3101] sigio instance 6 corrupted bogo-ops counter, 657 vs 0
stress-ng: fail:  [3101] sigio instance 6 hash error in bogo-ops counter and run flag, 3022228667 vs 0
stress-ng: fail:  [3101] sigio instance 7 corrupted bogo-ops counter, 239 vs 0
stress-ng: fail:  [3101] sigio instance 7 hash error in bogo-ops counter and run flag, 2339545388 vs 0
stress-ng: fail:  [3101] sigio instance 8 corrupted bogo-ops counter, 183062 vs 0
stress-ng: fail:  [3101] sigio instance 8 hash error in bogo-ops counter and run flag, 2294439106 vs 0
stress-ng: fail:  [3101] sigio instance 9 corrupted bogo-ops counter, 946 vs 0
stress-ng: fail:  [3101] sigio instance 9 hash error in bogo-ops counter and run flag, 2990832529 vs 0
stress-ng: fail:  [3101] sigio instance 10 corrupted bogo-ops counter, 2799 vs 0
stress-ng: fail:  [3101] sigio instance 10 hash error in bogo-ops counter and run flag, 1781985030 vs 0
stress-ng: fail:  [3101] sigio instance 11 corrupted bogo-ops counter, 2705 vs 0
stress-ng: fail:  [3101] sigio instance 11 hash error in bogo-ops counter and run flag, 3301490000 vs 0
stress-ng: fail:  [3101] sigio instance 21 corrupted bogo-ops counter, 238787 vs 0
stress-ng: fail:  [3101] sigio instance 21 hash error in bogo-ops counter and run flag, 2490210165 vs 0
stress-ng: fail:  [3101] sigio instance 28 corrupted bogo-ops counter, 1020 vs 0
stress-ng: fail:  [3101] sigio instance 28 hash error in bogo-ops counter and run flag, 3260422232 vs 0
stress-ng: fail:  [3101] metrics-check: stressor metrics corrupted, data is compromised

It looks like this is paired with some segfaults in dmesg:

stress-ng-pidfd[4417]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-pidfd[4417]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-pidfd[4417]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748349]: segfault (11) at 800100 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748349]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748390]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1
stress-ng-sigab[3748405]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748405]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748405]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748427]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1
stress-ng-sigab[3748376]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1
in stress-ng[107e8d000+3000]
in stress-ng[107e8d000+3000]
stress-ng-sigab[3748427]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748376]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748427]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748376]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748460]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748460]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748460]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748434]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748434]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748434]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748367]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748367]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748367]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748349]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
in stress-ng[107e8d000+3000]
stress-ng-sigab[3748507]: segfault (11) at 800100 nip 107e8fb14 lr 107e8fb04 code 1

in stress-ng[107e8d000+3000]
stress-ng-sigab[3748390]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 

stress-ng-sigab[3748491]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigab[3748491]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748491]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748390]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigab[3748507]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigab[3748507]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
show_signal_msg: 3 callbacks suppressed
stress-ng-sigio[2635277]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1
stress-ng-sigio[2635278]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1
stress-ng-sigio[2635279]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635279]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635279]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635280]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635280]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635280]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635283]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635283]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635283]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635285]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635285]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635285]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635289]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635289]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635289]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
in stress-ng[107e8d000+3000]
stress-ng-sigio[2635293]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635293]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635293]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635292]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635292]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635292]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635315]: segfault (11) at 800000 nip 107e8fb14 lr 107e8fb04 code 1 in stress-ng[107e8d000+3000]
stress-ng-sigio[2635315]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635315]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
in stress-ng[107e8d000+3000]

stress-ng-sigio[2635278]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635277]: code: 7d4903a6 e8490008 4e800421 e8410028 7c691b78 386100ac 912100ac 4bfc3be1 
stress-ng-sigio[2635278]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 
stress-ng-sigio[2635277]: code: 60000000 812100ac 2c090000 40820014 <e90f0000> 39200001 993c0940 99280008 

In one run, I had problems with a hardware interrupt, but I haven't seen
it reoccur so I can't be sure it came from your series:


mmc0: Timeout waiting for hardware cmd interrupt.        
mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
mmc0: sdhci: Sys addr:  0x00000000 | Version:  0x00001301
mmc0: sdhci: Blk size:  0x00000000 | Blk cnt:  0x00000000
mmc0: sdhci: Argument:  0x00000c00 | Trn mode: 0x00000000
mmc0: sdhci: Present:   0x01f00008 | Host ctl: 0x00000020
mmc0: sdhci: Power:     0x00000000 | Blk gap:  0x00000000
mmc0: sdhci: Wake-up:   0x00000000 | Clock:    0x000020e8
mmc0: sdhci: Timeout:   0x00000000 | Int stat: 0x00010001
mmc0: sdhci: Int enab:  0x007f0007 | Sig enab: 0x007f0003
mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00001402
mmc0: sdhci: Caps:      0x04fa0000 | Caps_1:   0x00000000
mmc0: sdhci: Cmd:       0x0000341a | Max curr: 0x00000000
mmc0: sdhci: Resp[0]:   0x00000000 | Resp[1]:  0x00000000
mmc0: sdhci: Resp[2]:   0x00000000 | Resp[3]:  0x00000000
mmc0: sdhci: Host ctl2: 0x00000000                       
mmc0: sdhci: ADMA Err:  0x00000000 | ADMA Ptr: 0x00000000
mmc0: sdhci: ============================================
mmc0: Timeout waiting for hardware cmd interrupt. 

Let me know if you'd like me to run any further tests.

Kind regards,
Daniel

>
> Thanks,
> Nick
>
> Nicholas Piggin (10):
>   powerpc/syscall: switch user_exit_irqoff and trace_hardirqs_off order
>   powerpc/64e/interrupt: always save nvgprs on interrupt
>   powerpc/64e/interrupt: use new interrupt return
>   powerpc/64e/interrupt: NMI save irq soft-mask state in C
>   powerpc/64e/interrupt: reconcile irq soft-mask state in C
>   powerpc/64e/interrupt: Use new interrupt context tracking scheme
>   powerpc/64e/interrupt: handle bad_page_fault in C
>   powerpc: clean up do_page_fault
>   powerpc: remove partial register save logic
>   powerpc: move norestart trap flag to bit 0
>
>  arch/powerpc/include/asm/asm-prototypes.h |   2 -
>  arch/powerpc/include/asm/bug.h            |   4 +-
>  arch/powerpc/include/asm/interrupt.h      |  66 ++--
>  arch/powerpc/include/asm/ptrace.h         |  36 +-
>  arch/powerpc/kernel/align.c               |   6 -
>  arch/powerpc/kernel/entry_64.S            |  40 +-
>  arch/powerpc/kernel/exceptions-64e.S      | 425 ++--------------------
>  arch/powerpc/kernel/interrupt.c           |  22 +-
>  arch/powerpc/kernel/irq.c                 |  76 ----
>  arch/powerpc/kernel/process.c             |  12 -
>  arch/powerpc/kernel/ptrace/ptrace-view.c  |  21 --
>  arch/powerpc/kernel/ptrace/ptrace.c       |   2 -
>  arch/powerpc/kernel/ptrace/ptrace32.c     |   4 -
>  arch/powerpc/kernel/signal_32.c           |   3 -
>  arch/powerpc/kernel/signal_64.c           |   2 -
>  arch/powerpc/kernel/traps.c               |  14 +-
>  arch/powerpc/lib/sstep.c                  |   4 -
>  arch/powerpc/mm/book3s64/hash_utils.c     |  16 +-
>  arch/powerpc/mm/fault.c                   |  28 +-
>  arch/powerpc/xmon/xmon.c                  |  23 +-
>  20 files changed, 130 insertions(+), 676 deletions(-)
>
> -- 
> 2.23.0

^ permalink raw reply

* Re: [PATCH 1/1] hotplug-cpu.c: show 'last online CPU' error in dlpar_cpu_remove()
From: Daniel Henrique Barboza @ 2021-03-22 23:30 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
In-Reply-To: <87a6qz8juk.fsf@mpe.ellerman.id.au>



On 3/19/21 8:26 AM, Michael Ellerman wrote:
> Daniel Henrique Barboza <danielhb413@gmail.com> writes:
>> Ping
>>
>> On 3/5/21 2:38 PM, Daniel Henrique Barboza wrote:
>>> Of all the reasons that dlpar_cpu_remove() can fail, the 'last online
>>> CPU' is one that can be caused directly by the user offlining CPUs
>>> in a partition/virtual machine that has hotplugged CPUs. Trying to
>>> reclaim a hotplugged CPU can fail if the CPU is now the last online in
>>> the system. This is easily reproduced using QEMU [1].
> 
> Sorry, I saw this earlier and never got around to replying.

No problem. Thanks for the review!

> 
> I'm wondering if we neet to catch it earlier, ie. in
> dlpar_offline_cpu().
> 
> By the time we return to dlpar_cpu_remove() we've dropped the
> cpu_add_remove_lock (cpu_maps_update_done), so num_online_cpus() could
> change out from under us, meaning the num_online_cpus() == 1 check might
> trigger incorrectly (or vice versa).
> 
> Something like the patch below (completely untested :D)

Makes sense. I'll try it out to see if it works.

> 
> And writing that patch makes me wonder, is == 1 the right check?
> 
> In most cases we'll remove all but one thread of the core, but we'll
> fail on the last thread. Leaving that core effectively stuck in SMT1. Is
> that useful behaviour? Should we instead check at the start that we can
> remove all threads of the core without going to zero online CPUs?

I think it's ok to allow SMT1 cores, speaking from QEMU perspective.
QEMU does not have a "core hotunplug" operation where the whole core is
hotunplugged at once. The CPU hotplug/unplug operations are done as single
CPU thread add/removal. If the user wants to run 4 cores, all of them with
SMT1, QEMU will allow it.

Libvirt does not operate with the core granularity either - you can specify
the amount of vcpus the guest should run with, and Libvirt will send
hotplug/unplug requests to QEMU to match the desired value. It doesn't
bother with how many threads of a core were offlined or not.


Thanks,


DHB



> 
> cheers
> 
> 
> diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> index 12cbffd3c2e3..498c22331ac8 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> @@ -271,6 +271,12 @@ static int dlpar_offline_cpu(struct device_node *dn)
>   			if (!cpu_online(cpu))
>   				break;
>   
> +			if (num_online_cpus() == 1) {
> +				pr_warn("Unable to remove last online CPU %pOFn\n", dn);
> +				rc = EBUSY;
> +				goto out_unlock;
> +			}
> +
>   			cpu_maps_update_done();
>   			rc = device_offline(get_cpu_device(cpu));
>   			if (rc)
> @@ -283,6 +289,7 @@ static int dlpar_offline_cpu(struct device_node *dn)
>   				thread);
>   		}
>   	}
> +out_unlock:
>   	cpu_maps_update_done();
>   
>   out:
> 

^ permalink raw reply

* Re: [PATCH next v1 2/3] printk: remove safe buffers
From: John Ogness @ 2021-03-22 21:58 UTC (permalink / raw)
  To: Petr Mladek
  Cc: Rafael Aquini, Alexey Kardashevskiy, Paul Mackerras, Tiezhu Yang,
	Peter Zijlstra, Yue Hu, Jordan Niethe, Kees Cook,
	Paul E. McKenney, Alistair Popple, Guilherme G. Piccoli,
	Nicholas Piggin, Steven Rostedt, Thomas Gleixner, kexec,
	linux-kernel, Sergey Senozhatsky, Eric Biederman, Andrew Morton,
	linuxppc-dev, Cédric Le Goater
In-Reply-To: <YFjbwxhZNnC52aSP@alley>

On 2021-03-22, Petr Mladek <pmladek@suse.com> wrote:
> On Mon 2021-03-22 12:16:15, John Ogness wrote:
>> On 2021-03-21, Sergey Senozhatsky <senozhatsky@chromium.org> wrote:
>> >> @@ -369,7 +70,10 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
>> >>  	 * Use the main logbuf even in NMI. But avoid calling console
>> >>  	 * drivers that might have their own locks.
>> >>  	 */
>> >> -	if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK)) {
>> >> +	if (this_cpu_read(printk_context) &
>> >> +	    (PRINTK_NMI_DIRECT_CONTEXT_MASK |
>> >> +	     PRINTK_NMI_CONTEXT_MASK |
>> >> +	     PRINTK_SAFE_CONTEXT_MASK)) {
>> >
>> > Do we need printk_nmi_direct_enter/exit() and
>> > PRINTK_NMI_DIRECT_CONTEXT_MASK?  Seems like all printk_safe() paths
>> > are now DIRECT - we store messages to the prb, but don't call console
>> > drivers.
>>
>> I was planning on waiting until the kthreads are introduced, in which
>> case printk_safe.c is completely removed.
>
> You want to keep printk_safe() context because it prevents calling
> consoles even in normal context. Namely, it prevents deadlock by
> recursively taking, for example, sem->lock in console_lock() or
> console_owner_lock in console_trylock_spinning(). Am I right?

Correct.

>> But I suppose I could switch
>> the 1 printk_nmi_direct_enter() user to printk_nmi_enter() so that
>> PRINTK_NMI_DIRECT_CONTEXT_MASK can be removed now. I would do this in a
>> 4th patch of the series.
>
> Yes, please unify the PRINTK_NMI_CONTEXT. One is enough.

Agreed. (But I'll go even further. See below.)

> I wonder if it would make sense to go even further at this stage.
> There will still be 4 contexts that modify the printk behavior after
> this patchset:
>
>   + printk_count set by printk_enter()/exit()
>       + prevents: infinite recursion
>       + context: any context
>       + action: skips entire printk at 3rd recursion level
>
>   + prink_context set by printk_safe_enter()/exit()
>       + prevents: dead lock caused by recursion into some
> 		console code in any context
>       + context: any
>       + action: skips console call at 1st recursion level

Technically, at this point printk_safe_enter() behavior is identical to
printk_nmi_enter(). Namely, prevent any recursive printk calls from
calling into the console code.

>   + printk_context set by printk_nmi_enter()/exit()
>       + prevents: dead lock caused by any console lock recursion
>       + context: NMI
>       + action: skips console calls at 0th recursion level
>
>   + kdb_trap_printk
>       + redirects printk() to kdb_printk() in kdb context
>
>
> What is possible?
>
> 1. We could get rid of printk_nmi_enter()/exit() and
>    PRINTK_NMI_CONTEXT completely already now. It is enough
>    to check in_nmi() in printk_func().
>
>    printk_nmi_enter() was added by the commit 42a0bb3f71383b457a7db362
>    ("printk/nmi: generic solution for safe printk in NMI"). It was
>    really needed to modify @printk_func pointer.
>
>    We did not remove it later when printk_function became a real
>    function. The idea was to track all printk contexts in a single
>    variable. But we never added kdb context.
>
>    It might make sense to remove it now. Peter Zijstra would be happy.
>    There already were some churns with tracking printk_context in NMI.
>    For example, see
>    https://lore.kernel.org/r/20200219150744.428764577@infradead.org
>
>    IMHO, it does not make sense to wait until the entire console-stuff
>    rework is done in this case.

Agreed. in_nmi() within vprintk_emit() is enough to detect if the
console code should be skipped:

    if (!in_sched && !in_nmi()) {
        ...
    }

> 2. I thought about unifying printk_safe_enter()/exit() and
>    printk_enter()/exit(). They both count recursion with
>    IRQs disabled, have similar name. But they are used
>    different way.
>
>    But better might be to rename printk_safe_enter()/exit() to
>    console_enter()/exit() or to printk_deferred_enter()/exit().
>    It would make more clear what it does now. And it might help
>    to better distinguish it from the new printk_enter()/exit().
>
>    This patchset actually splits the original printk_safe()
>    functionality into two:
>
>        + printk_count prevents infinite recursion
>        + printk_deferred_enter() deffers console handling.
>
>    I am not sure if it is worth it. But it might help people (even me)
>    when digging into the printk history. Different name will help to
>    understand the functionality at the given time.

I am also not sure if it is worth the extra "noise" just to give the
function a more appropriate name. The plan is to remove it completely
soon anyway. My vote is to leave the name as it is.

But I am willing to do the rename in an addtional patch if you
want. printk_deferred_enter() sounds fine to me. Please confirm if you
want me to do this.

John Ogness

^ permalink raw reply

* [PATCH 1/1] powerpc/iommu: Enable remaining IOMMU Pagesizes present in LoPAR
From: Leonardo Bras @ 2021-03-22 19:09 UTC (permalink / raw)
  To: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras,
	Christophe Leroy, Leonardo Bras, Joel Stanley,
	Alexey Kardashevskiy, brking
  Cc: linuxppc-dev, linux-kernel

According to LoPAR, ibm,query-pe-dma-window output named "IO Page Sizes"
will let the OS know all possible pagesizes that can be used for creating a
new DDW.

Currently Linux will only try using 3 of the 8 available options:
4K, 64K and 16M. According to LoPAR, Hypervisor may also offer 32M, 64M,
128M, 256M and 16G.

Enabling bigger pages would be interesting for direct mapping systems
with a lot of RAM, while using less TCE entries.

Signed-off-by: Leonardo Bras <leobras.c@gmail.com>
---
 arch/powerpc/include/asm/iommu.h       |  8 ++++++++
 arch/powerpc/platforms/pseries/iommu.c | 28 +++++++++++++++++++-------
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index deef7c94d7b6..c170048b7a1b 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -19,6 +19,14 @@
 #include <asm/pci-bridge.h>
 #include <asm/asm-const.h>
 
+#define IOMMU_PAGE_SHIFT_16G	34
+#define IOMMU_PAGE_SHIFT_256M	28
+#define IOMMU_PAGE_SHIFT_128M	27
+#define IOMMU_PAGE_SHIFT_64M	26
+#define IOMMU_PAGE_SHIFT_32M	25
+#define IOMMU_PAGE_SHIFT_16M	24
+#define IOMMU_PAGE_SHIFT_64K	16
+
 #define IOMMU_PAGE_SHIFT_4K      12
 #define IOMMU_PAGE_SIZE_4K       (ASM_CONST(1) << IOMMU_PAGE_SHIFT_4K)
 #define IOMMU_PAGE_MASK_4K       (~((1 << IOMMU_PAGE_SHIFT_4K) - 1))
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 9fc5217f0c8e..02958e80aa91 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -1099,6 +1099,24 @@ static void reset_dma_window(struct pci_dev *dev, struct device_node *par_dn)
 			 ret);
 }
 
+/* Returns page shift based on "IO Page Sizes" output at ibm,query-pe-dma-window. SeeL LoPAR */
+static int iommu_get_page_shift(u32 query_page_size)
+{
+	const int shift[] = {IOMMU_PAGE_SHIFT_4K,   IOMMU_PAGE_SHIFT_64K,  IOMMU_PAGE_SHIFT_16M,
+			     IOMMU_PAGE_SHIFT_32M,  IOMMU_PAGE_SHIFT_64M,  IOMMU_PAGE_SHIFT_128M,
+			     IOMMU_PAGE_SHIFT_256M, IOMMU_PAGE_SHIFT_16G};
+	int i = ARRAY_SIZE(shift) - 1;
+
+	/* Looks for the largest page size supported */
+	for (; i >= 0; i--) {
+		if (query_page_size & (1 << i))
+			return shift[i];
+	}
+
+	/* No valid page size found. */
+	return 0;
+}
+
 /*
  * If the PE supports dynamic dma windows, and there is space for a table
  * that can map all pages in a linear offset, then setup such a table,
@@ -1206,13 +1224,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
 			goto out_failed;
 		}
 	}
-	if (query.page_size & 4) {
-		page_shift = 24; /* 16MB */
-	} else if (query.page_size & 2) {
-		page_shift = 16; /* 64kB */
-	} else if (query.page_size & 1) {
-		page_shift = 12; /* 4kB */
-	} else {
+
+	page_shift = iommu_get_page_shift(query.page_size);
+	if (!page_shift) {
 		dev_dbg(&dev->dev, "no supported direct page size in mask %x",
 			  query.page_size);
 		goto out_failed;
-- 
2.29.2


^ permalink raw reply related

* Re: [PATCH] powerpc: kernel: Trivial spelling fixes throughout the file head_fsl_booke.S
From: Randy Dunlap @ 2021-03-22 18:50 UTC (permalink / raw)
  To: Bhaskar Chowdhury, mpe, benh, paulus, rppt, akpm,
	christophe.leroy, oss, npiggin, linuxppc-dev, linux-kernel
In-Reply-To: <20210315033732.4173500-1-unixbhaskar@gmail.com>

On 3/14/21 8:37 PM, Bhaskar Chowdhury wrote:
> s/virutal/virtual/
> s/mismach/mismatch/
> 
> Signed-off-by: Bhaskar Chowdhury <unixbhaskar@gmail.com>

Acked-by: Randy Dunlap <rdunlap@infradead.org>

> ---
>  As Randy pointed out I was changing the predefined macro name,so, reverted
>  or leave it alone.
>  Michael,sorry to run down a cold weave in your spine with my stupdity,this is
>  okay.
> 
>  arch/powerpc/kernel/head_fsl_booke.S | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S
> index 3f4a40cccef5..a955403247f1 100644
> --- a/arch/powerpc/kernel/head_fsl_booke.S
> +++ b/arch/powerpc/kernel/head_fsl_booke.S
> @@ -113,7 +113,7 @@ _ENTRY(_start);
> 
>  1:
>  	/*
> -	 * We have the runtime (virutal) address of our base.
> +	 * We have the runtime (virtual) address of our base.
>  	 * We calculate our shift of offset from a 64M page.
>  	 * We could map the 64M page we belong to at PAGE_OFFSET and
>  	 * get going from there.
> @@ -497,7 +497,7 @@ END_BTB_FLUSH_SECTION
>  #endif
>  #endif
> 
> -	bne	2f			/* Bail if permission/valid mismach */
> +	bne	2f			/* Bail if permission/valid mismatch */
> 
>  	/* Jump to common tlb load */
>  	b	finish_tlb_load
> @@ -592,7 +592,7 @@ END_BTB_FLUSH_SECTION
>  #endif
>  #endif
> 
> -	bne	2f			/* Bail if permission mismach */
> +	bne	2f			/* Bail if permission mismatch */
> 
>  	/* Jump to common TLB load point */
>  	b	finish_tlb_load
> --
> 2.30.2
> 


-- 
~Randy


^ permalink raw reply

* Re: [PATCH] docs: powerpc: Fix a typo
From: Randy Dunlap @ 2021-03-22 18:49 UTC (permalink / raw)
  To: Bhaskar Chowdhury, mpe, benh, paulus, corbet, linuxppc-dev,
	linux-doc, linux-kernel
In-Reply-To: <20210322062237.2971314-1-unixbhaskar@gmail.com>

On 3/21/21 11:22 PM, Bhaskar Chowdhury wrote:
> 
> s/struture/structure/
> 
> Signed-off-by: Bhaskar Chowdhury <unixbhaskar@gmail.com>

Acked-by: Randy Dunlap <rdunlap@infradead.org>

> ---
>  Documentation/powerpc/firmware-assisted-dump.rst | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/powerpc/firmware-assisted-dump.rst b/Documentation/powerpc/firmware-assisted-dump.rst
> index 20ea8cdee0aa..6c0ae070ba67 100644
> --- a/Documentation/powerpc/firmware-assisted-dump.rst
> +++ b/Documentation/powerpc/firmware-assisted-dump.rst
> @@ -171,7 +171,7 @@ that were present in CMA region::
>                                             (meta area)    |
>                                                            |
>                                                            |
> -                      Metadata: This area holds a metadata struture whose
> +                      Metadata: This area holds a metadata structure whose
>                        address is registered with f/w and retrieved in the
>                        second kernel after crash, on platforms that support
>                        tags (OPAL). Having such structure with info needed
> --
> 2.31.0
> 


-- 
~Randy


^ permalink raw reply

* Re: [PATCH] powerpc/asm: Fix a typo
From: Randy Dunlap @ 2021-03-22 18:45 UTC (permalink / raw)
  To: Bhaskar Chowdhury, mpe, benh, paulus, linuxppc-dev, linux-kernel
In-Reply-To: <20210322113254.2081445-1-unixbhaskar@gmail.com>

On 3/22/21 4:32 AM, Bhaskar Chowdhury wrote:
> 
> s/poiner/pointer/
> 
> Signed-off-by: Bhaskar Chowdhury <unixbhaskar@gmail.com>

Acked-by: Randy Dunlap <rdunlap@infradead.org>

However, it would be a GOOD THING to collect multiple similar
patches that are in e.g. arch/powerpc/ and send them as one patch
instead of many little patches.


> ---
>  arch/powerpc/include/asm/cpm2.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
> index 2211b934ecb4..bda45788cfcc 100644
> --- a/arch/powerpc/include/asm/cpm2.h
> +++ b/arch/powerpc/include/asm/cpm2.h
> @@ -594,7 +594,7 @@ typedef struct fcc_enet {
>  	uint	fen_p256c;	/* Total packets 256 < bytes <= 511 */
>  	uint	fen_p512c;	/* Total packets 512 < bytes <= 1023 */
>  	uint	fen_p1024c;	/* Total packets 1024 < bytes <= 1518 */
> -	uint	fen_cambuf;	/* Internal CAM buffer poiner */
> +	uint	fen_cambuf;	/* Internal CAM buffer pointer */
>  	ushort	fen_rfthr;	/* Received frames threshold */
>  	ushort	fen_rfcnt;	/* Received frames count */
>  } fcc_enet_t;
> --


-- 
~Randy


^ permalink raw reply

* Re: [PATCH] powerpc/8xx: Fix a typo
From: Randy Dunlap @ 2021-03-22 18:43 UTC (permalink / raw)
  To: Bhaskar Chowdhury, mpe, benh, paulus, christophe.leroy, rppt,
	npiggin, akpm, linuxppc-dev, linux-kernel
In-Reply-To: <20210322120314.2085782-1-unixbhaskar@gmail.com>

On 3/22/21 5:03 AM, Bhaskar Chowdhury wrote:
> 
> s/poiners/pointers/
> 
> Signed-off-by: Bhaskar Chowdhury <unixbhaskar@gmail.com>

Acked-by: Randy Dunlap <rdunlap@infradead.org>


> ---
>  arch/powerpc/kernel/head_8xx.S | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S
> index 46dff3f9c31f..8a85a984609a 100644
> --- a/arch/powerpc/kernel/head_8xx.S
> +++ b/arch/powerpc/kernel/head_8xx.S
> @@ -819,7 +819,7 @@ EXPORT_SYMBOL(empty_zero_page)
>  swapper_pg_dir:
>  	.space	PGD_TABLE_SIZE
> 
> -/* Room for two PTE table poiners, usually the kernel and current user
> +/* Room for two PTE table pointers, usually the kernel and current user
>   * pointer to their respective root page table (pgdir).
>   */
>  	.globl	abatron_pteptrs
> --


-- 
~Randy


^ permalink raw reply

* Re: [PATCH v3 19/41] KVM: PPC: Book3S HV P9: Stop handling hcalls in real-mode in the P9 path
From: Nicholas Piggin @ 2021-03-22 18:22 UTC (permalink / raw)
  To: Alexey Kardashevskiy, Cédric Le Goater, kvm-ppc; +Cc: linuxppc-dev
In-Reply-To: <cc1660a7-e81e-b7b3-a841-35fb77fb571b@kaod.org>

Excerpts from Cédric Le Goater's message of March 23, 2021 2:01 am:
> On 3/22/21 2:15 PM, Nicholas Piggin wrote:
>> Excerpts from Alexey Kardashevskiy's message of March 22, 2021 5:30 pm:
>>>
>>>
>>> On 06/03/2021 02:06, Nicholas Piggin wrote:
>>>> In the interest of minimising the amount of code that is run in>>> "real-mode", don't handle hcalls in real mode in the P9 path.
>>>>
>>>> POWER8 and earlier are much more expensive to exit from HV real mode
>>>> and switch to host mode, because on those processors HV interrupts get
>>>> to the hypervisor with the MMU off, and the other threads in the core
>>>> need to be pulled out of the guest, and SLBs all need to be saved,
>>>> ERATs invalidated, and host SLB reloaded before the MMU is re-enabled
>>>> in host mode. Hash guests also require a lot of hcalls to run. The
>>>> XICS interrupt controller requires hcalls to run.
>>>>
>>>> By contrast, POWER9 has independent thread switching, and in radix mode
>>>> the hypervisor is already in a host virtual memory mode when the HV
>>>> interrupt is taken. Radix + xive guests don't need hcalls to handle
>>>> interrupts or manage translations.
> 
> Do we need to handle the host-is-a-P9-without-xive case ?

I'm not sure really. Is there an intention for OPAL to be able to 
provide a fallback layer in the worst case? Maybe microwatt grows
HV capability before XIVE?

> 
>>>> So it's much less important to handle hcalls in real mode in P9.
>>>
>>> So acde25726bc6034b (which added if(kvm_is_radix(vcpu->kvm))return 
>>> H_TOO_HARD) can be reverted, pretty much?
>> 
>> Yes. Although that calls attention to the fact I missed doing
>> a P9 h_random handler in this patch. I'll fix that, then I think
>> acde2572 could be reverted entirely.
>> 
>> [...]
>> 
>>>>   	} else {
>>>>   		kvmppc_xive_push_vcpu(vcpu);
>>>>   		trap = kvmhv_load_hv_regs_and_go(vcpu, time_limit, lpcr);
>>>> -		kvmppc_xive_pull_vcpu(vcpu);
>>>> +		/* H_CEDE has to be handled now, not later */
>>>> +		/* XICS hcalls must be handled before xive is pulled */
>>>> +		if (trap == BOOK3S_INTERRUPT_SYSCALL &&
>>>> +		    !(vcpu->arch.shregs.msr & MSR_PR)) {
>>>> +			unsigned long req = kvmppc_get_gpr(vcpu, 3);
>>>>   
>>>> +			if (req == H_CEDE) {
>>>> +				kvmppc_cede(vcpu);
>>>> +				kvmppc_xive_cede_vcpu(vcpu); /* may un-cede */
>>>> +				kvmppc_set_gpr(vcpu, 3, 0);
>>>> +				trap = 0;
>>>> +			}
>>>> +			if (req == H_EOI || req == H_CPPR ||
>>>
>>> else if (req == H_EOI ... ?
>> 
>> Hummm, sure.
> 
> you could integrate the H_CEDE in the switch statement below.

Below is in a different file just for the emulation calls.

>> 
>> [...]
>> 
>>>> +void kvmppc_xive_cede_vcpu(struct kvm_vcpu *vcpu)
>>>> +{
>>>> +	void __iomem *esc_vaddr = (void __iomem *)vcpu->arch.xive_esc_vaddr;
>>>> +
>>>> +	if (!esc_vaddr)
>>>> +		return;
>>>> +
>>>> +	/* we are using XIVE with single escalation */
>>>> +
>>>> +	if (vcpu->arch.xive_esc_on) {
>>>> +		/*
>>>> +		 * If we still have a pending escalation, abort the cede,
>>>> +		 * and we must set PQ to 10 rather than 00 so that we don't
>>>> +		 * potentially end up with two entries for the escalation
>>>> +		 * interrupt in the XIVE interrupt queue.  In that case
>>>> +		 * we also don't want to set xive_esc_on to 1 here in
>>>> +		 * case we race with xive_esc_irq().
>>>> +		 */
>>>> +		vcpu->arch.ceded = 0;
>>>> +		/*
>>>> +		 * The escalation interrupts are special as we don't EOI them.
>>>> +		 * There is no need to use the load-after-store ordering offset
>>>> +		 * to set PQ to 10 as we won't use StoreEOI.
>>>> +		 */
>>>> +		__raw_readq(esc_vaddr + XIVE_ESB_SET_PQ_10);
>>>> +	} else {
>>>> +		vcpu->arch.xive_esc_on = true;
>>>> +		mb();
>>>> +		__raw_readq(esc_vaddr + XIVE_ESB_SET_PQ_00);
>>>> +	}
>>>> +	mb();
>>>
>>>
>>> Uff. Thanks for cut-n-pasting the comments, helped a lot to match this c 
>>> to that asm!
>> 
>> Glad it helped.
>>>> +}
> 
> I had to do the PowerNV models in QEMU to start understanding that stuff ... 
> 
>>>> +EXPORT_SYMBOL_GPL(kvmppc_xive_cede_vcpu);
>>>> +
>>>>   /*
>>>>    * This is a simple trigger for a generic XIVE IRQ. This must
>>>>    * only be called for interrupts that support a trigger page
>>>> @@ -2106,6 +2140,32 @@ static int kvmppc_xive_create(struct kvm_device *dev, u32 type)
>>>>   	return 0;
>>>>   }
>>>>   
>>>> +int kvmppc_xive_xics_hcall(struct kvm_vcpu *vcpu, u32 req)
>>>> +{
>>>> +	struct kvmppc_vcore *vc = vcpu->arch.vcore;
>>>
>>>
>>> Can a XIVE enabled guest issue these hcalls? Don't we want if 
>>> (!kvmppc_xics_enabled(vcpu)) and
>>>   if (xics_on_xive()) here, as kvmppc_rm_h_xirr() have? Some of these 
>>> hcalls do write to XIVE registers but some seem to change 
>>> kvmppc_xive_vcpu. Thanks,
>> 
>> Yes I think you're right, good catch. I'm not completely sure about all 
>> the xive and xics modes but a guest certainly can make any kind of hcall 
>> it likes and we have to sanity check it.
> 
> Yes. 
> 
>> We want to take the hcall here (in replacement of the real mode hcalls)
>> with the same condition. So it would be:
>> 
>>         if (!kvmppc_xics_enabled(vcpu))
>>                 return H_TOO_HARD;
> 
> Yes.
> 
> This test covers the case in which a vCPU does XICS hcalls without QEMU 
> having connected the vCPU to a XICS ICP. The ICP is the KVM XICS device 
> on P8 or XICS-on-XIVE on P9. It catches QEMU errors when the interrupt 
> mode is negotiated, we don't want the OS to do XICS hcalls after having 
> negotiated the XIVE interrupt mode. 

Okay.

> It's different for the XIVE hcalls (when running under XICS) because they 
> are all handled in QEMU. 

XIVE guest hcalls running on XICS host?

>>         if (!xics_on_xive())
>> 		return H_TOO_HARD;
> 
> I understand that this code is only called on P9 and with translation on.

Yes.

> On P9, we could have xics_on_xive() == 0 if XIVE is disabled at compile 
> time or with "xive=off" at boot time. But guests should be supported. 
> I don't see a reason to restrict the support even if these scenarios 
> are rather unusual if not very rare.
> 
> on P10, it's the same but since we don't have the XICS emulation layer 
> in OPAL, the host will be pretty useless. We don't care.
> 
> Since we are trying to handle hcalls, this is L0 and it can not be called 
> for nested guests, which would be another case of xics_on_xive() == 0. 
> We don't care either.

Okay so no xics_on_xive() test. I'll change that.

Thanks,
Nick

^ permalink raw reply

* Re: [PATCH v3 17/41] KVM: PPC: Book3S HV P9: implement kvmppc_xive_pull_vcpu in C
From: Nicholas Piggin @ 2021-03-22 18:13 UTC (permalink / raw)
  To: Cédric Le Goater, kvm-ppc; +Cc: linuxppc-dev
In-Reply-To: <11823cfb-3d10-8f2f-4caf-9b38a010ed31@kaod.org>

Excerpts from Cédric Le Goater's message of March 23, 2021 2:19 am:
> On 3/5/21 4:06 PM, Nicholas Piggin wrote:
>> This is more symmetric with kvmppc_xive_push_vcpu. The extra test in
>> the asm will go away in a later change.
>> 
>> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> 
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>> diff --git a/arch/powerpc/kvm/book3s_xive.c b/arch/powerpc/kvm/book3s_xive.c
>> index e7219b6f5f9a..52cdb9e2660a 100644
>> --- a/arch/powerpc/kvm/book3s_xive.c
>> +++ b/arch/powerpc/kvm/book3s_xive.c
>> @@ -127,6 +127,37 @@ void kvmppc_xive_push_vcpu(struct kvm_vcpu *vcpu)
>>  }
>>  EXPORT_SYMBOL_GPL(kvmppc_xive_push_vcpu);
>>  
>> +/*
>> + * Pull a vcpu's context from the XIVE on guest exit.
>> + * This assumes we are in virtual mode (MMU on)
> 
> should we add an assert on is_rm() ? 

I thought the same thing at first, but I think it should be okay.
kvmppc_xive_push_cpu does not have an assert, and in the next
patch the push and pull get moved out to where it is much clearer
to see the MMU is on.

Thanks,
Nick

^ permalink raw reply

* Re: [RFC PATCH 6/8] powerpc/mm/book3s64/hash: drop pre 2.06 tlbiel for clang
From: Nicholas Piggin @ 2021-03-22 18:11 UTC (permalink / raw)
  To: Christophe Leroy, Daniel Axtens, linuxppc-dev, llvmlinux
In-Reply-To: <7f7cafb5-e6c4-5015-5285-9f9ca25e52fb@csgroup.eu>

Excerpts from Christophe Leroy's message of March 23, 2021 2:49 am:
> 
> 
> Le 19/03/2021 à 03:01, Nicholas Piggin a écrit :
>> Excerpts from Daniel Axtens's message of February 25, 2021 1:10 pm:
>>> The llvm integrated assembler does not recognise the ISA 2.05 tlbiel
>>> version. Eventually do this more smartly.
>> 
>> The whole thing with TLBIE and TLBIEL in this file seems a bit too
>> clever. We should have PPC_TLBIE* macros for all of them.
> 
> I was expecting to drop PPC_* macros as much as possible taking into account the later binutils 
> support most of them (https://github.com/linuxppc/issues/issues/350). Was not expecting to go the 
> other direction.

The problem in this file is we generate 3 different tlbie and tlbiel
instructions with the same mnemonic corresponding to different ISA
versions.

This might actually be the one good place to use .machine to make sure 
the assembler generates the right thing. I'm not entirely sure it is
foolproof because some of the times the instruction variant is inferred
by the number of arguments it has yet arguments can be implicit. PPC_
define would be exactly explicit.

But if it can be made reasonably robust with .machine then I'd be okay
with that too.

Thanks,
Nick

^ permalink raw reply

* Re: [PATCH next v1 2/3] printk: remove safe buffers
From: Petr Mladek @ 2021-03-22 18:02 UTC (permalink / raw)
  To: John Ogness
  Cc: Rafael Aquini, Alexey Kardashevskiy, Paul Mackerras, Tiezhu Yang,
	Peter Zijlstra, Yue Hu, Jordan Niethe, Kees Cook,
	Paul E. McKenney, Alistair Popple, Guilherme G. Piccoli,
	Nicholas Piggin, Steven Rostedt, Thomas Gleixner, kexec,
	linux-kernel, Sergey Senozhatsky, Eric Biederman, Andrew Morton,
	linuxppc-dev, Cédric Le Goater
In-Reply-To: <87k0pzmoao.fsf@jogness.linutronix.de>

On Mon 2021-03-22 12:16:15, John Ogness wrote:
> On 2021-03-21, Sergey Senozhatsky <senozhatsky@chromium.org> wrote:
> >> @@ -369,7 +70,10 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
> >>  	 * Use the main logbuf even in NMI. But avoid calling console
> >>  	 * drivers that might have their own locks.
> >>  	 */
> >> -	if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK)) {
> >> +	if (this_cpu_read(printk_context) &
> >> +	    (PRINTK_NMI_DIRECT_CONTEXT_MASK |
> >> +	     PRINTK_NMI_CONTEXT_MASK |
> >> +	     PRINTK_SAFE_CONTEXT_MASK)) {
> >
> > Do we need printk_nmi_direct_enter/exit() and
> > PRINTK_NMI_DIRECT_CONTEXT_MASK?  Seems like all printk_safe() paths
> > are now DIRECT - we store messages to the prb, but don't call console
> > drivers.
>
> I was planning on waiting until the kthreads are introduced, in which
> case printk_safe.c is completely removed.

You want to keep printk_safe() context because it prevents calling
consoles even in normal context. Namely, it prevents deadlock by
recursively taking, for example, sem->lock in console_lock() or
console_owner_lock in console_trylock_spinning(). Am I right?


> But I suppose I could switch
> the 1 printk_nmi_direct_enter() user to printk_nmi_enter() so that
> PRINTK_NMI_DIRECT_CONTEXT_MASK can be removed now. I would do this in a
> 4th patch of the series.

Yes, please unify the PRINTK_NMI_CONTEXT. One is enough.

I wonder if it would make sense to go even further at this stage.
There will still be 4 contexts that modify the printk behavior after
this patchset:

  + printk_count set by printk_enter()/exit()
      + prevents: infinite recursion
      + context: any context
      + action: skips entire printk at 3rd recursion level

  + prink_context set by printk_safe_enter()/exit()
      + prevents: dead lock caused by recursion into some
		console code in any context
      + context: any
      + action: skips console call at 1st recursion level

  + printk_context set by printk_nmi_enter()/exit()
      + prevents: dead lock caused by any console lock recursion
      + context: NMI
      + action: skips console calls at 0th recursion level

  + kdb_trap_printk
      + redirects printk() to kdb_printk() in kdb context


What is possible?

1. We could get rid of printk_nmi_enter()/exit() and
   PRINTK_NMI_CONTEXT completely already now. It is enough
   to check in_nmi() in printk_func().

   printk_nmi_enter() was added by the commit 42a0bb3f71383b457a7db362
   ("printk/nmi: generic solution for safe printk in NMI"). It was
   really needed to modify @printk_func pointer.

   We did not remove it later when printk_function became a real
   function. The idea was to track all printk contexts in a single
   variable. But we never added kdb context.

   It might make sense to remove it now. Peter Zijstra would be happy.
   There already were some churns with tracking printk_context in NMI.
   For example, see
   https://lore.kernel.org/r/20200219150744.428764577@infradead.org

   IMHO, it does not make sense to wait until the entire console-stuff
   rework is done in this case.


2. I thought about unifying printk_safe_enter()/exit() and
   printk_enter()/exit(). They both count recursion with
   IRQs disabled, have similar name. But they are used
   different way.

   But better might be to rename printk_safe_enter()/exit() to
   console_enter()/exit() or to printk_deferred_enter()/exit().
   It would make more clear what it does now. And it might help
   to better distinguish it from the new printk_enter()/exit().

   This patchset actually splits the original printk_safe()
   functionality into two:

       + printk_count prevents infinite recursion
       + printk_deferred_enter() deffers console handling.

   I am not sure if it is worth it. But it might help people (even me)
   when digging into the printk history. Different name will help to
   understand the functionality at the given time.


What do you think, please?

Best Regards,
Petr

^ permalink raw reply

* Re: [PATCH v2 0/8] Implement EBPF on powerpc32
From: Andrii Nakryiko @ 2021-03-22 17:53 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: Song Liu, Daniel Borkmann, john fastabend, Andrii Nakryiko,
	Alexei Starovoitov, naveen.n.rao, Networking, Paul Mackerras,
	sandipan, KP Singh, Yonghong Song, bpf, linuxppc-dev, Martin Lau,
	open list
In-Reply-To: <cover.1616430991.git.christophe.leroy@csgroup.eu>

On Mon, Mar 22, 2021 at 9:37 AM Christophe Leroy
<christophe.leroy@csgroup.eu> wrote:
>
> This series implements extended BPF on powerpc32. For the implementation
> details, see the patch before the last.
>
> The following operations are not implemented:
>
>                 case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */
>                 case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */
>                 case BPF_STX | BPF_XADD | BPF_DW: /* *(u64 *)(dst + off) += src */
>
> The following operations are only implemented for power of two constants:
>
>                 case BPF_ALU64 | BPF_MOD | BPF_K: /* dst %= imm */
>                 case BPF_ALU64 | BPF_DIV | BPF_K: /* dst /= imm */
>
> Below are the results on a powerpc 885:
> - with the patch, with and without bpf_jit_enable
> - without the patch, with bpf_jit_enable (ie with CBPF)
>
> With the patch, with bpf_jit_enable = 1 :
>
> [   60.826529] test_bpf: Summary: 378 PASSED, 0 FAILED, [354/366 JIT'ed]
> [   60.832505] test_bpf: test_skb_segment: Summary: 2 PASSED, 0 FAILED
>
> With the patch, with bpf_jit_enable = 0 :
>
> [   75.186337] test_bpf: Summary: 378 PASSED, 0 FAILED, [0/366 JIT'ed]
> [   75.192325] test_bpf: test_skb_segment: Summary: 2 PASSED, 0 FAILED
>
> Without the patch, with bpf_jit_enable = 1 :
>
> [  186.112429] test_bpf: Summary: 371 PASSED, 7 FAILED, [119/366 JIT'ed]
>
> Couldn't run test_progs because it doesn't build (clang 11 crashes during the build).

Can you please try checking out the latest clang from sources and use
that one instead?

>
> Changes in v2:
> - Simplify 16 bits swap
> - Rework tailcall, use stack for tailcall counter
> - Fix handling of BPF_REG_FP:
>   - must be handler like any other register allthough only the lower 32 bits part is used as a pointer.
>   - r18 was TMP_REG, r17/r18 become de BPF_REG_FP
>   - r31 was BPF_REG_FP, it is now TMP_REG
> - removed bpf_jit32.h
> - Reorder register allocation dynamically to use the volatile registers as much as possible when not doing function calls (last patch - new)
>
> Christophe Leroy (8):
>   powerpc/bpf: Remove classical BPF support for PPC32
>   powerpc/bpf: Change register numbering for bpf_set/is_seen_register()
>   powerpc/bpf: Move common helpers into bpf_jit.h
>   powerpc/bpf: Move common functions into bpf_jit_comp.c
>   powerpc/bpf: Change values of SEEN_ flags
>   powerpc/asm: Add some opcodes in asm/ppc-opcode.h for PPC32 eBPF
>   powerpc/bpf: Implement extended BPF on PPC32
>   powerpc/bpf: Reallocate BPF registers to volatile registers when
>     possible on PPC32
>
>  Documentation/admin-guide/sysctl/net.rst |    2 +-
>  arch/powerpc/Kconfig                     |    3 +-
>  arch/powerpc/include/asm/ppc-opcode.h    |   12 +
>  arch/powerpc/net/Makefile                |    6 +-
>  arch/powerpc/net/bpf_jit.h               |   61 ++
>  arch/powerpc/net/bpf_jit32.h             |  139 ---
>  arch/powerpc/net/bpf_jit64.h             |   21 +-
>  arch/powerpc/net/bpf_jit_asm.S           |  226 -----
>  arch/powerpc/net/bpf_jit_comp.c          |  782 ++++-----------
>  arch/powerpc/net/bpf_jit_comp32.c        | 1095 ++++++++++++++++++++++
>  arch/powerpc/net/bpf_jit_comp64.c        |  295 +-----
>  11 files changed, 1372 insertions(+), 1270 deletions(-)
>  delete mode 100644 arch/powerpc/net/bpf_jit32.h
>  delete mode 100644 arch/powerpc/net/bpf_jit_asm.S
>  create mode 100644 arch/powerpc/net/bpf_jit_comp32.c
>
> --
> 2.25.0
>

^ permalink raw reply

* Re: [PATCH 1/2] vfio/pci: remove vfio_pci_nvlink2
From: Alex Williamson @ 2021-03-22 17:46 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jason Gunthorpe, kvm, David Airlie, linux-kernel, dri-devel,
	Alexey Kardashevskiy, Paul Mackerras, Daniel Vetter,
	Greg Kroah-Hartman, linux-api, linuxppc-dev
In-Reply-To: <20210322150155.797882-2-hch@lst.de>

On Mon, 22 Mar 2021 16:01:54 +0100
Christoph Hellwig <hch@lst.de> wrote:
> diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
> index 8ce36c1d53ca11..db7e782419d5d9 100644
> --- a/include/uapi/linux/vfio.h
> +++ b/include/uapi/linux/vfio.h
> @@ -332,19 +332,6 @@ struct vfio_region_info_cap_type {
>  #define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG	(2)
>  #define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG	(3)
>  
> -/* 10de vendor PCI sub-types */
> -/*
> - * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space.
> - */
> -#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM	(1)
> -
> -/* 1014 vendor PCI sub-types */
> -/*
> - * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU
> - * to do TLB invalidation on a GPU.
> - */
> -#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD	(1)
> -
>  /* sub-types for VFIO_REGION_TYPE_GFX */
>  #define VFIO_REGION_SUBTYPE_GFX_EDID            (1)
>  
> @@ -637,33 +624,6 @@ struct vfio_device_migration_info {
>   */
>  #define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE	3
>  
> -/*
> - * Capability with compressed real address (aka SSA - small system address)
> - * where GPU RAM is mapped on a system bus. Used by a GPU for DMA routing
> - * and by the userspace to associate a NVLink bridge with a GPU.
> - */
> -#define VFIO_REGION_INFO_CAP_NVLINK2_SSATGT	4
> -
> -struct vfio_region_info_cap_nvlink2_ssatgt {
> -	struct vfio_info_cap_header header;
> -	__u64 tgt;
> -};
> -
> -/*
> - * Capability with an NVLink link speed. The value is read by
> - * the NVlink2 bridge driver from the bridge's "ibm,nvlink-speed"
> - * property in the device tree. The value is fixed in the hardware
> - * and failing to provide the correct value results in the link
> - * not working with no indication from the driver why.
> - */
> -#define VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD	5
> -
> -struct vfio_region_info_cap_nvlink2_lnkspd {
> -	struct vfio_info_cap_header header;
> -	__u32 link_speed;
> -	__u32 __pad;
> -};
> -
>  /**
>   * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
>   *				    struct vfio_irq_info)

I'll leave any attempt to defend keeping this code to Alexey, but
minimally these region sub-types and capability IDs should probably be
reserved to avoid breaking whatever userspace might exist to consume
these.  Our ID space is sufficiently large that we don't need to
recycle them any time soon.  Thanks,

Alex


^ permalink raw reply

* Re: [PATCH] PCI: layerscape: Correct syntax by changing comma to semicolon
From: Lorenzo Pieralisi @ 2021-03-22 17:25 UTC (permalink / raw)
  To: Bjorn Helgaas, Krzysztof Wilczyński
  Cc: Rob Herring, Lorenzo Pieralisi, Minghuan Lian, linux-pci,
	Zheng Yongjun, Mingkai Hu, Roy Zang, linuxppc-dev,
	linux-arm-kernel
In-Reply-To: <20210311033745.1547044-1-kw@linux.com>

On Thu, 11 Mar 2021 03:37:45 +0000, Krzysztof Wilczyński wrote:
> Replace command with a semicolon to correct syntax and to prevent
> potential unspecified behaviour and/or unintended side effects.
> 
> Related:
>   https://lore.kernel.org/linux-pci/20201216131944.14990-1-zhengyongjun3@huawei.com/

Applied to pci/layerscape, thanks!

[1/1] PCI: layerscape: Correct syntax by changing comma to semicolon
      https://git.kernel.org/lpieralisi/pci/c/1b7996a528

Thanks,
Lorenzo

^ permalink raw reply

* Re: [PATCH 02/10] ARM: disable CONFIG_IDE in footbridge_defconfig
From: Russell King - ARM Linux admin @ 2021-03-22 17:03 UTC (permalink / raw)
  To: John Paul Adrian Glaubitz
  Cc: Jens Axboe, Thomas Bogendoerfer, linux-doc, linux-kernel,
	David S. Miller, linux-ide, linux-m68k, Ivan Kokshaysky,
	linux-alpha, Geert Uytterhoeven, Matt Turner, linux-mips,
	linuxppc-dev, Christoph Hellwig, linux-arm-kernel,
	Richard Henderson
In-Reply-To: <224b110e-7c42-4e19-800e-e0fa23d3bf7f@physik.fu-berlin.de>

On Mon, Mar 22, 2021 at 05:09:13PM +0100, John Paul Adrian Glaubitz wrote:
> On 3/22/21 4:15 PM, Russell King - ARM Linux admin wrote:
> > I'm quite surprised that the CY82C693 even works on Alpha - I've
> > asked for a lspci for that last week but nothing has yet been
> > forthcoming from whoever responded to your patch for Alpha - so I
> > can't compare what I'm seeing with what's happening with Alpha.
> 
> Here is lspci on my DEC Alpha XP-1000:
> 
> root@tsunami:~> lspci
> 0000:00:07.0 ISA bridge: Contaq Microsystems 82c693
> 0000:00:07.1 IDE interface: Contaq Microsystems 82c693
> 0000:00:07.2 IDE interface: Contaq Microsystems 82c693
> 0000:00:07.3 USB controller: Contaq Microsystems 82c693
> 0000:00:0d.0 VGA compatible controller: Texas Instruments TVP4020 [Permedia 2] (rev 01)
> 0001:01:03.0 Ethernet controller: Digital Equipment Corporation DECchip 21142/43 (rev 41)
> 0001:01:06.0 SCSI storage controller: QLogic Corp. ISP1020 Fast-wide SCSI (rev 06)
> 0001:01:08.0 PCI bridge: Digital Equipment Corporation DECchip 21152 (rev 03)
> 0001:02:09.0 Ethernet controller: Intel Corporation 82541PI Gigabit Ethernet Controller (rev 05)
> root@tsunami:~>

This is no good. What I asked last Thursday was:

"Could you send me the output of lspci -vvx -s 7.1 and lspci -vvx -s 7.2
please?"

so I can see the resources the kernel is using and a dump of the PCI
config space to see what the hardware is using.

Thanks.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* Re: [PATCH 02/10] ARM: disable CONFIG_IDE in footbridge_defconfig
From: Russell King - ARM Linux admin @ 2021-03-22 16:59 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jens Axboe, Thomas Bogendoerfer, linux-doc, linux-kernel,
	linux-mips, linux-ide, linux-m68k, Ivan Kokshaysky, linux-alpha,
	Geert Uytterhoeven, Matt Turner, linuxppc-dev, David S. Miller,
	linux-arm-kernel, Richard Henderson
In-Reply-To: <20210322151823.GA2764@lst.de>

On Mon, Mar 22, 2021 at 04:18:23PM +0100, Christoph Hellwig wrote:
> On Mon, Mar 22, 2021 at 03:15:03PM +0000, Russell King - ARM Linux admin wrote:
> > It gets worse than that though - due to a change to remove
> > pcibios_min_io from the generic code, moving it into the ARM
> > architecture code, this has caused a regression that prevents the
> > legacy resources being registered against the bus resource. So even
> > if they are there, they cause probe failures. I haven't found a
> > reasonable way to solve this yet, but until there is, there is no
> > way that the PATA driver can be used as the "legacy mode" support
> > is effectively done via the PCI code assigning virtual IO port
> > resources.
> > 
> > I'm quite surprised that the CY82C693 even works on Alpha - I've
> > asked for a lspci for that last week but nothing has yet been
> > forthcoming from whoever responded to your patch for Alpha - so I
> > can't compare what I'm seeing with what's happening with Alpha.
> 
> That sounds like something we could fix with a quirk for function 2
> in the PCI resource assignment code.  Can you show what vendor and
> device ID function 2 has so that I could try to come up with one?

I already have a quirk in arch/arm/kernel/bios32.c for this - but it
is no longer sufficient due to changes in the PCI layer, where much
of this is documented.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* Re: [RFC PATCH 6/8] powerpc/mm/book3s64/hash: drop pre 2.06 tlbiel for clang
From: Christophe Leroy @ 2021-03-22 16:49 UTC (permalink / raw)
  To: Nicholas Piggin, Daniel Axtens, linuxppc-dev, llvmlinux
In-Reply-To: <1616118111.i881ydq4h2.astroid@bobo.none>



Le 19/03/2021 à 03:01, Nicholas Piggin a écrit :
> Excerpts from Daniel Axtens's message of February 25, 2021 1:10 pm:
>> The llvm integrated assembler does not recognise the ISA 2.05 tlbiel
>> version. Eventually do this more smartly.
> 
> The whole thing with TLBIE and TLBIEL in this file seems a bit too
> clever. We should have PPC_TLBIE* macros for all of them.

I was expecting to drop PPC_* macros as much as possible taking into account the later binutils 
support most of them (https://github.com/linuxppc/issues/issues/350). Was not expecting to go the 
other direction.

See following series for an exemple of why we would want to get rid of PPC_* macros.

https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=231583&state=*

Christophe

> 
> Thanks,
> Nick
> 
>>
>> Signed-off-by: Daniel Axtens <dja@axtens.net>
>> ---
>>   arch/powerpc/mm/book3s64/hash_native.c | 10 ++++++++++
>>   1 file changed, 10 insertions(+)
>>
>> diff --git a/arch/powerpc/mm/book3s64/hash_native.c b/arch/powerpc/mm/book3s64/hash_native.c
>> index 52e170bd95ae..c5937f69a452 100644
>> --- a/arch/powerpc/mm/book3s64/hash_native.c
>> +++ b/arch/powerpc/mm/book3s64/hash_native.c
>> @@ -267,9 +267,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
>>   		va |= ssize << 8;
>>   		sllp = get_sllp_encoding(apsize);
>>   		va |= sllp << 5;
>> +#if 0
>>   		asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,0", %1)
>>   			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
>>   			     : "memory");
>> +#endif
>> +		asm volatile("tlbiel %0"
>> +			     : : "r" (va)
>> +			     : "memory");
>>   		break;
>>   	default:
>>   		/* We need 14 to 14 + i bits of va */
>> @@ -286,9 +291,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
>>   		 */
>>   		va |= (vpn & 0xfe);
>>   		va |= 1; /* L */
>> +#if 0
>>   		asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,1", %1)
>>   			     : : "r" (va), "i" (CPU_FTR_ARCH_206)
>>   			     : "memory");
>> +#endif
>> +		asm volatile("tlbiel %0"
>> +			     : : "r" (va)
>> +			     : "memory");
>>   		break;
>>   	}
>>   	trace_tlbie(0, 1, va, 0, 0, 0, 0);
>> -- 
>> 2.27.0
>>
>>

^ permalink raw reply

* [PATCH v2 8/8] powerpc/bpf: Reallocate BPF registers to volatile registers when possible on PPC32
From: Christophe Leroy @ 2021-03-22 16:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ast,
	daniel, andrii, kafai, songliubraving, yhs, john.fastabend,
	kpsingh, naveen.n.rao, sandipan
  Cc: netdev, bpf, linuxppc-dev, linux-kernel
In-Reply-To: <cover.1616430991.git.christophe.leroy@csgroup.eu>

When the BPF routine doesn't call any function, the non volatile
registers can be reallocated to volatile registers in order to
avoid having to save them/restore on the stack.

Before this patch, the test #359 ADD default X is:

   0:	7c 64 1b 78 	mr      r4,r3
   4:	38 60 00 00 	li      r3,0
   8:	94 21 ff b0 	stwu    r1,-80(r1)
   c:	60 00 00 00 	nop
  10:	92 e1 00 2c 	stw     r23,44(r1)
  14:	93 01 00 30 	stw     r24,48(r1)
  18:	93 21 00 34 	stw     r25,52(r1)
  1c:	93 41 00 38 	stw     r26,56(r1)
  20:	39 80 00 00 	li      r12,0
  24:	39 60 00 00 	li      r11,0
  28:	3b 40 00 00 	li      r26,0
  2c:	3b 20 00 00 	li      r25,0
  30:	7c 98 23 78 	mr      r24,r4
  34:	7c 77 1b 78 	mr      r23,r3
  38:	39 80 00 42 	li      r12,66
  3c:	39 60 00 00 	li      r11,0
  40:	7d 8c d2 14 	add     r12,r12,r26
  44:	39 60 00 00 	li      r11,0
  48:	7d 83 63 78 	mr      r3,r12
  4c:	82 e1 00 2c 	lwz     r23,44(r1)
  50:	83 01 00 30 	lwz     r24,48(r1)
  54:	83 21 00 34 	lwz     r25,52(r1)
  58:	83 41 00 38 	lwz     r26,56(r1)
  5c:	38 21 00 50 	addi    r1,r1,80
  60:	4e 80 00 20 	blr

After this patch, the same test has become:

   0:	7c 64 1b 78 	mr      r4,r3
   4:	38 60 00 00 	li      r3,0
   8:	94 21 ff b0 	stwu    r1,-80(r1)
   c:	60 00 00 00 	nop
  10:	39 80 00 00 	li      r12,0
  14:	39 60 00 00 	li      r11,0
  18:	39 00 00 00 	li      r8,0
  1c:	38 e0 00 00 	li      r7,0
  20:	7c 86 23 78 	mr      r6,r4
  24:	7c 65 1b 78 	mr      r5,r3
  28:	39 80 00 42 	li      r12,66
  2c:	39 60 00 00 	li      r11,0
  30:	7d 8c 42 14 	add     r12,r12,r8
  34:	39 60 00 00 	li      r11,0
  38:	7d 83 63 78 	mr      r3,r12
  3c:	38 21 00 50 	addi    r1,r1,80
  40:	4e 80 00 20 	blr

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
 arch/powerpc/net/bpf_jit.h        | 16 ++++++++++++++++
 arch/powerpc/net/bpf_jit64.h      |  2 +-
 arch/powerpc/net/bpf_jit_comp.c   |  2 ++
 arch/powerpc/net/bpf_jit_comp32.c | 30 ++++++++++++++++++++++++++++--
 arch/powerpc/net/bpf_jit_comp64.c |  4 ++++
 5 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
index a45b8266355d..776abef4d2a0 100644
--- a/arch/powerpc/net/bpf_jit.h
+++ b/arch/powerpc/net/bpf_jit.h
@@ -116,6 +116,15 @@ static inline bool is_nearbranch(int offset)
 #define SEEN_STACK	0x40000000 /* uses BPF stack */
 #define SEEN_TAILCALL	0x80000000 /* uses tail calls */
 
+#define SEEN_VREG_MASK	0x1ff80000 /* Volatile registers r3-r12 */
+#define SEEN_NVREG_MASK	0x0003ffff /* Non volatile registers r14-r31 */
+
+#ifdef CONFIG_PPC64
+extern const int b2p[MAX_BPF_JIT_REG + 2];
+#else
+extern const int b2p[MAX_BPF_JIT_REG + 1];
+#endif
+
 struct codegen_context {
 	/*
 	 * This is used to track register usage as well
@@ -129,6 +138,7 @@ struct codegen_context {
 	unsigned int seen;
 	unsigned int idx;
 	unsigned int stack_size;
+	int b2p[ARRAY_SIZE(b2p)];
 };
 
 static inline void bpf_flush_icache(void *start, void *end)
@@ -147,11 +157,17 @@ static inline void bpf_set_seen_register(struct codegen_context *ctx, int i)
 	ctx->seen |= 1 << (31 - i);
 }
 
+static inline void bpf_clear_seen_register(struct codegen_context *ctx, int i)
+{
+	ctx->seen &= ~(1 << (31 - i));
+}
+
 void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, u64 func);
 int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx,
 		       u32 *addrs, bool extra_pass);
 void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx);
 void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx);
+void bpf_jit_realloc_regs(struct codegen_context *ctx);
 
 #endif
 
diff --git a/arch/powerpc/net/bpf_jit64.h b/arch/powerpc/net/bpf_jit64.h
index b05f2e67bba1..7b713edfa7e2 100644
--- a/arch/powerpc/net/bpf_jit64.h
+++ b/arch/powerpc/net/bpf_jit64.h
@@ -39,7 +39,7 @@
 #define TMP_REG_2	(MAX_BPF_JIT_REG + 1)
 
 /* BPF to ppc register mappings */
-static const int b2p[] = {
+const int b2p[MAX_BPF_JIT_REG + 2] = {
 	/* function return value */
 	[BPF_REG_0] = 8,
 	/* function arguments */
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index efac89964873..798ac4350a82 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -143,6 +143,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
 	}
 
 	memset(&cgctx, 0, sizeof(struct codegen_context));
+	memcpy(cgctx.b2p, b2p, sizeof(cgctx.b2p));
 
 	/* Make sure that the stack is quadword aligned. */
 	cgctx.stack_size = round_up(fp->aux->stack_depth, 16);
@@ -167,6 +168,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
 		}
 	}
 
+	bpf_jit_realloc_regs(&cgctx);
 	/*
 	 * Pretend to build prologue, given the features we've seen.  This will
 	 * update ctgtx.idx as it pretends to output instructions, then we can
diff --git a/arch/powerpc/net/bpf_jit_comp32.c b/arch/powerpc/net/bpf_jit_comp32.c
index 29ce802d7534..003843273b43 100644
--- a/arch/powerpc/net/bpf_jit_comp32.c
+++ b/arch/powerpc/net/bpf_jit_comp32.c
@@ -37,7 +37,7 @@
 #define TMP_REG	(MAX_BPF_JIT_REG + 0)
 
 /* BPF to ppc register mappings */
-static const int b2p[] = {
+const int b2p[MAX_BPF_JIT_REG + 1] = {
 	/* function return value */
 	[BPF_REG_0] = 12,
 	/* function arguments */
@@ -60,7 +60,7 @@ static const int b2p[] = {
 
 static int bpf_to_ppc(struct codegen_context *ctx, int reg)
 {
-	return b2p[reg];
+	return ctx->b2p[reg];
 }
 
 /* PPC NVR range -- update this if we ever use NVRs below r17 */
@@ -77,6 +77,32 @@ static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg)
 	return BPF_PPC_STACKFRAME(ctx) - 4;
 }
 
+void bpf_jit_realloc_regs(struct codegen_context *ctx)
+{
+	if (ctx->seen & SEEN_FUNC)
+		return;
+
+	while (ctx->seen & SEEN_NVREG_MASK &&
+	      (ctx->seen & SEEN_VREG_MASK) != SEEN_VREG_MASK) {
+		int old = 32 - fls(ctx->seen & (SEEN_NVREG_MASK & 0xaaaaaaab));
+		int new = 32 - fls(~ctx->seen & (SEEN_VREG_MASK & 0xaaaaaaaa));
+		int i;
+
+		for (i = BPF_REG_0; i <= TMP_REG; i++) {
+			if (ctx->b2p[i] != old)
+				continue;
+			ctx->b2p[i] = new;
+			bpf_set_seen_register(ctx, new);
+			bpf_clear_seen_register(ctx, old);
+			if (i != TMP_REG) {
+				bpf_set_seen_register(ctx, new - 1);
+				bpf_clear_seen_register(ctx, old - 1);
+			}
+			break;
+		}
+	}
+}
+
 void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
 {
 	int i;
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index 8a1f9fb00e78..57a8c1153851 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -64,6 +64,10 @@ static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg)
 	BUG();
 }
 
+void bpf_jit_realloc_regs(struct codegen_context *ctx)
+{
+}
+
 void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
 {
 	int i;
-- 
2.25.0


^ permalink raw reply related

* [PATCH v2 7/8] powerpc/bpf: Implement extended BPF on PPC32
From: Christophe Leroy @ 2021-03-22 16:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ast,
	daniel, andrii, kafai, songliubraving, yhs, john.fastabend,
	kpsingh, naveen.n.rao, sandipan
  Cc: netdev, bpf, linuxppc-dev, linux-kernel
In-Reply-To: <cover.1616430991.git.christophe.leroy@csgroup.eu>

Implement Extended Berkeley Packet Filter on Powerpc 32

Test result with test_bpf module:

	test_bpf: Summary: 378 PASSED, 0 FAILED, [354/366 JIT'ed]

Registers mapping:

	[BPF_REG_0] = r11-r12
	/* function arguments */
	[BPF_REG_1] = r3-r4
	[BPF_REG_2] = r5-r6
	[BPF_REG_3] = r7-r8
	[BPF_REG_4] = r9-r10
	[BPF_REG_5] = r21-r22 (Args 9 and 10 come in via the stack)
	/* non volatile registers */
	[BPF_REG_6] = r23-r24
	[BPF_REG_7] = r25-r26
	[BPF_REG_8] = r27-r28
	[BPF_REG_9] = r29-r30
	/* frame pointer aka BPF_REG_10 */
	[BPF_REG_FP] = r17-r18
	/* eBPF jit internal registers */
	[BPF_REG_AX] = r19-r20
	[TMP_REG] = r31

As PPC32 doesn't have a redzone in the stack, a stack frame must always
be set in order to host at least the tail count counter.

The stack frame remains for tail calls, it is set by the first callee
and freed by the last callee.

r0 is used as temporary register as much as possible. It is referenced
directly in the code in order to avoid misusing it, because some
instructions interpret it as value 0 instead of register r0
(ex: addi, addis, stw, lwz, ...)

The following operations are not implemented:

		case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */
		case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */
		case BPF_STX | BPF_XADD | BPF_DW: /* *(u64 *)(dst + off) += src */

The following operations are only implemented for power of two constants:

		case BPF_ALU64 | BPF_MOD | BPF_K: /* dst %= imm */
		case BPF_ALU64 | BPF_DIV | BPF_K: /* dst /= imm */

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
v2:
- Simplify 16 bits swap
- Rework tailcall, use stack for tailcall counter
- Fix handling of BPF_REG_FP:
  - must be handler like any other register allthough only the lower 32 bits part is used as a pointer.
  - r18 was TMP_REG, r17/r18 become de BPF_REG_FP
  - r31 was BPF_REG_FP, it is now TMP_REG
- removed bpf_jit32.h
- Added a helper to convert BPF REG number to PPC register, to enable more dynamic in the future.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
 Documentation/admin-guide/sysctl/net.rst |    2 +-
 arch/powerpc/Kconfig                     |    2 +-
 arch/powerpc/net/Makefile                |    2 +-
 arch/powerpc/net/bpf_jit.h               |    4 +
 arch/powerpc/net/bpf_jit_comp32.c        | 1069 ++++++++++++++++++++++
 5 files changed, 1076 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/net/bpf_jit_comp32.c

diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst
index f2ab8a5b6a4b..685cc13f567b 100644
--- a/Documentation/admin-guide/sysctl/net.rst
+++ b/Documentation/admin-guide/sysctl/net.rst
@@ -64,6 +64,7 @@ two flavors of JITs, the newer eBPF JIT currently supported on:
   - arm64
   - arm32
   - ppc64
+  - ppc32
   - sparc64
   - mips64
   - s390x
@@ -73,7 +74,6 @@ two flavors of JITs, the newer eBPF JIT currently supported on:
 And the older cBPF JIT supported on the following archs:
 
   - mips
-  - ppc
   - sparc
 
 eBPF JITs are a superset of cBPF JITs, meaning the kernel will
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 5023acb5a3ef..2564c335b63e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -199,7 +199,7 @@ config PPC
 	select HAVE_DEBUG_STACKOVERFLOW
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE_WITH_REGS	if MPROFILE_KERNEL
-	select HAVE_EBPF_JIT			if PPC64
+	select HAVE_EBPF_JIT
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS	if !(CPU_LITTLE_ENDIAN && POWER7_CPU)
 	select HAVE_FAST_GUP
 	select HAVE_FTRACE_MCOUNT_RECORD
diff --git a/arch/powerpc/net/Makefile b/arch/powerpc/net/Makefile
index 969cde177880..8e60af32e51e 100644
--- a/arch/powerpc/net/Makefile
+++ b/arch/powerpc/net/Makefile
@@ -2,4 +2,4 @@
 #
 # Arch-specific network modules
 #
-obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_jit_comp64.o
+obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_jit_comp$(BITS).o
diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
index fb4656986fb9..a45b8266355d 100644
--- a/arch/powerpc/net/bpf_jit.h
+++ b/arch/powerpc/net/bpf_jit.h
@@ -42,6 +42,10 @@
 				EMIT(PPC_RAW_ORI(d, d, IMM_L(i)));	      \
 		} } while(0)
 
+#ifdef CONFIG_PPC32
+#define PPC_EX32(r, i)		EMIT(PPC_RAW_LI((r), (i) < 0 ? -1 : 0))
+#endif
+
 #define PPC_LI64(d, i)		do {					      \
 		if ((long)(i) >= -2147483648 &&				      \
 				(long)(i) < 2147483648)			      \
diff --git a/arch/powerpc/net/bpf_jit_comp32.c b/arch/powerpc/net/bpf_jit_comp32.c
new file mode 100644
index 000000000000..29ce802d7534
--- /dev/null
+++ b/arch/powerpc/net/bpf_jit_comp32.c
@@ -0,0 +1,1069 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * eBPF JIT compiler for PPC32
+ *
+ * Copyright 2020 Christophe Leroy <christophe.leroy@csgroup.eu>
+ *		  CS GROUP France
+ *
+ * Based on PPC64 eBPF JIT compiler by Naveen N. Rao
+ */
+#include <linux/moduleloader.h>
+#include <asm/cacheflush.h>
+#include <asm/asm-compat.h>
+#include <linux/netdevice.h>
+#include <linux/filter.h>
+#include <linux/if_vlan.h>
+#include <asm/kprobes.h>
+#include <linux/bpf.h>
+
+#include "bpf_jit.h"
+
+/*
+ * Stack layout:
+ *
+ *		[	prev sp		] <-------------
+ *		[   nv gpr save area	] 16 * 4	|
+ * fp (r31) -->	[   ebpf stack space	] upto 512	|
+ *		[     frame header	] 16		|
+ * sp (r1) --->	[    stack pointer	] --------------
+ */
+
+/* for gpr non volatile registers r17 to r31 (14) + tail call */
+#define BPF_PPC_STACK_SAVE	(15 * 4 + 4)
+/* stack frame, ensure this is quadword aligned */
+#define BPF_PPC_STACKFRAME(ctx)	(STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_SAVE + (ctx)->stack_size)
+
+/* BPF register usage */
+#define TMP_REG	(MAX_BPF_JIT_REG + 0)
+
+/* BPF to ppc register mappings */
+static const int b2p[] = {
+	/* function return value */
+	[BPF_REG_0] = 12,
+	/* function arguments */
+	[BPF_REG_1] = 4,
+	[BPF_REG_2] = 6,
+	[BPF_REG_3] = 8,
+	[BPF_REG_4] = 10,
+	[BPF_REG_5] = 22,
+	/* non volatile registers */
+	[BPF_REG_6] = 24,
+	[BPF_REG_7] = 26,
+	[BPF_REG_8] = 28,
+	[BPF_REG_9] = 30,
+	/* frame pointer aka BPF_REG_10 */
+	[BPF_REG_FP] = 18,
+	/* eBPF jit internal registers */
+	[BPF_REG_AX] = 20,
+	[TMP_REG] = 31,		/* 32 bits */
+};
+
+static int bpf_to_ppc(struct codegen_context *ctx, int reg)
+{
+	return b2p[reg];
+}
+
+/* PPC NVR range -- update this if we ever use NVRs below r17 */
+#define BPF_PPC_NVR_MIN		17
+#define BPF_PPC_TC		16
+
+static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg)
+{
+	if ((reg >= BPF_PPC_NVR_MIN && reg < 32) || reg == BPF_PPC_TC)
+		return BPF_PPC_STACKFRAME(ctx) - 4 * (32 - reg);
+
+	WARN(true, "BPF JIT is asking about unknown registers, will crash the stack");
+	/* Use the hole we have left for alignment */
+	return BPF_PPC_STACKFRAME(ctx) - 4;
+}
+
+void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
+{
+	int i;
+
+	/* First arg comes in as a 32 bits pointer. */
+	EMIT(PPC_RAW_MR(bpf_to_ppc(ctx, BPF_REG_1), __REG_R3));
+	EMIT(PPC_RAW_LI(bpf_to_ppc(ctx, BPF_REG_1) - 1, 0));
+	EMIT(PPC_RAW_STWU(__REG_R1, __REG_R1, -BPF_PPC_STACKFRAME(ctx)));
+
+	/*
+	 * Initialize tail_call_cnt in stack frame if we do tail calls.
+	 * Otherwise, put in NOPs so that it can be skipped when we are
+	 * invoked through a tail call.
+	 */
+	if (ctx->seen & SEEN_TAILCALL) {
+		EMIT(PPC_RAW_STW(bpf_to_ppc(ctx, BPF_REG_1) - 1, __REG_R1, bpf_jit_stack_offsetof(ctx, BPF_PPC_TC)));
+	} else {
+		EMIT(PPC_RAW_NOP());
+	}
+
+#define BPF_TAILCALL_PROLOGUE_SIZE	16
+
+	/*
+	 * We need a stack frame, but we don't necessarily need to
+	 * save/restore LR unless we call other functions
+	 */
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_MFLR(__REG_R0));
+
+	/*
+	 * Back up non-volatile regs -- registers r18-r31
+	 */
+	for (i = BPF_PPC_NVR_MIN; i <= 31; i++)
+		if (bpf_is_seen_register(ctx, i))
+			EMIT(PPC_RAW_STW(i, __REG_R1, bpf_jit_stack_offsetof(ctx, i)));
+
+	/* If needed retrieve arguments 9 and 10, ie 5th 64 bits arg.*/
+	if (bpf_is_seen_register(ctx, bpf_to_ppc(ctx, BPF_REG_5))) {
+		EMIT(PPC_RAW_LWZ(bpf_to_ppc(ctx, BPF_REG_5) - 1, __REG_R1, BPF_PPC_STACKFRAME(ctx)) + 8);
+		EMIT(PPC_RAW_LWZ(bpf_to_ppc(ctx, BPF_REG_5), __REG_R1, BPF_PPC_STACKFRAME(ctx)) + 12);
+	}
+
+	/* Setup frame pointer to point to the bpf stack area */
+	if (bpf_is_seen_register(ctx, bpf_to_ppc(ctx, BPF_REG_FP))) {
+		EMIT(PPC_RAW_LI(bpf_to_ppc(ctx, BPF_REG_FP) - 1, 0));
+		EMIT(PPC_RAW_ADDI(bpf_to_ppc(ctx, BPF_REG_FP), __REG_R1,
+				  STACK_FRAME_MIN_SIZE + ctx->stack_size));
+	}
+
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_STW(__REG_R0, __REG_R1, BPF_PPC_STACKFRAME(ctx) + PPC_LR_STKOFF));
+}
+
+static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx)
+{
+	int i;
+
+	/* Restore NVRs */
+	for (i = BPF_PPC_NVR_MIN; i <= 31; i++)
+		if (bpf_is_seen_register(ctx, i))
+			EMIT(PPC_RAW_LWZ(i, __REG_R1, bpf_jit_stack_offsetof(ctx, i)));
+}
+
+void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
+{
+	EMIT(PPC_RAW_MR(__REG_R3, bpf_to_ppc(ctx, BPF_REG_0)));
+
+	bpf_jit_emit_common_epilogue(image, ctx);
+
+	/* Tear down our stack frame */
+
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_LWZ(__REG_R0, __REG_R1, BPF_PPC_STACKFRAME(ctx) + PPC_LR_STKOFF));
+
+	EMIT(PPC_RAW_ADDI(__REG_R1, __REG_R1, BPF_PPC_STACKFRAME(ctx)));
+
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_MTLR(__REG_R0));
+
+	EMIT(PPC_RAW_BLR());
+}
+
+void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, u64 func)
+{
+	/* Load function address into r0 */
+	EMIT(PPC_RAW_LIS(__REG_R0, IMM_H(func)));
+	EMIT(PPC_RAW_ORI(__REG_R0, __REG_R0, IMM_L(func)));
+	EMIT(PPC_RAW_MTLR(__REG_R0));
+	EMIT(PPC_RAW_BLRL());
+}
+
+static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 out)
+{
+	/*
+	 * By now, the eBPF program has already setup parameters in r3-r6
+	 * r3-r4/BPF_REG_1 - pointer to ctx -- passed as is to the next bpf program
+	 * r5-r6/BPF_REG_2 - pointer to bpf_array
+	 * r7-r8/BPF_REG_3 - index in bpf_array
+	 */
+	int b2p_bpf_array = bpf_to_ppc(ctx, BPF_REG_2);
+	int b2p_index = bpf_to_ppc(ctx, BPF_REG_3);
+
+	/*
+	 * if (index >= array->map.max_entries)
+	 *   goto out;
+	 */
+	EMIT(PPC_RAW_LWZ(__REG_R0, b2p_bpf_array, offsetof(struct bpf_array, map.max_entries)));
+	EMIT(PPC_RAW_CMPLW(b2p_index, __REG_R0));
+	EMIT(PPC_RAW_LWZ(__REG_R0, __REG_R1, bpf_jit_stack_offsetof(ctx, BPF_PPC_TC)));
+	PPC_BCC(COND_GE, out);
+
+	/*
+	 * if (tail_call_cnt > MAX_TAIL_CALL_CNT)
+	 *   goto out;
+	 */
+	EMIT(PPC_RAW_CMPLWI(__REG_R0, MAX_TAIL_CALL_CNT));
+	/* tail_call_cnt++; */
+	EMIT(PPC_RAW_ADDIC(__REG_R0, __REG_R0, 1));
+	PPC_BCC(COND_GT, out);
+
+	/* prog = array->ptrs[index]; */
+	EMIT(PPC_RAW_RLWINM(__REG_R3, b2p_index, 2, 0, 29));
+	EMIT(PPC_RAW_ADD(__REG_R3, __REG_R3, b2p_bpf_array));
+	EMIT(PPC_RAW_LWZ(__REG_R3, __REG_R3, offsetof(struct bpf_array, ptrs)));
+	EMIT(PPC_RAW_STW(__REG_R0, __REG_R1, bpf_jit_stack_offsetof(ctx, BPF_PPC_TC)));
+
+	/*
+	 * if (prog == NULL)
+	 *   goto out;
+	 */
+	EMIT(PPC_RAW_CMPLWI(__REG_R3, 0));
+	PPC_BCC(COND_EQ, out);
+
+	/* goto *(prog->bpf_func + prologue_size); */
+	EMIT(PPC_RAW_LWZ(__REG_R3, __REG_R3, offsetof(struct bpf_prog, bpf_func)));
+
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_LWZ(__REG_R0, __REG_R1, BPF_PPC_STACKFRAME(ctx) + PPC_LR_STKOFF));
+
+	EMIT(PPC_RAW_ADDIC(__REG_R3, __REG_R3, BPF_TAILCALL_PROLOGUE_SIZE));
+
+	if (ctx->seen & SEEN_FUNC)
+		EMIT(PPC_RAW_MTLR(__REG_R0));
+
+	EMIT(PPC_RAW_MTCTR(__REG_R3));
+
+	EMIT(PPC_RAW_MR(__REG_R3, bpf_to_ppc(ctx, BPF_REG_1)));
+
+	/* tear restore NVRs, ... */
+	bpf_jit_emit_common_epilogue(image, ctx);
+
+	EMIT(PPC_RAW_BCTR());
+	/* out: */
+}
+
+/* Assemble the body code between the prologue & epilogue */
+int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx,
+		       u32 *addrs, bool extra_pass)
+{
+	const struct bpf_insn *insn = fp->insnsi;
+	int flen = fp->len;
+	int i, ret;
+
+	/* Start of epilogue code - will only be valid 2nd pass onwards */
+	u32 exit_addr = addrs[flen];
+
+	for (i = 0; i < flen; i++) {
+		u32 code = insn[i].code;
+		u32 dst_reg = bpf_to_ppc(ctx, insn[i].dst_reg);
+		u32 dst_reg_h = dst_reg - 1;
+		u32 src_reg = bpf_to_ppc(ctx, insn[i].src_reg);
+		u32 src_reg_h = src_reg - 1;
+		u32 tmp_reg = bpf_to_ppc(ctx, TMP_REG);
+		s16 off = insn[i].off;
+		s32 imm = insn[i].imm;
+		bool func_addr_fixed;
+		u64 func_addr;
+		u32 true_cond;
+
+		/*
+		 * addrs[] maps a BPF bytecode address into a real offset from
+		 * the start of the body code.
+		 */
+		addrs[i] = ctx->idx * 4;
+
+		/*
+		 * As an optimization, we note down which registers
+		 * are used so that we can only save/restore those in our
+		 * prologue and epilogue. We do this here regardless of whether
+		 * the actual BPF instruction uses src/dst registers or not
+		 * (for instance, BPF_CALL does not use them). The expectation
+		 * is that those instructions will have src_reg/dst_reg set to
+		 * 0. Even otherwise, we just lose some prologue/epilogue
+		 * optimization but everything else should work without
+		 * any issues.
+		 */
+		if (dst_reg >= 3 && dst_reg < 32) {
+			bpf_set_seen_register(ctx, dst_reg);
+			bpf_set_seen_register(ctx, dst_reg_h);
+		}
+
+		if (src_reg >= 3 && src_reg < 32) {
+			bpf_set_seen_register(ctx, src_reg);
+			bpf_set_seen_register(ctx, src_reg_h);
+		}
+
+		switch (code) {
+		/*
+		 * Arithmetic operations: ADD/SUB/MUL/DIV/MOD/NEG
+		 */
+		case BPF_ALU | BPF_ADD | BPF_X: /* (u32) dst += (u32) src */
+			EMIT(PPC_RAW_ADD(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_ADD | BPF_X: /* dst += src */
+			EMIT(PPC_RAW_ADDC(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_ADDE(dst_reg_h, dst_reg_h, src_reg_h));
+			break;
+		case BPF_ALU | BPF_SUB | BPF_X: /* (u32) dst -= (u32) src */
+			EMIT(PPC_RAW_SUB(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_SUB | BPF_X: /* dst -= src */
+			EMIT(PPC_RAW_SUBFC(dst_reg, src_reg, dst_reg));
+			EMIT(PPC_RAW_SUBFE(dst_reg_h, src_reg_h, dst_reg_h));
+			break;
+		case BPF_ALU | BPF_SUB | BPF_K: /* (u32) dst -= (u32) imm */
+			imm = -imm;
+			fallthrough;
+		case BPF_ALU | BPF_ADD | BPF_K: /* (u32) dst += (u32) imm */
+			if (IMM_HA(imm) & 0xffff)
+				EMIT(PPC_RAW_ADDIS(dst_reg, dst_reg, IMM_HA(imm)));
+			if (IMM_L(imm))
+				EMIT(PPC_RAW_ADDI(dst_reg, dst_reg, IMM_L(imm)));
+			break;
+		case BPF_ALU64 | BPF_SUB | BPF_K: /* dst -= imm */
+			imm = -imm;
+			fallthrough;
+		case BPF_ALU64 | BPF_ADD | BPF_K: /* dst += imm */
+			if (!imm)
+				break;
+
+			if (imm >= -32768 && imm < 32768) {
+				EMIT(PPC_RAW_ADDIC(dst_reg, dst_reg, imm));
+			} else {
+				PPC_LI32(__REG_R0, imm);
+				EMIT(PPC_RAW_ADDC(dst_reg, dst_reg, __REG_R0));
+			}
+			if (imm >= 0)
+				EMIT(PPC_RAW_ADDZE(dst_reg_h, dst_reg_h));
+			else
+				EMIT(PPC_RAW_ADDME(dst_reg_h, dst_reg_h));
+			break;
+		case BPF_ALU64 | BPF_MUL | BPF_X: /* dst *= src */
+			bpf_set_seen_register(ctx, tmp_reg);
+			EMIT(PPC_RAW_MULW(__REG_R0, dst_reg, src_reg_h));
+			EMIT(PPC_RAW_MULW(dst_reg_h, dst_reg_h, src_reg));
+			EMIT(PPC_RAW_MULHWU(tmp_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_MULW(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_ADD(dst_reg_h, dst_reg_h, __REG_R0));
+			EMIT(PPC_RAW_ADD(dst_reg_h, dst_reg_h, tmp_reg));
+			break;
+		case BPF_ALU | BPF_MUL | BPF_X: /* (u32) dst *= (u32) src */
+			EMIT(PPC_RAW_MULW(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU | BPF_MUL | BPF_K: /* (u32) dst *= (u32) imm */
+			if (imm >= -32768 && imm < 32768) {
+				EMIT(PPC_RAW_MULI(dst_reg, dst_reg, imm));
+			} else {
+				PPC_LI32(__REG_R0, imm);
+				EMIT(PPC_RAW_MULW(dst_reg, dst_reg, __REG_R0));
+			}
+			break;
+		case BPF_ALU64 | BPF_MUL | BPF_K: /* dst *= imm */
+			if (!imm) {
+				PPC_LI32(dst_reg, 0);
+				PPC_LI32(dst_reg_h, 0);
+				break;
+			}
+			if (imm == 1)
+				break;
+			if (imm == -1) {
+				EMIT(PPC_RAW_SUBFIC(dst_reg, dst_reg, 0));
+				EMIT(PPC_RAW_SUBFZE(dst_reg_h, dst_reg_h));
+				break;
+			}
+			bpf_set_seen_register(ctx, tmp_reg);
+			PPC_LI32(tmp_reg, imm);
+			EMIT(PPC_RAW_MULW(dst_reg_h, dst_reg_h, tmp_reg));
+			if (imm < 0)
+				EMIT(PPC_RAW_SUB(dst_reg_h, dst_reg_h, dst_reg));
+			EMIT(PPC_RAW_MULHWU(__REG_R0, dst_reg, tmp_reg));
+			EMIT(PPC_RAW_MULW(dst_reg, dst_reg, tmp_reg));
+			EMIT(PPC_RAW_ADD(dst_reg_h, dst_reg_h, __REG_R0));
+			break;
+		case BPF_ALU | BPF_DIV | BPF_X: /* (u32) dst /= (u32) src */
+			EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU | BPF_MOD | BPF_X: /* (u32) dst %= (u32) src */
+			EMIT(PPC_RAW_DIVWU(__REG_R0, dst_reg, src_reg));
+			EMIT(PPC_RAW_MULW(__REG_R0, src_reg, __REG_R0));
+			EMIT(PPC_RAW_SUB(dst_reg, dst_reg, __REG_R0));
+			break;
+		case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */
+			return -EOPNOTSUPP;
+		case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */
+			return -EOPNOTSUPP;
+		case BPF_ALU | BPF_DIV | BPF_K: /* (u32) dst /= (u32) imm */
+			if (!imm)
+				return -EINVAL;
+			if (imm == 1)
+				break;
+
+			PPC_LI32(__REG_R0, imm);
+			EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, __REG_R0));
+			break;
+		case BPF_ALU | BPF_MOD | BPF_K: /* (u32) dst %= (u32) imm */
+			if (!imm)
+				return -EINVAL;
+
+			if (!is_power_of_2((u32)imm)) {
+				bpf_set_seen_register(ctx, tmp_reg);
+				PPC_LI32(tmp_reg, imm);
+				EMIT(PPC_RAW_DIVWU(__REG_R0, dst_reg, tmp_reg));
+				EMIT(PPC_RAW_MULW(__REG_R0, tmp_reg, __REG_R0));
+				EMIT(PPC_RAW_SUB(dst_reg, dst_reg, __REG_R0));
+				break;
+			}
+			if (imm == 1)
+				EMIT(PPC_RAW_LI(dst_reg, 0));
+			else
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 32 - ilog2((u32)imm), 31));
+
+			break;
+		case BPF_ALU64 | BPF_MOD | BPF_K: /* dst %= imm */
+			if (!imm)
+				return -EINVAL;
+			if (imm < 0)
+				imm = -imm;
+			if (!is_power_of_2(imm))
+				return -EOPNOTSUPP;
+			if (imm == 1)
+				EMIT(PPC_RAW_LI(dst_reg, 0));
+			else
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 32 - ilog2(imm), 31));
+			EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			break;
+		case BPF_ALU64 | BPF_DIV | BPF_K: /* dst /= imm */
+			if (!imm)
+				return -EINVAL;
+			if (!is_power_of_2(abs(imm)))
+				return -EOPNOTSUPP;
+
+			if (imm < 0) {
+				EMIT(PPC_RAW_SUBFIC(dst_reg, dst_reg, 0));
+				EMIT(PPC_RAW_SUBFZE(dst_reg_h, dst_reg_h));
+				imm = -imm;
+			}
+			if (imm == 1)
+				break;
+			imm = ilog2(imm);
+			EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 32 - imm, imm, 31));
+			EMIT(PPC_RAW_RLWIMI(dst_reg, dst_reg_h, 32 - imm, 0, imm - 1));
+			EMIT(PPC_RAW_SRAWI(dst_reg_h, dst_reg_h, imm));
+			break;
+		case BPF_ALU | BPF_NEG: /* (u32) dst = -dst */
+			EMIT(PPC_RAW_NEG(dst_reg, dst_reg));
+			break;
+		case BPF_ALU64 | BPF_NEG: /* dst = -dst */
+			EMIT(PPC_RAW_SUBFIC(dst_reg, dst_reg, 0));
+			EMIT(PPC_RAW_SUBFZE(dst_reg_h, dst_reg_h));
+			break;
+
+		/*
+		 * Logical operations: AND/OR/XOR/[A]LSH/[A]RSH
+		 */
+		case BPF_ALU64 | BPF_AND | BPF_X: /* dst = dst & src */
+			EMIT(PPC_RAW_AND(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_AND(dst_reg_h, dst_reg_h, src_reg_h));
+			break;
+		case BPF_ALU | BPF_AND | BPF_X: /* (u32) dst = dst & src */
+			EMIT(PPC_RAW_AND(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */
+			if (imm >= 0)
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			fallthrough;
+		case BPF_ALU | BPF_AND | BPF_K: /* (u32) dst = dst & imm */
+			if (!IMM_H(imm)) {
+				EMIT(PPC_RAW_ANDI(dst_reg, dst_reg, IMM_L(imm)));
+			} else if (!IMM_L(imm)) {
+				EMIT(PPC_RAW_ANDIS(dst_reg, dst_reg, IMM_H(imm)));
+			} else if (imm == (((1 << fls(imm)) - 1) ^ ((1 << (ffs(i) - 1)) - 1))) {
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0,
+						    32 - fls(imm), 32 - ffs(imm)));
+			} else {
+				PPC_LI32(__REG_R0, imm);
+				EMIT(PPC_RAW_AND(dst_reg, dst_reg, __REG_R0));
+			}
+			break;
+		case BPF_ALU64 | BPF_OR | BPF_X: /* dst = dst | src */
+			EMIT(PPC_RAW_OR(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_OR(dst_reg_h, dst_reg_h, src_reg_h));
+			break;
+		case BPF_ALU | BPF_OR | BPF_X: /* dst = (u32) dst | (u32) src */
+			EMIT(PPC_RAW_OR(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_OR | BPF_K:/* dst = dst | imm */
+			/* Sign-extended */
+			if (imm < 0)
+				EMIT(PPC_RAW_LI(dst_reg_h, -1));
+			fallthrough;
+		case BPF_ALU | BPF_OR | BPF_K:/* dst = (u32) dst | (u32) imm */
+			if (IMM_L(imm))
+				EMIT(PPC_RAW_ORI(dst_reg, dst_reg, IMM_L(imm)));
+			if (IMM_H(imm))
+				EMIT(PPC_RAW_ORIS(dst_reg, dst_reg, IMM_H(imm)));
+			break;
+		case BPF_ALU64 | BPF_XOR | BPF_X: /* dst ^= src */
+			if (dst_reg == src_reg) {
+				EMIT(PPC_RAW_LI(dst_reg, 0));
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			} else {
+				EMIT(PPC_RAW_XOR(dst_reg, dst_reg, src_reg));
+				EMIT(PPC_RAW_XOR(dst_reg_h, dst_reg_h, src_reg_h));
+			}
+			break;
+		case BPF_ALU | BPF_XOR | BPF_X: /* (u32) dst ^= src */
+			if (dst_reg == src_reg)
+				EMIT(PPC_RAW_LI(dst_reg, 0));
+			else
+				EMIT(PPC_RAW_XOR(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_XOR | BPF_K: /* dst ^= imm */
+			if (imm < 0)
+				EMIT(PPC_RAW_NOR(dst_reg_h, dst_reg_h, dst_reg_h));
+			fallthrough;
+		case BPF_ALU | BPF_XOR | BPF_K: /* (u32) dst ^= (u32) imm */
+			if (IMM_L(imm))
+				EMIT(PPC_RAW_XORI(dst_reg, dst_reg, IMM_L(imm)));
+			if (IMM_H(imm))
+				EMIT(PPC_RAW_XORIS(dst_reg, dst_reg, IMM_H(imm)));
+			break;
+		case BPF_ALU | BPF_LSH | BPF_X: /* (u32) dst <<= (u32) src */
+			EMIT(PPC_RAW_SLW(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_LSH | BPF_X: /* dst <<= src; */
+			EMIT(PPC_RAW_ADDIC_DOT(__REG_R0, src_reg, -32));
+			PPC_BCC_SHORT(COND_LT, (ctx->idx + 4) * 4);
+			EMIT(PPC_RAW_SLW(dst_reg_h, dst_reg, __REG_R0));
+			EMIT(PPC_RAW_LI(dst_reg, 0));
+			PPC_JMP((ctx->idx + 6) * 4);
+			EMIT(PPC_RAW_SUBFIC(__REG_R0, src_reg, 32));
+			EMIT(PPC_RAW_SLW(dst_reg_h, dst_reg_h, src_reg));
+			EMIT(PPC_RAW_SRW(__REG_R0, dst_reg, __REG_R0));
+			EMIT(PPC_RAW_SLW(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_OR(dst_reg_h, dst_reg_h, __REG_R0));
+			break;
+		case BPF_ALU | BPF_LSH | BPF_K: /* (u32) dst <<== (u32) imm */
+			if (!imm)
+				break;
+			EMIT(PPC_RAW_SLWI(dst_reg, dst_reg, imm));
+			break;
+		case BPF_ALU64 | BPF_LSH | BPF_K: /* dst <<== imm */
+			if (imm < 0)
+				return -EINVAL;
+			if (!imm)
+				break;
+			if (imm < 32) {
+				EMIT(PPC_RAW_RLWINM(dst_reg_h, dst_reg_h, imm, 0, 31 - imm));
+				EMIT(PPC_RAW_RLWIMI(dst_reg_h, dst_reg, imm, 32 - imm, 31));
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, imm, 0, 31 - imm));
+				break;
+			}
+			if (imm < 64)
+				EMIT(PPC_RAW_RLWINM(dst_reg_h, dst_reg, imm, 0, 31 - imm));
+			else
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			EMIT(PPC_RAW_LI(dst_reg, 0));
+			break;
+		case BPF_ALU | BPF_RSH | BPF_X: /* (u32) dst >>= (u32) src */
+			EMIT(PPC_RAW_SRW(dst_reg, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_RSH | BPF_X: /* dst >>= src */
+			EMIT(PPC_RAW_ADDIC_DOT(__REG_R0, src_reg, -32));
+			PPC_BCC_SHORT(COND_LT, (ctx->idx + 4) * 4);
+			EMIT(PPC_RAW_SRW(dst_reg, dst_reg_h, __REG_R0));
+			EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			PPC_JMP((ctx->idx + 6) * 4);
+			EMIT(PPC_RAW_SUBFIC(0, src_reg, 32));
+			EMIT(PPC_RAW_SRW(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_SLW(__REG_R0, dst_reg_h, __REG_R0));
+			EMIT(PPC_RAW_SRW(dst_reg_h, dst_reg_h, src_reg));
+			EMIT(PPC_RAW_OR(dst_reg, dst_reg, __REG_R0));
+			break;
+		case BPF_ALU | BPF_RSH | BPF_K: /* (u32) dst >>= (u32) imm */
+			if (!imm)
+				break;
+			EMIT(PPC_RAW_SRWI(dst_reg, dst_reg, imm));
+			break;
+		case BPF_ALU64 | BPF_RSH | BPF_K: /* dst >>= imm */
+			if (imm < 0)
+				return -EINVAL;
+			if (!imm)
+				break;
+			if (imm < 32) {
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 32 - imm, imm, 31));
+				EMIT(PPC_RAW_RLWIMI(dst_reg, dst_reg_h, 32 - imm, 0, imm - 1));
+				EMIT(PPC_RAW_RLWINM(dst_reg_h, dst_reg_h, 32 - imm, imm, 31));
+				break;
+			}
+			if (imm < 64)
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg_h, 64 - imm, imm - 32, 31));
+			else
+				EMIT(PPC_RAW_LI(dst_reg, 0));
+			EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			break;
+		case BPF_ALU | BPF_ARSH | BPF_X: /* (s32) dst >>= src */
+			EMIT(PPC_RAW_SRAW(dst_reg_h, dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_ARSH | BPF_X: /* (s64) dst >>= src */
+			EMIT(PPC_RAW_ADDIC_DOT(__REG_R0, src_reg, -32));
+			PPC_BCC_SHORT(COND_LT, (ctx->idx + 4) * 4);
+			EMIT(PPC_RAW_SRAW(dst_reg, dst_reg_h, __REG_R0));
+			EMIT(PPC_RAW_SRAWI(dst_reg_h, dst_reg_h, 31));
+			PPC_JMP((ctx->idx + 6) * 4);
+			EMIT(PPC_RAW_SUBFIC(0, src_reg, 32));
+			EMIT(PPC_RAW_SRW(dst_reg, dst_reg, src_reg));
+			EMIT(PPC_RAW_SLW(__REG_R0, dst_reg_h, __REG_R0));
+			EMIT(PPC_RAW_SRAW(dst_reg_h, dst_reg_h, src_reg));
+			EMIT(PPC_RAW_OR(dst_reg, dst_reg, __REG_R0));
+			break;
+		case BPF_ALU | BPF_ARSH | BPF_K: /* (s32) dst >>= imm */
+			if (!imm)
+				break;
+			EMIT(PPC_RAW_SRAWI(dst_reg, dst_reg, imm));
+			break;
+		case BPF_ALU64 | BPF_ARSH | BPF_K: /* (s64) dst >>= imm */
+			if (imm < 0)
+				return -EINVAL;
+			if (!imm)
+				break;
+			if (imm < 32) {
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 32 - imm, imm, 31));
+				EMIT(PPC_RAW_RLWIMI(dst_reg, dst_reg_h, 32 - imm, 0, imm - 1));
+				EMIT(PPC_RAW_SRAWI(dst_reg_h, dst_reg_h, imm));
+				break;
+			}
+			if (imm < 64)
+				EMIT(PPC_RAW_SRAWI(dst_reg, dst_reg_h, imm - 32));
+			else
+				EMIT(PPC_RAW_SRAWI(dst_reg, dst_reg_h, 31));
+			EMIT(PPC_RAW_SRAWI(dst_reg_h, dst_reg_h, 31));
+			break;
+
+		/*
+		 * MOV
+		 */
+		case BPF_ALU64 | BPF_MOV | BPF_X: /* dst = src */
+			if (dst_reg == src_reg)
+				break;
+			EMIT(PPC_RAW_MR(dst_reg, src_reg));
+			EMIT(PPC_RAW_MR(dst_reg_h, src_reg_h));
+			break;
+		case BPF_ALU | BPF_MOV | BPF_X: /* (u32) dst = src */
+			/* special mov32 for zext */
+			if (imm == 1)
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			else if (dst_reg != src_reg)
+				EMIT(PPC_RAW_MR(dst_reg, src_reg));
+			break;
+		case BPF_ALU64 | BPF_MOV | BPF_K: /* dst = (s64) imm */
+			PPC_LI32(dst_reg, imm);
+			PPC_EX32(dst_reg_h, imm);
+			break;
+		case BPF_ALU | BPF_MOV | BPF_K: /* (u32) dst = imm */
+			PPC_LI32(dst_reg, imm);
+			break;
+
+		/*
+		 * BPF_FROM_BE/LE
+		 */
+		case BPF_ALU | BPF_END | BPF_FROM_LE:
+			switch (imm) {
+			case 16:
+				/* Copy 16 bits to upper part */
+				EMIT(PPC_RAW_RLWIMI(dst_reg, dst_reg, 16, 0, 15));
+				/* Rotate 8 bits right & mask */
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 24, 16, 31));
+				break;
+			case 32:
+				/*
+				 * Rotate word left by 8 bits:
+				 * 2 bytes are already in their final position
+				 * -- byte 2 and 4 (of bytes 1, 2, 3 and 4)
+				 */
+				EMIT(PPC_RAW_RLWINM(__REG_R0, dst_reg, 8, 0, 31));
+				/* Rotate 24 bits and insert byte 1 */
+				EMIT(PPC_RAW_RLWIMI(__REG_R0, dst_reg, 24, 0, 7));
+				/* Rotate 24 bits and insert byte 3 */
+				EMIT(PPC_RAW_RLWIMI(__REG_R0, dst_reg, 24, 16, 23));
+				EMIT(PPC_RAW_MR(dst_reg, __REG_R0));
+				break;
+			case 64:
+				bpf_set_seen_register(ctx, tmp_reg);
+				EMIT(PPC_RAW_RLWINM(tmp_reg, dst_reg, 8, 0, 31));
+				EMIT(PPC_RAW_RLWINM(__REG_R0, dst_reg_h, 8, 0, 31));
+				/* Rotate 24 bits and insert byte 1 */
+				EMIT(PPC_RAW_RLWIMI(tmp_reg, dst_reg, 24, 0, 7));
+				EMIT(PPC_RAW_RLWIMI(__REG_R0, dst_reg_h, 24, 0, 7));
+				/* Rotate 24 bits and insert byte 3 */
+				EMIT(PPC_RAW_RLWIMI(tmp_reg, dst_reg, 24, 16, 23));
+				EMIT(PPC_RAW_RLWIMI(__REG_R0, dst_reg_h, 24, 16, 23));
+				EMIT(PPC_RAW_MR(dst_reg, __REG_R0));
+				EMIT(PPC_RAW_MR(dst_reg_h, tmp_reg));
+				break;
+			}
+			break;
+		case BPF_ALU | BPF_END | BPF_FROM_BE:
+			switch (imm) {
+			case 16:
+				/* zero-extend 16 bits into 32 bits */
+				EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 16, 31));
+				break;
+			case 32:
+			case 64:
+				/* nop */
+				break;
+			}
+			break;
+
+		/*
+		 * BPF_ST(X)
+		 */
+		case BPF_STX | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = src */
+			EMIT(PPC_RAW_STB(src_reg, dst_reg, off));
+			break;
+		case BPF_ST | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = imm */
+			PPC_LI32(__REG_R0, imm);
+			EMIT(PPC_RAW_STB(__REG_R0, dst_reg, off));
+			break;
+		case BPF_STX | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = src */
+			EMIT(PPC_RAW_STH(src_reg, dst_reg, off));
+			break;
+		case BPF_ST | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = imm */
+			PPC_LI32(__REG_R0, imm);
+			EMIT(PPC_RAW_STH(__REG_R0, dst_reg, off));
+			break;
+		case BPF_STX | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = src */
+			EMIT(PPC_RAW_STW(src_reg, dst_reg, off));
+			break;
+		case BPF_ST | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = imm */
+			PPC_LI32(__REG_R0, imm);
+			EMIT(PPC_RAW_STW(__REG_R0, dst_reg, off));
+			break;
+		case BPF_STX | BPF_MEM | BPF_DW: /* (u64 *)(dst + off) = src */
+			EMIT(PPC_RAW_STW(src_reg_h, dst_reg, off));
+			EMIT(PPC_RAW_STW(src_reg, dst_reg, off + 4));
+			break;
+		case BPF_ST | BPF_MEM | BPF_DW: /* *(u64 *)(dst + off) = imm */
+			PPC_LI32(__REG_R0, imm);
+			EMIT(PPC_RAW_STW(__REG_R0, dst_reg, off + 4));
+			PPC_EX32(__REG_R0, imm);
+			EMIT(PPC_RAW_STW(__REG_R0, dst_reg, off));
+			break;
+
+		/*
+		 * BPF_STX XADD (atomic_add)
+		 */
+		case BPF_STX | BPF_XADD | BPF_W: /* *(u32 *)(dst + off) += src */
+			bpf_set_seen_register(ctx, tmp_reg);
+			/* Get offset into TMP_REG */
+			EMIT(PPC_RAW_LI(tmp_reg, off));
+			/* load value from memory into r0 */
+			EMIT(PPC_RAW_LWARX(__REG_R0, tmp_reg, dst_reg, 0));
+			/* add value from src_reg into this */
+			EMIT(PPC_RAW_ADD(__REG_R0, __REG_R0, src_reg));
+			/* store result back */
+			EMIT(PPC_RAW_STWCX(__REG_R0, tmp_reg, dst_reg));
+			/* we're done if this succeeded */
+			PPC_BCC_SHORT(COND_NE, (ctx->idx - 3) * 4);
+			break;
+
+		case BPF_STX | BPF_XADD | BPF_DW: /* *(u64 *)(dst + off) += src */
+			return -EOPNOTSUPP;
+
+		/*
+		 * BPF_LDX
+		 */
+		case BPF_LDX | BPF_MEM | BPF_B: /* dst = *(u8 *)(ul) (src + off) */
+			EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off));
+			if (!fp->aux->verifier_zext)
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			break;
+		case BPF_LDX | BPF_MEM | BPF_H: /* dst = *(u16 *)(ul) (src + off) */
+			EMIT(PPC_RAW_LHZ(dst_reg, src_reg, off));
+			if (!fp->aux->verifier_zext)
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			break;
+		case BPF_LDX | BPF_MEM | BPF_W: /* dst = *(u32 *)(ul) (src + off) */
+			EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off));
+			if (!fp->aux->verifier_zext)
+				EMIT(PPC_RAW_LI(dst_reg_h, 0));
+			break;
+		case BPF_LDX | BPF_MEM | BPF_DW: /* dst = *(u64 *)(ul) (src + off) */
+			EMIT(PPC_RAW_LWZ(dst_reg_h, src_reg, off));
+			EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off + 4));
+			break;
+
+		/*
+		 * Doubleword load
+		 * 16 byte instruction that uses two 'struct bpf_insn'
+		 */
+		case BPF_LD | BPF_IMM | BPF_DW: /* dst = (u64) imm */
+			PPC_LI32(dst_reg_h, (u32)insn[i + 1].imm);
+			PPC_LI32(dst_reg, (u32)insn[i].imm);
+			/* Adjust for two bpf instructions */
+			addrs[++i] = ctx->idx * 4;
+			break;
+
+		/*
+		 * Return/Exit
+		 */
+		case BPF_JMP | BPF_EXIT:
+			/*
+			 * If this isn't the very last instruction, branch to
+			 * the epilogue. If we _are_ the last instruction,
+			 * we'll just fall through to the epilogue.
+			 */
+			if (i != flen - 1)
+				PPC_JMP(exit_addr);
+			/* else fall through to the epilogue */
+			break;
+
+		/*
+		 * Call kernel helper or bpf function
+		 */
+		case BPF_JMP | BPF_CALL:
+			ctx->seen |= SEEN_FUNC;
+
+			ret = bpf_jit_get_func_addr(fp, &insn[i], extra_pass,
+						    &func_addr, &func_addr_fixed);
+			if (ret < 0)
+				return ret;
+
+			if (bpf_is_seen_register(ctx, bpf_to_ppc(ctx, BPF_REG_5))) {
+				EMIT(PPC_RAW_STW(bpf_to_ppc(ctx, BPF_REG_5) - 1, __REG_R1, 8));
+				EMIT(PPC_RAW_STW(bpf_to_ppc(ctx, BPF_REG_5), __REG_R1, 12));
+			}
+
+			bpf_jit_emit_func_call_rel(image, ctx, func_addr);
+
+			EMIT(PPC_RAW_MR(bpf_to_ppc(ctx, BPF_REG_0) - 1, __REG_R3));
+			EMIT(PPC_RAW_MR(bpf_to_ppc(ctx, BPF_REG_0), __REG_R4));
+			break;
+
+		/*
+		 * Jumps and branches
+		 */
+		case BPF_JMP | BPF_JA:
+			PPC_JMP(addrs[i + 1 + off]);
+			break;
+
+		case BPF_JMP | BPF_JGT | BPF_K:
+		case BPF_JMP | BPF_JGT | BPF_X:
+		case BPF_JMP | BPF_JSGT | BPF_K:
+		case BPF_JMP | BPF_JSGT | BPF_X:
+		case BPF_JMP32 | BPF_JGT | BPF_K:
+		case BPF_JMP32 | BPF_JGT | BPF_X:
+		case BPF_JMP32 | BPF_JSGT | BPF_K:
+		case BPF_JMP32 | BPF_JSGT | BPF_X:
+			true_cond = COND_GT;
+			goto cond_branch;
+		case BPF_JMP | BPF_JLT | BPF_K:
+		case BPF_JMP | BPF_JLT | BPF_X:
+		case BPF_JMP | BPF_JSLT | BPF_K:
+		case BPF_JMP | BPF_JSLT | BPF_X:
+		case BPF_JMP32 | BPF_JLT | BPF_K:
+		case BPF_JMP32 | BPF_JLT | BPF_X:
+		case BPF_JMP32 | BPF_JSLT | BPF_K:
+		case BPF_JMP32 | BPF_JSLT | BPF_X:
+			true_cond = COND_LT;
+			goto cond_branch;
+		case BPF_JMP | BPF_JGE | BPF_K:
+		case BPF_JMP | BPF_JGE | BPF_X:
+		case BPF_JMP | BPF_JSGE | BPF_K:
+		case BPF_JMP | BPF_JSGE | BPF_X:
+		case BPF_JMP32 | BPF_JGE | BPF_K:
+		case BPF_JMP32 | BPF_JGE | BPF_X:
+		case BPF_JMP32 | BPF_JSGE | BPF_K:
+		case BPF_JMP32 | BPF_JSGE | BPF_X:
+			true_cond = COND_GE;
+			goto cond_branch;
+		case BPF_JMP | BPF_JLE | BPF_K:
+		case BPF_JMP | BPF_JLE | BPF_X:
+		case BPF_JMP | BPF_JSLE | BPF_K:
+		case BPF_JMP | BPF_JSLE | BPF_X:
+		case BPF_JMP32 | BPF_JLE | BPF_K:
+		case BPF_JMP32 | BPF_JLE | BPF_X:
+		case BPF_JMP32 | BPF_JSLE | BPF_K:
+		case BPF_JMP32 | BPF_JSLE | BPF_X:
+			true_cond = COND_LE;
+			goto cond_branch;
+		case BPF_JMP | BPF_JEQ | BPF_K:
+		case BPF_JMP | BPF_JEQ | BPF_X:
+		case BPF_JMP32 | BPF_JEQ | BPF_K:
+		case BPF_JMP32 | BPF_JEQ | BPF_X:
+			true_cond = COND_EQ;
+			goto cond_branch;
+		case BPF_JMP | BPF_JNE | BPF_K:
+		case BPF_JMP | BPF_JNE | BPF_X:
+		case BPF_JMP32 | BPF_JNE | BPF_K:
+		case BPF_JMP32 | BPF_JNE | BPF_X:
+			true_cond = COND_NE;
+			goto cond_branch;
+		case BPF_JMP | BPF_JSET | BPF_K:
+		case BPF_JMP | BPF_JSET | BPF_X:
+		case BPF_JMP32 | BPF_JSET | BPF_K:
+		case BPF_JMP32 | BPF_JSET | BPF_X:
+			true_cond = COND_NE;
+			/* fallthrough; */
+
+cond_branch:
+			switch (code) {
+			case BPF_JMP | BPF_JGT | BPF_X:
+			case BPF_JMP | BPF_JLT | BPF_X:
+			case BPF_JMP | BPF_JGE | BPF_X:
+			case BPF_JMP | BPF_JLE | BPF_X:
+			case BPF_JMP | BPF_JEQ | BPF_X:
+			case BPF_JMP | BPF_JNE | BPF_X:
+				/* unsigned comparison */
+				EMIT(PPC_RAW_CMPLW(dst_reg_h, src_reg_h));
+				PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+				EMIT(PPC_RAW_CMPLW(dst_reg, src_reg));
+				break;
+			case BPF_JMP32 | BPF_JGT | BPF_X:
+			case BPF_JMP32 | BPF_JLT | BPF_X:
+			case BPF_JMP32 | BPF_JGE | BPF_X:
+			case BPF_JMP32 | BPF_JLE | BPF_X:
+			case BPF_JMP32 | BPF_JEQ | BPF_X:
+			case BPF_JMP32 | BPF_JNE | BPF_X:
+				/* unsigned comparison */
+				EMIT(PPC_RAW_CMPLW(dst_reg, src_reg));
+				break;
+			case BPF_JMP | BPF_JSGT | BPF_X:
+			case BPF_JMP | BPF_JSLT | BPF_X:
+			case BPF_JMP | BPF_JSGE | BPF_X:
+			case BPF_JMP | BPF_JSLE | BPF_X:
+				/* signed comparison */
+				EMIT(PPC_RAW_CMPW(dst_reg_h, src_reg_h));
+				PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+				EMIT(PPC_RAW_CMPLW(dst_reg, src_reg));
+				break;
+			case BPF_JMP32 | BPF_JSGT | BPF_X:
+			case BPF_JMP32 | BPF_JSLT | BPF_X:
+			case BPF_JMP32 | BPF_JSGE | BPF_X:
+			case BPF_JMP32 | BPF_JSLE | BPF_X:
+				/* signed comparison */
+				EMIT(PPC_RAW_CMPW(dst_reg, src_reg));
+				break;
+			case BPF_JMP | BPF_JSET | BPF_X:
+				EMIT(PPC_RAW_AND_DOT(__REG_R0, dst_reg_h, src_reg_h));
+				PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+				EMIT(PPC_RAW_AND_DOT(__REG_R0, dst_reg, src_reg));
+				break;
+			case BPF_JMP32 | BPF_JSET | BPF_X: {
+				EMIT(PPC_RAW_AND_DOT(__REG_R0, dst_reg, src_reg));
+				break;
+			case BPF_JMP | BPF_JNE | BPF_K:
+			case BPF_JMP | BPF_JEQ | BPF_K:
+			case BPF_JMP | BPF_JGT | BPF_K:
+			case BPF_JMP | BPF_JLT | BPF_K:
+			case BPF_JMP | BPF_JGE | BPF_K:
+			case BPF_JMP | BPF_JLE | BPF_K:
+				/*
+				 * Need sign-extended load, so only positive
+				 * values can be used as imm in cmplwi
+				 */
+				if (imm >= 0 && imm < 32768) {
+					EMIT(PPC_RAW_CMPLWI(dst_reg_h, 0));
+					PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+					EMIT(PPC_RAW_CMPLWI(dst_reg, imm));
+				} else {
+					/* sign-extending load ... but unsigned comparison */
+					PPC_EX32(__REG_R0, imm);
+					EMIT(PPC_RAW_CMPLW(dst_reg_h, __REG_R0));
+					PPC_LI32(__REG_R0, imm);
+					PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+					EMIT(PPC_RAW_CMPLW(dst_reg, __REG_R0));
+				}
+				break;
+			case BPF_JMP32 | BPF_JNE | BPF_K:
+			case BPF_JMP32 | BPF_JEQ | BPF_K:
+			case BPF_JMP32 | BPF_JGT | BPF_K:
+			case BPF_JMP32 | BPF_JLT | BPF_K:
+			case BPF_JMP32 | BPF_JGE | BPF_K:
+			case BPF_JMP32 | BPF_JLE | BPF_K:
+				if (imm >= 0 && imm < 65536) {
+					EMIT(PPC_RAW_CMPLWI(dst_reg, imm));
+				} else {
+					PPC_LI32(__REG_R0, imm);
+					EMIT(PPC_RAW_CMPLW(dst_reg, __REG_R0));
+				}
+				break;
+			}
+			case BPF_JMP | BPF_JSGT | BPF_K:
+			case BPF_JMP | BPF_JSLT | BPF_K:
+			case BPF_JMP | BPF_JSGE | BPF_K:
+			case BPF_JMP | BPF_JSLE | BPF_K:
+				if (imm >= 0 && imm < 65536) {
+					EMIT(PPC_RAW_CMPWI(dst_reg_h, imm < 0 ? -1 : 0));
+					PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+					EMIT(PPC_RAW_CMPLWI(dst_reg, imm));
+				} else {
+					/* sign-extending load */
+					EMIT(PPC_RAW_CMPWI(dst_reg_h, imm < 0 ? -1 : 0));
+					PPC_LI32(__REG_R0, imm);
+					PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+					EMIT(PPC_RAW_CMPLW(dst_reg, __REG_R0));
+				}
+				break;
+			case BPF_JMP32 | BPF_JSGT | BPF_K:
+			case BPF_JMP32 | BPF_JSLT | BPF_K:
+			case BPF_JMP32 | BPF_JSGE | BPF_K:
+			case BPF_JMP32 | BPF_JSLE | BPF_K:
+				/*
+				 * signed comparison, so any 16-bit value
+				 * can be used in cmpwi
+				 */
+				if (imm >= -32768 && imm < 32768) {
+					EMIT(PPC_RAW_CMPWI(dst_reg, imm));
+				} else {
+					/* sign-extending load */
+					PPC_LI32(__REG_R0, imm);
+					EMIT(PPC_RAW_CMPW(dst_reg, __REG_R0));
+				}
+				break;
+			case BPF_JMP | BPF_JSET | BPF_K:
+				/* andi does not sign-extend the immediate */
+				if (imm >= 0 && imm < 32768) {
+					/* PPC_ANDI is _only/always_ dot-form */
+					EMIT(PPC_RAW_ANDI(__REG_R0, dst_reg, imm));
+				} else {
+					PPC_LI32(__REG_R0, imm);
+					if (imm < 0) {
+						EMIT(PPC_RAW_CMPWI(dst_reg_h, 0));
+						PPC_BCC_SHORT(COND_NE, (ctx->idx + 2) * 4);
+					}
+					EMIT(PPC_RAW_AND_DOT(__REG_R0, dst_reg, __REG_R0));
+				}
+				break;
+			case BPF_JMP32 | BPF_JSET | BPF_K:
+				/* andi does not sign-extend the immediate */
+				if (imm >= -32768 && imm < 32768) {
+					/* PPC_ANDI is _only/always_ dot-form */
+					EMIT(PPC_RAW_ANDI(__REG_R0, dst_reg, imm));
+				} else {
+					PPC_LI32(__REG_R0, imm);
+					EMIT(PPC_RAW_AND_DOT(__REG_R0, dst_reg, __REG_R0));
+				}
+				break;
+			}
+			PPC_BCC(true_cond, addrs[i + 1 + off]);
+			break;
+
+		/*
+		 * Tail call
+		 */
+		case BPF_JMP | BPF_TAIL_CALL:
+			ctx->seen |= SEEN_TAILCALL;
+			bpf_jit_emit_tail_call(image, ctx, addrs[i + 1]);
+			break;
+
+		default:
+			/*
+			 * The filter contains something cruel & unusual.
+			 * We don't handle it, but also there shouldn't be
+			 * anything missing from our list.
+			 */
+			pr_err_ratelimited("eBPF filter opcode %04x (@%d) unsupported\n", code, i);
+			return -EOPNOTSUPP;
+		}
+		if (BPF_CLASS(code) == BPF_ALU && !fp->aux->verifier_zext &&
+		    !insn_is_zext(&insn[i + 1]))
+			EMIT(PPC_RAW_LI(dst_reg_h, 0));
+	}
+
+	/* Set end-of-body-code address for exit. */
+	addrs[i] = ctx->idx * 4;
+
+	return 0;
+}
-- 
2.25.0




^ permalink raw reply related

* [PATCH v2 6/8] powerpc/asm: Add some opcodes in asm/ppc-opcode.h for PPC32 eBPF
From: Christophe Leroy @ 2021-03-22 16:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ast,
	daniel, andrii, kafai, songliubraving, yhs, john.fastabend,
	kpsingh, naveen.n.rao, sandipan
  Cc: netdev, bpf, linuxppc-dev, linux-kernel
In-Reply-To: <cover.1616430991.git.christophe.leroy@csgroup.eu>

The following opcodes will be needed for the implementation
of eBPF for PPC32. Add them in asm/ppc-opcode.h

PPC_RAW_ADDE
PPC_RAW_ADDZE
PPC_RAW_ADDME
PPC_RAW_MFLR
PPC_RAW_ADDIC
PPC_RAW_ADDIC_DOT
PPC_RAW_SUBFC
PPC_RAW_SUBFE
PPC_RAW_SUBFIC
PPC_RAW_SUBFZE
PPC_RAW_ANDIS
PPC_RAW_NOR

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
 arch/powerpc/include/asm/ppc-opcode.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index ed161ef2b3ca..5b60020dc1f4 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -437,6 +437,9 @@
 #define PPC_RAW_STFDX(s, a, b)		(0x7c0005ae | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_LVX(t, a, b)		(0x7c0000ce | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_STVX(s, a, b)		(0x7c0001ce | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b))
+#define PPC_RAW_ADDE(t, a, b)		(0x7c000114 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b))
+#define PPC_RAW_ADDZE(t, a)		(0x7c000194 | ___PPC_RT(t) | ___PPC_RA(a))
+#define PPC_RAW_ADDME(t, a)		(0x7c0001d4 | ___PPC_RT(t) | ___PPC_RA(a))
 #define PPC_RAW_ADD(t, a, b)		(PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_ADD_DOT(t, a, b)	(PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
 #define PPC_RAW_ADDC(t, a, b)		(0x7c000014 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b))
@@ -445,11 +448,14 @@
 #define PPC_RAW_BLR()			(PPC_INST_BLR)
 #define PPC_RAW_BLRL()			(0x4e800021)
 #define PPC_RAW_MTLR(r)			(0x7c0803a6 | ___PPC_RT(r))
+#define PPC_RAW_MFLR(t)			(PPC_INST_MFLR | ___PPC_RT(t))
 #define PPC_RAW_BCTR()			(PPC_INST_BCTR)
 #define PPC_RAW_MTCTR(r)		(PPC_INST_MTCTR | ___PPC_RT(r))
 #define PPC_RAW_ADDI(d, a, i)		(PPC_INST_ADDI | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i))
 #define PPC_RAW_LI(r, i)		PPC_RAW_ADDI(r, 0, i)
 #define PPC_RAW_ADDIS(d, a, i)		(PPC_INST_ADDIS | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i))
+#define PPC_RAW_ADDIC(d, a, i)		(0x30000000 | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i))
+#define PPC_RAW_ADDIC_DOT(d, a, i)	(0x34000000 | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i))
 #define PPC_RAW_LIS(r, i)		PPC_RAW_ADDIS(r, 0, i)
 #define PPC_RAW_STDX(r, base, b)	(0x7c00012a | ___PPC_RS(r) | ___PPC_RA(base) | ___PPC_RB(b))
 #define PPC_RAW_STDU(r, base, i)	(0xf8000001 | ___PPC_RS(r) | ___PPC_RA(base) | ((i) & 0xfffc))
@@ -472,6 +478,10 @@
 #define PPC_RAW_CMPLW(a, b)		(0x7c000040 | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_CMPLD(a, b)		(0x7c200040 | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_SUB(d, a, b)		(0x7c000050 | ___PPC_RT(d) | ___PPC_RB(a) | ___PPC_RA(b))
+#define PPC_RAW_SUBFC(d, a, b)		(0x7c000010 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b))
+#define PPC_RAW_SUBFE(d, a, b)		(0x7c000110 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b))
+#define PPC_RAW_SUBFIC(d, a, i)		(0x20000000 | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i))
+#define PPC_RAW_SUBFZE(d, a)		(0x7c000190 | ___PPC_RT(d) | ___PPC_RA(a))
 #define PPC_RAW_MULD(d, a, b)		(0x7c0001d2 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_MULW(d, a, b)		(0x7c0001d6 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b))
 #define PPC_RAW_MULHWU(d, a, b)		(0x7c000016 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b))
@@ -484,11 +494,13 @@
 #define PPC_RAW_DIVDEU_DOT(t, a, b)	(0x7c000312 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1)
 #define PPC_RAW_AND(d, a, b)		(0x7c000038 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b))
 #define PPC_RAW_ANDI(d, a, i)		(0x70000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
+#define PPC_RAW_ANDIS(d, a, i)		(0x74000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
 #define PPC_RAW_AND_DOT(d, a, b)	(0x7c000039 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b))
 #define PPC_RAW_OR(d, a, b)		(0x7c000378 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b))
 #define PPC_RAW_MR(d, a)		PPC_RAW_OR(d, a, a)
 #define PPC_RAW_ORI(d, a, i)		(PPC_INST_ORI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
 #define PPC_RAW_ORIS(d, a, i)		(PPC_INST_ORIS | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
+#define PPC_RAW_NOR(d, a, b)		(0x7c0000f8 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b))
 #define PPC_RAW_XOR(d, a, b)		(0x7c000278 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b))
 #define PPC_RAW_XORI(d, a, i)		(0x68000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
 #define PPC_RAW_XORIS(d, a, i)		(0x6c000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i))
-- 
2.25.0


^ permalink raw reply related


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