Skip to contentSkip to conversation

PCI: rcar: Finish transition to L1 state in rcar_pcie_config_access()

PATCH Superseded [email protected] 5 participants 20 messages Jan 22, 2022 – Feb 18, 2022

Subsystems (4)

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/2 PCI: rcar: Finish transition to L1 state in rcar_pcie_config_access()

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

Avoid triggering the abort in rcar_pcie_config_access() by checking whether the controller is in the transition state, and if so, finish the transition right away. This prevents a lot of unnecessary exceptions, although not all of them.

Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

---
V2: Pull DEFINE_SPINLOCK(pmsr_lock) and rcar_pcie_wakeup() out of ifdef(CONFIG_ARM),
since this change is applicable even on arm64
V3: - Convert non-zero return value from rcar_pcie_wakeup() in either
PCIBIOS_SET_FAILED in rcar_pcie_config_access(), or, 1 in rcar_pcie_aarch32_abort_handler().
- Set error response using PCI_SET_ERROR_RESPONSE() in rcar_pcie_config_access()
- Fix double spinlock unlock in rcar_pcie_aarch32_abort_handler().

---
 drivers/pci/controller/pcie-rcar-host.c | 76 +++++++++++++++----------
 1 file changed, 45 insertions(+), 31 deletions(-)
drivers/pci/controller/pcie-rcar-host.c +45 -31
index 38b6e02edfa9..7d38a9c50093 100644
@@ -65,6 +65,42 @@ struct rcar_pcie_host {
 	int			(*phy_init_fn)(struct rcar_pcie_host *host);
 };
 
+static DEFINE_SPINLOCK(pmsr_lock);
+
+static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base)
+{
+	unsigned long flags;
+	u32 pmsr, val;
+	int ret = 0;
+
+	spin_lock_irqsave(&pmsr_lock, flags);
+
+	if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
+		ret = -EINVAL;
+		goto unlock_exit;
+	}
+
+	pmsr = readl(pcie_base + PMSR);
+
+	/*
+	 * Test if the PCIe controller received PM_ENTER_L1 DLLP and
+	 * the PCIe controller is not in L1 link state. If true, apply
+	 * fix, which will put the controller into L1 link state, from
+	 * which it can return to L0s/L0 on its own.
+	 */
+	if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
+		writel(L1IATN, pcie_base + PMCTLR);
+		ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
+						val & L1FAEG, 10, 1000);
+		WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
+		writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
+	}
+
+unlock_exit:
+	spin_unlock_irqrestore(&pmsr_lock, flags);
+	return ret;
+}
+
 static struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi)
 {
 	return container_of(msi, struct rcar_pcie_host, msi);
@@ -85,6 +121,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 {
 	struct rcar_pcie *pcie = &host->pcie;
 	unsigned int dev, func, reg, index;
+	int ret;
+
+	/* Wake the bus up in case it is in L1 state. */
+	ret = rcar_pcie_wakeup(pcie->dev, pcie->base);
+	if (ret) {
+		PCI_SET_ERROR_RESPONSE(data);
+		return PCIBIOS_SET_FAILED;
+	}
 
 	dev = PCI_SLOT(devfn);
 	func = PCI_FUNC(devfn);
@@ -1050,40 +1094,10 @@ static struct platform_driver rcar_pcie_driver = {
 };
 
 #ifdef CONFIG_ARM
-static DEFINE_SPINLOCK(pmsr_lock);
 static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
 		unsigned int fsr, struct pt_regs *regs)
 {
-	unsigned long flags;
-	u32 pmsr, val;
-	int ret = 0;
-
-	spin_lock_irqsave(&pmsr_lock, flags);
-
-	if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
-		ret = 1;
-		goto unlock_exit;
-	}
-
-	pmsr = readl(pcie_base + PMSR);
-
-	/*
-	 * Test if the PCIe controller received PM_ENTER_L1 DLLP and
-	 * the PCIe controller is not in L1 link state. If true, apply
-	 * fix, which will put the controller into L1 link state, from
-	 * which it can return to L0s/L0 on its own.
-	 */
-	if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
-		writel(L1IATN, pcie_base + PMCTLR);
-		ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
-						val & L1FAEG, 10, 1000);
-		WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
-		writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
-	}
-
-unlock_exit:
-	spin_unlock_irqrestore(&pmsr_lock, flags);
-	return ret;
+	return !!rcar_pcie_wakeup(pcie_dev, pcie_base);
 }
 
 static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
Signature · 1 line
2.34.1
#1 lore raw

PATCH v3 2/2 PCI: rcar: Return all Fs from read which triggered an exception

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

---
V2: Rebase on 1/2
V3: - Add .text.fixup on all three ldr/str/isb instructions and call
fixup_exception() in the abort handler to trigger the fixup.
- Propagate return value from read/write accessors, in case the access fails, return PCIBIOS_SET_FAILED, else PCIBIOS_SUCCESSFUL.

---
 drivers/pci/controller/pcie-rcar-host.c | 53 +++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 4 deletions(-)
drivers/pci/controller/pcie-rcar-host.c +49 -4
index 7d38a9c50093..b2e521ee95eb 100644
@@ -114,6 +114,51 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
 	return val >> shift;
 }
 
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)				\
+		"1:	" instr " %1, [%2]\n"				\
+		"2:	isb\n"						\
+		"3:	.pushsection .text.fixup,\"ax\"\n"		\
+		"	.align	2\n"					\
+		"4:	mov	%0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+		"	b	3b\n"					\
+		"	.popsection\n"					\
+		"	.pushsection __ex_table,\"a\"\n"		\
+		"	.align	3\n"					\
+		"	.long	1b, 4b\n"				\
+		"	.long	1b, 4b\n"				\
+		"	.popsection\n"
+#endif
+
+int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("str")
+	: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
+#else
+	rcar_pci_write_reg(pcie, val, reg);
+#endif
+	return error;
+}
+
+int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("ldr")
+	: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
+
+	if (error != PCIBIOS_SUCCESSFUL)
+		*val = 0xffffffff;
+#else
+	*val = rcar_pci_read_reg(pcie, reg);
+#endif
+	return error;
+}
+
 /* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
 static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		unsigned char access_type, struct pci_bus *bus,
@@ -185,14 +230,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	if (access_type == RCAR_PCI_ACCESS_READ)
-		*data = rcar_pci_read_reg(pcie, PCIECDR);
+		ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
 	else
-		rcar_pci_write_reg(pcie, *data, PCIECDR);
+		ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);
 
 	/* Disable the configuration access */
 	rcar_pci_write_reg(pcie, 0, PCIECCTLR);
 
-	return PCIBIOS_SUCCESSFUL;
+	return ret;
 }
 
 static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
@@ -1097,7 +1142,7 @@ static struct platform_driver rcar_pcie_driver = {
 static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
 		unsigned int fsr, struct pt_regs *regs)
 {
-	return !!rcar_pcie_wakeup(pcie_dev, pcie_base);
+	return !fixup_exception(regs);
 }
 
 static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
Signature · 1 line
2.34.1
Reviewed-by on v3 2/2
#2 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 26 lines · On Sat, Jan 22, 2022 at 11:15 PM <[email protected]> wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

Quoted 9 lines
Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

provided once concern gets resolved:

Quoted 5 lines
drivers/pci/controller/pcie-rcar-host.c +5 -0
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)                            \
+               "1:     " instr " %1, [%2]\n"                           \
+               "2:     isb\n"                                          \
+               "3:     .pushsection .text.fixup,\"ax\"\n"              \

drivers/pci/controller/pcie-rcar-host.c static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) · line 131

Quoted 10 lines
drivers/pci/controller/pcie-rcar-host.c +10 -0
+               "       .align  2\n"                                    \
+               "4:     mov     %0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+               "       b       3b\n"                                   \
+               "       .popsection\n"                                  \
+               "       .pushsection __ex_table,\"a\"\n"                \
+               "       .align  3\n"                                    \
+               "       .long   1b, 4b\n"                               \
+               "       .long   1b, 4b\n"                               \
+               "       .popsection\n"
+#endif

You list the fixup for the ldr/str instruction here twice, (.long 1b,4b), but no fixup for the isb instruction (.long 2b, 4b). Your description says that the fault happens on the isb, not the ldr, so I don't understand what is going on here.

Arnd

#3 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

On 1/23/22 15:12, Arnd Bergmann wrote:

[...]

Quoted 12 lines
Quoted 12 lines
drivers/pci/controller/pcie-rcar-host.c +12 -0
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)                            \
+               "1:     " instr " %1, [%2]\n"                           \
+               "2:     isb\n"                                          \
+               "3:     .pushsection .text.fixup,\"ax\"\n"              \
+               "       .align  2\n"                                    \
+               "4:     mov     %0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+               "       b       3b\n"                                   \
+               "       .popsection\n"                                  \
+               "       .pushsection __ex_table,\"a\"\n"                \
+               "       .align  3\n"                                    \
+               "       .long   1b, 4b\n"                               \
Quoted 8 lines

drivers/pci/controller/pcie-rcar-host.c static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) · line 131

Quoted 3 lines
drivers/pci/controller/pcie-rcar-host.c +3 -0
+               "       .long   1b, 4b\n"                               \
+               "       .popsection\n"
+#endif

You list the fixup for the ldr/str instruction here twice, (.long 1b,4b), but no fixup for the isb instruction (.long 2b, 4b). Your description says that the fault happens on the isb, not the ldr, so I don't understand what is going on here.

Copy-paste error when deduplicating the patch content, should be

.long 1b, 4b
.long 2b, 4b

to cover both ldr/str and isb.

I can imagine on CA7, the abort would trigger on the ldr/str already.

#4 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

In reply to [email protected]
Quoted 6 lines · On Saturday 22 January 2022 23:15:54 [email protected] wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

Hello!

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

It is really important to know this fact.

I'm in impression that this patch still is not enough as similar issues are also in other PCIe controllers which I know...

Quoted 123 lines

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

---
V2: Rebase on 1/2
V3: - Add .text.fixup on all three ldr/str/isb instructions and call
fixup_exception() in the abort handler to trigger the fixup.
- Propagate return value from read/write accessors, in case the access fails, return PCIBIOS_SET_FAILED, else PCIBIOS_SUCCESSFUL.

---
 drivers/pci/controller/pcie-rcar-host.c | 53 +++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 4 deletions(-)
drivers/pci/controller/pcie-rcar-host.c +49 -4
index 7d38a9c50093..b2e521ee95eb 100644
@@ -114,6 +114,51 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
 	return val >> shift;
 }
 
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)				\
+		"1:	" instr " %1, [%2]\n"				\
+		"2:	isb\n"						\
+		"3:	.pushsection .text.fixup,\"ax\"\n"		\
+		"	.align	2\n"					\
+		"4:	mov	%0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+		"	b	3b\n"					\
+		"	.popsection\n"					\
+		"	.pushsection __ex_table,\"a\"\n"		\
+		"	.align	3\n"					\
+		"	.long	1b, 4b\n"				\
+		"	.long	1b, 4b\n"				\
+		"	.popsection\n"
+#endif
+
+int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("str")
+	: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
+#else
+	rcar_pci_write_reg(pcie, val, reg);
+#endif
+	return error;
+}
+
+int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("ldr")
+	: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
+
+	if (error != PCIBIOS_SUCCESSFUL)
+		*val = 0xffffffff;
+#else
+	*val = rcar_pci_read_reg(pcie, reg);
+#endif
+	return error;
+}
+
 /* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
 static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		unsigned char access_type, struct pci_bus *bus,
@@ -185,14 +230,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	if (access_type == RCAR_PCI_ACCESS_READ)
-		*data = rcar_pci_read_reg(pcie, PCIECDR);
+		ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
 	else
-		rcar_pci_write_reg(pcie, *data, PCIECDR);
+		ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);
 
 	/* Disable the configuration access */
 	rcar_pci_write_reg(pcie, 0, PCIECCTLR);
 
-	return PCIBIOS_SUCCESSFUL;
+	return ret;
 }
 
 static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
@@ -1097,7 +1142,7 @@ static struct platform_driver rcar_pcie_driver = {
 static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
 		unsigned int fsr, struct pt_regs *regs)
 {
-	return !!rcar_pcie_wakeup(pcie_dev, pcie_base);
+	return !fixup_exception(regs);
 }
 
 static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
Signature · 1 line
2.34.1
#5 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 13 lines · Pali Rohár
Quoted 6 lines · On Saturday 22 January 2022 23:15:54 [email protected] wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

Hello!

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

Quoted 7 lines

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

Do you have some specific test which fails ?

This patch addresses the case where the link transition to L1 state has to be completed manually. If the CPU accesses the config space before that happened, you get an imprecise data abort.

Quoted 4 lines

It is really important to know this fact.

I'm in impression that this patch still is not enough as similar issues are also in other PCIe controllers which I know...

Do you have a suggestion for a patch which would be enough on this hardware ?

#6 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 18 lines · On Sunday 23 January 2022 17:31:28 Marek Vasut wrote:
Quoted 17 lines · Pali Rohár
Quoted 6 lines · On Saturday 22 January 2022 23:15:54 [email protected] wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

Hello!

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Quoted 3 lines

This patch addresses the case where the link transition to L1 state has to be completed manually. If the CPU accesses the config space before that happened, you get an imprecise data abort.

Yes, I see. But it does not have to complete and the question how is handled this case... And that is why is needed to know what happens in such cases.

And IIRC you cannot go from L1 state directly to L0, but only via Recovery state. And from Recovery you may end up in Detect state. (e.g. after hot unplug or if some buggy card with kernel quirk is used)

Quoted 7 lines
Quoted 4 lines

It is really important to know this fact.

I'm in impression that this patch still is not enough as similar issues are also in other PCIe controllers which I know...

Do you have a suggestion for a patch which would be enough on this hardware ?

I do not have enough information.

#7 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Quoted 14 lines
Quoted 3 lines

This patch addresses the case where the link transition to L1 state has to be completed manually. If the CPU accesses the config space before that happened, you get an imprecise data abort.

Yes, I see. But it does not have to complete and the question how is handled this case... And that is why is needed to know what happens in such cases.

And IIRC you cannot go from L1 state directly to L0, but only via Recovery state. And from Recovery you may end up in Detect state. (e.g. after hot unplug or if some buggy card with kernel quirk is used)

Quoted 2 lines
Quoted 2 lines

It is really important to know this fact.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

I'm in impression that this patch still is not enough as similar issues are also in other PCIe controllers which I know...

Do you have a suggestion for a patch which would be enough on this hardware ?

I do not have enough information.

I see

#8 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

#9 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 56 lines · Pali Rohár
Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Quoted 4 lines

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

#10 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 57 lines · On Saturday 29 January 2022 05:39:40 Marek Vasut wrote:
Quoted 56 lines · Pali Rohár
Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Quoted 7 lines
Quoted 4 lines

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

#11 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 63 lines · On Monday 31 January 2022 13:53:41 Pali Rohár wrote:
Quoted 62 lines · On Saturday 29 January 2022 05:39:40 Marek Vasut wrote:
Quoted 60 lines · Pali Rohár
Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

Quoted 5 lines
Quoted 2 lines

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

PING? Also what lspci see for the root port and card itself during hot reset?

#12 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 64 lines · Pali Rohár
Quoted 63 lines · On Monday 31 January 2022 13:53:41 Pali Rohár wrote:
Quoted 62 lines · On Saturday 29 January 2022 05:39:40 Marek Vasut wrote:
Quoted 60 lines · Pali Rohár
Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

PING? Also what lspci see for the root port and card itself during hot reset?

If I recall, lspci showed the root port and card.

#13 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 68 lines · On Thursday 17 February 2022 13:59:38 Marek Vasut wrote:
Quoted 67 lines · Pali Rohár
Quoted 66 lines · On Monday 31 January 2022 13:53:41 Pali Rohár wrote:
Quoted 64 lines · On Saturday 29 January 2022 05:39:40 Marek Vasut wrote:
Quoted 56 lines · Pali Rohár
Quoted 41 lines · On Monday 24 January 2022 06:46:47 Marek Vasut wrote:

On 1/23/22 17:49, Pali Rohár wrote:

Hi,

[...]

Quoted 35 lines
Quoted 7 lines
Quoted 7 lines

I must admit that this patch from its initial version evolved into giant hack...
https://lore.kernel.org/linux-pci/[email protected]/

During review of the previous patch I have asked some important questions but I have not got any answer to them. So I'm reminding it:
https://lore.kernel.org/linux-pci/20210805183024.ftdwknkttfwwogks@pali/

Quoted 5 lines
Quoted 3 lines

So could please answer what happens when PCIe controller is in some non-L* state and either MMIO happen or config read happens or config write happens?

What kind of non-L state ?

E.g. Hot Reset, Detect, Polling, Configuration or Recovery.

Quoted 1 line

Do you have some specific test which fails ?

Yes, by putting PCIe controller into one of those states. I have already wrote you in some previous email to trigger hot reset as this is the easiest test and can be done also by userspace (setpci).

Link goes to Recovery state automatically when doing link retraining (e.g. by setting RT bit in PCIe Root Port config space) and from Recovery to Configuration or directly back to L0. So testing this path needs precise timing and repeating it more times to trigger.

So the easiest test is really via PCIe Hot Reset by setting Secondary Bus Reset bit in Bridge Control register of PCIe Root Port. After this is link in Hot Reset and does not go back to L0 until you clear that bit. So in this state you can do all these operations which cause aborts, like calling that kernel function which is reading from config space which belongs to device on the other end of the PCIe link or doing MMIO read / write operation of mapped memory which again belongs to other end of PCIe link.

Quoted 6 lines
Quoted 3 lines

Or instead of Hot Reset, you can set link disable bit in config space of PCIe Root Port. Then link also would not be in L0 state (until you clear that bit), so again you have lot of time to do same tests.

Can you give me the exact setpci invocation ? If so, then I can test this for you on the hardware.

