* Re: [PATCH] net: ethernet: ucc: fix spelling mistake: "tx-late-collsion" -> "tx-late-collision"
From: David Miller @ 2018-04-30 13:30 UTC (permalink / raw)
To: colin.king
Cc: leoyang.li, netdev, linuxppc-dev, kernel-janitors, linux-kernel
In-Reply-To: <20180428095707.7424-1-colin.king@canonical.com>
From: Colin King <colin.king@canonical.com>
Date: Sat, 28 Apr 2018 10:57:07 +0100
> From: Colin Ian King <colin.king@canonical.com>
>
> Trivial fix to spelling mistake in tx_fw_stat_gstrings text
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
Applied.
^ permalink raw reply
* [PATCH 2/2] powerpc: wii_defconfig: Enable GPIO-related options
From: Jonathan Neuschäfer @ 2018-04-30 13:42 UTC (permalink / raw)
To: linuxppc-dev
Cc: Joel Stanley, Jonathan Neuschäfer, Benjamin Herrenschmidt,
Paul Mackerras, Michael Ellerman, Greg Kroah-Hartman,
Benjamin Gilbert, Robin H. Johnson, linux-kernel
In-Reply-To: <20180430134252.17345-1-j.neuschaefer@gmx.net>
Now that there's a GPIO driver for the Wii, let's enable the following
drivers:
- the GPIO driver itself
- gpio-keys
- gpio-poweroff
- gpio-leds and a few LED triggers
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
---
arch/powerpc/configs/wii_defconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/powerpc/configs/wii_defconfig b/arch/powerpc/configs/wii_defconfig
index 3167b9d7f3e5..40a3b5c09765 100644
--- a/arch/powerpc/configs/wii_defconfig
+++ b/arch/powerpc/configs/wii_defconfig
@@ -58,6 +58,7 @@ CONFIG_INPUT_FF_MEMLESS=m
CONFIG_INPUT_JOYDEV=y
CONFIG_INPUT_EVDEV=y
# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
# CONFIG_MOUSE_PS2 is not set
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_MISC=y
@@ -72,6 +73,9 @@ CONFIG_I2C_CHARDEV=y
CONFIG_I2C_GPIO=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_HLWD=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO=y
# CONFIG_HWMON is not set
CONFIG_SSB_DEBUG=y
CONFIG_FB=y
@@ -89,6 +93,10 @@ CONFIG_HID_APPLE=m
CONFIG_HID_WACOM=m
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_PANIC=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_GENERIC=y
CONFIG_EXT2_FS=y
--
2.17.0
^ permalink raw reply related
* [PATCH 1/2] powerpc: wii_defconfig: Disable Ethernet driver support code
From: Jonathan Neuschäfer @ 2018-04-30 13:42 UTC (permalink / raw)
To: linuxppc-dev
Cc: Joel Stanley, Jonathan Neuschäfer, Benjamin Herrenschmidt,
Paul Mackerras, Michael Ellerman, Robin H. Johnson,
Benjamin Gilbert, Greg Kroah-Hartman, linux-kernel
The Wii doesn't have built-in Ethernet and USB Ethernet adapters are in
a different menu. Disable CONFIG_ETHERNET to save some space in support
code for Ethernet drivers.
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-367 (-367)
Function old new delta
kernel_config_data 13691 13324 -367
Total: Before=8341718, After=8341351, chg -0.00%
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
---
arch/powerpc/configs/wii_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/powerpc/configs/wii_defconfig b/arch/powerpc/configs/wii_defconfig
index 0b0f78823a1b..3167b9d7f3e5 100644
--- a/arch/powerpc/configs/wii_defconfig
+++ b/arch/powerpc/configs/wii_defconfig
@@ -49,6 +49,7 @@ CONFIG_BLK_DEV_RAM_COUNT=2
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_NETDEVICES=y
+# CONFIG_ETHERNET is not set
CONFIG_B43=y
CONFIG_B43_SDIO=y
# CONFIG_B43_PHY_LP is not set
--
2.17.0
^ permalink raw reply related
* Re: [PATCH v10 06/25] mm: make pte_unmap_same compatible with SPF
From: Laurent Dufour @ 2018-04-30 14:07 UTC (permalink / raw)
To: Minchan Kim
Cc: akpm, mhocko, peterz, kirill, ak, dave, jack, Matthew Wilcox,
benh, mpe, paulus, Thomas Gleixner, Ingo Molnar, hpa, Will Deacon,
Sergey Senozhatsky, Andrea Arcangeli, Alexei Starovoitov,
kemi.wang, sergey.senozhatsky.work, Daniel Jordan, David Rientjes,
Jerome Glisse, Ganesh Mahendran, linux-kernel, linux-mm, haren,
khandual, npiggin, bsingharora, paulmck, Tim Chen, linuxppc-dev,
x86
In-Reply-To: <20180423063157.GB114098@rodete-desktop-imager.corp.google.com>
On 23/04/2018 08:31, Minchan Kim wrote:
> On Tue, Apr 17, 2018 at 04:33:12PM +0200, Laurent Dufour wrote:
>> pte_unmap_same() is making the assumption that the page table are still
>> around because the mmap_sem is held.
>> This is no more the case when running a speculative page fault and
>> additional check must be made to ensure that the final page table are still
>> there.
>>
>> This is now done by calling pte_spinlock() to check for the VMA's
>> consistency while locking for the page tables.
>>
>> This is requiring passing a vm_fault structure to pte_unmap_same() which is
>> containing all the needed parameters.
>>
>> As pte_spinlock() may fail in the case of a speculative page fault, if the
>> VMA has been touched in our back, pte_unmap_same() should now return 3
>> cases :
>> 1. pte are the same (0)
>> 2. pte are different (VM_FAULT_PTNOTSAME)
>> 3. a VMA's changes has been detected (VM_FAULT_RETRY)
>>
>> The case 2 is handled by the introduction of a new VM_FAULT flag named
>> VM_FAULT_PTNOTSAME which is then trapped in cow_user_page().
>
> I don't see such logic in this patch.
> Maybe you introduces it later? If so, please comment on it.
> Or just return 0 in case of 2 without introducing VM_FAULT_PTNOTSAME.
Late in the series, pte_spinlock() will check for the VMA's changes and may
return 1. This will be then required to handle the 3 cases presented above.
I can move this handling later in the series, but I wondering if this will make
it more easier to read.
>
>> If VM_FAULT_RETRY is returned, it is passed up to the callers to retry the
>> page fault while holding the mmap_sem.
>>
>> Acked-by: David Rientjes <rientjes@google.com>
>> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
>> ---
>> include/linux/mm.h | 1 +
>> mm/memory.c | 39 ++++++++++++++++++++++++++++-----------
>> 2 files changed, 29 insertions(+), 11 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 4d1aff80669c..714da99d77a3 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -1208,6 +1208,7 @@ static inline void clear_page_pfmemalloc(struct page *page)
>> #define VM_FAULT_NEEDDSYNC 0x2000 /* ->fault did not modify page tables
>> * and needs fsync() to complete (for
>> * synchronous page faults in DAX) */
>> +#define VM_FAULT_PTNOTSAME 0x4000 /* Page table entries have changed */
>>
>> #define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | \
>> VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE | \
>> diff --git a/mm/memory.c b/mm/memory.c
>> index 0b9a51f80e0e..f86efcb8e268 100644
>> --- a/mm/memory.c
>> +++ b/mm/memory.c
>> @@ -2309,21 +2309,29 @@ static inline bool pte_map_lock(struct vm_fault *vmf)
>> * parts, do_swap_page must check under lock before unmapping the pte and
>> * proceeding (but do_wp_page is only called after already making such a check;
>> * and do_anonymous_page can safely check later on).
>> + *
>> + * pte_unmap_same() returns:
>> + * 0 if the PTE are the same
>> + * VM_FAULT_PTNOTSAME if the PTE are different
>> + * VM_FAULT_RETRY if the VMA has changed in our back during
>> + * a speculative page fault handling.
>> */
>> -static inline int pte_unmap_same(struct mm_struct *mm, pmd_t *pmd,
>> - pte_t *page_table, pte_t orig_pte)
>> +static inline int pte_unmap_same(struct vm_fault *vmf)
>> {
>> - int same = 1;
>> + int ret = 0;
>> +
>> #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
>> if (sizeof(pte_t) > sizeof(unsigned long)) {
>> - spinlock_t *ptl = pte_lockptr(mm, pmd);
>> - spin_lock(ptl);
>> - same = pte_same(*page_table, orig_pte);
>> - spin_unlock(ptl);
>> + if (pte_spinlock(vmf)) {
>> + if (!pte_same(*vmf->pte, vmf->orig_pte))
>> + ret = VM_FAULT_PTNOTSAME;
>> + spin_unlock(vmf->ptl);
>> + } else
>> + ret = VM_FAULT_RETRY;
>> }
>> #endif
>> - pte_unmap(page_table);
>> - return same;
>> + pte_unmap(vmf->pte);
>> + return ret;
>> }
>>
>> static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va, struct vm_area_struct *vma)
>> @@ -2912,10 +2920,19 @@ int do_swap_page(struct vm_fault *vmf)
>> pte_t pte;
>> int locked;
>> int exclusive = 0;
>> - int ret = 0;
>> + int ret;
>>
>> - if (!pte_unmap_same(vma->vm_mm, vmf->pmd, vmf->pte, vmf->orig_pte))
>> + ret = pte_unmap_same(vmf);
>> + if (ret) {
>> + /*
>> + * If pte != orig_pte, this means another thread did the
>> + * swap operation in our back.
>> + * So nothing else to do.
>> + */
>> + if (ret == VM_FAULT_PTNOTSAME)
>> + ret = 0;
>> goto out;
>> + }
>>
>> entry = pte_to_swp_entry(vmf->orig_pte);
>> if (unlikely(non_swap_entry(entry))) {
>> --
>> 2.7.4
>>
>
^ permalink raw reply
* [PATCH 00/15] hvc and powerpc opal console latency reduction
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
I'm seeing scattered reports of hard lockups triggering in the OPAL
console code. I haven't got a full latency trace -- they are difficult
to reproduce and sometimes just show up in dmesg of a bug report when
the system is having other issues. But it does seem like there are
some improvements that can be made to the hvc console and powerpc
opal driver.
I'm not sure how this series should be merged yet, but we can cross
that if/when it comes. Possibly all can go via the tty tree with
acks from powerpc maintainers.
Comments appreciated.
Thanks,
Nick
--
Nicholas Piggin (15):
powerpc/powernv: opal_put_chars partial write fix
powerpc/powernv: Fix OPAL console driver OPAL_BUSY loops
powerpc/powernv: opal-kmsg standardise OPAL_BUSY handling
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_atomic
tty: hvc: remove unexplained "just in case" spin delay
tty: hvc: use mutex instead of spinlock for hvc_structs lock
tty: hvc: hvc_poll break hv read loop
tty: hvc: hvc_poll may sleep
tty: hvc: hvc_write may sleep
tty: hvc: introduce the hv_ops.flush operation for hvc drivers
powerpc/powernv: provide a console flush operation for opal hvc driver
arch/powerpc/include/asm/opal.h | 3 +
arch/powerpc/platforms/powernv/opal-kmsg.c | 30 +---
arch/powerpc/platforms/powernv/opal.c | 176 +++++++++++++------
drivers/tty/hvc/hvc_console.c | 194 +++++++++++++--------
drivers/tty/hvc/hvc_console.h | 1 +
drivers/tty/hvc/hvc_opal.c | 33 ++--
6 files changed, 276 insertions(+), 161 deletions(-)
--
2.17.0
^ permalink raw reply
* [PATCH 01/15] powerpc/powernv: opal_put_chars partial write fix
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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.
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 48fbb41af5d1..e695b836fd49 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 02/15] powerpc/powernv: Fix OPAL console driver OPAL_BUSY loops
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
The OPAL console driver does not delay in case it gets OPAL_BUSY or
OPAL_BUSY_EVENT from firmware.
It can't yet be made to sleep because it is called under spinlock,
but it can be changed to the standard OPAL_BUSY loop form, and a
delay added to keep it from hitting the firmware too frequently.
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 e695b836fd49..6b621d47ac29 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 03/15] powerpc/powernv: opal-kmsg standardise OPAL_BUSY handling
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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.
Reviewed-by: 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 04/15] powerpc/powernv: opal-kmsg use flush fallback from console code
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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.
Reviewed-by: 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 05/15] powerpc/powernv: Implement and use opal_flush_console
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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>
Reviewed-by: 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 6b621d47ac29..6640fccbf30c 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 06/15] powerpc/powernv: Remove OPALv1 support from opal console driver
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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 by turning 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 6640fccbf30c..0ddb63226695 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 07/15] powerpc/powernv: move opal console flushing to udbg
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-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 0ddb63226695..55d4b1983110 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 08/15] powerpc/powernv: implement opal_put_chars_atomic
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
The RAW console does not need writes to be atomic, so relax
opal_put_chars to be able to do partial writes, and implement an
_atomic variant which does not take a spinlock. This API is used
in xmon, so the less locking that is 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 | 37 +++++++++++++++++++--------
drivers/tty/hvc/hvc_opal.c | 18 +++++++++----
3 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index bbff49fab0e5..5d7072411561 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_atomic(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 55d4b1983110..bcdb90ada938 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,22 @@ 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)
+{
+ return __opal_put_chars(vtermno, data, total_len, false);
+}
+
+/*
+ * opal_put_chars_atomic will not perform partial-writes. Data will be
+ * atomically written to the terminal or not at all. This is not strictly
+ * true at the moment because console space can race with OPAL's console
+ * writes.
+ */
+int opal_put_chars_atomic(uint32_t vtermno, const char *data, int total_len)
+{
+ return __opal_put_chars(vtermno, data, total_len, true);
+}
+
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..0a72f98ee082 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -183,9 +183,15 @@ static int hvc_opal_probe(struct platform_device *dev)
return -ENOMEM;
pv->proto = proto;
hvc_opal_privs[termno] = pv;
- if (proto == HV_PROTOCOL_HVSI)
- hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars,
+ if (proto == HV_PROTOCOL_HVSI) {
+ /*
+ * We want put_chars to be atomic to avoid mangling of
+ * hvsi packets.
+ */
+ hvsilib_init(&pv->hvsi,
+ opal_get_chars, opal_put_chars_atomic,
termno, 0);
+ }
/* Instanciate now to establish a mapping index==vtermno */
hvc_instantiate(termno, termno, ops);
@@ -376,8 +382,9 @@ void __init hvc_opal_init_early(void)
else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
ops = &hvc_opal_hvsi_ops;
- hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars,
- opal_put_chars, index, 1);
+ hvsilib_init(&hvc_opal_boot_priv.hvsi,
+ opal_get_chars, opal_put_chars_atomic,
+ index, 1);
/* HVSI, perform the handshake now */
hvsilib_establish(&hvc_opal_boot_priv.hvsi);
pr_devel("hvc_opal: Found HVSI console\n");
@@ -409,7 +416,8 @@ void __init udbg_init_debug_opal_hvsi(void)
hvc_opal_privs[index] = &hvc_opal_boot_priv;
hvc_opal_boot_termno = index;
udbg_init_opal_common();
- hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars,
+ hvsilib_init(&hvc_opal_boot_priv.hvsi,
+ opal_get_chars, opal_put_chars_atomic,
index, 1);
hvsilib_establish(&hvc_opal_boot_priv.hvsi);
}
--
2.17.0
^ permalink raw reply related
* [PATCH 09/15] tty: hvc: remove unexplained "just in case" spin delay
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
This delay was in the very first OPAL console commit 6.5 years ago,
and came from the vio hvc driver. 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 0a72f98ee082..786e76a1e06d 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -313,14 +313,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
* [PATCH 10/15] tty: hvc: use mutex instead of spinlock for hvc_structs lock
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
This allows hvc operations to sleep under the lock.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
drivers/tty/hvc/hvc_console.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 7709fcc707f4..fddb63322c67 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -73,7 +73,7 @@ static LIST_HEAD(hvc_structs);
* Protect the list of hvc_struct instances from inserts and removals during
* list traversal.
*/
-static DEFINE_SPINLOCK(hvc_structs_lock);
+static DEFINE_MUTEX(hvc_structs_mutex);
/*
* This value is used to assign a tty->index value to a hvc_struct based
@@ -83,7 +83,7 @@ static DEFINE_SPINLOCK(hvc_structs_lock);
static int last_hvc = -1;
/*
- * Do not call this function with either the hvc_structs_lock or the hvc_struct
+ * Do not call this function with either the hvc_structs_mutex or the hvc_struct
* lock held. If successful, this function increments the kref reference
* count against the target hvc_struct so it should be released when finished.
*/
@@ -92,25 +92,24 @@ static struct hvc_struct *hvc_get_by_index(int index)
struct hvc_struct *hp;
unsigned long flags;
- spin_lock(&hvc_structs_lock);
+ mutex_lock(&hvc_structs_mutex);
list_for_each_entry(hp, &hvc_structs, next) {
spin_lock_irqsave(&hp->lock, flags);
if (hp->index == index) {
tty_port_get(&hp->port);
spin_unlock_irqrestore(&hp->lock, flags);
- spin_unlock(&hvc_structs_lock);
+ mutex_unlock(&hvc_structs_mutex);
return hp;
}
spin_unlock_irqrestore(&hp->lock, flags);
}
hp = NULL;
+ mutex_unlock(&hvc_structs_mutex);
- spin_unlock(&hvc_structs_lock);
return hp;
}
-
/*
* Initial console vtermnos for console API usage prior to full console
* initialization. Any vty adapter outside this range will not have usable
@@ -224,13 +223,13 @@ static void hvc_port_destruct(struct tty_port *port)
struct hvc_struct *hp = container_of(port, struct hvc_struct, port);
unsigned long flags;
- spin_lock(&hvc_structs_lock);
+ mutex_lock(&hvc_structs_mutex);
spin_lock_irqsave(&hp->lock, flags);
list_del(&(hp->next));
spin_unlock_irqrestore(&hp->lock, flags);
- spin_unlock(&hvc_structs_lock);
+ mutex_unlock(&hvc_structs_mutex);
kfree(hp);
}
@@ -733,11 +732,11 @@ static int khvcd(void *unused)
try_to_freeze();
wmb();
if (!cpus_are_in_xmon()) {
- spin_lock(&hvc_structs_lock);
+ mutex_lock(&hvc_structs_mutex);
list_for_each_entry(hp, &hvc_structs, next) {
poll_mask |= hvc_poll(hp);
}
- spin_unlock(&hvc_structs_lock);
+ mutex_unlock(&hvc_structs_mutex);
} else
poll_mask |= HVC_POLL_READ;
if (hvc_kicked)
@@ -871,7 +870,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
INIT_WORK(&hp->tty_resize, hvc_set_winsz);
spin_lock_init(&hp->lock);
- spin_lock(&hvc_structs_lock);
+ mutex_lock(&hvc_structs_mutex);
/*
* find index to use:
@@ -891,7 +890,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
vtermnos[i] = vtermno;
list_add_tail(&(hp->next), &hvc_structs);
- spin_unlock(&hvc_structs_lock);
+ mutex_unlock(&hvc_structs_mutex);
/* check if we need to re-register the kernel console */
hvc_check_console(i);
--
2.17.0
^ permalink raw reply related
* [PATCH 11/15] tty: hvc: hvc_poll break hv read loop
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
Avoid looping with the spinlock held while there is read data
being returned from the hv driver. Instead note if the entire
size returned by tty_buffer_request_room was read, and request
another read poll.
This limits the critical section lengths, and provides more
even service to other consoles in case there is a pathological
condition.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
drivers/tty/hvc/hvc_console.c | 88 ++++++++++++++++++-----------------
1 file changed, 45 insertions(+), 43 deletions(-)
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index fddb63322c67..745ac220fce8 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -592,7 +592,7 @@ static u32 timeout = MIN_TIMEOUT;
int hvc_poll(struct hvc_struct *hp)
{
struct tty_struct *tty;
- int i, n, poll_mask = 0;
+ int i, n, count, poll_mask = 0;
char buf[N_INBUF] __ALIGNED__;
unsigned long flags;
int read_total = 0;
@@ -618,7 +618,7 @@ int hvc_poll(struct hvc_struct *hp)
/* Now check if we can get data (are we throttled ?) */
if (tty_throttled(tty))
- goto throttled;
+ goto out;
/* If we aren't notifier driven and aren't throttled, we always
* request a reschedule
@@ -627,56 +627,58 @@ int hvc_poll(struct hvc_struct *hp)
poll_mask |= HVC_POLL_READ;
/* Read data if any */
- for (;;) {
- int count = tty_buffer_request_room(&hp->port, N_INBUF);
- /* If flip is full, just reschedule a later read */
- if (count == 0) {
+ count = tty_buffer_request_room(&hp->port, N_INBUF);
+
+ /* If flip is full, just reschedule a later read */
+ if (count == 0) {
+ poll_mask |= HVC_POLL_READ;
+ goto out;
+ }
+
+ n = hp->ops->get_chars(hp->vtermno, buf, count);
+ if (n <= 0) {
+ /* Hangup the tty when disconnected from host */
+ if (n == -EPIPE) {
+ spin_unlock_irqrestore(&hp->lock, flags);
+ tty_hangup(tty);
+ spin_lock_irqsave(&hp->lock, flags);
+ } else if ( n == -EAGAIN ) {
+ /*
+ * Some back-ends can only ensure a certain min
+ * num of bytes read, which may be > 'count'.
+ * Let the tty clear the flip buff to make room.
+ */
poll_mask |= HVC_POLL_READ;
- break;
}
+ goto out;
+ }
- n = hp->ops->get_chars(hp->vtermno, buf, count);
- if (n <= 0) {
- /* Hangup the tty when disconnected from host */
- if (n == -EPIPE) {
- spin_unlock_irqrestore(&hp->lock, flags);
- tty_hangup(tty);
- spin_lock_irqsave(&hp->lock, flags);
- } else if ( n == -EAGAIN ) {
- /*
- * Some back-ends can only ensure a certain min
- * num of bytes read, which may be > 'count'.
- * Let the tty clear the flip buff to make room.
- */
- poll_mask |= HVC_POLL_READ;
- }
- break;
- }
- for (i = 0; i < n; ++i) {
+ for (i = 0; i < n; ++i) {
#ifdef CONFIG_MAGIC_SYSRQ
- if (hp->index == hvc_console.index) {
- /* Handle the SysRq Hack */
- /* XXX should support a sequence */
- if (buf[i] == '\x0f') { /* ^O */
- /* if ^O is pressed again, reset
- * sysrq_pressed and flip ^O char */
- sysrq_pressed = !sysrq_pressed;
- if (sysrq_pressed)
- continue;
- } else if (sysrq_pressed) {
- handle_sysrq(buf[i]);
- sysrq_pressed = 0;
+ if (hp->index == hvc_console.index) {
+ /* Handle the SysRq Hack */
+ /* XXX should support a sequence */
+ if (buf[i] == '\x0f') { /* ^O */
+ /* if ^O is pressed again, reset
+ * sysrq_pressed and flip ^O char */
+ sysrq_pressed = !sysrq_pressed;
+ if (sysrq_pressed)
continue;
- }
+ } else if (sysrq_pressed) {
+ handle_sysrq(buf[i]);
+ sysrq_pressed = 0;
+ continue;
}
-#endif /* CONFIG_MAGIC_SYSRQ */
- tty_insert_flip_char(&hp->port, buf[i], 0);
}
-
- read_total += n;
+#endif /* CONFIG_MAGIC_SYSRQ */
+ tty_insert_flip_char(&hp->port, buf[i], 0);
}
- throttled:
+ if (n == count)
+ poll_mask |= HVC_POLL_READ;
+ read_total = n;
+
+ out:
/* Wakeup write queue if necessary */
if (hp->do_wakeup) {
hp->do_wakeup = 0;
--
2.17.0
^ permalink raw reply related
* [PATCH 12/15] tty: hvc: hvc_poll may sleep
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
Introduce points where hvc_poll drops the lock, enables interrupts,
and reschedules.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
drivers/tty/hvc/hvc_console.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 745ac220fce8..2abfc0b15fbb 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -589,7 +589,7 @@ static u32 timeout = MIN_TIMEOUT;
#define HVC_POLL_READ 0x00000001
#define HVC_POLL_WRITE 0x00000002
-int hvc_poll(struct hvc_struct *hp)
+static int __hvc_poll(struct hvc_struct *hp, bool may_sleep)
{
struct tty_struct *tty;
int i, n, count, poll_mask = 0;
@@ -611,6 +611,12 @@ int hvc_poll(struct hvc_struct *hp)
timeout = (written_total) ? 0 : MIN_TIMEOUT;
}
+ if (may_sleep) {
+ spin_unlock_irqrestore(&hp->lock, flags);
+ cond_resched();
+ spin_lock_irqsave(&hp->lock, flags);
+ }
+
/* No tty attached, just skip */
tty = tty_port_tty_get(&hp->port);
if (tty == NULL)
@@ -698,6 +704,11 @@ int hvc_poll(struct hvc_struct *hp)
return poll_mask;
}
+
+int hvc_poll(struct hvc_struct *hp)
+{
+ return __hvc_poll(hp, false);
+}
EXPORT_SYMBOL_GPL(hvc_poll);
/**
@@ -736,7 +747,8 @@ static int khvcd(void *unused)
if (!cpus_are_in_xmon()) {
mutex_lock(&hvc_structs_mutex);
list_for_each_entry(hp, &hvc_structs, next) {
- poll_mask |= hvc_poll(hp);
+ poll_mask |= __hvc_poll(hp, true);
+ cond_resched();
}
mutex_unlock(&hvc_structs_mutex);
} else
--
2.17.0
^ permalink raw reply related
* [PATCH 13/15] tty: hvc: hvc_write may sleep
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
Rework the hvc_write loop to drop and re-take the spinlock on each
iteration, add a cond_resched. Don't bother with an initial hvc_push
initially, which makes the logic simpler -- just do a hvc_push on
each time around the loop.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
drivers/tty/hvc/hvc_console.c | 36 ++++++++++++++++++++---------------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 2abfc0b15fbb..6131d5084c42 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -493,23 +493,29 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
if (hp->port.count <= 0)
return -EIO;
- spin_lock_irqsave(&hp->lock, flags);
+ while (count > 0) {
+ spin_lock_irqsave(&hp->lock, flags);
- /* Push pending writes */
- if (hp->n_outbuf > 0)
- hvc_push(hp);
-
- while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) {
- if (rsize > count)
- rsize = count;
- memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
- count -= rsize;
- buf += rsize;
- hp->n_outbuf += rsize;
- written += rsize;
- hvc_push(hp);
+ rsize = hp->outbuf_size - hp->n_outbuf;
+
+ if (rsize) {
+ if (rsize > count)
+ rsize = count;
+ memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
+ count -= rsize;
+ buf += rsize;
+ hp->n_outbuf += rsize;
+ written += rsize;
+ }
+
+ if (hp->n_outbuf > 0)
+ hvc_push(hp);
+
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ if (count)
+ cond_resched();
}
- spin_unlock_irqrestore(&hp->lock, flags);
/*
* Racy, but harmless, kick thread if there is still pending data.
--
2.17.0
^ permalink raw reply related
* [PATCH 14/15] tty: hvc: introduce the hv_ops.flush operation for hvc drivers
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
Use .flush to wait for drivers to flush their console outside of
the spinlock, to reduce lock/irq latencies.
Flush the hvc console driver after each write, which can help
messages make it out to the console after a crash.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
drivers/tty/hvc/hvc_console.c | 35 +++++++++++++++++++++++++++++++++--
drivers/tty/hvc/hvc_console.h | 1 +
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 6131d5084c42..5414c4a87bea 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -110,6 +110,29 @@ static struct hvc_struct *hvc_get_by_index(int index)
return hp;
}
+static int __hvc_flush(const struct hv_ops *ops, uint32_t vtermno, bool wait)
+{
+ if (wait)
+ might_sleep();
+
+ if (ops->flush)
+ return ops->flush(vtermno, wait);
+ return 0;
+}
+
+static int hvc_console_flush(const struct hv_ops *ops, uint32_t vtermno)
+{
+ return __hvc_flush(ops, vtermno, false);
+}
+
+/*
+ * Wait for the console to flush before writing more to it. This sleeps.
+ */
+static int hvc_flush(struct hvc_struct *hp)
+{
+ return __hvc_flush(hp->ops, hp->vtermno, true);
+}
+
/*
* Initial console vtermnos for console API usage prior to full console
* initialization. Any vty adapter outside this range will not have usable
@@ -155,8 +178,12 @@ static void hvc_console_print(struct console *co, const char *b,
if (r <= 0) {
/* throw away characters on error
* but spin in case of -EAGAIN */
- if (r != -EAGAIN)
+ if (r != -EAGAIN) {
i = 0;
+ } else {
+ hvc_console_flush(cons_ops[index],
+ vtermnos[index]);
+ }
} else if (r > 0) {
i -= r;
if (i > 0)
@@ -164,6 +191,7 @@ static void hvc_console_print(struct console *co, const char *b,
}
}
}
+ hvc_console_flush(cons_ops[index], vtermnos[index]);
}
static struct tty_driver *hvc_console_device(struct console *c, int *index)
@@ -513,8 +541,11 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
spin_unlock_irqrestore(&hp->lock, flags);
- if (count)
+ if (count) {
+ if (hp->n_outbuf > 0)
+ hvc_flush(hp);
cond_resched();
+ }
}
/*
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h
index ea63090e013f..e9319954c832 100644
--- a/drivers/tty/hvc/hvc_console.h
+++ b/drivers/tty/hvc/hvc_console.h
@@ -54,6 +54,7 @@ struct hvc_struct {
struct hv_ops {
int (*get_chars)(uint32_t vtermno, char *buf, int count);
int (*put_chars)(uint32_t vtermno, const char *buf, int count);
+ int (*flush)(uint32_t vtermno, bool wait);
/* Callbacks for notification. Called in open, close and hangup */
int (*notifier_add)(struct hvc_struct *hp, int irq);
--
2.17.0
^ permalink raw reply related
* [PATCH 15/15] powerpc/powernv: provide a console flush operation for opal hvc driver
From: Nicholas Piggin @ 2018-04-30 14:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: Nicholas Piggin, Benjamin Herrenschmidt, Greg Kroah-Hartman,
Jiri Slaby, linux-kernel
In-Reply-To: <20180430145558.4308-1-npiggin@gmail.com>
Provide the flush hv_op for the opal hvc driver. This will flush the
firmware console buffers without spinning with interrupts disabled.
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/include/asm/opal.h | 1 +
arch/powerpc/platforms/powernv/opal.c | 83 +++++++++++++++++----------
drivers/tty/hvc/hvc_opal.c | 2 +
3 files changed, 55 insertions(+), 31 deletions(-)
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 5d7072411561..a426d2caaf9f 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -304,6 +304,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_atomic(uint32_t vtermno, const char *buf, int total_len);
+extern int opal_flush_chars(uint32_t vtermno, bool wait);
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 bcdb90ada938..048bc09afc83 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -370,12 +370,8 @@ static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, b
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);
+ if (rc == OPAL_BUSY_EVENT)
opal_poll_events(NULL);
- } else if (rc == OPAL_BUSY_EVENT) {
- mdelay(OPAL_BUSY_DELAY_MS);
- }
written = -EAGAIN;
goto out;
}
@@ -401,15 +397,6 @@ static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, b
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
- * 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.
- */
- if (written == -EAGAIN)
- opal_flush_console(vtermno);
-
return written;
}
@@ -429,40 +416,74 @@ int opal_put_chars_atomic(uint32_t vtermno, const char *data, int total_len)
return __opal_put_chars(vtermno, data, total_len, true);
}
-int opal_flush_console(uint32_t vtermno)
+static s64 __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);
+ WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
+
+ opal_poll_events(&evt);
+ if (!(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT))
+ return OPAL_SUCCESS;
+ return OPAL_BUSY;
- return OPAL_SUCCESS;
+ } else {
+ rc = opal_console_flush(vtermno);
+ if (rc == OPAL_BUSY_EVENT) {
+ opal_poll_events(NULL);
+ rc = OPAL_BUSY;
+ }
+ return rc;
}
- 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);
+}
+
+/*
+ * opal_flush_console spins until the console is flushed
+ */
+int opal_flush_console(uint32_t vtermno)
+{
+ for (;;) {
+ s64 rc = __opal_flush_console(vtermno);
+
+ if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) {
+ mdelay(1);
+ continue;
+ }
+
+ return opal_error_code(rc);
+ }
+}
+
+/*
+ * opal_flush_chars is an hvc interface that sleeps until the console is
+ * flushed if wait, otherwise it will return -EBUSY if the console has data,
+ * -EAGAIN if it has data and some of it was flushed.
+ */
+int opal_flush_chars(uint32_t vtermno, bool wait)
+{
+ for (;;) {
+ s64 rc = __opal_flush_console(vtermno);
+
+ if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) {
+ if (wait) {
+ msleep(OPAL_BUSY_DELAY_MS);
+ continue;
}
+ if (rc == OPAL_PARTIAL)
+ return -EAGAIN;
}
- } while (rc == OPAL_PARTIAL); /* More to flush */
- return opal_error_code(rc);
+ return opal_error_code(rc);
+ }
}
static int opal_recover_mce(struct pt_regs *regs,
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index 786e76a1e06d..fd306033f8d4 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -52,6 +52,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,
+ .flush = opal_flush_chars,
.notifier_add = notifier_add_irq,
.notifier_del = notifier_del_irq,
.notifier_hangup = notifier_hangup_irq,
@@ -141,6 +142,7 @@ static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
static const struct hv_ops hvc_opal_hvsi_ops = {
.get_chars = hvc_opal_hvsi_get_chars,
.put_chars = hvc_opal_hvsi_put_chars,
+ .flush = opal_flush_chars,
.notifier_add = hvc_opal_hvsi_open,
.notifier_del = hvc_opal_hvsi_close,
.notifier_hangup = hvc_opal_hvsi_hangup,
--
2.17.0
^ permalink raw reply related
* Re: [PATCH v10 08/25] mm: VMA sequence count
From: Laurent Dufour @ 2018-04-30 15:14 UTC (permalink / raw)
To: Minchan Kim
Cc: akpm, mhocko, peterz, kirill, ak, dave, jack, Matthew Wilcox,
benh, mpe, paulus, Thomas Gleixner, Ingo Molnar, hpa, Will Deacon,
Sergey Senozhatsky, Andrea Arcangeli, Alexei Starovoitov,
kemi.wang, sergey.senozhatsky.work, Daniel Jordan, David Rientjes,
Jerome Glisse, Ganesh Mahendran, linux-kernel, linux-mm, haren,
khandual, npiggin, bsingharora, paulmck, Tim Chen, linuxppc-dev,
x86
In-Reply-To: <20180423064259.GC114098@rodete-desktop-imager.corp.google.com>
On 23/04/2018 08:42, Minchan Kim wrote:
> On Tue, Apr 17, 2018 at 04:33:14PM +0200, Laurent Dufour wrote:
>> From: Peter Zijlstra <peterz@infradead.org>
>>
>> Wrap the VMA modifications (vma_adjust/unmap_page_range) with sequence
>> counts such that we can easily test if a VMA is changed.
>
> So, seqcount is to protect modifying all attributes of vma?
The seqcount is used to protect fields that will be used during the speculative
page fault like boundaries, protections.
>>
>> The unmap_page_range() one allows us to make assumptions about
>> page-tables; when we find the seqcount hasn't changed we can assume
>> page-tables are still valid.
>
> Hmm, seqcount covers page-table, too.
> Please describe what the seqcount want to protect.
The calls to vm_write_begin/end() in unmap_page_range() are used to detect when
a VMA is being unmap and thus that new page fault should not be satisfied for
this VMA. This is protecting the VMA unmapping operation, not the page tables
themselves.
>>
>> The flip side is that we cannot distinguish between a vma_adjust() and
>> the unmap_page_range() -- where with the former we could have
>> re-checked the vma bounds against the address.
>>
>> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
>>
>> [Port to 4.12 kernel]
>> [Build depends on CONFIG_SPECULATIVE_PAGE_FAULT]
>> [Introduce vm_write_* inline function depending on
>> CONFIG_SPECULATIVE_PAGE_FAULT]
>> [Fix lock dependency between mapping->i_mmap_rwsem and vma->vm_sequence by
>> using vm_raw_write* functions]
>> [Fix a lock dependency warning in mmap_region() when entering the error
>> path]
>> [move sequence initialisation INIT_VMA()]
>> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
>> ---
>> include/linux/mm.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mm_types.h | 3 +++
>> mm/memory.c | 2 ++
>> mm/mmap.c | 31 +++++++++++++++++++++++++++++++
>> 4 files changed, 80 insertions(+)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index efc1248b82bd..988daf7030c9 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -1264,6 +1264,9 @@ struct zap_details {
>> static inline void INIT_VMA(struct vm_area_struct *vma)
>> {
>> INIT_LIST_HEAD(&vma->anon_vma_chain);
>> +#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
>> + seqcount_init(&vma->vm_sequence);
>> +#endif
>> }
>>
>> struct page *_vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
>> @@ -1386,6 +1389,47 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping,
>> unmap_mapping_range(mapping, holebegin, holelen, 0);
>> }
>>
>> +#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
>> +static inline void vm_write_begin(struct vm_area_struct *vma)
>> +{
>> + write_seqcount_begin(&vma->vm_sequence);
>> +}
>> +static inline void vm_write_begin_nested(struct vm_area_struct *vma,
>> + int subclass)
>> +{
>> + write_seqcount_begin_nested(&vma->vm_sequence, subclass);
>> +}
>> +static inline void vm_write_end(struct vm_area_struct *vma)
>> +{
>> + write_seqcount_end(&vma->vm_sequence);
>> +}
>> +static inline void vm_raw_write_begin(struct vm_area_struct *vma)
>> +{
>> + raw_write_seqcount_begin(&vma->vm_sequence);
>> +}
>> +static inline void vm_raw_write_end(struct vm_area_struct *vma)
>> +{
>> + raw_write_seqcount_end(&vma->vm_sequence);
>> +}
>> +#else
>> +static inline void vm_write_begin(struct vm_area_struct *vma)
>> +{
>> +}
>> +static inline void vm_write_begin_nested(struct vm_area_struct *vma,
>> + int subclass)
>> +{
>> +}
>> +static inline void vm_write_end(struct vm_area_struct *vma)
>> +{
>> +}
>> +static inline void vm_raw_write_begin(struct vm_area_struct *vma)
>> +{
>> +}
>> +static inline void vm_raw_write_end(struct vm_area_struct *vma)
>> +{
>> +}
>> +#endif /* CONFIG_SPECULATIVE_PAGE_FAULT */
>> +
>> extern int access_process_vm(struct task_struct *tsk, unsigned long addr,
>> void *buf, int len, unsigned int gup_flags);
>> extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
>> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>> index 21612347d311..db5e9d630e7a 100644
>> --- a/include/linux/mm_types.h
>> +++ b/include/linux/mm_types.h
>> @@ -335,6 +335,9 @@ struct vm_area_struct {
>> struct mempolicy *vm_policy; /* NUMA policy for the VMA */
>> #endif
>> struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
>> +#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
>> + seqcount_t vm_sequence;
>> +#endif
>> } __randomize_layout;
>>
>> struct core_thread {
>> diff --git a/mm/memory.c b/mm/memory.c
>> index f86efcb8e268..f7fed053df80 100644
>> --- a/mm/memory.c
>> +++ b/mm/memory.c
>> @@ -1503,6 +1503,7 @@ void unmap_page_range(struct mmu_gather *tlb,
>> unsigned long next;
>>
>> BUG_ON(addr >= end);
>
> The comment about saying it aims for page-table stability will help.
A comment may be added mentioning that we use the seqcount to indicate that the
VMA is modified, being unmapped. But there is not a real page table protection,
and I think this may be confusing to talk about page table stability here.
>
>> + vm_write_begin(vma);
>> tlb_start_vma(tlb, vma);
>> pgd = pgd_offset(vma->vm_mm, addr);
>> do {
>> @@ -1512,6 +1513,7 @@ void unmap_page_range(struct mmu_gather *tlb,
>> next = zap_p4d_range(tlb, vma, pgd, addr, next, details);
>> } while (pgd++, addr = next, addr != end);
>> tlb_end_vma(tlb, vma);
>> + vm_write_end(vma);
>> }
>>
>>
>> diff --git a/mm/mmap.c b/mm/mmap.c
>> index 8bd9ae1dfacc..813e49589ea1 100644
>> --- a/mm/mmap.c
>> +++ b/mm/mmap.c
>> @@ -692,6 +692,30 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
>> long adjust_next = 0;
>> int remove_next = 0;
>>
>> + /*
>> + * Why using vm_raw_write*() functions here to avoid lockdep's warning ?
>> + *
>> + * Locked is complaining about a theoretical lock dependency, involving
>> + * 3 locks:
>> + * mapping->i_mmap_rwsem --> vma->vm_sequence --> fs_reclaim
>> + *
>> + * Here are the major path leading to this dependency :
>> + * 1. __vma_adjust() mmap_sem -> vm_sequence -> i_mmap_rwsem
>> + * 2. move_vmap() mmap_sem -> vm_sequence -> fs_reclaim
>> + * 3. __alloc_pages_nodemask() fs_reclaim -> i_mmap_rwsem
>> + * 4. unmap_mapping_range() i_mmap_rwsem -> vm_sequence
>> + *
>> + * So there is no way to solve this easily, especially because in
>> + * unmap_mapping_range() the i_mmap_rwsem is grab while the impacted
>> + * VMAs are not yet known.
>> + * However, the way the vm_seq is used is guarantying that we will
>> + * never block on it since we just check for its value and never wait
>> + * for it to move, see vma_has_changed() and handle_speculative_fault().
>> + */
>> + vm_raw_write_begin(vma);
>> + if (next)
>> + vm_raw_write_begin(next);
>> +
>> if (next && !insert) {
>> struct vm_area_struct *exporter = NULL, *importer = NULL;
>>
>> @@ -902,6 +926,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
>> anon_vma_merge(vma, next);
>> mm->map_count--;
>> mpol_put(vma_policy(next));
>> + vm_raw_write_end(next);
>> kmem_cache_free(vm_area_cachep, next);
>> /*
>> * In mprotect's case 6 (see comments on vma_merge),
>> @@ -916,6 +941,8 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
>> * "vma->vm_next" gap must be updated.
>> */
>> next = vma->vm_next;
>> + if (next)
>> + vm_raw_write_begin(next);
>> } else {
>> /*
>> * For the scope of the comment "next" and
>> @@ -962,6 +989,10 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
>> if (insert && file)
>> uprobe_mmap(insert);
>>
>> + if (next && next != vma)
>> + vm_raw_write_end(next);
>> + vm_raw_write_end(vma);
>> +
>> validate_mm(mm);
>>
>> return 0;
>> --
>> 2.7.4
>>
>
^ permalink raw reply
* Re: [PATCH 0/4] powerpc/xive: add support for H_INT_RESET
From: Cédric Le Goater @ 2018-04-30 16:21 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20180403071548.19829-1-clg@kaod.org>
On 04/03/2018 09:15 AM, Cédric Le Goater wrote:
> H_INT_RESET performs a reset of the Hypervisor internal interrupt
> structures, removing all settings done with H_INT_SET_SOURCE_CONFIG
> and H_INT_SET_QUEUE_CONFIG. This is most important for kdump and kexec
> to be able to restart in a clean state.
>
> First patch closes a window in the kexec sequence in which the
> H_INT_RESET hcall can be run concurrently with other hcalls when CPUs
> are brought down. The other patches wire up the reset hcall making
> sure it supports H_LONG_BUSY_* returned by the Hypervisor.
Does anyone has some time to take a look ?
Thanks,
C.
> Cédric Le Goater (4):
> powerpc/64/kexec: fix race in kexec when XIVE is shutdowned
> powerpc/xive: fix hcall H_INT_RESET to support long busy delays
> powerpc/xive: shutdown XIVE when kexec or kdump is performed
> powerpc/xive: prepare all hcalls to support long busy delays
>
> arch/powerpc/kernel/machine_kexec_64.c | 8 +--
> arch/powerpc/platforms/pseries/kexec.c | 7 ++-
> arch/powerpc/sysdev/xive/spapr.c | 89 +++++++++++++++++++++++++++++-----
> 3 files changed, 85 insertions(+), 19 deletions(-)
>
^ permalink raw reply
* Re: [PATCH v10 24/25] x86/mm: add speculative pagefault handling
From: Punit Agrawal @ 2018-04-30 18:43 UTC (permalink / raw)
To: Laurent Dufour
Cc: akpm, mhocko, peterz, kirill, ak, dave, jack, Matthew Wilcox,
benh, mpe, paulus, Thomas Gleixner, Ingo Molnar, hpa, Will Deacon,
Sergey Senozhatsky, Andrea Arcangeli, Alexei Starovoitov,
kemi.wang, sergey.senozhatsky.work, Daniel Jordan, David Rientjes,
Jerome Glisse, Ganesh Mahendran, linux-kernel, linux-mm, haren,
khandual, npiggin, bsingharora, paulmck, Tim Chen, linuxppc-dev,
x86
In-Reply-To: <1523975611-15978-25-git-send-email-ldufour@linux.vnet.ibm.com>
Hi Laurent,
I am looking to add support for speculative page fault handling to
arm64 (effectively porting this patch) and had a few questions.
Apologies if I've missed an obvious explanation for my queries. I'm
jumping in bit late to the discussion.
On Tue, Apr 17, 2018 at 3:33 PM, Laurent Dufour
<ldufour@linux.vnet.ibm.com> wrote:
> From: Peter Zijlstra <peterz@infradead.org>
>
> Try a speculative fault before acquiring mmap_sem, if it returns with
> VM_FAULT_RETRY continue with the mmap_sem acquisition and do the
> traditional fault.
>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
>
> [Clearing of FAULT_FLAG_ALLOW_RETRY is now done in
> handle_speculative_fault()]
> [Retry with usual fault path in the case VM_ERROR is returned by
> handle_speculative_fault(). This allows signal to be delivered]
> [Don't build SPF call if !CONFIG_SPECULATIVE_PAGE_FAULT]
> [Try speculative fault path only for multi threaded processes]
> [Try reuse to the VMA fetch during the speculative path in case of retry]
> [Call reuse_spf_or_find_vma()]
> [Handle memory protection key fault]
> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
> ---
> arch/x86/mm/fault.c | 42 ++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 38 insertions(+), 4 deletions(-)
>
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index 73bd8c95ac71..59f778386df5 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -1220,7 +1220,7 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
> struct mm_struct *mm;
> int fault, major = 0;
> unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
> - u32 pkey;
> + u32 pkey, *pt_pkey = &pkey;
>
> tsk = current;
> mm = tsk->mm;
> @@ -1310,6 +1310,30 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
> flags |= FAULT_FLAG_INSTRUCTION;
>
> /*
> + * Do not try speculative page fault for kernel's pages and if
> + * the fault was due to protection keys since it can't be resolved.
> + */
> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT) &&
> + !(error_code & X86_PF_PK)) {
You can simplify this condition by dropping the IS_ENABLED() check as
you already provide an alternate implementation of
handle_speculative_fault() when CONFIG_SPECULATIVE_PAGE_FAULT is not
defined.
> + fault = handle_speculative_fault(mm, address, flags, &vma);
> + if (fault != VM_FAULT_RETRY) {
> + perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, address);
> + /*
> + * Do not advertise for the pkey value since we don't
> + * know it.
> + * This is not a matter as we checked for X86_PF_PK
> + * earlier, so we should not handle pkey fault here,
> + * but to be sure that mm_fault_error() callees will
> + * not try to use it, we invalidate the pointer.
> + */
> + pt_pkey = NULL;
> + goto done;
> + }
> + } else {
> + vma = NULL;
> + }
The else part can be dropped if vma is initialised to NULL when it is
declared at the top of the function.
> +
> + /*
> * When running in the kernel we expect faults to occur only to
> * addresses in user space. All other faults represent errors in
> * the kernel and should generate an OOPS. Unfortunately, in the
> @@ -1342,7 +1366,8 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
> might_sleep();
> }
>
> - vma = find_vma(mm, address);
> + if (!vma || !can_reuse_spf_vma(vma, address))
> + vma = find_vma(mm, address);
Is there a measurable benefit from reusing the vma?
Dropping the vma reference unconditionally after speculative page
fault handling gets rid of the implicit state when "vma != NULL"
(increased ref-count). I found it a bit confusing to follow.
> if (unlikely(!vma)) {
> bad_area(regs, error_code, address);
> return;
> @@ -1409,8 +1434,15 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
> if (flags & FAULT_FLAG_ALLOW_RETRY) {
> flags &= ~FAULT_FLAG_ALLOW_RETRY;
> flags |= FAULT_FLAG_TRIED;
> - if (!fatal_signal_pending(tsk))
> + if (!fatal_signal_pending(tsk)) {
> + /*
> + * Do not try to reuse this vma and fetch it
> + * again since we will release the mmap_sem.
> + */
> + if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
> + vma = NULL;
Regardless of the above comment, can the vma be reset here unconditionally?
Thanks,
Punit
^ permalink raw reply
* Re: [PATCH v10 17/25] mm: protect mm_rb tree with a rwlock
From: Punit Agrawal @ 2018-04-30 18:47 UTC (permalink / raw)
To: Laurent Dufour
Cc: akpm, mhocko, peterz, kirill, ak, dave, jack, Matthew Wilcox,
benh, mpe, paulus, Thomas Gleixner, Ingo Molnar, hpa, Will Deacon,
Sergey Senozhatsky, Andrea Arcangeli, Alexei Starovoitov,
kemi.wang, sergey.senozhatsky.work, Daniel Jordan, David Rientjes,
Jerome Glisse, Ganesh Mahendran, linux-kernel, linux-mm, haren,
khandual, npiggin, bsingharora, paulmck, Tim Chen, linuxppc-dev,
x86
In-Reply-To: <1523975611-15978-18-git-send-email-ldufour@linux.vnet.ibm.com>
Hi Laurent,
One nitpick below.
On Tue, Apr 17, 2018 at 3:33 PM, Laurent Dufour
<ldufour@linux.vnet.ibm.com> wrote:
> This change is inspired by the Peter's proposal patch [1] which was
> protecting the VMA using SRCU. Unfortunately, SRCU is not scaling well in
> that particular case, and it is introducing major performance degradation
> due to excessive scheduling operations.
>
> To allow access to the mm_rb tree without grabbing the mmap_sem, this patch
> is protecting it access using a rwlock. As the mm_rb tree is a O(log n)
> search it is safe to protect it using such a lock. The VMA cache is not
> protected by the new rwlock and it should not be used without holding the
> mmap_sem.
>
> To allow the picked VMA structure to be used once the rwlock is released, a
> use count is added to the VMA structure. When the VMA is allocated it is
> set to 1. Each time the VMA is picked with the rwlock held its use count
> is incremented. Each time the VMA is released it is decremented. When the
> use count hits zero, this means that the VMA is no more used and should be
> freed.
>
> This patch is preparing for 2 kind of VMA access :
> - as usual, under the control of the mmap_sem,
> - without holding the mmap_sem for the speculative page fault handler.
>
> Access done under the control the mmap_sem doesn't require to grab the
> rwlock to protect read access to the mm_rb tree, but access in write must
> be done under the protection of the rwlock too. This affects inserting and
> removing of elements in the RB tree.
>
> The patch is introducing 2 new functions:
> - vma_get() to find a VMA based on an address by holding the new rwlock.
> - vma_put() to release the VMA when its no more used.
> These services are designed to be used when access are made to the RB tree
> without holding the mmap_sem.
>
> When a VMA is removed from the RB tree, its vma->vm_rb field is cleared and
> we rely on the WMB done when releasing the rwlock to serialize the write
> with the RMB done in a later patch to check for the VMA's validity.
>
> When free_vma is called, the file associated with the VMA is closed
> immediately, but the policy and the file structure remained in used until
> the VMA's use count reach 0, which may happens later when exiting an
> in progress speculative page fault.
>
> [1] https://patchwork.kernel.org/patch/5108281/
>
> Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
> Cc: Matthew Wilcox <willy@infradead.org>
> Signed-off-by: Laurent Dufour <ldufour@linux.vnet.ibm.com>
> ---
> include/linux/mm.h | 1 +
> include/linux/mm_types.h | 4 ++
> kernel/fork.c | 3 ++
> mm/init-mm.c | 3 ++
> mm/internal.h | 6 +++
> mm/mmap.c | 115 +++++++++++++++++++++++++++++++++++------------
> 6 files changed, 104 insertions(+), 28 deletions(-)
>
[...]
> diff --git a/mm/mmap.c b/mm/mmap.c
> index 5601f1ef8bb9..a82950960f2e 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -160,6 +160,27 @@ void unlink_file_vma(struct vm_area_struct *vma)
> }
> }
>
> +static void __free_vma(struct vm_area_struct *vma)
> +{
> + if (vma->vm_file)
> + fput(vma->vm_file);
> + mpol_put(vma_policy(vma));
> + kmem_cache_free(vm_area_cachep, vma);
> +}
> +
> +#ifdef CONFIG_SPECULATIVE_PAGE_FAULT
> +void put_vma(struct vm_area_struct *vma)
> +{
> + if (atomic_dec_and_test(&vma->vm_ref_count))
> + __free_vma(vma);
> +}
> +#else
> +static inline void put_vma(struct vm_area_struct *vma)
> +{
> + return __free_vma(vma);
Please drop the "return".
Thanks,
Punit
[...]
^ permalink raw reply
* Re: [PATCH v4 2/6] iommu: of: make of_pci_map_rid() available for other devices too
From: Bjorn Helgaas @ 2018-04-30 20:56 UTC (permalink / raw)
To: Nipun Gupta
Cc: robin.murphy, will.deacon, robh+dt, robh, mark.rutland,
catalin.marinas, gregkh, hch, joro, m.szyprowski, shawnguo,
frowand.list, bhelgaas, iommu, linux-kernel, devicetree,
linux-arm-kernel, linuxppc-dev, linux-pci, bharat.bhushan,
stuyoder, laurentiu.tudor, leoyang.li
In-Reply-To: <1525069641-8523-3-git-send-email-nipun.gupta@nxp.com>
On Mon, Apr 30, 2018 at 11:57:17AM +0530, Nipun Gupta wrote:
> iommu-map property is also used by devices with fsl-mc. This
> patch moves the of_pci_map_rid to generic location, so that it
> can be used by other busses too.
>
> 'of_pci_map_rid' is renamed here to 'of_map_rid' and there is no
> functional change done in the API.
>
> Signed-off-by: Nipun Gupta <nipun.gupta@nxp.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
> ---
> drivers/iommu/of_iommu.c | 5 +--
> drivers/of/base.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++
> drivers/of/irq.c | 5 +--
> drivers/pci/of.c | 101 ----------------------------------------------
> include/linux/of.h | 11 +++++
> include/linux/of_pci.h | 10 -----
> 6 files changed, 117 insertions(+), 117 deletions(-)
>
> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
> index 5c36a8b..811e160 100644
> --- a/drivers/iommu/of_iommu.c
> +++ b/drivers/iommu/of_iommu.c
> @@ -149,9 +149,8 @@ static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
> struct of_phandle_args iommu_spec = { .args_count = 1 };
> int err;
>
> - err = of_pci_map_rid(info->np, alias, "iommu-map",
> - "iommu-map-mask", &iommu_spec.np,
> - iommu_spec.args);
> + err = of_map_rid(info->np, alias, "iommu-map", "iommu-map-mask",
> + &iommu_spec.np, iommu_spec.args);
> if (err)
> return err == -ENODEV ? NO_IOMMU : err;
>
> diff --git a/drivers/of/base.c b/drivers/of/base.c
> index 848f549..c7aac81 100644
> --- a/drivers/of/base.c
> +++ b/drivers/of/base.c
> @@ -1995,3 +1995,105 @@ int of_find_last_cache_level(unsigned int cpu)
>
> return cache_level;
> }
> +
> +/**
> + * of_map_rid - Translate a requester ID through a downstream mapping.
> + * @np: root complex device node.
> + * @rid: device requester ID to map.
> + * @map_name: property name of the map to use.
> + * @map_mask_name: optional property name of the mask to use.
> + * @target: optional pointer to a target device node.
> + * @id_out: optional pointer to receive the translated ID.
> + *
> + * Given a device requester ID, look up the appropriate implementation-defined
> + * platform ID and/or the target device which receives transactions on that
> + * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or
> + * @id_out may be NULL if only the other is required. If @target points to
> + * a non-NULL device node pointer, only entries targeting that node will be
> + * matched; if it points to a NULL value, it will receive the device node of
> + * the first matching target phandle, with a reference held.
> + *
> + * Return: 0 on success or a standard error code on failure.
> + */
> +int of_map_rid(struct device_node *np, u32 rid,
> + const char *map_name, const char *map_mask_name,
> + struct device_node **target, u32 *id_out)
> +{
> + u32 map_mask, masked_rid;
> + int map_len;
> + const __be32 *map = NULL;
> +
> + if (!np || !map_name || (!target && !id_out))
> + return -EINVAL;
> +
> + map = of_get_property(np, map_name, &map_len);
> + if (!map) {
> + if (target)
> + return -ENODEV;
> + /* Otherwise, no map implies no translation */
> + *id_out = rid;
> + return 0;
> + }
> +
> + if (!map_len || map_len % (4 * sizeof(*map))) {
> + pr_err("%pOF: Error: Bad %s length: %d\n", np,
> + map_name, map_len);
> + return -EINVAL;
> + }
> +
> + /* The default is to select all bits. */
> + map_mask = 0xffffffff;
> +
> + /*
> + * Can be overridden by "{iommu,msi}-map-mask" property.
> + * If of_property_read_u32() fails, the default is used.
> + */
> + if (map_mask_name)
> + of_property_read_u32(np, map_mask_name, &map_mask);
> +
> + masked_rid = map_mask & rid;
> + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
> + struct device_node *phandle_node;
> + u32 rid_base = be32_to_cpup(map + 0);
> + u32 phandle = be32_to_cpup(map + 1);
> + u32 out_base = be32_to_cpup(map + 2);
> + u32 rid_len = be32_to_cpup(map + 3);
> +
> + if (rid_base & ~map_mask) {
> + pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n",
> + np, map_name, map_name,
> + map_mask, rid_base);
> + return -EFAULT;
> + }
> +
> + if (masked_rid < rid_base || masked_rid >= rid_base + rid_len)
> + continue;
> +
> + phandle_node = of_find_node_by_phandle(phandle);
> + if (!phandle_node)
> + return -ENODEV;
> +
> + if (target) {
> + if (*target)
> + of_node_put(phandle_node);
> + else
> + *target = phandle_node;
> +
> + if (*target != phandle_node)
> + continue;
> + }
> +
> + if (id_out)
> + *id_out = masked_rid - rid_base + out_base;
> +
> + pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n",
> + np, map_name, map_mask, rid_base, out_base,
> + rid_len, rid, masked_rid - rid_base + out_base);
> + return 0;
> + }
> +
> + pr_err("%pOF: Invalid %s translation - no match for rid 0x%x on %pOF\n",
> + np, map_name, rid, target && *target ? *target : NULL);
> + return -EFAULT;
> +}
> +EXPORT_SYMBOL_GPL(of_map_rid);
> diff --git a/drivers/of/irq.c b/drivers/of/irq.c
> index 02ad93a..e1f6f39 100644
> --- a/drivers/of/irq.c
> +++ b/drivers/of/irq.c
> @@ -22,7 +22,6 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_irq.h>
> -#include <linux/of_pci.h>
> #include <linux/string.h>
> #include <linux/slab.h>
>
> @@ -588,8 +587,8 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
> * "msi-map" property.
> */
> for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
> - if (!of_pci_map_rid(parent_dev->of_node, rid_in, "msi-map",
> - "msi-map-mask", np, &rid_out))
> + if (!of_map_rid(parent_dev->of_node, rid_in, "msi-map",
> + "msi-map-mask", np, &rid_out))
> break;
> return rid_out;
> }
> diff --git a/drivers/pci/of.c b/drivers/pci/of.c
> index a28355c..d2cebbe 100644
> --- a/drivers/pci/of.c
> +++ b/drivers/pci/of.c
> @@ -362,107 +362,6 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
> EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
> #endif /* CONFIG_OF_ADDRESS */
>
> -/**
> - * of_pci_map_rid - Translate a requester ID through a downstream mapping.
> - * @np: root complex device node.
> - * @rid: PCI requester ID to map.
> - * @map_name: property name of the map to use.
> - * @map_mask_name: optional property name of the mask to use.
> - * @target: optional pointer to a target device node.
> - * @id_out: optional pointer to receive the translated ID.
> - *
> - * Given a PCI requester ID, look up the appropriate implementation-defined
> - * platform ID and/or the target device which receives transactions on that
> - * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or
> - * @id_out may be NULL if only the other is required. If @target points to
> - * a non-NULL device node pointer, only entries targeting that node will be
> - * matched; if it points to a NULL value, it will receive the device node of
> - * the first matching target phandle, with a reference held.
> - *
> - * Return: 0 on success or a standard error code on failure.
> - */
> -int of_pci_map_rid(struct device_node *np, u32 rid,
> - const char *map_name, const char *map_mask_name,
> - struct device_node **target, u32 *id_out)
> -{
> - u32 map_mask, masked_rid;
> - int map_len;
> - const __be32 *map = NULL;
> -
> - if (!np || !map_name || (!target && !id_out))
> - return -EINVAL;
> -
> - map = of_get_property(np, map_name, &map_len);
> - if (!map) {
> - if (target)
> - return -ENODEV;
> - /* Otherwise, no map implies no translation */
> - *id_out = rid;
> - return 0;
> - }
> -
> - if (!map_len || map_len % (4 * sizeof(*map))) {
> - pr_err("%pOF: Error: Bad %s length: %d\n", np,
> - map_name, map_len);
> - return -EINVAL;
> - }
> -
> - /* The default is to select all bits. */
> - map_mask = 0xffffffff;
> -
> - /*
> - * Can be overridden by "{iommu,msi}-map-mask" property.
> - * If of_property_read_u32() fails, the default is used.
> - */
> - if (map_mask_name)
> - of_property_read_u32(np, map_mask_name, &map_mask);
> -
> - masked_rid = map_mask & rid;
> - for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
> - struct device_node *phandle_node;
> - u32 rid_base = be32_to_cpup(map + 0);
> - u32 phandle = be32_to_cpup(map + 1);
> - u32 out_base = be32_to_cpup(map + 2);
> - u32 rid_len = be32_to_cpup(map + 3);
> -
> - if (rid_base & ~map_mask) {
> - pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores rid-base (0x%x)\n",
> - np, map_name, map_name,
> - map_mask, rid_base);
> - return -EFAULT;
> - }
> -
> - if (masked_rid < rid_base || masked_rid >= rid_base + rid_len)
> - continue;
> -
> - phandle_node = of_find_node_by_phandle(phandle);
> - if (!phandle_node)
> - return -ENODEV;
> -
> - if (target) {
> - if (*target)
> - of_node_put(phandle_node);
> - else
> - *target = phandle_node;
> -
> - if (*target != phandle_node)
> - continue;
> - }
> -
> - if (id_out)
> - *id_out = masked_rid - rid_base + out_base;
> -
> - pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n",
> - np, map_name, map_mask, rid_base, out_base,
> - rid_len, rid, masked_rid - rid_base + out_base);
> - return 0;
> - }
> -
> - pr_err("%pOF: Invalid %s translation - no match for rid 0x%x on %pOF\n",
> - np, map_name, rid, target && *target ? *target : NULL);
> - return -EFAULT;
> -}
> -
> #if IS_ENABLED(CONFIG_OF_IRQ)
> /**
> * of_irq_parse_pci - Resolve the interrupt for a PCI device
> diff --git a/include/linux/of.h b/include/linux/of.h
> index 4d25e4f..f4251c3 100644
> --- a/include/linux/of.h
> +++ b/include/linux/of.h
> @@ -545,6 +545,10 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
>
> extern int of_cpu_node_to_id(struct device_node *np);
>
> +int of_map_rid(struct device_node *np, u32 rid,
> + const char *map_name, const char *map_mask_name,
> + struct device_node **target, u32 *id_out);
> +
> #else /* CONFIG_OF */
>
> static inline void of_core_init(void)
> @@ -931,6 +935,13 @@ static inline int of_cpu_node_to_id(struct device_node *np)
> return -ENODEV;
> }
>
> +static inline int of_map_rid(struct device_node *np, u32 rid,
> + const char *map_name, const char *map_mask_name,
> + struct device_node **target, u32 *id_out)
> +{
> + return -EINVAL;
> +}
> +
> #define of_match_ptr(_ptr) NULL
> #define of_match_node(_matches, _node) NULL
> #endif /* CONFIG_OF */
> diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
> index 091033a..a23b44a 100644
> --- a/include/linux/of_pci.h
> +++ b/include/linux/of_pci.h
> @@ -17,9 +17,6 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
> int of_get_pci_domain_nr(struct device_node *node);
> int of_pci_get_max_link_speed(struct device_node *node);
> void of_pci_check_probe_only(void);
> -int of_pci_map_rid(struct device_node *np, u32 rid,
> - const char *map_name, const char *map_mask_name,
> - struct device_node **target, u32 *id_out);
> #else
> static inline struct device_node *of_pci_find_child_device(struct device_node *parent,
> unsigned int devfn)
> @@ -44,13 +41,6 @@ static inline int of_pci_get_devfn(struct device_node *np)
> return -1;
> }
>
> -static inline int of_pci_map_rid(struct device_node *np, u32 rid,
> - const char *map_name, const char *map_mask_name,
> - struct device_node **target, u32 *id_out)
> -{
> - return -EINVAL;
> -}
> -
> static inline int
> of_pci_get_max_link_speed(struct device_node *node)
> {
> --
> 1.9.1
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox