Skip to contentSkip to conversation

Address error and recovery for AER and DPC

PATCH Superseded Oza Pawandeep 4 participants 25 messages Feb 28, 2018 – May 8, 2018

Subsystems (2)

v12 of 17 Superseded Latest is v17 →

lore
Share & tools
lore
#0 lore raw

v12 cover Address error and recovery for AER and DPC

This patch set brings in error handling support for DPC

The current implementation of AER and error message broadcasting to the EP driver is tightly coupled and limited to AER service driver. It is important to factor out broadcasting and other link handling callbacks. So that not only when AER gets triggered, but also when DPC get triggered (for e.g. ERR_FATAL), callbacks are handled appropriately.

DPC should enumerate the devices after recovering the link, which is achieved by implementing error_resume callback.

Changes since v11:
Bjorn's comments addressed.
> rename pcie-err.c to err.c
> removed EXPORT_SYMBOL
> made generic find_serivce function in port driver.
> removed mutex patch as no need to have mutex in pcie_do_recovery > brough in DPC_FATAL in aer.h
> so now all the error codes (AER and DPC) are unified in aer.h
Changes since v10:
Christoph Hellwig's, David Laight's and Randy Dunlap's
comments addressed.
> renamed pci_do_recovery to pcie_do_recovery
> removed inner braces in conditional statements.
> restrctured the code in pci_wait_for_link
> EXPORT_SYMBOL_GPL
Changes since v9:
Sinan's comments addressed.
> bool active = true; unnecessary variable removed.
Changes since v8:
Fixed Kbuild errors.
Changes since v7:
Rebased the code on pci master
> https://kernel.googlesource.com/pub/scm/linux/kernel/git/helgaas/pci
Changes since v6:
Sinan's and Stefan's comments implemented.
> reordered patch 6 and 7
> cleaned up
Changes since v5:
Sinan's and Keith's comments incorporated.
> made separate patch for mutex
> unified error repotting codes into driver/pci/pci.h
> got rid of wait link active/inactive and
made generic function in driver/pci/pci.c
Changes since v4:
Bjorn's comments incorporated.
> Renamed only do_recovery.
> moved the things more locally to drivers/pci/pci.h
Changes since v3:
Bjorn's comments incorporated.
> Made separate patch renaming generic pci_err.c
> Introduce pci_err.h to contain all the error types and recovery > removed all the dependencies on pci.h
Changes since v2:
Based on feedback from Keith:
"
When DPC is triggered due to receipt of an uncorrectable error Message, the Requester ID from the Message is recorded in the DPC Error Source ID register and that Message is discarded and not forwarded Upstream. "
Removed the patch where AER checks if DPC service is active
Changes since v1:
Kbuild errors fixed:
> pci_find_dpc_dev made static
> ras_event.h updated
> pci_find_aer_service call with CONFIG check
> pci_find_dpc_service call with CONFIG check

Oza Pawandeep (6):
PCI/AER: Rename error recovery to generic PCI naming PCI/AER: Factor out error reporting from AER PCI/PORTDRV: Implement generic find service PCI/DPC: Unify and plumb error handling into DPC
PCI: Unify wait for link active into generic PCI PCI/DPC: Enumerate the devices after DPC trigger event

 drivers/pci/hotplug/pciehp_hpc.c   |  20 +--
 drivers/pci/pci.c                  |  29 +++
 drivers/pci/pci.h                  |   5 +
 drivers/pci/pcie/Makefile          |   2 +-
 drivers/pci/pcie/aer/aerdrv.h      |  30 ----
 drivers/pci/pcie/aer/aerdrv_core.c | 317 +-------------------------------
 drivers/pci/pcie/err.c             | 359 +++++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/pcie-dpc.c        |  90 ++++++++--
 drivers/pci/pcie/portdrv.h         |   2 +
 drivers/pci/pcie/portdrv_core.c    |  43 +++++
 include/linux/aer.h                |   2 +
 11 files changed, 521 insertions(+), 378 deletions(-)
 create mode 100644 drivers/pci/pcie/err.c
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#1 lore raw

PATCH v12 1/6 PCI/AER: Rename error recovery to generic PCI naming

This patch renames error recovery to generic name with pcie prefix

drivers/pci/pci.h +3 -0
index fcd8191..abc514e 100644
@@ -342,6 +342,9 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
 
 void pci_enable_acs(struct pci_dev *dev);
 
+/* PCI error reporting and recovery */
+void pcie_do_recovery(struct pci_dev *dev, int severity);
+
 #ifdef CONFIG_PCIEASPM
 void pcie_aspm_init_link_state(struct pci_dev *pdev);
 void pcie_aspm_exit_link_state(struct pci_dev *pdev);
drivers/pci/pcie/aer/aerdrv_core.c +4 -4
index a4bfea5..aeb83a0 100644
@@ -478,7 +478,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
 }
 
 /**
- * do_recovery - handle nonfatal/fatal error recovery process
+ * pcie_do_recovery - handle nonfatal/fatal error recovery process
  * @dev: pointer to a pci_dev data structure of agent detecting an error
  * @severity: error severity type
  *
@@ -486,7 +486,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
  * error detected message to all downstream drivers within a hierarchy in
  * question and return the returned code.
  */
-static void do_recovery(struct pci_dev *dev, int severity)
+static void pcie_do_recovery(struct pci_dev *dev, int severity)
 {
 	pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
 	enum pci_channel_state state;
@@ -566,7 +566,7 @@ static void handle_error_source(struct pcie_device *aerdev,
 			pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
 					info->status);
 	} else