Call "setpci -s $bdf_root_port BRIDGE_CONTROL" with address of the PCIe Root Port device (parent of selected device). This will print value of bridge control register. Logical OR it with value 0x20 (Secondary Bus Reset Bit) and call "setpci -s $bdf_root_port BRIDGE_CONTROL=$new_value". After this call is link in the Hot Reset state and you can do any test. To bring link back, call setpci again with cleared 0x20 bit mask.

Quoted 4 lines

Similar test you can done also with setting Link Disable bit (bit 4) in PCIe Link Control register. Offset to this register is not static and you can figure it out from lspci -s $bdf_root_port -vv output. Retrain Link is bit 5 in the same register.

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

PING? Also what lspci see for the root port and card itself during hot reset?

If I recall, lspci showed the root port and card.

This is suspicious. Card should not respond to config read requests when is in hot reset state. Could you send output of lspci -vvxx of the root port and also card during this test? Maybe it is possible that root port has broken BRIDGE_CONTROL register and did not put card into Hot Reset state?

#14 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

On 2/17/22 14:04, Pali Rohár wrote:

[...]

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

PING? Also what lspci see for the root port and card itself during hot reset?

Quoted 7 lines
Quoted 1 line

If I recall, lspci showed the root port and card.

This is suspicious. Card should not respond to config read requests when is in hot reset state. Could you send output of lspci -vvxx of the root port and also card during this test? Maybe it is possible that root port has broken BRIDGE_CONTROL register and did not put card into Hot Reset state?

Yes, I could set the hardware up again and run more tests, it will take some time, but I can still do that.

But before I spend any more time running tests for you here, I have to admit, it seems to me running all those tests is completely off-topic in context of these two bugfixes here.

So maybe it would make sense to stop the discussion here and move it to separate thread ?

I have to admit, I also don't quite understand what it is that you're trying to find out with all those tests.

#15 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

Quoted 20 lines · On Friday 18 February 2022 02:53:39 Marek Vasut wrote:

On 2/17/22 14:04, Pali Rohár wrote:

[...]

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Flipping either bit makes no difference, suspend/resume behaves the same and the link always recovers.

Ok, perfect! And what happens without suspend/resume (just in normal conditions)? E.g. during active usage of some PCIe card (wifi, sata, etc..).

PING? Also what lspci see for the root port and card itself during hot reset?

Quoted 7 lines
Quoted 1 line

If I recall, lspci showed the root port and card.

This is suspicious. Card should not respond to config read requests when is in hot reset state. Could you send output of lspci -vvxx of the root port and also card during this test? Maybe it is possible that root port has broken BRIDGE_CONTROL register and did not put card into Hot Reset state?

Quoted 6 lines

Yes, I could set the hardware up again and run more tests, it will take some time, but I can still do that.

But before I spend any more time running tests for you here, I have to admit, it seems to me running all those tests is completely off-topic in context of these two bugfixes here.

I do not think this is off-topic. Issue here is caused when controller is not in L0 state and this test is something which deterministically put controller into non-L0 state (Hot Reset). The best verification of all race conditions and similar timing problems is to to setup scenario in which timing windows can be under full control. Which this can can do.

I saw more issues related to PCIe slave errors and I'm feeling that this patch is just hacking one or two consequences and not fixing the source of the problem globally.

In most cases slave errors are (incorrectly) reported to CPU when PCIe controller receive UR/CA response from the bus or if controller itself generate UR/CA response for request from CPU.

Quoted 5 lines

So maybe it would make sense to stop the discussion here and move it to separate thread ?

I have to admit, I also don't quite understand what it is that you're trying to find out with all those tests.

Moreover if this test shows that PCI Bridge registers do not work properly then it is something which must be fixed too.

There were more discussions about catching and recovering from ARM CPU aborts and all patches for catching asynchronous exceptions were rejected because they cannot work by their _imprecise_ nature.

And also there were discussions (not sure if on ML or IRC) if the PCI core / drivers are the correct place for ARMv7 exceptions / data aborts.

#16 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

In reply to [email protected]
Quoted 81 lines · On Sat, Jan 22, 2022 at 11:15:54PM +0100, [email protected] wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

---
V2: Rebase on 1/2
V3: - Add .text.fixup on all three ldr/str/isb instructions and call
fixup_exception() in the abort handler to trigger the fixup.
- Propagate return value from read/write accessors, in case the access fails, return PCIBIOS_SET_FAILED, else PCIBIOS_SUCCESSFUL.

---
 drivers/pci/controller/pcie-rcar-host.c | 53 +++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 4 deletions(-)
