LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 3/9] powerpc/powernv: opal_put_chars partial write fix
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

The intention here is to consume and discard the remaining buffer
upon error. This works if there has not been a previous partial write.
If there has been, then total_len is no longer total number of bytes
to copy. total_len is always "bytes left to copy", so it should be
added to written bytes.

This code may not be exercised any more if partial writes will not be
hit, but this is a small bugfix before a larger change.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 516e23de5a3d..87d4c0aa7f64 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -388,7 +388,7 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 		/* Closed or other error drop */
 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
 		    rc != OPAL_BUSY_EVENT) {
-			written = total_len;
+			written += total_len;
 			break;
 		}
 		if (rc == OPAL_SUCCESS) {
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 4/9] powerpc/powernv: OPAL console standardise OPAL_BUSY loops
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

Convert to using the standard delay poll/delay form.

The console code:

- Did not previously delay or sleep in its busy loop.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal.c | 38 ++++++++++++++++-----------
 1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 87d4c0aa7f64..473c8ce14a34 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -378,33 +378,41 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 	/* We still try to handle partial completions, though they
 	 * should no longer happen.
 	 */
-	rc = OPAL_BUSY;
-	while(total_len > 0 && (rc == OPAL_BUSY ||
-				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
+
+	while (total_len > 0) {
 		olen = cpu_to_be64(total_len);
-		rc = opal_console_write(vtermno, &olen, data);
+
+		rc = OPAL_BUSY;
+		while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+			rc = opal_console_write(vtermno, &olen, data);
+			if (rc == OPAL_BUSY_EVENT) {
+				mdelay(OPAL_BUSY_DELAY_MS);
+				opal_poll_events(NULL);
+			} else if (rc == OPAL_BUSY) {
+				mdelay(OPAL_BUSY_DELAY_MS);
+			}
+		}
+
 		len = be64_to_cpu(olen);
 
 		/* Closed or other error drop */
-		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
-		    rc != OPAL_BUSY_EVENT) {
-			written += total_len;
+		if (rc != OPAL_SUCCESS) {
+			written += total_len; /* drop remaining chars */
 			break;
 		}
-		if (rc == OPAL_SUCCESS) {
-			total_len -= len;
-			data += len;
-			written += len;
-		}
+
+		total_len -= len;
+		data += len;
+		written += len;
+
 		/* This is a bit nasty but we need that for the console to
 		 * flush when there aren't any interrupts. We will clean
 		 * things a bit later to limit that to synchronous path
 		 * such as the kernel console and xmon/udbg
 		 */
-		do
+		do {
 			opal_poll_events(&evt);
-		while(rc == OPAL_SUCCESS &&
-			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
+		} while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
 	}
 	spin_unlock_irqrestore(&opal_write_lock, flags);
 	return written;
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 5/9] powerpc/powernv: OPAL platform standardise OPAL_BUSY loops
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

Convert to using the standard delay poll/delay form.

The platform code:

- Used delay when called from a schedule()able context.
- Did not previously delay or sleep in the OPAL_BUSY_EVENT case.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal.c  |  8 +++++---
 arch/powerpc/platforms/powernv/setup.c | 16 ++++++++++------
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 473c8ce14a34..0f03199a8664 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -907,10 +907,12 @@ void opal_shutdown(void)
 	 */
 	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_sync_host_reboot();
-		if (rc == OPAL_BUSY)
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
 			opal_poll_events(NULL);
-		else
-			mdelay(10);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 
 	/* Unregister memory dump region */
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index ef8c9ce53a61..ddaea5ba9008 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -217,10 +217,12 @@ static void  __noreturn pnv_restart(char *cmd)
 
 	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_cec_reboot();
-		if (rc == OPAL_BUSY_EVENT)
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
 			opal_poll_events(NULL);
-		else
-			mdelay(10);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 	for (;;)
 		opal_poll_events(NULL);
@@ -234,10 +236,12 @@ static void __noreturn pnv_power_off(void)
 
 	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_cec_power_down(0);
-		if (rc == OPAL_BUSY_EVENT)
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
 			opal_poll_events(NULL);
-		else
-			mdelay(10);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 	for (;;)
 		opal_poll_events(NULL);
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 6/9] powerpc/powernv: OPAL NVRAM driver standardise OPAL_BUSY delays
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

Convert to using the standard delay poll/delay form.

The NVRAM driver:

- Did not previously delay or sleep in its busy loop.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal-nvram.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
index ba2ff06a2c98..1bceb95f422d 100644
--- a/arch/powerpc/platforms/powernv/opal-nvram.c
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
@@ -11,6 +11,7 @@
 
 #define DEBUG
 
+#include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/of.h>
@@ -56,8 +57,12 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
 
 	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_write_nvram(__pa(buf), count, off);
-		if (rc == OPAL_BUSY_EVENT)
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
 			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 
 	if (rc)
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 7/9] powerpc/powernv: OPAL dump support standardise OPAL_BUSY delays
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Vasant Hegde
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

Convert to using the standard delay poll/delay form.

The dump code:

- Did not previously delay or sleep in the OPAL_BUSY case.
- Used a 20ms sleep.

Cc: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal-dump.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c
index 0dc8fa4e0af2..4ab749ceafc2 100644
--- a/arch/powerpc/platforms/powernv/opal-dump.c
+++ b/arch/powerpc/platforms/powernv/opal-dump.c
@@ -260,12 +260,14 @@ static int64_t dump_read_data(struct dump_obj *dump)
 	addr = __pa(list);
 
 	/* Fetch data */
-	rc = OPAL_BUSY_EVENT;
+	rc = OPAL_BUSY;
 	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_dump_read(dump->id, addr);
 		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
 			opal_poll_events(NULL);
-			msleep(20);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
 		}
 	}
 
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 8/9] powerpc/xive: standardise OPAL_BUSY delays
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

Convert to using the standard delay poll/delay form.

The XIVE driver:

- Did not previously loop on the OPAL_BUSY_EVENT case.
- Used a 1ms sleep.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/sysdev/xive/native.c | 193 +++++++++++++++++-------------
 1 file changed, 111 insertions(+), 82 deletions(-)

diff --git a/arch/powerpc/sysdev/xive/native.c b/arch/powerpc/sysdev/xive/native.c
index d22aeb0b69e1..682f79dabb4a 100644
--- a/arch/powerpc/sysdev/xive/native.c
+++ b/arch/powerpc/sysdev/xive/native.c
@@ -103,14 +103,18 @@ EXPORT_SYMBOL_GPL(xive_native_populate_irq_data);
 
 int xive_native_configure_irq(u32 hw_irq, u32 target, u8 prio, u32 sw_irq)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_irq_config(hw_irq, target, prio, sw_irq);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
+
 	return rc == 0 ? 0 : -ENXIO;
 }
 EXPORT_SYMBOL_GPL(xive_native_configure_irq);
@@ -159,12 +163,17 @@ int xive_native_configure_queue(u32 vp_id, struct xive_q *q, u8 prio,
 	}
 
 	/* Configure and enable the queue in HW */
-	for (;;) {
+	rc = OPAL_BUSY;
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_queue_info(vp_id, prio, qpage_phys, order, flags);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
+
 	if (rc) {
 		pr_err("Error %lld setting queue for prio %d\n", rc, prio);
 		rc = -EIO;
@@ -183,14 +192,17 @@ EXPORT_SYMBOL_GPL(xive_native_configure_queue);
 
 static void __xive_native_disable_queue(u32 vp_id, struct xive_q *q, u8 prio)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 
 	/* Disable the queue in HW */
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_queue_info(vp_id, prio, 0, 0, 0);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 	if (rc)
 		pr_err("Error %lld disabling queue for prio %d\n", rc, prio);
@@ -240,7 +252,7 @@ static int xive_native_get_ipi(unsigned int cpu, struct xive_cpu *xc)
 {
 	struct device_node *np;
 	unsigned int chip_id;
-	s64 irq;
+	s64 rc = OPAL_BUSY;
 
 	/* Find the chip ID */
 	np = of_get_cpu_node(cpu, NULL);
@@ -250,33 +262,39 @@ static int xive_native_get_ipi(unsigned int cpu, struct xive_cpu *xc)
 	}
 
 	/* Allocate an IPI and populate info about it */
-	for (;;) {
-		irq = opal_xive_allocate_irq(chip_id);
-		if (irq == OPAL_BUSY) {
-			msleep(1);
-			continue;
-		}
-		if (irq < 0) {
-			pr_err("Failed to allocate IPI on CPU %d\n", cpu);
-			return -ENXIO;
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+		rc = opal_xive_allocate_irq(chip_id);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
 		}
-		xc->hw_ipi = irq;
-		break;
 	}
+	if (rc < 0) {
+		pr_err("Failed to allocate IPI on CPU %d\n", cpu);
+		return -ENXIO;
+	}
+	xc->hw_ipi = rc;
+
 	return 0;
 }
 #endif /* CONFIG_SMP */
 
 u32 xive_native_alloc_irq(void)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_allocate_irq(OPAL_XIVE_ANY_CHIP);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
+
 	if (rc < 0)
 		return 0;
 	return rc;
@@ -285,11 +303,16 @@ EXPORT_SYMBOL_GPL(xive_native_alloc_irq);
 
 void xive_native_free_irq(u32 irq)
 {
-	for (;;) {
-		s64 rc = opal_xive_free_irq(irq);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+	s64 rc = OPAL_BUSY;
+
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+		rc = opal_xive_free_irq(irq);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 }
 EXPORT_SYMBOL_GPL(xive_native_free_irq);
@@ -297,20 +320,11 @@ EXPORT_SYMBOL_GPL(xive_native_free_irq);
 #ifdef CONFIG_SMP
 static void xive_native_put_ipi(unsigned int cpu, struct xive_cpu *xc)
 {
-	s64 rc;
-
 	/* Free the IPI */
 	if (!xc->hw_ipi)
 		return;
-	for (;;) {
-		rc = opal_xive_free_irq(xc->hw_ipi);
-		if (rc == OPAL_BUSY) {
-			msleep(1);
-			continue;
-		}
-		xc->hw_ipi = 0;
-		break;
-	}
+	xive_native_free_irq(xc->hw_ipi);
+	xc->hw_ipi = 0;
 }
 #endif /* CONFIG_SMP */
 
@@ -381,7 +395,7 @@ static void xive_native_eoi(u32 hw_irq)
 
 static void xive_native_setup_cpu(unsigned int cpu, struct xive_cpu *xc)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 	u32 vp;
 	__be64 vp_cam_be;
 	u64 vp_cam;
@@ -392,12 +406,16 @@ static void xive_native_setup_cpu(unsigned int cpu, struct xive_cpu *xc)
 	/* Enable the pool VP */
 	vp = xive_pool_vps + cpu;
 	pr_debug("CPU %d setting up pool VP 0x%x\n", cpu, vp);
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_vp_info(vp, OPAL_XIVE_VP_ENABLED, 0);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
+
 	if (rc) {
 		pr_err("Failed to enable pool VP on CPU %d\n", cpu);
 		return;
@@ -425,7 +443,7 @@ static void xive_native_setup_cpu(unsigned int cpu, struct xive_cpu *xc)
 
 static void xive_native_teardown_cpu(unsigned int cpu, struct xive_cpu *xc)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 	u32 vp;
 
 	if (xive_pool_vps == XIVE_INVALID_VP)
@@ -436,11 +454,14 @@ static void xive_native_teardown_cpu(unsigned int cpu, struct xive_cpu *xc)
 
 	/* Disable it */
 	vp = xive_pool_vps + cpu;
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_vp_info(vp, 0, 0);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 }
 
@@ -627,7 +648,7 @@ static bool xive_native_provision_pages(void)
 
 u32 xive_native_alloc_vp_block(u32 max_vcpus)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 	u32 order;
 
 	order = fls(max_vcpus) - 1;
@@ -637,25 +658,25 @@ u32 xive_native_alloc_vp_block(u32 max_vcpus)
 	pr_debug("VP block alloc, for max VCPUs %d use order %d\n",
 		 max_vcpus, order);
 
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_alloc_vp_block(order);
-		switch (rc) {
-		case OPAL_BUSY:
-			msleep(1);
-			break;
-		case OPAL_XIVE_PROVISIONING:
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		} else if (rc == OPAL_XIVE_PROVISIONING) {
 			if (!xive_native_provision_pages())
 				return XIVE_INVALID_VP;
-			break;
-		default:
-			if (rc < 0) {
-				pr_err("OPAL failed to allocate VCPUs order %d, err %lld\n",
-				       order, rc);
-				return XIVE_INVALID_VP;
-			}
-			return rc;
+			rc = OPAL_BUSY; /* go around again */
 		}
 	}
+	if (rc < 0) {
+		pr_err("OPAL failed to allocate VCPUs order %d, err %lld\n",
+		       order, rc);
+		return XIVE_INVALID_VP;
+	}
+	return rc;
 }
 EXPORT_SYMBOL_GPL(xive_native_alloc_vp_block);
 