-		do_recovery(dev, info->severity);
+		pcie_do_recovery(dev, info->severity);
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -631,7 +631,7 @@ static void aer_recover_work_func(struct work_struct *work)
 		}
 		cper_print_aer(pdev, entry.severity, entry.regs);
 		if (entry.severity != AER_CORRECTABLE)
-			do_recovery(pdev, entry.severity);
+			pcie_do_recovery(pdev, entry.severity);
 		pci_dev_put(pdev);
 	}
 }
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#2 lore raw

PATCH v12 2/6 PCI/AER: Factor out error reporting from AER

In reply to Oza Pawandeep

This patch factors out error reporting callbacks, which are currently tightly coupled with AER.

DPC should be able to register callbacks and attempt recovery when DPC trigger event occurs.

drivers/pci/pcie/Makefile +1 -1
index 223e4c3..f0b1a78 100644
@@ -6,7 +6,7 @@
 # Build PCI Express ASPM if needed
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
-pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o err.o
 pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
drivers/pci/pcie/aer/aerdrv.h +0 -30
index 5449e5c..bc9db53 100644
@@ -76,36 +76,6 @@ struct aer_rpc {
 					 */
 };
 
-struct aer_broadcast_data {
-	enum pci_channel_state state;
-	enum pci_ers_result result;
-};
-
-static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
-		enum pci_ers_result new)
-{
-	if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
-		return PCI_ERS_RESULT_NO_AER_DRIVER;
-
-	if (new == PCI_ERS_RESULT_NONE)
-		return orig;
-
-	switch (orig) {
-	case PCI_ERS_RESULT_CAN_RECOVER:
-	case PCI_ERS_RESULT_RECOVERED:
-		orig = new;
-		break;
-	case PCI_ERS_RESULT_DISCONNECT:
-		if (new == PCI_ERS_RESULT_NEED_RESET)
-			orig = PCI_ERS_RESULT_NEED_RESET;
-		break;
-	default:
-		break;
-	}
-
-	return orig;
-}
-
 extern struct bus_type pcie_port_bus_type;
 void aer_isr(struct work_struct *work);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
drivers/pci/pcie/aer/aerdrv_core.c +2 -287

313 lines not on this page · raw message

drivers/pci/pcie/err.c new file +332 -0

332 lines not on this page · raw message

drivers/pci/pcie/portdrv.h +1 -0
index a854bc5..9a8d0dd 100644
@@ -79,4 +79,5 @@ static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
 static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
 #endif /* !CONFIG_ACPI */
 
+struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev);
 #endif /* _PORTDRV_H_ */
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#3 lore raw

PATCH v12 3/6 PCI/PORTDRV: Implement generic find service

In reply to Oza Pawandeep

This patch implements generic pcie_port_find_service() routine.

drivers/pci/pcie/aer/aerdrv_core.c +0 -26
index 4acec3b..aeb8236 100644
@@ -231,32 +231,6 @@ static bool find_source_device(struct pci_dev *parent,
 	return true;
 }
 
-static int find_aer_service_iter(struct device *device, void *data)
-{
-	struct pcie_port_service_driver *service_driver, **drv;
-
-	drv = (struct pcie_port_service_driver **) data;
-
-	if (device->bus == &pcie_port_bus_type && device->driver) {
-		service_driver = to_service_driver(device->driver);
-		if (service_driver->service == PCIE_PORT_SERVICE_AER) {
-			*drv = service_driver;
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev)
-{
-	struct pcie_port_service_driver *drv = NULL;
-
-	device_for_each_child(&dev->dev, &drv, find_aer_service_iter);
-
-	return drv;
-}
-
 /**
  * handle_error_source - handle logging error into an event log
  * @aerdev: pointer to pcie_device data structure of the root port
drivers/pci/pcie/err.c +1 -1
index c48eb0a..98aeec4 100644
@@ -194,7 +194,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
 	}
 
 	/* Use the aer driver of the component firstly */
-	driver = find_aer_service(udev);
+	driver = pcie_port_find_service(udev, PCIE_PORT_SERVICE_AER);
 
 	if (driver && driver->reset_link) {
 		status = driver->reset_link(udev);
drivers/pci/pcie/portdrv.h +2 -1
index 9a8d0dd..419bdf3 100644
@@ -79,5 +79,6 @@ static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
 static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
 #endif /* !CONFIG_ACPI */
 
-struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev);
+struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
+							u32 service);
 #endif /* _PORTDRV_H_ */
drivers/pci/pcie/portdrv_core.c +44 -0

56 lines not on this page · raw message

Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#4 lore raw

Re: [PATCH v12 3/6] PCI/PORTDRV: Implement generic find service

drivers/pci/pcie/portdrv_core.c static int remove_iter(struct device *dev, void *data) · line 451

Quoted 4 lines · Oza Pawandeep
drivers/pci/pcie/portdrv_core.c +4 -0
+struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
+							u32 service)
+{
+	struct pcie_port_service_driver *drv = NULL;

Remove initialization

drivers/pci/pcie/portdrv_core.c static int remove_iter(struct device *dev, void *data) · line 454

Quoted 3 lines
drivers/pci/pcie/portdrv_core.c +3 -0
+	struct portdrv_service_data pdrvs;
+
+	pdrvs.service = service;

initialize pdrvs.drv = NULL here.

Quoted 5 lines
drivers/pci/pcie/portdrv_core.c +5 -0
+	device_for_each_child(&dev->dev, &pdrvs, find_service_iter);
+
+	drv = pdrvs.drv;
+	return drv;
+}
Signature · 3 lines
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#5 lore raw

Re: [PATCH v12 3/6] PCI/PORTDRV: Implement generic find service

Quoted 9 lines · Sinan Kaya

drivers/pci/pcie/portdrv_core.c static int remove_iter(struct device *dev, void *data) · line 451

Quoted 5 lines · Oza Pawandeep

+struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,

drivers/pci/pcie/portdrv_core.c +3 -0
+							u32 service)
+{
+	struct pcie_port_service_driver *drv = NULL;

Remove initialization

Quoted 6 lines

drivers/pci/pcie/portdrv_core.c static int remove_iter(struct device *dev, void *data) · line 454

Quoted 3 lines
drivers/pci/pcie/portdrv_core.c +3 -0
+	struct portdrv_service_data pdrvs;
+
+	pdrvs.service = service;

initialize pdrvs.drv = NULL here.

good point, will take care of this. [along with other comments if any]

Quoted 5 lines
Quoted 5 lines
drivers/pci/pcie/portdrv_core.c +5 -0
+	device_for_each_child(&dev->dev, &pdrvs, find_service_iter);
+
+	drv = pdrvs.drv;
+	return drv;
+}
#6 lore raw

PATCH v12 4/6 PCI/DPC: Unify and plumb error handling into DPC

In reply to Oza Pawandeep

Current DPC driver does not do recovery, e.g. calling end-point's driver's callbacks, which sanitize the sw.

DPC driver implements link_reset callback, and calls pci_do_recovery().

drivers/pci/pcie/err.c +17 -6
index 98aeec4..9f1c983 100644
@@ -19,6 +19,7 @@
 #include <linux/aer.h>
 #include <linux/pcieport_if.h>
 #include "portdrv.h"
+#include "./../pci.h"
 
 struct aer_broadcast_data {
 	enum pci_channel_state state;
@@ -179,11 +180,12 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev)
 	return PCI_ERS_RESULT_RECOVERED;
 }
 
-static pci_ers_result_t reset_link(struct pci_dev *dev)
+static pci_ers_result_t reset_link(struct pci_dev *dev, int severity)
 {
 	struct pci_dev *udev;
 	pci_ers_result_t status;
 	struct pcie_port_service_driver *driver = NULL;
+	u32 service;
 
 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
 		/* Reset this port for all subordinates */
@@ -193,8 +195,12 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
 		udev = dev->bus->self;
 	}
 
-	/* Use the aer driver of the component firstly */
-	driver = pcie_port_find_service(udev, PCIE_PORT_SERVICE_AER);
+	if (severity == DPC_FATAL)
+		service = PCIE_PORT_SERVICE_DPC;
+	else
+		service = PCIE_PORT_SERVICE_AER;
+
+	driver = pcie_port_find_service(udev, service);
 
 	if (driver && driver->reset_link) {
 		status = driver->reset_link(udev);
@@ -281,7 +287,8 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 	pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
 	enum pci_channel_state state;
 
-	if (severity == AER_FATAL)
+	if ((severity == AER_FATAL) ||
+	    (severity == DPC_FATAL))
 		state = pci_channel_io_frozen;
 	else
 		state = pci_channel_io_normal;
@@ -291,10 +298,13 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 			"error_detected",
 			report_error_detected);
 
-	if (severity == AER_FATAL) {
-		result = reset_link(dev);
+	if ((severity == AER_FATAL) ||
+	    (severity == DPC_FATAL)) {
+		result = reset_link(dev, severity);
 		if (result != PCI_ERS_RESULT_RECOVERED)
 			goto failed;
+		else if (severity == DPC_FATAL)
+			goto resume;
 	}
 
 	if (status == PCI_ERS_RESULT_CAN_RECOVER)
@@ -318,6 +328,7 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 	if (status != PCI_ERS_RESULT_RECOVERED)
 		goto failed;
 
+resume:
 	broadcast_error_message(dev,
 				state,
 				"resume",
drivers/pci/pcie/pcie-dpc.c +58 -6

109 lines not on this page · raw message

include/linux/aer.h +2 -0
index 8f87bbe..9cfd0b8 100644
@@ -15,6 +15,8 @@
 #define AER_FATAL			1
 #define AER_CORRECTABLE			2
 
+#define DPC_FATAL			4
+
 struct pci_dev;
 
 struct aer_header_log_regs {
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#7 lore raw

PATCH v12 5/6 PCI: Unify wait for link active into generic PCI

In reply to Oza Pawandeep

Clients such as HP, DPC are using pcie_wait_link_active(), which waits till the link becomes active or inactive.

Made generic function and moved it to drivers/pci/pci.c

drivers/pci/hotplug/pciehp_hpc.c +3 -17
index 18a42f8..e0c2b8e 100644
@@ -231,25 +231,11 @@ bool pciehp_check_link_active(struct controller *ctrl)
 	return ret;
 }
 
-static void __pcie_wait_link_active(struct controller *ctrl, bool active)
-{
-	int timeout = 1000;
-
-	if (pciehp_check_link_active(ctrl) == active)
-		return;
-	while (timeout > 0) {
-		msleep(10);
-		timeout -= 10;
-		if (pciehp_check_link_active(ctrl) == active)
-			return;
-	}
-	ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
-			active ? "set" : "cleared");
-}
-
 static void pcie_wait_link_active(struct controller *ctrl)
 {
-	__pcie_wait_link_active(ctrl, true);
+	struct pci_dev *pdev = ctrl_dev(ctrl);
+
+	pcie_wait_for_link(pdev, true);
 }
 
 static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
drivers/pci/pci.c +29 -0
index f6a4dd1..bbea997 100644
@@ -4176,6 +4176,35 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
 	return 0;
 }
 