drivers/pci/controller/pcie-rcar-host.c +29 -0
index 7d38a9c50093..b2e521ee95eb 100644
@@ -114,6 +114,51 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
 	return val >> shift;
 }
 
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)				\
+		"1:	" instr " %1, [%2]\n"				\
+		"2:	isb\n"						\
+		"3:	.pushsection .text.fixup,\"ax\"\n"		\
+		"	.align	2\n"					\
+		"4:	mov	%0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+		"	b	3b\n"					\
+		"	.popsection\n"					\
+		"	.pushsection __ex_table,\"a\"\n"		\
+		"	.align	3\n"					\
+		"	.long	1b, 4b\n"				\
+		"	.long	1b, 4b\n"				\
+		"	.popsection\n"
+#endif
+
+int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("str")
+	: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
+#else
+	rcar_pci_write_reg(pcie, val, reg);
+#endif
+	return error;
+}
+

drivers/pci/controller/pcie-rcar-host.c static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) · line 155

Quoted 10 lines
drivers/pci/controller/pcie-rcar-host.c +10 -0
+int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("ldr")
+	: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
+
+	if (error != PCIBIOS_SUCCESSFUL)
+		*val = 0xffffffff;

PCI_SET_ERROR_RESPONSE()?

Maybe also use PCI_ERROR_RESPONSE in the subject and commit log instead of "all Fs" to make it more greppable.

Quoted 39 lines
drivers/pci/controller/pcie-rcar-host.c +10 -4
+#else
+	*val = rcar_pci_read_reg(pcie, reg);
+#endif
+	return error;
+}
+
 /* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
 static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		unsigned char access_type, struct pci_bus *bus,
@@ -185,14 +230,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	if (access_type == RCAR_PCI_ACCESS_READ)
-		*data = rcar_pci_read_reg(pcie, PCIECDR);
+		ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
 	else
-		rcar_pci_write_reg(pcie, *data, PCIECDR);
+		ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);
 
 	/* Disable the configuration access */
 	rcar_pci_write_reg(pcie, 0, PCIECCTLR);
 
-	return PCIBIOS_SUCCESSFUL;
+	return ret;
 }
 
 static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
@@ -1097,7 +1142,7 @@ static struct platform_driver rcar_pcie_driver = {
 static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
 		unsigned int fsr, struct pt_regs *regs)
 {
-	return !!rcar_pcie_wakeup(pcie_dev, pcie_base);
+	return !fixup_exception(regs);
 }
 
 static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
Signature · 1 line
2.34.1
#17 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

In reply to [email protected]
I love your patch! Yet something to improve:

Hi,

I love your patch! Yet something to improve:

[auto build test ERROR on helgaas-pci/next]
[also build test ERROR on v5.17-rc1 next-20220127]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/marek-vasut-gmail-com/PCI-rcar-Finish-transition-to-L1-state-in-rcar_pcie_config_access/20220123-061742
base:   https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next
config: arm-buildonly-randconfig-r002-20220127 (https://download.01.org/0day-ci/archive/20220128/202201281037.40i8elct-lkp(a)intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project f400a6012c668dfaa73462caf067ceb074e66c47)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm cross compiling tool for clang build
# apt-get install binutils-arm-linux-gnueabi
# https://github.com/0day-ci/linux/commit/1eeb0cd756244c956c8b0fa809f2d4e5bee530b4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review marek-vasut-gmail-com/PCI-rcar-Finish-transition-to-L1-state-in-rcar_pcie_config_access/20220123-061742 git checkout 1eeb0cd756244c956c8b0fa809f2d4e5bee530b4
# save the config file to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate

All errors (new ones prefixed by >>):

drivers/pci/controller/pcie-rcar-host.c:133:5: warning: no previous prototype for function 'rcar_pci_write_reg_workaround' [-Wmissing-prototypes] int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)

       ^
   drivers/pci/controller/pcie-rcar-host.c:133:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
   ^
   static 
   drivers/pci/controller/pcie-rcar-host.c:146:5: warning: no previous prototype for function 'rcar_pci_read_reg_workaround' [-Wmissing-prototypes]
   int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
       ^
   drivers/pci/controller/pcie-rcar-host.c:146:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
   ^
   static 
