diff mbox series

[PATCHv4,03/12] PCI: DPC: Save and restore control state

Message ID 20180920162717.31066-4-keith.busch@intel.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show
Series pci error handling fixes | expand

Commit Message

Keith Busch Sept. 20, 2018, 4:27 p.m. UTC
This patch provides DPC save and restore capabilities. This is necessary
for the driver to observe DPC events in the event the configuration
space needs to be restored after a reset.

Signed-off-by: Keith Busch <keith.busch@intel.com>
---
 drivers/pci/pci.c      |  2 ++
 drivers/pci/pci.h      |  8 +++++++
 drivers/pci/pcie/dpc.c | 61 +++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 65 insertions(+), 6 deletions(-)

Comments

Sinan Kaya Sept. 20, 2018, 7:46 p.m. UTC | #1
On 9/20/2018 12:27 PM, Keith Busch wrote:
>   	/*
>   	 * DPC disables the Link automatically in hardware, so it has
>   	 * already been reset by the time we get here.
>   	 */
> -	devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
> -	pciedev = to_pcie_device(devdpc);
> -	dpc = get_service_data(pciedev);
> +	dpc = to_dpc_dev(pdev);

I thought that the struct pci_dev sent here is the bridge and we
need to locate the struct device of the DPC object here.

Isn't this change wrong now?
Sinan Kaya Sept. 20, 2018, 7:47 p.m. UTC | #2
On 9/20/2018 3:46 PM, Sinan Kaya wrote:
> On 9/20/2018 12:27 PM, Keith Busch wrote:
>>       /*
>>        * DPC disables the Link automatically in hardware, so it has
>>        * already been reset by the time we get here.
>>        */
>> -    devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
>> -    pciedev = to_pcie_device(devdpc);
>> -    dpc = get_service_data(pciedev);
>> +    dpc = to_dpc_dev(pdev);
> 
> I thought that the struct pci_dev sent here is the bridge and we
> need to locate the struct device of the DPC object here.
> 
> Isn't this change wrong now?

Gosh, I should have looked 30 lines above. Nevermind.
Keith Busch Sept. 20, 2018, 7:54 p.m. UTC | #3
On Thu, Sep 20, 2018 at 03:47:36PM -0400, Sinan Kaya wrote:
> On 9/20/2018 3:46 PM, Sinan Kaya wrote:
> > On 9/20/2018 12:27 PM, Keith Busch wrote:
> > >       /*
> > >        * DPC disables the Link automatically in hardware, so it has
> > >        * already been reset by the time we get here.
> > >        */
> > > -    devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
> > > -    pciedev = to_pcie_device(devdpc);
> > > -    dpc = get_service_data(pciedev);
> > > +    dpc = to_dpc_dev(pdev);
> > 
> > I thought that the struct pci_dev sent here is the bridge and we
> > need to locate the struct device of the DPC object here.
> > 
> > Isn't this change wrong now?
> 
> Gosh, I should have looked 30 lines above. Nevermind.

Yeah, this part should be equivalent to before. This patch just created
the opportunity to move this to a common helper, otherwise it would have
been duplicated two more times.
diff mbox series

Patch

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c4801a83fcc5..bcb70b7c190c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1284,6 +1284,7 @@  int pci_save_state(struct pci_dev *dev)
 	if (i != 0)
 		return i;
 
+	pci_save_dpc_state(dev);
 	return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1389,6 +1390,7 @@  void pci_restore_state(struct pci_dev *dev)
 	pci_restore_ats_state(dev);
 	pci_restore_vc_state(dev);
 	pci_restore_rebar_state(dev);
+	pci_restore_dpc_state(dev);
 
 	pci_cleanup_aer_error_status_regs(dev);
 
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 6e0d1528d471..5a5c6099b253 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -346,6 +346,14 @@  int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 #endif	/* CONFIG_PCIEAER */
 
+#ifdef CONFIG_PCIE_DPC
+void pci_save_dpc_state(struct pci_dev *dev);
+void pci_restore_dpc_state(struct pci_dev *dev);
+#else
+void pci_save_dpc_state(struct pci_dev *dev) {}
+void pci_restore_dpc_state(struct pci_dev *dev) {}
+#endif
+
 #ifdef CONFIG_PCI_ATS
 void pci_restore_ats_state(struct pci_dev *dev);
 #else
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index a1fd16bf1cab..ed815a28512e 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -44,6 +44,58 @@  static const char * const rp_pio_error_string[] = {
 	"Memory Request Completion Timeout",		 /* Bit Position 18 */
 };
 
+static struct dpc_dev *to_dpc_dev(struct pci_dev *dev)
+{
+	struct device *device;
+
+	device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_DPC);
+	if (!device)
+		return NULL;
+	return get_service_data(to_pcie_device(device));
+}
+
+void pci_save_dpc_state(struct pci_dev *dev)
+{
+	struct dpc_dev *dpc;
+	struct pci_cap_saved_state *save_state;
+	u16 *cap;
+
+	if (!pci_is_pcie(dev))
+		return;
+
+	dpc = to_dpc_dev(dev);
+	if (!dpc)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+	if (!save_state)
+		return;
+
+	cap = (u16 *)&save_state->cap.data[0];
+	pci_read_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, cap);
+}
+
+void pci_restore_dpc_state(struct pci_dev *dev)
+{
+	struct dpc_dev *dpc;
+	struct pci_cap_saved_state *save_state;
+	u16 *cap;
+
+	if (!pci_is_pcie(dev))
+		return;
+
+	dpc = to_dpc_dev(dev);
+	if (!dpc)
+		return;
+
+	save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+	if (!save_state)
+		return;
+
+	cap = (u16 *)&save_state->cap.data[0];
+	pci_write_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, *cap);
+}
+
 static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 {
 	unsigned long timeout = jiffies + HZ;
@@ -67,18 +119,13 @@  static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
 	struct dpc_dev *dpc;
-	struct pcie_device *pciedev;
-	struct device *devdpc;
-
 	u16 cap;
 
 	/*
 	 * DPC disables the Link automatically in hardware, so it has
 	 * already been reset by the time we get here.
 	 */
-	devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
-	pciedev = to_pcie_device(devdpc);
-	dpc = get_service_data(pciedev);
+	dpc = to_dpc_dev(pdev);
 	cap = dpc->cap_pos;
 
 	/*
@@ -259,6 +306,8 @@  static int dpc_probe(struct pcie_device *dev)
 		FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
 		FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
 		FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
+
+	pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16));
 	return status;
 }