@@ -674,30 +695,38 @@ EXPORT_SYMBOL_GPL(xive_native_free_vp_block);
 
 int xive_native_enable_vp(u32 vp_id, bool single_escalation)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 	u64 flags = OPAL_XIVE_VP_ENABLED;
 
 	if (single_escalation)
 		flags |= OPAL_XIVE_VP_SINGLE_ESCALATION;
-	for (;;) {
+
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_vp_info(vp_id, flags, 0);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
+
 	return rc ? -EIO : 0;
 }
 EXPORT_SYMBOL_GPL(xive_native_enable_vp);
 
 int xive_native_disable_vp(u32 vp_id)
 {
-	s64 rc;
+	s64 rc = OPAL_BUSY;
 
-	for (;;) {
+	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
 		rc = opal_xive_set_vp_info(vp_id, 0, 0);
-		if (rc != OPAL_BUSY)
-			break;
-		msleep(1);
+		if (rc == OPAL_BUSY_EVENT) {
+			msleep(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY) {
+			msleep(OPAL_BUSY_DELAY_MS);
+		}
 	}
 	return rc ? -EIO : 0;
 }
-- 
2.17.0

^ permalink raw reply related

* [PATCH v2 9/9] powerpc/powernv: opal-kmsg standardise OPAL_BUSY handling
From: Nicholas Piggin @ 2018-04-09  5:24 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Russell Currey
In-Reply-To: <20180409052431.26405-1-npiggin@gmail.com>

OPAL_CONSOLE_FLUSH is documented as being able to return OPAL_BUSY,
so implement the standard OPAL_BUSY handling for it.

Cc: Russell Currey <ruscur@russell.cc>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal-kmsg.c | 24 ++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal-kmsg.c b/arch/powerpc/platforms/powernv/opal-kmsg.c
index 6f1214d4de92..f8f41ccce75f 100644
--- a/arch/powerpc/platforms/powernv/opal-kmsg.c
+++ b/arch/powerpc/platforms/powernv/opal-kmsg.c
@@ -12,6 +12,7 @@
  */
 
 #include <linux/kmsg_dump.h>
+#include <linux/delay.h>
 
 #include <asm/opal.h>
 #include <asm/opal-api.h>
@@ -26,8 +27,7 @@
 static void force_opal_console_flush(struct kmsg_dumper *dumper,
 				     enum kmsg_dump_reason reason)
 {
-	int i;
-	int64_t ret;
+	s64 rc;
 
 	/*
 	 * Outside of a panic context the pollers will continue to run,
@@ -37,14 +37,22 @@ static void force_opal_console_flush(struct kmsg_dumper *dumper,
 		return;
 
 	if (opal_check_token(OPAL_CONSOLE_FLUSH)) {
-		ret = opal_console_flush(0);
+		do  {
+			rc = OPAL_BUSY;
+			while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+				rc = opal_console_flush(0);
+				if (rc == OPAL_BUSY_EVENT) {
+					mdelay(OPAL_BUSY_DELAY_MS);
+					opal_poll_events(NULL);
+				} else if (rc == OPAL_BUSY) {
+					mdelay(OPAL_BUSY_DELAY_MS);
+				}
+			}
+		} while (rc == OPAL_PARTIAL); /* More to flush */
 
-		if (ret == OPAL_UNSUPPORTED || ret == OPAL_PARAMETER)
-			return;
-
-		/* Incrementally flush until there's nothing left */
-		while (opal_console_flush(0) != OPAL_SUCCESS);
 	} else {
+		int i;
+
 		/*
 		 * If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
 		 * the console can still be flushed by calling the polling
-- 
2.17.0

^ permalink raw reply related

* [PATCH 0/6] improve OPAL cosole flushing and locking
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin

This series of patches aims to improve the robustness of the OPAL
console by simplifying and removing locks. If this code is interrupted
due to a panic or xmon request, it may block the xmon IO or panic
console flush because they use the opal console too.

So reducing locking and relaxing atomicity requirements is important
for improving kernel debugging.

Nicholas Piggin (6):
  powerpc/powernv: opal-kmsg use flush fallback from console code
  powerpc/powernv: Implement and use opal_flush_console
  powerpc/powernv: Remove OPALv1 support from opal console driver
  powerpc/powernv: move opal console flushing to udbg
  powerpc/powernv: implement opal_put_chars_nonatomic
  drivers/tty/hvc: remove unexplained "just in case" spin delay

 arch/powerpc/include/asm/opal.h            |   2 +
 arch/powerpc/platforms/powernv/opal-kmsg.c |  38 +-----
 arch/powerpc/platforms/powernv/opal.c      | 141 ++++++++++++++-------
 drivers/tty/hvc/hvc_opal.c                 |  17 ++-
 4 files changed, 106 insertions(+), 92 deletions(-)

-- 
2.17.0

^ permalink raw reply

* [PATCH 1/6] powerpc/powernv: opal-kmsg use flush fallback from console code
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Russell Currey
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

Use the more refined and tested event polling loop from opal_put_chars
as the fallback console flush in the opal-kmsg path. This loop is used
by the console driver today, whereas the opal-kmsg fallback is not
likely to have been used for years.

Use WARN_ONCE rather than a printk when the fallback is invoked to
prepare for moving the console flush into a common function.

Cc: Russell Currey <ruscur@russell.cc>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal-kmsg.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal-kmsg.c b/arch/powerpc/platforms/powernv/opal-kmsg.c
index f8f41ccce75f..fd2bbf4fd6dc 100644
--- a/arch/powerpc/platforms/powernv/opal-kmsg.c
+++ b/arch/powerpc/platforms/powernv/opal-kmsg.c
@@ -51,20 +51,17 @@ static void force_opal_console_flush(struct kmsg_dumper *dumper,
 		} while (rc == OPAL_PARTIAL); /* More to flush */
 
 	} else {
-		int i;
+		__be64 evt;
 
+		WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
 		/*
 		 * If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
 		 * the console can still be flushed by calling the polling
-		 * function enough times to flush the buffer.  We don't know
-		 * how much output still needs to be flushed, but we can be
-		 * generous since the kernel is in panic and doesn't need
-		 * to do much else.
+		 * function while it has OPAL_EVENT_CONSOLE_OUTPUT events.
 		 */
-		printk(KERN_NOTICE "opal: OPAL_CONSOLE_FLUSH missing.\n");
-		for (i = 0; i < 1024; i++) {
-			opal_poll_events(NULL);
-		}
+		do {
+			opal_poll_events(&evt);
+		} while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
 	}
 }
 
-- 
2.17.0

^ permalink raw reply related

* [PATCH 2/6] powerpc/powernv: Implement and use opal_flush_console
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt, Russell Currey
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

A new console flushing firmware API was introduced to replace event
polling loops, and implemented in opal-kmsg with affddff69c55e
("powerpc/powernv: Add a kmsg_dumper that flushes console output on
panic"), to flush the console in the panic path.

The OPAL console driver has other situations where interrupts are off
and it needs to flush the console synchronously. These still use a
polling loop.

So move the opal-kmsg flush code to opal_flush_console, and use the
new function in opal-kmsg and opal_put_chars.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Russell Currey <ruscur@russell.cc>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/include/asm/opal.h            |  1 +
 arch/powerpc/platforms/powernv/opal-kmsg.c | 35 ++----------------
 arch/powerpc/platforms/powernv/opal.c      | 42 +++++++++++++++++++---
 3 files changed, 42 insertions(+), 36 deletions(-)

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 03e1a920491e..bbff49fab0e5 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -303,6 +303,7 @@ extern void opal_configure_cores(void);
 
 extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
 extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
+extern int opal_flush_console(uint32_t vtermno);
 
 extern void hvc_opal_init_early(void);
 
diff --git a/arch/powerpc/platforms/powernv/opal-kmsg.c b/arch/powerpc/platforms/powernv/opal-kmsg.c
index fd2bbf4fd6dc..55691950d981 100644
--- a/arch/powerpc/platforms/powernv/opal-kmsg.c
+++ b/arch/powerpc/platforms/powernv/opal-kmsg.c
@@ -12,7 +12,6 @@
  */
 
 #include <linux/kmsg_dump.h>
-#include <linux/delay.h>
 
 #include <asm/opal.h>
 #include <asm/opal-api.h>
@@ -24,11 +23,9 @@
  * may not be completely printed.  This function does not actually dump the
  * message, it just ensures that OPAL completely flushes the console buffer.
  */
-static void force_opal_console_flush(struct kmsg_dumper *dumper,
+static void kmsg_dump_opal_console_flush(struct kmsg_dumper *dumper,
 				     enum kmsg_dump_reason reason)
 {
-	s64 rc;
-
 	/*
 	 * Outside of a panic context the pollers will continue to run,
 	 * so we don't need to do any special flushing.
@@ -36,37 +33,11 @@ static void force_opal_console_flush(struct kmsg_dumper *dumper,
 	if (reason != KMSG_DUMP_PANIC)
 		return;
 
-	if (opal_check_token(OPAL_CONSOLE_FLUSH)) {
-		do  {
-			rc = OPAL_BUSY;
-			while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
-				rc = opal_console_flush(0);
-				if (rc == OPAL_BUSY_EVENT) {
-					mdelay(OPAL_BUSY_DELAY_MS);
-					opal_poll_events(NULL);
-				} else if (rc == OPAL_BUSY) {
-					mdelay(OPAL_BUSY_DELAY_MS);
-				}
-			}
-		} while (rc == OPAL_PARTIAL); /* More to flush */
-
-	} else {
-		__be64 evt;
-
-		WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
-		/*
-		 * If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
-		 * the console can still be flushed by calling the polling
-		 * function while it has OPAL_EVENT_CONSOLE_OUTPUT events.
-		 */
-		do {
-			opal_poll_events(&evt);
-		} while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
-	}
+	opal_flush_console(0);
 }
 
 static struct kmsg_dumper opal_kmsg_dumper = {
-	.dump = force_opal_console_flush
+	.dump = kmsg_dump_opal_console_flush
 };
 
 void __init opal_kmsg_init(void)
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 0f03199a8664..d54ac3736c34 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -350,7 +350,6 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 	__be64 olen;
 	s64 len, rc;
 	unsigned long flags;
-	__be64 evt;
 
 	if (!opal.entry)
 		return -ENODEV;
@@ -371,7 +370,7 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 		/* Closed -> drop characters */
 		if (rc)
 			return total_len;
-		opal_poll_events(NULL);
+		opal_flush_console(vtermno);
 		return -EAGAIN;
 	}
 
@@ -410,12 +409,47 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 		 * things a bit later to limit that to synchronous path
 		 * such as the kernel console and xmon/udbg
 		 */
+		opal_flush_console(vtermno);
+	}
+	spin_unlock_irqrestore(&opal_write_lock, flags);
+
+	return written;
+}
+
+int opal_flush_console(uint32_t vtermno)
+{
+	s64 rc;
+
+	if (!opal_check_token(OPAL_CONSOLE_FLUSH)) {
+		__be64 evt;
+
+		WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
+		/*
+		 * If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
+		 * the console can still be flushed by calling the polling
+		 * function while it has OPAL_EVENT_CONSOLE_OUTPUT events.
+		 */
 		do {
 			opal_poll_events(&evt);
 		} while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
+
+		return OPAL_SUCCESS;
 	}
-	spin_unlock_irqrestore(&opal_write_lock, flags);
-	return written;
+
+	do  {
+		rc = OPAL_BUSY;
+		while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+			rc = opal_console_flush(vtermno);
+			if (rc == OPAL_BUSY_EVENT) {
+				mdelay(OPAL_BUSY_DELAY_MS);
+				opal_poll_events(NULL);
+			} else if (rc == OPAL_BUSY) {
+				mdelay(OPAL_BUSY_DELAY_MS);
+			}
+		}
+	} while (rc == OPAL_PARTIAL); /* More to flush */
+
+	return opal_error_code(rc);
 }
 
 static int opal_recover_mce(struct pt_regs *regs,
-- 
2.17.0

^ permalink raw reply related

* [PATCH 3/6] powerpc/powernv: Remove OPALv1 support from opal console driver
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

opal_put_chars deals with partial writes because in OPALv1,
opal_console_write_buffer_space did not work correctly. That firmware
is not supported.

This reworks the opal_put_chars code to no longer deal with partial
writes and turn them into full writes. Partial write handling is still
supported in terms of what gets returned to the caller, but it may not
go to the console atomically. A warning message is printed in this
case.

This allows console flushing to be moved out of the opal_write_lock
spinlock. That could cause the lock to be held for long periods if the
console is busy (especially if it was being spammed by firmware),
which is dangerous because the lock is taken by xmon to debug the
system. Flushing outside the lock improves the situation a bit.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal.c | 86 +++++++++++++--------------
 1 file changed, 40 insertions(+), 46 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index d54ac3736c34..a045c446a910 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -346,10 +346,10 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
 
 int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 {
-	int written = 0;
-	__be64 olen;
-	s64 len, rc;
 	unsigned long flags;
+	int written;
+	__be64 olen;
+	s64 rc;
 
 	if (!opal.entry)
 		return -ENODEV;
@@ -357,62 +357,56 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 	/* We want put_chars to be atomic to avoid mangling of hvsi
 	 * packets. To do that, we first test for room and return
 	 * -EAGAIN if there isn't enough.
-	 *
-	 * Unfortunately, opal_console_write_buffer_space() doesn't
-	 * appear to work on opal v1, so we just assume there is
-	 * enough room and be done with it
 	 */
 	spin_lock_irqsave(&opal_write_lock, flags);
 	rc = opal_console_write_buffer_space(vtermno, &olen);
-	len = be64_to_cpu(olen);
-	if (rc || len < total_len) {
-		spin_unlock_irqrestore(&opal_write_lock, flags);
+	if (rc || be64_to_cpu(olen) < total_len) {
 		/* Closed -> drop characters */
 		if (rc)
-			return total_len;
-		opal_flush_console(vtermno);
-		return -EAGAIN;
+			written = total_len;
+		else
+			written = -EAGAIN;
+		goto out;
 	}
 
-	/* We still try to handle partial completions, though they
-	 * should no longer happen.
-	 */
-
-	while (total_len > 0) {
-		olen = cpu_to_be64(total_len);
-
-		rc = OPAL_BUSY;
-		while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
-			rc = opal_console_write(vtermno, &olen, data);
-			if (rc == OPAL_BUSY_EVENT) {
-				mdelay(OPAL_BUSY_DELAY_MS);
-				opal_poll_events(NULL);
-			} else if (rc == OPAL_BUSY) {
-				mdelay(OPAL_BUSY_DELAY_MS);
-			}
-		}
-
-		len = be64_to_cpu(olen);
-
-		/* Closed or other error drop */
-		if (rc != OPAL_SUCCESS) {
-			written += total_len; /* drop remaining chars */
-			break;
+	/* Should not get a partial write here because space is available. */
+	olen = cpu_to_be64(total_len);
+	rc = opal_console_write(vtermno, &olen, data);
+	if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+		if (rc == OPAL_BUSY_EVENT) {
+			mdelay(OPAL_BUSY_DELAY_MS);
+			opal_poll_events(NULL);
+		} else if (rc == OPAL_BUSY_EVENT) {
+			mdelay(OPAL_BUSY_DELAY_MS);
 		}
+		written = -EAGAIN;
+		goto out;
+	}
 
-		total_len -= len;
-		data += len;
-		written += len;
+	/* Closed or other error drop */
+	if (rc != OPAL_SUCCESS) {
+		written = opal_error_code(rc);
+		goto out;
+	}
 
-		/* This is a bit nasty but we need that for the console to
-		 * flush when there aren't any interrupts. We will clean
-		 * things a bit later to limit that to synchronous path
-		 * such as the kernel console and xmon/udbg
-		 */
-		opal_flush_console(vtermno);
+	written = be64_to_cpu(olen);
+	if (written < total_len) {
+		/* Should not happen */
+		pr_warn("atomic console write returned partial len=%d written=%d\n", total_len, written);
+		if (!written)
+			written = -EAGAIN;
 	}
+
+out:
 	spin_unlock_irqrestore(&opal_write_lock, flags);
 
+	/* This is a bit nasty but we need that for the console to
+	 * flush when there aren't any interrupts. We will clean
+	 * things a bit later to limit that to synchronous path
+	 * such as the kernel console and xmon/udbg
+	 */
+	opal_flush_console(vtermno);
+
 	return written;
 }
 
-- 
2.17.0

^ permalink raw reply related

* [PATCH 4/6] powerpc/powernv: move opal console flushing to udbg
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

OPAL console writes do not have to synchronously flush firmware /
hardware buffers unless they are going through the udbg path.

Remove the unconditional flushing from opal_put_chars. Flush if
there was no space in the buffer as an optimisation (callers loop
waiting for success in that case). udbg flushing is moved to
udbg_opal_putc.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/platforms/powernv/opal.c | 12 +++++++-----
 drivers/tty/hvc/hvc_opal.c            |  5 +++++
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index a045c446a910..b05500a70f58 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -400,12 +400,14 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 out:
 	spin_unlock_irqrestore(&opal_write_lock, flags);
 
-	/* This is a bit nasty but we need that for the console to
-	 * flush when there aren't any interrupts. We will clean
-	 * things a bit later to limit that to synchronous path
-	 * such as the kernel console and xmon/udbg
+	/* In the -EAGAIN case, callers loop, so we have to flush the console
+	 * here in case they have interrupts off (and we don't want to wait
+	 * for async flushing if we can make immediate progress here). If
+	 * necessary the API could be made entirely non-flushing if the
+	 * callers had a ->flush API to use.
 	 */
-	opal_flush_console(vtermno);
+	if (written == -EAGAIN)
+		opal_flush_console(vtermno);
 
 	return written;
 }
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index 2ed07ca6389e..af122ad7f06d 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -275,6 +275,11 @@ static void udbg_opal_putc(char c)
 			count = hvc_opal_hvsi_put_chars(termno, &c, 1);
 			break;
 		}
+
+		/* This is needed for the cosole to flush
+		 * when there aren't any interrupts.
+		 */
+		opal_flush_console(termno);
 	} while(count == 0 || count == -EAGAIN);
 }
 
-- 
2.17.0

^ permalink raw reply related

* [PATCH 5/6] powerpc/powernv: implement opal_put_chars_nonatomic
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