>> drivers/pci/controller/pcie-rcar-host.c:138:3: error: instruction requires: data-barriers
                   __rcar_pci_rw_reg_workaround("str")
                   ^
   drivers/pci/controller/pcie-rcar-host.c:120:4: note: expanded from macro '__rcar_pci_rw_reg_workaround'
                   "2:     isb\n"                                          \
                    ^
   <inline asm>:2:4: note: instantiated into assembly here
   2:      isb
           ^
   drivers/pci/controller/pcie-rcar-host.c:151:3: error: instruction requires: data-barriers
                   __rcar_pci_rw_reg_workaround("ldr")
                   ^
   drivers/pci/controller/pcie-rcar-host.c:120:4: note: expanded from macro '__rcar_pci_rw_reg_workaround'
                   "2:     isb\n"                                          \
                    ^
   <inline asm>:2:4: note: instantiated into assembly here
   2:      isb
           ^
>> drivers/pci/controller/pcie-rcar-host.c:138:3: error: instruction requires: data-barriers
                   __rcar_pci_rw_reg_workaround("str")
                   ^
   drivers/pci/controller/pcie-rcar-host.c:120:4: note: expanded from macro '__rcar_pci_rw_reg_workaround'
                   "2:     isb\n"                                          \
                    ^
   <inline asm>:2:4: note: instantiated into assembly here
   2:      isb
           ^
   drivers/pci/controller/pcie-rcar-host.c:151:3: error: instruction requires: data-barriers
                   __rcar_pci_rw_reg_workaround("ldr")
                   ^
   drivers/pci/controller/pcie-rcar-host.c:120:4: note: expanded from macro '__rcar_pci_rw_reg_workaround'
                   "2:     isb\n"                                          \
                    ^
   <inline asm>:2:4: note: instantiated into assembly here
   2:      isb
           ^

2 warnings and 4 errors generated.

vim +138 drivers/pci/controller/pcie-rcar-host.c

132
133 int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
134 {
135 int error = PCIBIOS_SUCCESSFUL;
136 #ifdef CONFIG_ARM
137 asm volatile(

 > 138			__rcar_pci_rw_reg_workaround("str")

139 : "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
140 #else
141 rcar_pci_write_reg(pcie, val, reg);
142 #endif
143 return error;
144 }
145

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#18 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

In reply to [email protected]
Quoted 20 lines · On Saturday 22 January 2022 23:15:54 [email protected] wrote:

From: Marek Vasut <[email protected]>

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

Quoted 4 lines

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

I'm looking at this again and I do not think that this is reliable. Asynchronous aborts are by definition asynchronous. Placing isb looks like a hack to decrease probability that asynchronous abort would be triggered at wrong time.

Marek: Cannot you change the code to trigger proper synchronous abort for this operation? If this is ARMv7 system, what about trying to change memory mapping where is the accessing address to strongly-ordered? Writing to strongly-ordered ARMv7 mapping could not report asynchronous aborts anymore, but I'm not sure.

Marek: Are you sure that also ldr instruction is causing asynchronous abort? This is strange as normally load from memory mapped config space could not finish earlier than data from config space are fetched. Normally these load instructions should cause synchronous abort on data errors.

Bjorn, Lorenzo, Krzysztof: These kind of aborts are common for lot of ARM SoCs and I think that some generic/core PCI code would be useful. I talked with more people and these aborts really affects more drivers, just mainline kernel does not have fixes/workarounds. Common suggestions (as workarounds) in vendors kernels are to disable ASPM or other PCIe feature (which has probability to trigger this error) or stop using any PCIe switches, etc... This is really ridiculous but everything comes to the issue that some device in the PCIe topology sends UR or CA response, or that PCIe controller itself send this response. Lot of PCIe controllers on ARM SoCs converts PCIe UR or CA as fatal aborts to CPU (AXI slave error) and crash is there. Note that at least during PCIe device enumeration is UR or CA fully valid response which should never cause CPU/SoC to crash.

Lorenzo: Do you have an idea if it is possible to completely mute AXI slave errors which comes from PCIe controllers?

Quoted 105 lines
Cc Arnd Bergmann <[email protected]>
Cc Bjorn Helgaas <[email protected]>
Cc Geert Uytterhoeven <[email protected]>
Cc Krzysztof Wilczyński <[email protected]>
Cc Lorenzo Pieralisi <[email protected]>
Cc Wolfram Sang <[email protected]>
Cc Yoshihiro Shimoda <[email protected]>

---
V2: Rebase on 1/2
V3: - Add .text.fixup on all three ldr/str/isb instructions and call
fixup_exception() in the abort handler to trigger the fixup.
- Propagate return value from read/write accessors, in case the access fails, return PCIBIOS_SET_FAILED, else PCIBIOS_SUCCESSFUL.

---
 drivers/pci/controller/pcie-rcar-host.c | 53 +++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 4 deletions(-)
drivers/pci/controller/pcie-rcar-host.c +49 -4
index 7d38a9c50093..b2e521ee95eb 100644
@@ -114,6 +114,51 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
 	return val >> shift;
 }
 