+/**
+ * pcie_wait_for_link - Wait for link till it's active/inactive
+ * @pdev: Bridge device
+ * @active: waiting for active or inactive ?
+ *
+ * Use this to wait till link becomes active or inactive.
+ */
+bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
+{
+	int timeout = 1000;
+	bool ret;
+	u16 lnk_status;
+
+	for (;;) {
+		pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+		ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+		if (ret == active)
+			return true;
+		if (timeout <= 0)
+			break;
+		timeout -= 10;
+	}
+
+	pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
+		 active ? "set" : "cleared");
+
+	return false;
+}
+
 void pci_reset_secondary_bus(struct pci_dev *dev)
 {
 	u16 ctrl;
drivers/pci/pci.h +2 -0
index abc514e..5c44fbc 100644
@@ -345,6 +345,8 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
 /* PCI error reporting and recovery */
 void pcie_do_recovery(struct pci_dev *dev, int severity);
 
+bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
+
 #ifdef CONFIG_PCIEASPM
 void pcie_aspm_init_link_state(struct pci_dev *pdev);
 void pcie_aspm_exit_link_state(struct pci_dev *pdev);
drivers/pci/pcie/pcie-dpc.c +1 -11
index 517c8b4..8e1553b 100644
@@ -95,19 +95,9 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 
 static void dpc_wait_link_inactive(struct dpc_dev *dpc)
 {
-	unsigned long timeout = jiffies + HZ;
 	struct pci_dev *pdev = dpc->dev->port;
-	struct device *dev = &dpc->dev->device;
-	u16 lnk_status;
 
-	pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-	while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
-					!time_after(jiffies, timeout)) {
-		msleep(10);
-		pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-	}
-	if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
-		dev_warn(dev, "Link state not disabled for DPC event\n");
+	pcie_wait_for_link(pdev, false);
 }
 
 /**
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#8 lore raw

PATCH v12 6/6 PCI/DPC: Enumerate the devices after DPC trigger event

In reply to Oza Pawandeep

Implement error_resume callback in DPC so, after DPC trigger event enumerates the devices beneath.

drivers/pci/pcie/err.c +21 -5
index 9f1c983..d439bfd 100644
@@ -237,7 +237,8 @@ static pci_ers_result_t reset_link(struct pci_dev *dev, int severity)
 static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
 	enum pci_channel_state state,
 	char *error_mesg,
-	int (*cb)(struct pci_dev *, void *))
+	int (*cb)(struct pci_dev *, void *),
+	int severity)
 {
 	struct aer_broadcast_data result_data;
 
@@ -249,6 +250,17 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
 		result_data.result = PCI_ERS_RESULT_RECOVERED;
 
 	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+		/* If DPC is triggered, call resume error handler
+		 * because, at this point we can safely assume that
+		 * link recovery has happened, this is only handled if
+		 * callback is resume, as this function can be called
+		 * with multiple callbacks.
+		 */
+		if ((severity == DPC_FATAL) &&
+			(cb == report_resume)) {
+			cb(dev, NULL);
+			return PCI_ERS_RESULT_RECOVERED;
+		}
 		/*
 		 * If the error is reported by a bridge, we think this error
 		 * is related to the downstream link of the bridge, so we
@@ -296,7 +308,8 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 	status = broadcast_error_message(dev,
 			state,
 			"error_detected",
-			report_error_detected);
+			report_error_detected,
+			severity);
 
 	if ((severity == AER_FATAL) ||
 	    (severity == DPC_FATAL)) {
@@ -311,7 +324,8 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 		status = broadcast_error_message(dev,
 				state,
 				"mmio_enabled",
-				report_mmio_enabled);
+				report_mmio_enabled,
+				severity);
 
 	if (status == PCI_ERS_RESULT_NEED_RESET) {
 		/*
@@ -322,7 +336,8 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 		status = broadcast_error_message(dev,
 				state,
 				"slot_reset",
-				report_slot_reset);
+				report_slot_reset,
+				severity);
 	}
 
 	if (status != PCI_ERS_RESULT_RECOVERED)
@@ -332,7 +347,8 @@ void pcie_do_recovery(struct pci_dev *dev, int severity)
 	broadcast_error_message(dev,
 				state,
 				"resume",
-				report_resume);
+				report_resume,
+				severity);
 
 	dev_info(&dev->dev, "Device recovery successful\n");
 	return;
drivers/pci/pcie/pcie-dpc.c +17 -1
index 8e1553b..fb6bbd3 100644
@@ -101,9 +101,24 @@ static void dpc_wait_link_inactive(struct dpc_dev *dpc)
 }
 
 /**
- * dpc_reset_link - reset link DPC routine
+ * dpc_error_resume - enumerate the devices beneath
  * @pdev: pointer to Root Port's pci_dev data structure
  *
+ * Invoked by Port Bus driver during fatal recovery.
+ */
+static void dpc_error_resume(struct pci_dev *pdev)
+{
+	if (pcie_wait_for_link(pdev, true)) {
+		pci_lock_rescan_remove();
+		pci_rescan_bus(pdev->bus);
+		pci_unlock_rescan_remove();
+	}
+}
+
+/**
+ * dpc_reset_link - reset link DPC routine
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
  * Invoked by Port Bus driver when performing link reset at Root Port.
  */
 static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
@@ -338,6 +353,7 @@ static void dpc_remove(struct pcie_device *dev)
 	.service	= PCIE_PORT_SERVICE_DPC,
 	.probe		= dpc_probe,
 	.remove		= dpc_remove,
+	.error_resume	= dpc_error_resume,
 	.reset_link     = dpc_reset_link,
 };
 
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.,
a Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#9 lore raw
In reply to Oza Pawandeep
Quoted 10 lines · Oza Pawandeep

This patch set brings in error handling support for DPC

The current implementation of AER and error message broadcasting to the EP driver is tightly coupled and limited to AER service driver. It is important to factor out broadcasting and other link handling callbacks. So that not only when AER gets triggered, but also when DPC get triggered (for e.g. ERR_FATAL), callbacks are handled appropriately.

DPC should enumerate the devices after recovering the link, which is achieved by implementing error_resume callback.

The main point of this series is to call the driver error handling callbacks (error_detected(), mmio_enabled(), slot_reset(), etc.) when DPC events occur. We've always called them for AER events, but prior to this series, we didn't call them for DPC events.

That's a good thing -- we should treat DPC events as much like AER events as possible.

This series does make it more obvious that there's still a big difference between AER and DPC handling -- for DPC, we remove and re-enumerate all the devices, but we don't for AER.

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

Bjorn

#10 lore raw
Quoted 10 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

Was it the absence of error callback? or are we missing something in our assumptions?

Signature · 3 lines
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#11 lore raw
Quoted 8 lines · Sinan Kaya
Quoted 7 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

Quoted 8 lines
Quoted 3 lines

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed. Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

#12 lore raw
Quoted 25 lines · Keith Busch
Quoted 8 lines · Sinan Kaya
Quoted 7 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

Quoted 8 lines
Quoted 3 lines

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed. Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

Quoted 4 lines

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Now it begs the question, that

after DPC trigger

should we enumerate the devices, ?
or
error handling callbacks, followed by stop devices followed by enumeration ? or
error handling callbacks, followed by enumeration ? (no stop devices)

Regards,
Oza.

#13 lore raw
Quoted 33 lines · On Mon, Mar 12, 2018 at 08:16:38PM +0530, [email protected] wrote:
Quoted 25 lines · Keith Busch
Quoted 8 lines · Sinan Kaya
Quoted 7 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

Quoted 8 lines
Quoted 3 lines

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed. Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

Quoted 4 lines

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Now it begs the question, that

Quoted 7 lines

after DPC trigger

should we enumerate the devices, ?
or
error handling callbacks, followed by stop devices followed by enumeration ? or
error handling callbacks, followed by enumeration ? (no stop devices)

I'm not sure I understand. The link is disabled while DPC is triggered, so if anything, you'd want to un-enumerate everything below the contained port (that's what it does today).

After releasing a slot from DPC, the link is allowed to retrain. If there is a working device on the other side, a link up event occurs. That event is handled by the pciehp driver, and that schedules enumeration no matter what you do to the DPC driver.

#14 lore raw
Quoted 42 lines · Keith Busch
Quoted 33 lines · On Mon, Mar 12, 2018 at 08:16:38PM +0530, [email protected] wrote:
Quoted 25 lines · Keith Busch
Quoted 8 lines · Sinan Kaya
Quoted 7 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

Quoted 8 lines
Quoted 3 lines

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed. Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

Quoted 4 lines

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Now it begs the question, that

Quoted 7 lines

after DPC trigger

should we enumerate the devices, ?
or
error handling callbacks, followed by stop devices followed by enumeration ? or
error handling callbacks, followed by enumeration ? (no stop devices)

Quoted 8 lines

I'm not sure I understand. The link is disabled while DPC is triggered, so if anything, you'd want to un-enumerate everything below the contained port (that's what it does today).

After releasing a slot from DPC, the link is allowed to retrain. If there is a working device on the other side, a link up event occurs. That event is handled by the pciehp driver, and that schedules enumeration no matter what you do to the DPC driver.

yes, that is what i current, but this patch-set makes DPC aware of error handling driver callbacks.

besides, in absence of pciehp there is nobody to do enumeration.

And, I was talking about pci_stop_and_remove_bus_device() in dpc. if DPC calls driver's error callbacks, is it required to stop the devices ?

#15 lore raw
Quoted 6 lines · On Mon, Mar 12, 2018 at 09:04:47PM +0530, [email protected] wrote:
Quoted 5 lines · Keith Busch

I'm not sure I understand. The link is disabled while DPC is triggered, so if anything, you'd want to un-enumerate everything below the contained
port (that's what it does today).

Quoted 8 lines
Quoted 5 lines

After releasing a slot from DPC, the link is allowed to retrain. If there
is a working device on the other side, a link up event occurs. That event is handled by the pciehp driver, and that schedules enumeration no matter what you do to the DPC driver.

yes, that is what i current, but this patch-set makes DPC aware of error handling driver callbacks.

I've been questioning the utility of doing that since the very first version of this patch set.

Quoted 1 line

besides, in absence of pciehp there is nobody to do enumeration.

If you configure your kernel to not have a feature, you don't get to expect the feature works.

You can still manually rescan through sysfs, /sys/bus/pci/rescan.

Quoted 2 lines

And, I was talking about pci_stop_and_remove_bus_device() in dpc. if DPC calls driver's error callbacks, is it required to stop the devices ?

DPC is PCIe's preferred surprise removal mechanism. If you don't want the DPC driver to handle removing devices downstream the containment, how do you want those devices get removed? Just calling driver's error callbacks leaves the kernel's view of the topology in the wrong state.

#16 lore raw

I was just writing a reply to you. You acted first :)

Quoted 10 lines · Keith Busch
Quoted 7 lines
Quoted 5 lines

After releasing a slot from DPC, the link is allowed to retrain. If there
is a working device on the other side, a link up event occurs. That event is handled by the pciehp driver, and that schedules enumeration no matter what you do to the DPC driver.

yes, that is what i current, but this patch-set makes DPC aware of error handling driver callbacks.

I've been questioning the utility of doing that since the very first version of this patch set.

I think we should all agree that shutting down the device drivers with active work is not safe. There could be outstanding work that the endpoint driver needs to take care of.

That was the motivation for this change so that we give endpoint drivers an error callback when something goes wrong.

The rest is implementation detail that we can all figure out.

Signature · 3 lines
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#17 lore raw
Quoted 14 lines · Sinan Kaya

I was just writing a reply to you. You acted first :)

Quoted 10 lines · Keith Busch
Quoted 7 lines
Quoted 5 lines

After releasing a slot from DPC, the link is allowed to retrain. If there
is a working device on the other side, a link up event occurs. That event is handled by the pciehp driver, and that schedules enumeration no matter what you do to the DPC driver.

yes, that is what i current, but this patch-set makes DPC aware of error handling driver callbacks.

I've been questioning the utility of doing that since the very first version of this patch set.

Quoted 8 lines

I think we should all agree that shutting down the device drivers with active work is not safe. There could be outstanding work that the endpoint driver needs to take care of.

That was the motivation for this change so that we give endpoint drivers an error callback when something goes wrong.

The rest is implementation detail that we can all figure out.

I'm not sure if I agree here. All Linux device drivers are supposed to cope with sudden/unexpected loss of communication at any time. This includes cleaning up appropriately when requested to unbind from an inaccessible device with active outstanding work.

#18 lore raw
In reply to Keith Busch

[+cc Alex]

Quoted 13 lines · Keith Busch
Quoted 12 lines · Sinan Kaya
Quoted 10 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

Quoted 8 lines
Quoted 4 lines

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed.

When DPC is triggered, the port takes the link down. When we handle an uncorrectable (nonfatal or fatal) AER event, software takes the link down.

In both cases, devices on the other side are at least reset. Whenever the link goes down, it's conceivable the device could be replaced with a different one before the link comes back up. Is this why you remove and re-enumerate? (See tangent [1] below.)

The point is that from the device's hardware perspective, these scenarios are the same (it sent a ERR_NONFATAL or ERR_FATAL message and it sees the link go down). I think we should make them the same on the software side, too: the driver should see the same callbacks, in the same order, whether we're doing AER or DPC.

If removing and re-enumerating is the right thing for DPC, I think that means it's also the right thing for AER.

Along this line, we had a question a while back about logging AER information after a DPC trigger. Obviously we can't collect any logged information from the downstream devices while link is down, but I noticed the AER status bits are RW1CS, which means they're sticky and are not modified by hot reset or FLR.

So we may be able to read and log the AER information after the DPC driver brings the link back up. We may want to do the same with AER, i.e., reset the downstream devices first, then collect the AER status bits after the link comes back up.

Quoted 4 lines

Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

I agree with this; I think this is heading toward doing remove/re-enumerate on AER errors as well because it has also reset the device.

Quoted 4 lines

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Thanks for this tip. The only thing I've found so far is the mention of Surprise Down triggering DPC in the last paragraph of sec 6.7.5. Are there other references I should look at? I haven't found the recommendation about not setting 'Surprise' in Slot Capabilities yet.

Bjorn

[1] Tangent: I have similar concerns with the device reset paths. We currently save some config state, reset the device, and restore the config state. It's theoretically possible that the device was replaced or came up with different firmware after the reset, so I would really prefer to remove and re-enumerate there, too. But that would make it hard for things up the stack that want to reset the device but not re-setup the whole stack.

I wonder if DPC is going to cause trouble for that scenario. That's not an argument against DPC, but it might be a stronger reason to figure out how to deal with remove/re-enumerate in those stacks.

#19 lore raw
Quoted 25 lines · Bjorn Helgaas

[+cc Alex]

Quoted 13 lines · Keith Busch
Quoted 12 lines · Sinan Kaya
Quoted 10 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

Quoted 8 lines
Quoted 4 lines

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed.

Quoted 8 lines

When DPC is triggered, the port takes the link down. When we handle an uncorrectable (nonfatal or fatal) AER event, software takes the link down.

In both cases, devices on the other side are at least reset. Whenever the link goes down, it's conceivable the device could be replaced with a different one before the link comes back up. Is this why you remove and re-enumerate? (See tangent [1] below.)

Yes. Truthfully, DPC events due to changing topologies was the motivating use case when we initially developed this. We were also going for simplicity (at least initially), and remove + re-enumerate seemed safe without concerning this driver with other capability regsiters, or coordinating with/depending on other drivers. For example, a successful reset may depend on any particular driver calling pci_restore_state from a good saved state.

Quoted 15 lines

The point is that from the device's hardware perspective, these scenarios are the same (it sent a ERR_NONFATAL or ERR_FATAL message and it sees the link go down). I think we should make them the same on the software side, too: the driver should see the same callbacks, in the same order, whether we're doing AER or DPC.

If removing and re-enumerating is the right thing for DPC, I think that means it's also the right thing for AER.

Along this line, we had a question a while back about logging AER information after a DPC trigger. Obviously we can't collect any logged information from the downstream devices while link is down, but I noticed the AER status bits are RW1CS, which means they're sticky and are not modified by hot reset or FLR.

Quoted 4 lines

So we may be able to read and log the AER information after the DPC driver brings the link back up. We may want to do the same with AER, i.e., reset the downstream devices first, then collect the AER status bits after the link comes back up.

I totally agree. We could consult Slot and AER status to guide a smarter approach.

Quoted 14 lines
Quoted 4 lines

Calling a driver's error handler for the wrong device in an unknown state may have undefined results. Enumerating the slot from scratch should be safe, and will assign resources, tune bus settings, and bind to the matching driver.

I agree with this; I think this is heading toward doing remove/re-enumerate on AER errors as well because it has also reset the device.

Quoted 4 lines

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Quoted 4 lines

Thanks for this tip. The only thing I've found so far is the mention of Surprise Down triggering DPC in the last paragraph of sec 6.7.5. Are there other references I should look at? I haven't found the recommendation about not setting 'Surprise' in Slot Capabilities yet.

No problem, it's in the "IMPLEMENTATION NOTE" at the end of 6.2.10.4, "Avoid Disabled Link and Hot-Plug Surprise Use with DPC".

Outside the spec, Microsemi as one of the PCI-SIG contributors and early adopters of the capability published a white paper "Hot and Surprise Plug Recommendations for Enterprise PCIe" providing guidance for OS drivers using DPC. We originally developed to that guidance. The paper unfortunately appears to be pay-walled now. :(

DPC triggers don't necessarily mean a surprise removal occurred, and I understand those conditions are motivating motivating these current proposals. I've no qualms adding smarter handling as long as we don't break removals: there are installations relying on this.

Quoted 11 lines

[1] Tangent: I have similar concerns with the device reset paths. We currently save some config state, reset the device, and restore the config state. It's theoretically possible that the device was replaced or came up with different firmware after the reset, so I would really prefer to remove and re-enumerate there, too. But that would make it hard for things up the stack that want to reset the device but not re-setup the whole stack.

I wonder if DPC is going to cause trouble for that scenario. That's not an argument against DPC, but it might be a stronger reason to figure out how to deal with remove/re-enumerate in those stacks.

Indeed, that's a great point. From a storage perspective, when a removal tears down the block devices, re-adding the same device initializes as a new block handle. Applications with open file descriptors on old handles are going to have a bad time. You can open through device mappers to avoid those problems, but inflight IO may be aborted.

I assume other classes of devices have similar implications to consider, so I agree expanding remove/re-enumerate may need to be considered carefully.

#20 lore raw
Quoted 35 lines · Keith Busch
Quoted 25 lines · Bjorn Helgaas

[+cc Alex]

Quoted 13 lines · Keith Busch
Quoted 12 lines · Sinan Kaya
Quoted 10 lines · Bjorn Helgaas

On Wed, Feb 28, 2018 at 10:34:11PM +0530, Oza Pawandeep wrote:

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

Quoted 8 lines
Quoted 4 lines

I agree this should be possible. Code execution path should be almost identical to fatal error case.

Is there any reason why you went to stop driver path, Keith?

The fact is the link is truly down during a DPC event. When the link is enabled again, you don't know at that point if the device(s) on the other side have changed.

Quoted 8 lines

When DPC is triggered, the port takes the link down. When we handle an uncorrectable (nonfatal or fatal) AER event, software takes the link down.

In both cases, devices on the other side are at least reset. Whenever the link goes down, it's conceivable the device could be replaced with a different one before the link comes back up. Is this why you remove and re-enumerate? (See tangent [1] below.)

Quoted 7 lines

Yes. Truthfully, DPC events due to changing topologies was the motivating use case when we initially developed this. We were also going for simplicity (at least initially), and remove + re-enumerate seemed safe without concerning this driver with other capability regsiters, or coordinating with/depending on other drivers. For example, a successful reset may depend on any particular driver calling pci_restore_state from a good saved state.

The spec is recommending code to use "Hotplug Surprise" to differentiate these two cases we are looking for.

The use case Keith is looking for is for hotplug support.
The case I and Oza are more interested is for error handling on platforms with no hotplug support.

According to the spec, if "Hotplug Surprise" is set in slot capabilities, then hotplug driver handles link up and DPC driver doesn't interfere with its operation. Hotplug driver observes link up interrupt like it is doing today. When link up event is observed, hotplug driver will do the enumeration.

If "Hotplug Surprise" bit is not set, it is the job of the DPC driver to bring up the link. I believe this path should follow the AER driver path as there is a very well defined error reporting and recovery framework in the code.

The link comes back up automatically when DPC driver handles its interrupt very similar to what secondary bus reset does for AER. I don't believe there is a hotplug possibility under this condition since it is not supported to begin with.

Should we plumb the "Hotplug Surprise" condition into the code to satisfy both cases and leave the error handling path according to this code series?

Quoted 10 lines
Quoted 10 lines

The point is that from the device's hardware perspective, these scenarios are the same (it sent a ERR_NONFATAL or ERR_FATAL message and it sees the link go down). I think we should make them the same on the software side, too: the driver should see the same callbacks, in the same order, whether we're doing AER or DPC.

If removing and re-enumerating is the right thing for DPC, I think that means it's also the right thing for AER.

Signature · 3 lines
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#21 lore raw
Quoted 18 lines · Sinan Kaya

The spec is recommending code to use "Hotplug Surprise" to differentiate these two cases we are looking for.

The use case Keith is looking for is for hotplug support.
The case I and Oza are more interested is for error handling on platforms with no hotplug support.

According to the spec, if "Hotplug Surprise" is set in slot capabilities, then hotplug driver handles link up and DPC driver doesn't interfere with its operation. Hotplug driver observes link up interrupt like it is doing today. When link up event is observed, hotplug driver will do the enumeration.

If "Hotplug Surprise" bit is not set, it is the job of the DPC driver to bring up the link. I believe this path should follow the AER driver path as there is a very well defined error reporting and recovery framework in the code.

Quoted 7 lines

The link comes back up automatically when DPC driver handles its interrupt very similar to what secondary bus reset does for AER. I don't believe there is a hotplug possibility under this condition since it is not supported to begin with.

Should we plumb the "Hotplug Surprise" condition into the code to satisfy both cases and leave the error handling path according to this code series?

I'm on board with this, but I think you mean "Hotplug Capable" rather than "Hotplug Surprise". The former may co-exist with DPC and handles the link-ups, leaving DPC responsible for handling the link-down.

The "Hotplug Surprise" capability is recommended to not co-exist with DPC since that capability may suppress the "surprise link down" notification that DPC needs to see in order to trigger.

If the "Hotplug Capable" bit is not set, detecting the link-up after a DPC event would have to fall under a different component's responsibility, so I think I'm finally seeing your point.

#22 lore raw
Quoted 24 lines · Keith Busch
Quoted 23 lines · Sinan Kaya

The spec is recommending code to use "Hotplug Surprise" to differentiate these two cases we are looking for.

The use case Keith is looking for is for hotplug support.
The case I and Oza are more interested is for error handling on platforms with no hotplug support.

According to the spec, if "Hotplug Surprise" is set in slot capabilities, then hotplug driver handles link up and DPC driver doesn't interfere with its operation. Hotplug driver observes link up interrupt like it is doing today. When link up event is observed, hotplug driver will do the enumeration.

If "Hotplug Surprise" bit is not set, it is the job of the DPC driver to bring up the link. I believe this path should follow the AER driver path as there is a very well defined error reporting and recovery framework in the code.

The link comes back up automatically when DPC driver handles its interrupt very similar to what secondary bus reset does for AER. I don't believe there is a hotplug possibility under this condition since it is not supported to begin with.

Quoted 7 lines
Quoted 2 lines

Should we plumb the "Hotplug Surprise" condition into the code to satisfy both cases and leave the error handling path according to this code series?

I'm on board with this, but I think you mean "Hotplug Capable" rather than "Hotplug Surprise". The former may co-exist with DPC and handles the link-ups, leaving DPC responsible for handling the link-down.

Yes, we can go with "Hotplug Capable".

Quoted 8 lines

The "Hotplug Surprise" capability is recommended to not co-exist with DPC since that capability may suppress the "surprise link down" notification that DPC needs to see in order to trigger.

If the "Hotplug Capable" bit is not set, detecting the link-up after a DPC event would have to fall under a different component's responsibility, so I think I'm finally seeing your point.

OK. To simplify our life, we can check if hotplug service is attached to this device or not and follow two different paths.

We can get rid of re-enumeration and stop devices for the non-hotplug case and make it behave exactly like AER.

Bjorn, will that work for you?

Signature · 3 lines
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
#23 lore raw
In reply to Keith Busch
Quoted 13 lines · Keith Busch
Quoted 11 lines · Bjorn Helgaas
Quoted 4 lines · Keith Busch

Per spec, DPC is the recommended way for handling surprise removal events and even recommends DPC capable slots *not* set 'Surprise' in Slot Capabilities so that removals are always handled by DPC. This service driver was developed with that use in mind.

Thanks for this tip. The only thing I've found so far is the mention of Surprise Down triggering DPC in the last paragraph of sec 6.7.5. Are there other references I should look at? I haven't found the recommendation about not setting 'Surprise' in Slot Capabilities yet.

Quoted 8 lines

No problem, it's in the "IMPLEMENTATION NOTE" at the end of 6.2.10.4, "Avoid Disabled Link and Hot-Plug Surprise Use with DPC".

Outside the spec, Microsemi as one of the PCI-SIG contributors and early adopters of the capability published a white paper "Hot and Surprise Plug Recommendations for Enterprise PCIe" providing guidance for OS drivers using DPC. We originally developed to that guidance. The paper unfortunately appears to be pay-walled now. :(

Any chance you have a URL or contact info for this pay-walled white paper? Sounds like it might have useful information in it.

#24 lore raw
In reply to Bjorn Helgaas
Quoted 29 lines · Bjorn Helgaas
Quoted 10 lines · Oza Pawandeep

This patch set brings in error handling support for DPC

The current implementation of AER and error message broadcasting to the EP driver is tightly coupled and limited to AER service driver. It is important to factor out broadcasting and other link handling callbacks. So that not only when AER gets triggered, but also when DPC get triggered (for e.g. ERR_FATAL), callbacks are handled appropriately.

DPC should enumerate the devices after recovering the link, which is achieved by implementing error_resume callback.

The main point of this series is to call the driver error handling callbacks (error_detected(), mmio_enabled(), slot_reset(), etc.) when DPC events occur. We've always called them for AER events, but prior to this series, we didn't call them for DPC events.

That's a good thing -- we should treat DPC events as much like AER events as possible.

This series does make it more obvious that there's still a big difference between AER and DPC handling -- for DPC, we remove and re-enumerate all the devices, but we don't for AER.

That difference has been there since the beginning of DPC, so it has nothing to do with *this* series EXCEPT for the fact that it really complicates the logic you're adding to reset_link() and broadcast_error_message().

Quoted 5 lines

We ought to be able to simplify that somehow because the only real difference between AER and DPC should be that DPC automatically disables the link and AER does it in software.

Bjorn

Ok, so here is slight modification which I will do and will post final looking patches.

1) do not enumerate the devices after link is up (since AER does not do it)
2) remove the call to pci_stop_and_remove_bus_device(dev); because now DPC also makes use of driver's callback. (like AER does)

above 2 changes should make behavior of DPC identical to AER.

Regards,
Oza.