The RAW console does not need writes to be atomic, so implement a
_nonatomic variant which does not take a spinlock. This API is used
in xmon, so the less locking thta's used, the better chance there is
that a crash can be debugged.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 arch/powerpc/include/asm/opal.h       |  1 +
 arch/powerpc/platforms/powernv/opal.c | 35 +++++++++++++++++++--------
 drivers/tty/hvc/hvc_opal.c            |  4 +--
 3 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index bbff49fab0e5..66954d671831 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -303,6 +303,7 @@ extern void opal_configure_cores(void);
 
 extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
 extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
+extern int opal_put_chars_nonatomic(uint32_t vtermno, const char *buf, int total_len);
 extern int opal_flush_console(uint32_t vtermno);
 
 extern void hvc_opal_init_early(void);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index b05500a70f58..dc77fc57d1e9 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -344,9 +344,9 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
 	return 0;
 }
 
-int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
+static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, bool atomic)
 {
-	unsigned long flags;
+	unsigned long flags = 0 /* shut up gcc */;
 	int written;
 	__be64 olen;
 	s64 rc;
@@ -354,11 +354,8 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 	if (!opal.entry)
 		return -ENODEV;
 
-	/* We want put_chars to be atomic to avoid mangling of hvsi
-	 * packets. To do that, we first test for room and return
-	 * -EAGAIN if there isn't enough.
-	 */
-	spin_lock_irqsave(&opal_write_lock, flags);
+	if (atomic)
+		spin_lock_irqsave(&opal_write_lock, flags);
 	rc = opal_console_write_buffer_space(vtermno, &olen);
 	if (rc || be64_to_cpu(olen) < total_len) {
 		/* Closed -> drop characters */
@@ -391,14 +388,18 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 
 	written = be64_to_cpu(olen);
 	if (written < total_len) {
-		/* Should not happen */
-		pr_warn("atomic console write returned partial len=%d written=%d\n", total_len, written);
+		if (atomic) {
+			/* Should not happen */
+			pr_warn("atomic console write returned partial "
+				"len=%d written=%d\n", total_len, written);
+		}
 		if (!written)
 			written = -EAGAIN;
 	}
 
 out:
-	spin_unlock_irqrestore(&opal_write_lock, flags);
+	if (atomic)
+		spin_unlock_irqrestore(&opal_write_lock, flags);
 
 	/* In the -EAGAIN case, callers loop, so we have to flush the console
 	 * here in case they have interrupts off (and we don't want to wait
@@ -412,6 +413,20 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
 	return written;
 }
 
+int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
+{
+	/* We want put_chars to be atomic to avoid mangling of hvsi
+	 * packets. To do that, we first test for room and return
+	 * -EAGAIN if there isn't enough.
+	 */
+	return __opal_put_chars(vtermno, data, total_len, true);
+}
+
+int opal_put_chars_nonatomic(uint32_t vtermno, const char *data, int total_len)
+{
+	return __opal_put_chars(vtermno, data, total_len, false);
+}
+
 int opal_flush_console(uint32_t vtermno)
 {
 	s64 rc;
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index af122ad7f06d..e151cfacf2a7 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -51,7 +51,7 @@ static u32 hvc_opal_boot_termno;
 
 static const struct hv_ops hvc_opal_raw_ops = {
 	.get_chars = opal_get_chars,
-	.put_chars = opal_put_chars,
+	.put_chars = opal_put_chars_nonatomic,
 	.notifier_add = notifier_add_irq,
 	.notifier_del = notifier_del_irq,
 	.notifier_hangup = notifier_hangup_irq,
@@ -269,7 +269,7 @@ static void udbg_opal_putc(char c)
 	do {
 		switch(hvc_opal_boot_priv.proto) {
 		case HV_PROTOCOL_RAW:
-			count = opal_put_chars(termno, &c, 1);
+			count = opal_put_chars_nonatomic(termno, &c, 1);
 			break;
 		case HV_PROTOCOL_HVSI:
 			count = hvc_opal_hvsi_put_chars(termno, &c, 1);
-- 
2.17.0

^ permalink raw reply related

* [PATCH 6/6] drivers/tty/hvc: remove unexplained "just in case" spin delay
From: Nicholas Piggin @ 2018-04-09  5:40 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nicholas Piggin, Benjamin Herrenschmidt
In-Reply-To: <20180409054056.27292-1-npiggin@gmail.com>

This delay was in the very first OPAL console commit 6.5 years ago.
The firmware console has hardened sufficiently to remove it.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 drivers/tty/hvc/hvc_opal.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index e151cfacf2a7..436b98258e60 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -307,14 +307,8 @@ static int udbg_opal_getc(void)
 	int ch;
 	for (;;) {
 		ch = udbg_opal_getc_poll();
-		if (ch == -1) {
-			/* This shouldn't be needed...but... */
-			volatile unsigned long delay;
-			for (delay=0; delay < 2000000; delay++)
-				;
-		} else {
+		if (ch != -1)
 			return ch;
-		}
 	}
 }
 
-- 
2.17.0

^ permalink raw reply related

* Re: [PATCH v2 3/9] powerpc/powernv: opal_put_chars partial write fix
From: Benjamin Herrenschmidt @ 2018-04-09  5:50 UTC (permalink / raw)
  To: Nicholas Piggin, linuxppc-dev
In-Reply-To: <20180409052431.26405-4-npiggin@gmail.com>

On Mon, 2018-04-09 at 15:24 +1000, Nicholas Piggin wrote:
> The intention here is to consume and discard the remaining buffer
> upon error. This works if there has not been a previous partial write.
> If there has been, then total_len is no longer total number of bytes
> to copy. total_len is always "bytes left to copy", so it should be
> added to written bytes.
> 
> This code may not be exercised any more if partial writes will not be
> hit, but this is a small bugfix before a larger change.
> 

Reviewed-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  arch/powerpc/platforms/powernv/opal.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index 516e23de5a3d..87d4c0aa7f64 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -388,7 +388,7 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
>  		/* Closed or other error drop */
>  		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
>  		    rc != OPAL_BUSY_EVENT) {
> -			written = total_len;
> +			written += total_len;
>  			break;
>  		}
>  		if (rc == OPAL_SUCCESS) {

^ permalink raw reply

* Re: [PATCH v2 4/9] powerpc/powernv: OPAL console standardise OPAL_BUSY loops
From: Benjamin Herrenschmidt @ 2018-04-09  5:53 UTC (permalink / raw)
  To: Nicholas Piggin, linuxppc-dev
In-Reply-To: <20180409052431.26405-5-npiggin@gmail.com>

On Mon, 2018-04-09 at 15:24 +1000, Nicholas Piggin wrote:
> Convert to using the standard delay poll/delay form.
> 
> The console code:
> 
> - Did not previously delay or sleep in its busy loop.
> 
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Does it help with anything ? We don't technically *have* to delay or
wait, I thought it would be good to try to hit the console as fast as
possible in that case...

Ben.

> ---
>  arch/powerpc/platforms/powernv/opal.c | 38 ++++++++++++++++-----------
>  1 file changed, 23 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index 87d4c0aa7f64..473c8ce14a34 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -378,33 +378,41 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
>  	/* We still try to handle partial completions, though they
>  	 * should no longer happen.
>  	 */
> -	rc = OPAL_BUSY;
> -	while(total_len > 0 && (rc == OPAL_BUSY ||
> -				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
> +
> +	while (total_len > 0) {
>  		olen = cpu_to_be64(total_len);
> -		rc = opal_console_write(vtermno, &olen, data);
> +
> +		rc = OPAL_BUSY;
> +		while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
> +			rc = opal_console_write(vtermno, &olen, data);
> +			if (rc == OPAL_BUSY_EVENT) {
> +				mdelay(OPAL_BUSY_DELAY_MS);
> +				opal_poll_events(NULL);
> +			} else if (rc == OPAL_BUSY) {
> +				mdelay(OPAL_BUSY_DELAY_MS);
> +			}
> +		}
> +
>  		len = be64_to_cpu(olen);
>  
>  		/* Closed or other error drop */
> -		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
> -		    rc != OPAL_BUSY_EVENT) {
> -			written += total_len;
> +		if (rc != OPAL_SUCCESS) {
> +			written += total_len; /* drop remaining chars */
>  			break;
>  		}
> -		if (rc == OPAL_SUCCESS) {
> -			total_len -= len;
> -			data += len;
> -			written += len;
> -		}
> +
> +		total_len -= len;
> +		data += len;
> +		written += len;
> +
>  		/* This is a bit nasty but we need that for the console to
>  		 * flush when there aren't any interrupts. We will clean
>  		 * things a bit later to limit that to synchronous path
>  		 * such as the kernel console and xmon/udbg
>  		 */
> -		do
> +		do {
>  			opal_poll_events(&evt);
> -		while(rc == OPAL_SUCCESS &&
> -			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
> +		} while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
>  	}
>  	spin_unlock_irqrestore(&opal_write_lock, flags);
>  	return written;

^ permalink raw reply

* Re: [PATCH 5/6] powerpc/powernv: implement opal_put_chars_nonatomic
From: Benjamin Herrenschmidt @ 2018-04-09  5:57 UTC (permalink / raw)
  To: Nicholas Piggin, linuxppc-dev
In-Reply-To: <20180409054056.27292-6-npiggin@gmail.com>

On Mon, 2018-04-09 at 15:40 +1000, Nicholas Piggin wrote:
> The RAW console does not need writes to be atomic, so implement a
> _nonatomic variant which does not take a spinlock. This API is used
> in xmon, so the less locking thta's used, the better chance there is
> that a crash can be debugged.

I find the term "nonatomic" confusing... don't we have a problem if we
start hitting OPAL without a lock where we can't trust
opal_console_write_buffer_space anymore ? I think we need to handle
partial writes in that case. Maybe we should return how much was
written and leave the caller to deal with it.

I was hoping (but that isn't the case) that by nonatomic you actually
meant calls that could be done in a non-atomic context, where we can do
msleep instead of mdelay. That would be handy for the console coming
from the hvc thread (the tty one).

Cheers,
Ben.

> 
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  arch/powerpc/include/asm/opal.h       |  1 +
>  arch/powerpc/platforms/powernv/opal.c | 35 +++++++++++++++++++--------
>  drivers/tty/hvc/hvc_opal.c            |  4 +--
>  3 files changed, 28 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index bbff49fab0e5..66954d671831 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -303,6 +303,7 @@ extern void opal_configure_cores(void);
>  
>  extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
>  extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
> +extern int opal_put_chars_nonatomic(uint32_t vtermno, const char *buf, int total_len);
>  extern int opal_flush_console(uint32_t vtermno);
>  
>  extern void hvc_opal_init_early(void);
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index b05500a70f58..dc77fc57d1e9 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -344,9 +344,9 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
>  	return 0;
>  }
>  
> -int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
> +static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, bool atomic)
>  {
> -	unsigned long flags;
> +	unsigned long flags = 0 /* shut up gcc */;
>  	int written;
>  	__be64 olen;
>  	s64 rc;
> @@ -354,11 +354,8 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
>  	if (!opal.entry)
>  		return -ENODEV;
>  
> -	/* We want put_chars to be atomic to avoid mangling of hvsi
> -	 * packets. To do that, we first test for room and return
> -	 * -EAGAIN if there isn't enough.
> -	 */
> -	spin_lock_irqsave(&opal_write_lock, flags);
> +	if (atomic)
> +		spin_lock_irqsave(&opal_write_lock, flags);
>  	rc = opal_console_write_buffer_space(vtermno, &olen);
>  	if (rc || be64_to_cpu(olen) < total_len) {
>  		/* Closed -> drop characters */
> @@ -391,14 +388,18 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
>  
>  	written = be64_to_cpu(olen);
>  	if (written < total_len) {
> -		/* Should not happen */
> -		pr_warn("atomic console write returned partial len=%d written=%d\n", total_len, written);
> +		if (atomic) {
> +			/* Should not happen */
> +			pr_warn("atomic console write returned partial "
> +				"len=%d written=%d\n", total_len, written);
> +		}
>  		if (!written)
>  			written = -EAGAIN;
>  	}
>  
>  out:
> -	spin_unlock_irqrestore(&opal_write_lock, flags);
> +	if (atomic)
> +		spin_unlock_irqrestore(&opal_write_lock, flags);
>  
>  	/* In the -EAGAIN case, callers loop, so we have to flush the console
>  	 * here in case they have interrupts off (and we don't want to wait
> @@ -412,6 +413,20 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
>  	return written;
>  }
>  
> +int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
> +{
> +	/* We want put_chars to be atomic to avoid mangling of hvsi
> +	 * packets. To do that, we first test for room and return
> +	 * -EAGAIN if there isn't enough.
> +	 */
> +	return __opal_put_chars(vtermno, data, total_len, true);
> +}
> +
> +int opal_put_chars_nonatomic(uint32_t vtermno, const char *data, int total_len)
> +{
> +	return __opal_put_chars(vtermno, data, total_len, false);
> +}
> +
>  int opal_flush_console(uint32_t vtermno)
>  {
>  	s64 rc;
> diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
> index af122ad7f06d..e151cfacf2a7 100644
> --- a/drivers/tty/hvc/hvc_opal.c
> +++ b/drivers/tty/hvc/hvc_opal.c
> @@ -51,7 +51,7 @@ static u32 hvc_opal_boot_termno;
>  
>  static const struct hv_ops hvc_opal_raw_ops = {
>  	.get_chars = opal_get_chars,
> -	.put_chars = opal_put_chars,
> +	.put_chars = opal_put_chars_nonatomic,
>  	.notifier_add = notifier_add_irq,
>  	.notifier_del = notifier_del_irq,
>  	.notifier_hangup = notifier_hangup_irq,
> @@ -269,7 +269,7 @@ static void udbg_opal_putc(char c)
>  	do {
>  		switch(hvc_opal_boot_priv.proto) {
>  		case HV_PROTOCOL_RAW:
> -			count = opal_put_chars(termno, &c, 1);
> +			count = opal_put_chars_nonatomic(termno, &c, 1);
>  			break;
>  		case HV_PROTOCOL_HVSI:
>  			count = hvc_opal_hvsi_put_chars(termno, &c, 1);

^ permalink raw reply

* Re: [PATCH 6/6] drivers/tty/hvc: remove unexplained "just in case" spin delay
From: Benjamin Herrenschmidt @ 2018-04-09  6:03 UTC (permalink / raw)
  To: Nicholas Piggin, linuxppc-dev
In-Reply-To: <20180409054056.27292-7-npiggin@gmail.com>

On Mon, 2018-04-09 at 15:40 +1000, Nicholas Piggin wrote:
> This delay was in the very first OPAL console commit 6.5 years ago.
> The firmware console has hardened sufficiently to remove it.
> 

Reviewed-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  drivers/tty/hvc/hvc_opal.c | 8 +-------
>  1 file changed, 1 insertion(+), 7 deletions(-)
> 
> diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
> index e151cfacf2a7..436b98258e60 100644
> --- a/drivers/tty/hvc/hvc_opal.c
> +++ b/drivers/tty/hvc/hvc_opal.c
> @@ -307,14 +307,8 @@ static int udbg_opal_getc(void)
>  	int ch;
>  	for (;;) {
>  		ch = udbg_opal_getc_poll();
> -		if (ch == -1) {
> -			/* This shouldn't be needed...but... */
> -			volatile unsigned long delay;
> -			for (delay=0; delay < 2000000; delay++)
> -				;
> -		} else {
> +		if (ch != -1)
>  			return ch;
> -		}
>  	}
>  }
>  

^ permalink raw reply

* Re: [PATCH v2 4/9] powerpc/powernv: OPAL console standardise OPAL_BUSY loops
From: Nicholas Piggin @ 2018-04-09  6:13 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, skiboot
In-Reply-To: <1523253213.11062.7.camel@kernel.crashing.org>

On Mon, 09 Apr 2018 15:53:33 +1000
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:

> On Mon, 2018-04-09 at 15:24 +1000, Nicholas Piggin wrote:
> > Convert to using the standard delay poll/delay form.
> > 
> > The console code:
> > 
> > - Did not previously delay or sleep in its busy loop.
> > 
> > Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>  
> 
> Does it help with anything ? We don't technically *have* to delay or
> wait, I thought it would be good to try to hit the console as fast as
> possible in that case...

We can always make exceptions to the standard form, but in those
cases I would like to document it in the OPAL API and comment for
the Linux side.

My thinking in this case is that it reduces time in firmware and
in particular holding console locks. Is it likely / possible that
we don't have enough buffering or some other issue makes it worth
retrying so quickly?

Thanks,
Nick

^ permalink raw reply

* Re: [PATCH 5/6] powerpc/powernv: implement opal_put_chars_nonatomic
From: Nicholas Piggin @ 2018-04-09  6:23 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1523253475.11062.10.camel@kernel.crashing.org>

On Mon, 09 Apr 2018 15:57:55 +1000
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:

> On Mon, 2018-04-09 at 15:40 +1000, Nicholas Piggin wrote:
> > The RAW console does not need writes to be atomic, so implement a
> > _nonatomic variant which does not take a spinlock. This API is used
> > in xmon, so the less locking thta's used, the better chance there is
> > that a crash can be debugged.  
> 
> I find the term "nonatomic" confusing...

I guess it is to go with the "atomic" comment for the hvsi console
case -- all characters must get to the console together or not at
all.

> don't we have a problem if we
> start hitting OPAL without a lock where we can't trust
> opal_console_write_buffer_space anymore ? I think we need to handle
> partial writes in that case. Maybe we should return how much was
> written and leave the caller to deal with it.

Yes, the _nonatomic variant doesn't use opal_console_write_buffer_space
and it does handle partial writes by returning written bytes (although
callers generally tend to loop at the moment, we might do something
smarter with them later).

> I was hoping (but that isn't the case) that by nonatomic you actually
> meant calls that could be done in a non-atomic context, where we can do
> msleep instead of mdelay. That would be handy for the console coming
> from the hvc thread (the tty one).

Ah right, no. However we no longer loop until everything is written, so
the hvc console driver (or the console layer) should be able to deal with
that with sleeping. I don't think we need to put it at this level of the
driver, but I don't know much about the console code.

Thanks,
Nick

^ permalink raw reply

* Re: [PATCH] ASoC: fsl_ssi: Fix mode setting when changing channel number
From: Mika Penttilä @ 2018-04-09  6:55 UTC (permalink / raw)
  To: Nicolin Chen, broonie
  Cc: linux-kernel, linuxppc-dev, alsa-devel, tiwai, perex, lgirdwood,
	fabio.estevam, timur
In-Reply-To: <1523162421-26400-1-git-send-email-nicoleotsuka@gmail.com>

On 04/08/2018 07:40 AM, Nicolin Chen wrote:
> This is a partial revert (in a cleaner way) of commit ebf08ae3bc90
> ("ASoC: fsl_ssi: Keep ssi->i2s_net updated") to fix a regression
> at test cases when switching between mono and stereo audio.
> 
> The problem is that ssi->i2s_net is initialized in set_dai_fmt()
> only, while this set_dai_fmt() is only called during the dai-link
> probe(). The original patch assumed set_dai_fmt() would be called
> during every playback instance, so it failed at the overriding use
> cases.
> 
> This patch adds the local variable i2s_net back to let regular use
> cases still follow the mode settings from the set_dai_fmt().
> 
> Meanwhile, the original commit of keeping ssi->i2s_net updated was
> to make set_tdm_slot() clean by checking the ssi->i2s_net directly
> instead of reading SCR register. However, the change itself is not
> necessary (or even harmful) because the set_tdm_slot() might fail
> to check the slot number for Normal-Mode-None-Net settings while
> mono audio cases still need 2 slots. So this patch can also fix it.
> And it adds an extra line of comments to declare ssi->i2s_net does
> not reflect the register value but merely the initial setting from
> the set_dai_fmt().
> 
> Reported-by: Mika Penttilä <mika.penttila@nextfour.com>
> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
> Cc: Mika Penttilä <mika.penttila@nextfour.com>
> ---
>  sound/soc/fsl/fsl_ssi.c | 14 +++++++++++---
>  1 file changed, 11 insertions(+), 3 deletions(-)
> 
> diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
> index 0823b08..89df2d9 100644
> --- a/sound/soc/fsl/fsl_ssi.c
> +++ b/sound/soc/fsl/fsl_ssi.c
> @@ -217,6 +217,7 @@ struct fsl_ssi_soc_data {
>   * @dai_fmt: DAI configuration this device is currently used with
>   * @streams: Mask of current active streams: BIT(TX) and BIT(RX)
>   * @i2s_net: I2S and Network mode configurations of SCR register
> + *           (this is the initial settings based on the DAI format)
>   * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
>   * @use_dma: DMA is used or FIQ with stream filter
>   * @use_dual_fifo: DMA with support for dual FIFO mode
> @@ -829,16 +830,23 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
>  	}
>  
>  	if (!fsl_ssi_is_ac97(ssi)) {
> +		/*
> +		 * Keep the ssi->i2s_net intact while having a local variable
> +		 * to override settings for special use cases. Otherwise, the
> +		 * ssi->i2s_net will lose the settings for regular use cases.
> +		 */
> +		u8 i2s_net = ssi->i2s_net;
> +
>  		/* Normal + Network mode to send 16-bit data in 32-bit frames */
>  		if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16)
> -			ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
> +			i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET;
>  
>  		/* Use Normal mode to send mono data at 1st slot of 2 slots */
>  		if (channels == 1)
> -			ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL;
> +			i2s_net = SSI_SCR_I2S_MODE_NORMAL;
>  
>  		regmap_update_bits(regs, REG_SSI_SCR,
> -				   SSI_SCR_I2S_NET_MASK, ssi->i2s_net);
> +				   SSI_SCR_I2S_NET_MASK, i2s_net);
>  	}
>  
>  	/* In synchronous mode, the SSI uses STCCR for capture */
> 

This patch fixes my problems, so: 

Tested-by: Mika Penttilä <mika.penttila@nextfour.com>


--Mika

^ permalink raw reply

* Re: [PATCH V6 2/4] powerpc/mm: Add support for handling > 512TB address in SLB miss
From: Christophe LEROY @ 2018-04-09  7:19 UTC (permalink / raw)
  To: Aneesh Kumar K.V, benh, paulus, mpe; +Cc: linuxppc-dev
In-Reply-To: <20180326100450.18614-3-aneesh.kumar@linux.vnet.ibm.com>



Le 26/03/2018 à 12:04, Aneesh Kumar K.V a écrit :
> For addresses above 512TB we allocate additional mmu contexts. To make
> it all easy, addresses above 512TB are handled with IR/DR=1 and with
> stack frame setup.
> 
> The mmu_context_t is also updated to track the new extended_ids. To
> support upto 4PB we need a total 8 contexts.
> 
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
> [mpe: Minor formatting tweaks and comment wording, switch BUG to WARN
>        in get_ea_context().]
> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

Compilation fails on mpc885_ads_defconfig + CONFIG_HUGETLBFS :

   CC      arch/powerpc/mm/slice.o
arch/powerpc/mm/slice.c: In function 'slice_get_unmapped_area':
arch/powerpc/mm/slice.c:655:2: error: implicit declaration of function 
'need_extra_context' [-Werror=implicit-function-declaration]
arch/powerpc/mm/slice.c:656:3: error: implicit declaration of function 
'alloc_extended_context' [-Werror=implicit-function-declaration]
cc1: all warnings being treated as errors
make[1]: *** [arch/powerpc/mm/slice.o] Error 1
make: *** [arch/powerpc/mm] Error 2

Christophe

> ---
>   arch/powerpc/include/asm/book3s/64/hash-4k.h  |   6 ++
>   arch/powerpc/include/asm/book3s/64/hash-64k.h |   6 ++
>   arch/powerpc/include/asm/book3s/64/mmu.h      |  33 +++++++-
>   arch/powerpc/include/asm/mmu_context.h        |  39 ++++++++++
>   arch/powerpc/include/asm/processor.h          |   6 ++
>   arch/powerpc/kernel/exceptions-64s.S          |  11 ++-
>   arch/powerpc/kernel/traps.c                   |  12 ---
>   arch/powerpc/mm/copro_fault.c                 |   2 +-
>   arch/powerpc/mm/hash_utils_64.c               |   4 +-
>   arch/powerpc/mm/mmu_context_book3s64.c        |  15 +++-
>   arch/powerpc/mm/pgtable-hash64.c              |   2 +-
>   arch/powerpc/mm/slb.c                         | 108 ++++++++++++++++++++++++++
>   arch/powerpc/mm/slb_low.S                     |  11 ++-
>   arch/powerpc/mm/slice.c                       |  15 +++-
>   arch/powerpc/mm/tlb_hash64.c                  |   2 +-
>   15 files changed, 245 insertions(+), 27 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> index 67c5475311ee..1a35eb944481 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h
> @@ -11,6 +11,12 @@
>   #define H_PUD_INDEX_SIZE  9
>   #define H_PGD_INDEX_SIZE  9
>   
> +/*
> + * Each context is 512TB. But on 4k we restrict our max TASK size to 64TB
> + * Hence also limit max EA bits to 64TB.
> + */
> +#define MAX_EA_BITS_PER_CONTEXT		46
> +
>   #ifndef __ASSEMBLY__
>   #define H_PTE_TABLE_SIZE	(sizeof(pte_t) << H_PTE_INDEX_SIZE)
>   #define H_PMD_TABLE_SIZE	(sizeof(pmd_t) << H_PMD_INDEX_SIZE)
> diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> index 3bcf269f8f55..8d0cbbb31023 100644
> --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h
> +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h
> @@ -7,6 +7,12 @@
>   #define H_PUD_INDEX_SIZE  7
>   #define H_PGD_INDEX_SIZE  8
>   
> +/*
> + * Each context is 512TB size. SLB miss for first context/default context
> + * is handled in the hotpath.
> + */
> +#define MAX_EA_BITS_PER_CONTEXT		49
> +
>   /*
>    * 64k aligned address free up few of the lower bits of RPN for us
>    * We steal that here. For more deatils look at pte_pfn/pfn_pte()
> diff --git a/arch/powerpc/include/asm/book3s/64/mmu.h b/arch/powerpc/include/asm/book3s/64/mmu.h
> index c8c836e8ad1b..5094696eecd6 100644
> --- a/arch/powerpc/include/asm/book3s/64/mmu.h
> +++ b/arch/powerpc/include/asm/book3s/64/mmu.h
> @@ -91,7 +91,18 @@ struct slice_mask {
>   };
>   
>   typedef struct {
> -	mm_context_id_t id;
> +	union {
> +		/*
> +		 * We use id as the PIDR content for radix. On hash we can use
> +		 * more than one id. The extended ids are used when we start
> +		 * having address above 512TB. We allocate one extended id
> +		 * for each 512TB. The new id is then used with the 49 bit
> +		 * EA to build a new VA. We always use ESID_BITS_1T_MASK bits
> +		 * from EA and new context ids to build the new VAs.
> +		 */
> +		mm_context_id_t id;
> +		mm_context_id_t extended_id[TASK_SIZE_USER64/TASK_CONTEXT_SIZE];
> +	};
>   	u16 user_psize;		/* page size index */
>   
>   	/* Number of bits in the mm_cpumask */
> @@ -196,5 +207,25 @@ extern void radix_init_pseries(void);
>   static inline void radix_init_pseries(void) { };
>   #endif
>   
> +static inline int get_ea_context(mm_context_t *ctx, unsigned long ea)
> +{
> +	int index = ea >> MAX_EA_BITS_PER_CONTEXT;
> +
> +	if (likely(index < ARRAY_SIZE(ctx->extended_id)))
> +		return ctx->extended_id[index];
> +
> +	/* should never happen */
> +	WARN_ON(1);
> +	return 0;
> +}
> +
> +static inline unsigned long get_user_vsid(mm_context_t *ctx,
> +					  unsigned long ea, int ssize)
> +{
> +	unsigned long context = get_ea_context(ctx, ea);
> +
> +	return get_vsid(context, ea, ssize);
> +}
> +
>   #endif /* __ASSEMBLY__ */
>   #endif /* _ASM_POWERPC_BOOK3S_64_MMU_H_ */
> diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
> index 3a15b6db9501..1835ca1505d6 100644
> --- a/arch/powerpc/include/asm/mmu_context.h
> +++ b/arch/powerpc/include/asm/mmu_context.h
> @@ -60,12 +60,51 @@ extern int hash__alloc_context_id(void);
>   extern void hash__reserve_context_id(int id);
>   extern void __destroy_context(int context_id);
>   static inline void mmu_context_init(void) { }
> +
> +static inline int alloc_extended_context(struct mm_struct *mm,
> +					 unsigned long ea)
> +{
> +	int context_id;
> +
> +	int index = ea >> MAX_EA_BITS_PER_CONTEXT;
> +
> +	context_id = hash__alloc_context_id();
> +	if (context_id < 0)
> +		return context_id;
> +
> +	VM_WARN_ON(mm->context.extended_id[index]);
> +	mm->context.extended_id[index] = context_id;
> +	return context_id;
> +}
> +
> +static inline bool need_extra_context(struct mm_struct *mm, unsigned long ea)
> +{
> +	int context_id;
> +
> +	context_id = get_ea_context(&mm->context, ea);
> +	if (!context_id)
> +		return true;
> +	return false;
> +}
> +
>   #else
>   extern void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next,
>   			       struct task_struct *tsk);
>   extern unsigned long __init_new_context(void);
>   extern void __destroy_context(unsigned long context_id);
>   extern void mmu_context_init(void);
> +static inline int alloc_extended_context(struct mm_struct *mm,
> +					 unsigned long ea)
> +{
> +	/* non book3s_64 should never find this called */
> +	WARN_ON(1);
> +	return -ENOMEM;
> +}
> +
> +static inline bool need_extra_context(struct mm_struct *mm, unsigned long ea)
> +{
> +	return false;
> +}
>   #endif
>   
>   #if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE) && defined(CONFIG_PPC_RADIX_MMU)
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index 01299cdc9806..75b084486ce1 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -119,9 +119,15 @@ void release_thread(struct task_struct *);
>    */
>   #define TASK_SIZE_USER64		TASK_SIZE_512TB
>   #define DEFAULT_MAP_WINDOW_USER64	TASK_SIZE_128TB
> +#define TASK_CONTEXT_SIZE		TASK_SIZE_512TB
>   #else
>   #define TASK_SIZE_USER64		TASK_SIZE_64TB
>   #define DEFAULT_MAP_WINDOW_USER64	TASK_SIZE_64TB
> +/*
> + * We don't need to allocate extended context ids for 4K page size, because
> + * we limit the max effective address on this config to 64TB.
> + */
> +#define TASK_CONTEXT_SIZE		TASK_SIZE_64TB
>   #endif
>   
>   /*
> diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
> index 6bee20c43feb..1a0aa70bcb2b 100644
> --- a/arch/powerpc/kernel/exceptions-64s.S
> +++ b/arch/powerpc/kernel/exceptions-64s.S
> @@ -621,7 +621,10 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_TYPE_RADIX)
>   	lwz	r9,PACA_EXSLB+EX_CCR(r13)	/* get saved CR */
>   	mtlr	r10
>   
> -	beq-	8f		/* if bad address, make full stack frame */
> +	/*
> +	 * Large address, check whether we have to allocate new contexts.
> +	 */
> +	beq-	8f
>   
>   	bne-	cr5,2f		/* if unrecoverable exception, oops */
>   
> @@ -685,7 +688,7 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_TYPE_RADIX)
>   	mr	r3,r12
>   	mfspr	r11,SPRN_SRR0
>   	mfspr	r12,SPRN_SRR1
> -	LOAD_HANDLER(r10,bad_addr_slb)
> +	LOAD_HANDLER(r10, large_addr_slb)
>   	mtspr	SPRN_SRR0,r10
>   	ld	r10,PACAKMSR(r13)
>   	mtspr	SPRN_SRR1,r10
> @@ -700,7 +703,7 @@ EXC_COMMON_BEGIN(unrecov_slb)
>   	bl	unrecoverable_exception
>   	b	1b
>   
> -EXC_COMMON_BEGIN(bad_addr_slb)
> +EXC_COMMON_BEGIN(large_addr_slb)
>   	EXCEPTION_PROLOG_COMMON(0x380, PACA_EXSLB)
>   	RECONCILE_IRQ_STATE(r10, r11)
>   	ld	r3, PACA_EXSLB+EX_DAR(r13)
> @@ -710,7 +713,7 @@ EXC_COMMON_BEGIN(bad_addr_slb)
>   	std	r10, _TRAP(r1)
>   2:	bl	save_nvgprs
>   	addi	r3, r1, STACK_FRAME_OVERHEAD
> -	bl	slb_miss_bad_addr
> +	bl	slb_miss_large_addr
>   	b	ret_from_except
>   
>   EXC_REAL_BEGIN(hardware_interrupt, 0x500, 0x100)
> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> index 1e48d157196a..f200bfd98b17 100644
> --- a/arch/powerpc/kernel/traps.c
> +++ b/arch/powerpc/kernel/traps.c
> @@ -1495,18 +1495,6 @@ void alignment_exception(struct pt_regs *regs)
>   	exception_exit(prev_state);
>   }
>   
> -void slb_miss_bad_addr(struct pt_regs *regs)
> -{
> -	enum ctx_state prev_state = exception_enter();
> -
> -	if (user_mode(regs))
> -		_exception(SIGSEGV, regs, SEGV_BNDERR, regs->dar);
> -	else
> -		bad_page_fault(regs, regs->dar, SIGSEGV);
> -
> -	exception_exit(prev_state);
> -}
> -
>   void StackOverflow(struct pt_regs *regs)
>   {
>   	printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
> diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c
> index 697b70ad1195..7d0945bd3a61 100644
> --- a/arch/powerpc/mm/copro_fault.c
> +++ b/arch/powerpc/mm/copro_fault.c
> @@ -112,7 +112,7 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
>   			return 1;
>   		psize = get_slice_psize(mm, ea);
>   		ssize = user_segment_size(ea);
> -		vsid = get_vsid(mm->context.id, ea, ssize);
> +		vsid = get_user_vsid(&mm->context, ea, ssize);
>   		vsidkey = SLB_VSID_USER;
>   		break;
>   	case VMALLOC_REGION_ID:
> diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
> index b578148d89e6..f62325d4f5f5 100644
> --- a/arch/powerpc/mm/hash_utils_64.c
> +++ b/arch/powerpc/mm/hash_utils_64.c
> @@ -1261,7 +1261,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
>   		}
>   		psize = get_slice_psize(mm, ea);
>   		ssize = user_segment_size(ea);
> -		vsid = get_vsid(mm->context.id, ea, ssize);
> +		vsid = get_user_vsid(&mm->context, ea, ssize);
>   		break;
>   	case VMALLOC_REGION_ID:
>   		vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
> @@ -1526,7 +1526,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
>   
>   	/* Get VSID */
>   	ssize = user_segment_size(ea);
> -	vsid = get_vsid(mm->context.id, ea, ssize);
> +	vsid = get_user_vsid(&mm->context, ea, ssize);
>   	if (!vsid)
>   		return;
>   	/*
> diff --git a/arch/powerpc/mm/mmu_context_book3s64.c b/arch/powerpc/mm/mmu_context_book3s64.c
> index 422be81bf69f..b75194dff64c 100644
> --- a/arch/powerpc/mm/mmu_context_book3s64.c
> +++ b/arch/powerpc/mm/mmu_context_book3s64.c
> @@ -179,6 +179,19 @@ void __destroy_context(int context_id)
>   }
>   EXPORT_SYMBOL_GPL(__destroy_context);
>   
> +static void destroy_contexts(mm_context_t *ctx)
> +{
> +	int index, context_id;
> +
> +	spin_lock(&mmu_context_lock);
> +	for (index = 0; index < ARRAY_SIZE(ctx->extended_id); index++) {
> +		context_id = ctx->extended_id[index];
> +		if (context_id)
> +			ida_remove(&mmu_context_ida, context_id);
> +	}
> +	spin_unlock(&mmu_context_lock);
> +}
> +
>   #ifdef CONFIG_PPC_64K_PAGES
>   static void destroy_pagetable_page(struct mm_struct *mm)
>   {
> @@ -217,7 +230,7 @@ void destroy_context(struct mm_struct *mm)
>   	else
>   		subpage_prot_free(mm);
>   	destroy_pagetable_page(mm);
> -	__destroy_context(mm->context.id);
> +	destroy_contexts(&mm->context);
>   	mm->context.id = MMU_NO_CONTEXT;
>   }
>   
> diff --git a/arch/powerpc/mm/pgtable-hash64.c b/arch/powerpc/mm/pgtable-hash64.c
> index 469808e77e58..a87b18cf6749 100644
> --- a/arch/powerpc/mm/pgtable-hash64.c
> +++ b/arch/powerpc/mm/pgtable-hash64.c
> @@ -320,7 +320,7 @@ void hpte_do_hugepage_flush(struct mm_struct *mm, unsigned long addr,
>   
>   	if (!is_kernel_addr(addr)) {
>   		ssize = user_segment_size(addr);
> -		vsid = get_vsid(mm->context.id, addr, ssize);
> +		vsid = get_user_vsid(&mm->context, addr, ssize);
>   		WARN_ON(vsid == 0);
>   	} else {
>   		vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
> diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c
> index 13cfe413b40d..66577cc66dc9 100644
> --- a/arch/powerpc/mm/slb.c
> +++ b/arch/powerpc/mm/slb.c
> @@ -22,6 +22,7 @@
>   #include <asm/cacheflush.h>
>   #include <asm/smp.h>
>   #include <linux/compiler.h>
> +#include <linux/context_tracking.h>
>   #include <linux/mm_types.h>
>   
>   #include <asm/udbg.h>
> @@ -340,3 +341,110 @@ void slb_initialize(void)
>   
>   	asm volatile("isync":::"memory");
>   }
> +
> +static void insert_slb_entry(unsigned long vsid, unsigned long ea,
> +			     int bpsize, int ssize)
> +{
> +	unsigned long flags, vsid_data, esid_data;
> +	enum slb_index index;
> +	int slb_cache_index;
> +
> +	/*
> +	 * We are irq disabled, hence should be safe to access PACA.
> +	 */
> +	index = get_paca()->stab_rr;
> +
> +	/*
> +	 * simple round-robin replacement of slb starting at SLB_NUM_BOLTED.
> +	 */
> +	if (index < (mmu_slb_size - 1))
> +		index++;
> +	else
> +		index = SLB_NUM_BOLTED;
> +
> +	get_paca()->stab_rr = index;
> +
> +	flags = SLB_VSID_USER | mmu_psize_defs[bpsize].sllp;
> +	vsid_data = (vsid << slb_vsid_shift(ssize)) | flags |
> +		    ((unsigned long) ssize << SLB_VSID_SSIZE_SHIFT);
> +	esid_data = mk_esid_data(ea, ssize, index);
> +
> +	asm volatile("slbmte %0, %1" : : "r" (vsid_data), "r" (esid_data)
> +		     : "memory");
> +
> +	/*
> +	 * Now update slb cache entries
> +	 */
> +	slb_cache_index = get_paca()->slb_cache_ptr;
> +	if (slb_cache_index < SLB_CACHE_ENTRIES) {
> +		/*
> +		 * We have space in slb cache for optimized switch_slb().
> +		 * Top 36 bits from esid_data as per ISA
> +		 */
> +		get_paca()->slb_cache[slb_cache_index++] = esid_data >> 28;
> +		get_paca()->slb_cache_ptr++;
> +	} else {
> +		/*
> +		 * Our cache is full and the current cache content strictly
> +		 * doesn't indicate the active SLB conents. Bump the ptr
> +		 * so that switch_slb() will ignore the cache.
> +		 */
> +		get_paca()->slb_cache_ptr = SLB_CACHE_ENTRIES + 1;
> +	}
> +}
> +
> +static void handle_multi_context_slb_miss(int context_id, unsigned long ea)
> +{
> +	struct mm_struct *mm = current->mm;
> +	unsigned long vsid;
> +	int bpsize;
> +
> +	/*
> +	 * We are always above 1TB, hence use high user segment size.
> +	 */
> +	vsid = get_vsid(context_id, ea, mmu_highuser_ssize);
> +	bpsize = get_slice_psize(mm, ea);
> +	insert_slb_entry(vsid, ea, bpsize, mmu_highuser_ssize);
> +}
> +
> +void slb_miss_large_addr(struct pt_regs *regs)
> +{
> +	enum ctx_state prev_state = exception_enter();
> +	unsigned long ea = regs->dar;
> +	int context;
> +
> +	if (REGION_ID(ea) != USER_REGION_ID)
> +		goto slb_bad_addr;
> +
> +	/*
> +	 * Are we beyound what the page table layout supports ?
> +	 */
> +	if ((ea & ~REGION_MASK) >= H_PGTABLE_RANGE)
> +		goto slb_bad_addr;
> +
> +	/* Lower address should have been handled by asm code */
> +	if (ea < (1UL << MAX_EA_BITS_PER_CONTEXT))
> +		goto slb_bad_addr;
> +
> +	/*
> +	 * consider this as bad access if we take a SLB miss
> +	 * on an address above addr limit.
> +	 */
> +	if (ea >= current->mm->context.slb_addr_limit)
> +		goto slb_bad_addr;
> +
> +	context = get_ea_context(&current->mm->context, ea);
> +	if (!context)
> +		goto slb_bad_addr;
> +
> +	handle_multi_context_slb_miss(context, ea);
> +	exception_exit(prev_state);
> +	return;
> +
> +slb_bad_addr:
> +	if (user_mode(regs))
> +		_exception(SIGSEGV, regs, SEGV_BNDERR, ea);
> +	else
> +		bad_page_fault(regs, ea, SIGSEGV);
> +	exception_exit(prev_state);
> +}
> diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S
> index 2c7c717fd2ea..a83fbd2a4a24 100644
> --- a/arch/powerpc/mm/slb_low.S
> +++ b/arch/powerpc/mm/slb_low.S
> @@ -75,10 +75,15 @@ ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_68_BIT_VA)
>    */
>   _GLOBAL(slb_allocate)
>   	/*
> -	 * check for bad kernel/user address
> -	 * (ea & ~REGION_MASK) >= PGTABLE_RANGE
> +	 * Check if the address falls within the range of the first context, or
> +	 * if we may need to handle multi context. For the first context we
> +	 * allocate the slb entry via the fast path below. For large address we
> +	 * branch out to C-code and see if additional contexts have been
> +	 * allocated.
> +	 * The test here is:
> +	 *   (ea & ~REGION_MASK) >= (1ull << MAX_EA_BITS_PER_CONTEXT)
>   	 */
> -	rldicr. r9,r3,4,(63 - H_PGTABLE_EADDR_SIZE - 4)
> +	rldicr. r9,r3,4,(63 - MAX_EA_BITS_PER_CONTEXT - 4)
>   	bne-	8f
>   
>   	srdi	r9,r3,60		/* get region */
> diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c
> index 09ac1a709d0c..9cd87d11fe4e 100644
> --- a/arch/powerpc/mm/slice.c
> +++ b/arch/powerpc/mm/slice.c
> @@ -648,6 +648,15 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
>   	slice_print_mask(" mask", &potential_mask);
>   
>    convert:
> +	/*
> +	 * Try to allocate the context before we do slice convert
> +	 * so that we handle the context allocation failure gracefully.
> +	 */
> +	if (need_extra_context(mm, newaddr)) {
> +		if (alloc_extended_context(mm, newaddr) < 0)
> +			return -ENOMEM;
> +	}
> +
>   	slice_andnot_mask(&potential_mask, &potential_mask, &good_mask);
>   	if (compat_maskp && !fixed)
>   		slice_andnot_mask(&potential_mask, &potential_mask, compat_maskp);
> @@ -658,10 +667,14 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
>   		if (psize > MMU_PAGE_BASE)
>   			on_each_cpu(slice_flush_segments, mm, 1);
>   	}
> +	return newaddr;
>   
>   return_addr:
> +	if (need_extra_context(mm, newaddr)) {
> +		if (alloc_extended_context(mm, newaddr) < 0)
> +			return -ENOMEM;
> +	}
>   	return newaddr;
> -
>   }
>   EXPORT_SYMBOL_GPL(slice_get_unmapped_area);
>   
> diff --git a/arch/powerpc/mm/tlb_hash64.c b/arch/powerpc/mm/tlb_hash64.c
> index 9b23f12e863c..87d71dd25441 100644
> --- a/arch/powerpc/mm/tlb_hash64.c
> +++ b/arch/powerpc/mm/tlb_hash64.c
> @@ -89,7 +89,7 @@ void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
>   	/* Build full vaddr */
>   	if (!is_kernel_addr(addr)) {
>   		ssize = user_segment_size(addr);
> -		vsid = get_vsid(mm->context.id, addr, ssize);
> +		vsid = get_user_vsid(&mm->context, addr, ssize);
>   	} else {
>   		vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
>   		ssize = mmu_kernel_ssize;
> 

^ permalink raw reply

* [PATCH] drivers/of: Introduce ARCH_HAS_OWN_OF_NUMA
From: Oliver O'Halloran @ 2018-04-09  7:46 UTC (permalink / raw)
  To: linux-kernel
  Cc: sfr, linux-nvdimm, Oliver O'Halloran, devicetree, sparclinux,
	linuxppc-dev

Some OF platforms (pseries and some SPARC systems) has their own
implementations of NUMA affinity detection rather than using the generic
OF_NUMA driver, which mainly exists for arm64. For other platforms one
of two fallbacks provided by the base OF driver are used depending on
CONFIG_NUMA.

In the CONFIG_NUMA=n case the fallback is an inline function in of.h.
In the =y case the fallback is a real function which is defined as a
weak symbol so that it may be overwritten by the architecture if desired.

The problem with this arrangement is that the real implementations all
export of_node_to_nid(). Unfortunately it's not possible to export the
fallback since it would clash with the non-weak version. As a result
we get build failures when:

a) CONFIG_NUMA=y && CONFIG_OF=y, and
b) The platform doesn't implement of_node_to_nid(), and
c) A module uses of_node_to_nid()