+#ifdef CONFIG_ARM
+#define __rcar_pci_rw_reg_workaround(instr)				\
+		"1:	" instr " %1, [%2]\n"				\
+		"2:	isb\n"						\
+		"3:	.pushsection .text.fixup,\"ax\"\n"		\
+		"	.align	2\n"					\
+		"4:	mov	%0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
+		"	b	3b\n"					\
+		"	.popsection\n"					\
+		"	.pushsection __ex_table,\"a\"\n"		\
+		"	.align	3\n"					\
+		"	.long	1b, 4b\n"				\
+		"	.long	1b, 4b\n"				\
+		"	.popsection\n"
+#endif
+
+int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("str")
+	: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
+#else
+	rcar_pci_write_reg(pcie, val, reg);
+#endif
+	return error;
+}
+
+int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val, unsigned int reg)
+{
+	int error = PCIBIOS_SUCCESSFUL;
+#ifdef CONFIG_ARM
+	asm volatile(
+		__rcar_pci_rw_reg_workaround("ldr")
+	: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
+
+	if (error != PCIBIOS_SUCCESSFUL)
+		*val = 0xffffffff;
+#else
+	*val = rcar_pci_read_reg(pcie, reg);
+#endif
+	return error;
+}
+
 /* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
 static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		unsigned char access_type, struct pci_bus *bus,
@@ -185,14 +230,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
 	if (access_type == RCAR_PCI_ACCESS_READ)
-		*data = rcar_pci_read_reg(pcie, PCIECDR);
+		ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
 	else
-		rcar_pci_write_reg(pcie, *data, PCIECDR);
+		ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);
 
 	/* Disable the configuration access */
 	rcar_pci_write_reg(pcie, 0, PCIECCTLR);
 
-	return PCIBIOS_SUCCESSFUL;
+	return ret;
 }
 
 static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
@@ -1097,7 +1142,7 @@ static struct platform_driver rcar_pcie_driver = {
 static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
 		unsigned int fsr, struct pt_regs *regs)
 {
-	return !!rcar_pcie_wakeup(pcie_dev, pcie_base);
+	return !fixup_exception(regs);
 }
 
 static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
Signature · 1 line
2.34.1
#19 lore raw

Re: [PATCH v3 2/2] PCI: rcar: Return all Fs from read which triggered an exception

On 2/16/22 12:50, Pali Rohár wrote:

Hi,

Quoted 18 lines
Quoted 18 lines

In case the controller is transitioning to L1 in rcar_pcie_config_access(), any read/write access to PCIECDR triggers asynchronous external abort. This is because the transition to L1 link state must be manually finished by the driver. The PCIe IP can transition back from L1 state to L0 on its own.

The current asynchronous external abort hook implementation restarts the instruction which finally triggered the fault, which can be a different instruction than the read/write instruction which started the faulting access. Usually the instruction which finally triggers the fault is one which has some data dependency on the result of the read/write. In case of read, the read value after fixup is undefined, while a read value of faulting read should be all Fs.

It is possible to enforce the fault using 'isb' instruction placed right after the read/write instruction which started the faulting access. Add custom register accessors which perform the read/write followed immediately by 'isb'.

Quoted 6 lines
Quoted 4 lines

This way, the fault always happens on the 'isb' and in case of read, which is located one instruction before the 'isb', it is now possible to fix up the return value of the read in the asynchronous external abort hook and make that read return all Fs.

I'm looking at this again and I do not think that this is reliable.

Are you still running into any problems on your hardware with these patches applied ?

Quoted 3 lines

Asynchronous aborts are by definition asynchronous. Placing isb looks like a hack to decrease probability that asynchronous abort would be triggered at wrong time.

That is exactly what this patch fixes, the ISB enforces the async exception at the right moment so it can be fixed up in the fixup handler (thanks Arnd).

Quoted 5 lines

Marek: Cannot you change the code to trigger proper synchronous abort for this operation? If this is ARMv7 system, what about trying to change memory mapping where is the accessing address to strongly-ordered? Writing to strongly-ordered ARMv7 mapping could not report asynchronous aborts anymore, but I'm not sure.

No, last time I tried tweaking the mapping, that didn't lead to sync aborts.

Quoted 5 lines

Marek: Are you sure that also ldr instruction is causing asynchronous abort? This is strange as normally load from memory mapped config space could not finish earlier than data from config space are fetched. Normally these load instructions should cause synchronous abort on data errors.

I am positive LDR triggers async abort on this hardware, since that's how this problem was triggered by Geert.

[...]