Given b) will be true for most platforms this is fairly easy to hit
and has been observed on ia64 and x86.

This patch remedies the problem by introducing the ARCH_HAS_OWN_OF_NUMA
Kconfig option which is selected if an architecture provides an
implementation of of_node_to_nid(). If a platform does not use it's own,
or the generic OF_NUMA, then always use the inline fallback in of.h so
we don't need to futz around with exports.

Cc: devicetree@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Fixes: 298535c00a2c ("of, numa: Add NUMA of binding implementation.")
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
 arch/powerpc/Kconfig | 1 +
 arch/sparc/Kconfig   | 1 +
 drivers/of/Kconfig   | 3 +++
 drivers/of/base.c    | 7 -------
 include/linux/of.h   | 2 +-
 5 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c32a181a7cbb..74ce5f3564ae 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -625,6 +625,7 @@ config NUMA
 	bool "NUMA support"
 	depends on PPC64
 	default y if SMP && PPC_PSERIES
+	select ARCH_HAS_OWN_OF_NUMA
 
 config NODES_SHIFT
 	int
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 8767e45f1b2b..f8071f1c3edb 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -299,6 +299,7 @@ config GENERIC_LOCKBREAK
 config NUMA
 	bool "NUMA support"
 	depends on SPARC64 && SMP
+	select ARCH_HAS_OWN_OF_NUMA
 
 config NODES_SHIFT
 	int "Maximum NUMA Nodes (as a power of 2)"
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index ad3fcad4d75b..01c62b747b25 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -103,4 +103,7 @@ config OF_OVERLAY
 config OF_NUMA
 	bool
 
+config ARCH_HAS_OWN_OF_NUMA
+	bool
+
 endif # OF
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 848f549164cd..82a9584bb0e2 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -84,13 +84,6 @@ int of_n_size_cells(struct device_node *np)
 }
 EXPORT_SYMBOL(of_n_size_cells);
 
-#ifdef CONFIG_NUMA
-int __weak of_node_to_nid(struct device_node *np)
-{
-	return NUMA_NO_NODE;
-}
-#endif
-
 static struct device_node **phandle_cache;
 static u32 phandle_cache_mask;
 
diff --git a/include/linux/of.h b/include/linux/of.h
index 4d25e4f952d9..9bb42dac5e65 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -942,7 +942,7 @@ static inline int of_cpu_node_to_id(struct device_node *np)
 #define of_node_cmp(s1, s2)		strcasecmp((s1), (s2))
 #endif
 
-#if defined(CONFIG_OF) && defined(CONFIG_NUMA)
+#if defined(CONFIG_OF_NUMA) || defined(CONFIG_ARCH_HAS_OWN_OF_NUMA)
 extern int of_node_to_nid(struct device_node *np);
 #else
 static inline int of_node_to_nid(struct device_node *device)
-- 
2.9.5

^ permalink raw reply related

* Re: [PATCH v2 4/9] powerpc/powernv: OPAL console standardise OPAL_BUSY loops
From: Benjamin Herrenschmidt @ 2018-04-09  8:22 UTC (permalink / raw)
  To: Nicholas Piggin; +Cc: linuxppc-dev, skiboot
In-Reply-To: <20180409161342.02f41b8a@roar.ozlabs.ibm.com>

On Mon, 2018-04-09 at 16:13 +1000, Nicholas Piggin wrote:
> We can always make exceptions to the standard form, but in those
> cases I would like to document it in the OPAL API and comment for
> the Linux side.
> 
> My thinking in this case is that it reduces time in firmware and
> in particular holding console locks. Is it likely / possible that
> we don't have enough buffering or some other issue makes it worth
> retrying so quickly?

Not sure to be honest, but yeah limiting the lock contention inside
OPAL is probably not a bad idea.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 5/6] powerpc/powernv: implement opal_put_chars_nonatomic
From: Benjamin Herrenschmidt @ 2018-04-09  8:24 UTC (permalink / raw)
  To: Nicholas Piggin; +Cc: linuxppc-dev
In-Reply-To: <20180409162314.20e486c3@roar.ozlabs.ibm.com>

On Mon, 2018-04-09 at 16:23 +1000, Nicholas Piggin wrote:
> On Mon, 09 Apr 2018 15:57:55 +1000
> Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> 
> > On Mon, 2018-04-09 at 15:40 +1000, Nicholas Piggin wrote:
> > > The RAW console does not need writes to be atomic, so implement a
> > > _nonatomic variant which does not take a spinlock. This API is used
> > > in xmon, so the less locking thta's used, the better chance there is
> > > that a crash can be debugged.  
> > 
> > I find the term "nonatomic" confusing...
> 
> I guess it is to go with the "atomic" comment for the hvsi console
> case -- all characters must get to the console together or not at
> all.

Yeah ok, it's just that in Linux "atomic" usually means something else
:-) Why not just call it "unlocked" which is what it's about and
matches existing practices thorough the kernel ?

> > don't we have a problem if we
> > start hitting OPAL without a lock where we can't trust
> > opal_console_write_buffer_space anymore ? I think we need to handle
> > partial writes in that case. Maybe we should return how much was
> > written and leave the caller to deal with it.
> 
> Yes, the _nonatomic variant doesn't use opal_console_write_buffer_space
> and it does handle partial writes by returning written bytes (although
> callers generally tend to loop at the moment, we might do something
> smarter with them later).
> 
> > I was hoping (but that isn't the case) that by nonatomic you actually
> > meant calls that could be done in a non-atomic context, where we can do
> > msleep instead of mdelay. That would be handy for the console coming
> > from the hvc thread (the tty one).
> 
> Ah right, no. However we no longer loop until everything is written, so
> the hvc console driver (or the console layer) should be able to deal with
> that with sleeping. I don't think we need to put it at this level of the
> driver, but I don't know much about the console code.

Ok, so hopefully we shouldn't be hitting the delay..

Cheers,
Ben.

^ permalink raw reply


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