Page MenuHomeFreeBSD

D19646.id55238.diff
No OneTemporary

D19646.id55238.diff

Index: sys/amd64/vmm/io/ppt.c
===================================================================
--- sys/amd64/vmm/io/ppt.c
+++ sys/amd64/vmm/io/ppt.c
@@ -356,25 +356,12 @@
static void
ppt_pci_reset(device_t dev)
{
- int ps;
if (pcie_flr(dev,
- max(pcie_get_max_completion_timeout(dev) / 1000, 10),
- true))
+ max(pcie_get_max_completion_timeout(dev) / 1000, 10), true))
return;
- /*
- * If FLR fails, attempt a power-management reset by cycling
- * the device in/out of D3 state.
- * PCI spec says we can only go into D3 state from D0 state.
- * Transition from D[12] into D0 before going to D3 state.
- */
- ps = pci_get_powerstate(dev);
- if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3)
- pci_set_powerstate(dev, PCI_POWERSTATE_D0);
- if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3)
- pci_set_powerstate(dev, PCI_POWERSTATE_D3);
- pci_set_powerstate(dev, ps);
+ pci_hard_reset(dev);
}
int
Index: sys/dev/pci/pci.c
===================================================================
--- sys/dev/pci/pci.c
+++ sys/dev/pci/pci.c
@@ -6365,6 +6365,153 @@
return (true);
}
+/*
+ * Try link drop and retrain of the downstream port of upstream
+ * switch, for PCIe. According to the PCIe 3.0 spec 6.6.1, this must
+ * cause Conventional Hot reset of the device in the slot.
+ * Alternative, for PCIe, could be the secondary bus reset initiatied
+ * on the upstream switch PCIR_BRIDGECTL_1, bit 6.
+ */
+static int
+pcie_link_reset(device_t port)
+{
+ struct pci_devinfo *pdinfo;
+ devclass_t pci_class;
+ uint16_t v;
+
+ pci_class = devclass_find("pci");
+ if (device_get_devclass(port) != pci_class)
+ return (EINVAL);
+ pdinfo = device_get_ivars(port);
+ if (pdinfo->cfg.pcie.pcie_location == 0)
+ return (EINVAL);
+ if (pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_DOWNSTREAM_PORT &&
+ pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_ROOT_PORT)
+ return (ENOENT);
+
+ v = pci_read_config(port, pdinfo->cfg.pcie.pcie_location +
+ PCIER_LINK_CTL, 2);
+ v |= PCIEM_LINK_CTL_LINK_DIS;
+ pci_write_config(port, pdinfo->cfg.pcie.pcie_location +
+ PCIER_LINK_CTL, v, 2);
+ pause("pcier1", hz / 50); /* 20 ms */
+ v &= ~PCIEM_LINK_CTL_LINK_DIS;
+ v |= PCIEM_LINK_CTL_RETRAIN_LINK;
+ pci_write_config(port, pdinfo->cfg.pcie.pcie_location +
+ PCIER_LINK_CTL, v, 2);
+ pause("pcier2", hz / 10); /* 100 ms */
+ return (0);
+}
+
+/*
+ * Issue Conventional Hot reset, and if not possible (e.g. device is
+ * not PCIe or link reset failed), power reset.
+ */
+void
+pci_hard_reset(device_t dev)
+{
+ device_t pci, port;
+ devclass_t pci_class;
+ struct pci_devinfo *dinfo;
+ int error, ps;
+
+ pci_class = devclass_find("pci");
+ if (device_get_devclass(dev) != pci_class)
+ return;
+ dinfo = device_get_ivars(dev);
+ pci_cfg_save(dev, dinfo, 0);
+
+ error = ENOENT;
+ if (dinfo->cfg.pcie.pcie_location != 0) {
+ pci = device_get_parent(dev);
+ if (device_get_devclass(pci) != pci_class)
+ error = EINVAL;
+ else {
+ port = device_get_parent(pci);
+ error = pcie_link_reset(port);
+ }
+ }
+
+ /* If not PCIe or link method failed, try power reset */
+ if (error != 0) {
+ ps = pci_get_powerstate(dev);
+ if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3)
+ pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+ pci_set_powerstate(dev, PCI_POWERSTATE_D3);
+ pci_set_powerstate(dev, ps);
+ error = ps == pci_get_powerstate(dev) ? 0 : EIO;
+ }
+
+ if (error == 0)
+ pci_cfg_restore(dev, dinfo);
+}
+
+/*
+ * Issue Conventional Hot reset to the device, while detaching drivers
+ * from the device itself and all sibling PCIe functions in the same
+ * slot. PCI(e) configuration is saved around the reset.
+ *
+ * Typical Conventional reset affects all functions of multi-function
+ * device. More, drivers accessing functions during the reset get
+ * very confused, and could corrupt the hardware state when it brought
+ * up from the cold.
+ */
+int
+pcie_down_port_link_reset(device_t dev)
+{
+ device_t dev1, bus, pcib, *children;
+ devclass_t pci_class;
+ struct pci_devinfo *pdinfo;
+ int error, i, nchildren;
+
+ error = EINVAL;
+ children = NULL;
+ nchildren = 0;
+
+ mtx_lock(&Giant);
+ pci_class = devclass_find("pci");
+ bus = device_get_parent(dev);
+ if (bus == NULL)
+ goto done;
+ pcib = device_get_parent(bus);
+ if (pcib == NULL ||
+ device_get_devclass(device_get_parent(pcib)) != pci_class)
+ goto done;
+
+ pdinfo = device_get_ivars(pcib);
+ if (pdinfo->cfg.pcie.pcie_location == 0)
+ return (EINVAL);
+ if (pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_DOWNSTREAM_PORT &&
+ pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_ROOT_PORT)
+ goto done;
+
+ error = device_get_children(bus, &children, &nchildren);
+ if (error != 0)
+ goto done;
+ for (i = 0; i < nchildren; i++) {
+ dev1 = children[i];
+ error = device_quiesce(dev1);
+ if (error == 0)
+ error = device_detach(dev1);
+ pci_save_state(dev1);
+ }
+
+ pcie_link_reset(pcib);
+
+ for (i = 0; i < nchildren; i++) {
+ dev1 = children[i];
+ pci_restore_state(dev1);
+ if (device_get_state(dev1) != DS_ATTACHED)
+ device_probe_and_attach(dev1);
+ }
+ error = 0;
+
+done:
+ mtx_unlock(&Giant);
+ free(children, M_TEMP);
+ return (error);
+}
+
const struct pci_device_table *
pci_match_device(device_t child, const struct pci_device_table *id, size_t nelt)
{
Index: sys/dev/pci/pcivar.h
===================================================================
--- sys/dev/pci/pcivar.h
+++ sys/dev/pci/pcivar.h
@@ -671,6 +671,7 @@
device_t pci_find_pcie_root_port(device_t dev);
int pci_get_max_payload(device_t dev);
int pci_get_max_read_req(device_t dev);
+void pci_hard_reset(device_t dev);
void pci_restore_state(device_t dev);
void pci_save_state(device_t dev);
int pci_set_max_read_req(device_t dev, int size);
@@ -679,6 +680,7 @@
uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask,
uint32_t value, int width);
bool pcie_flr(device_t dev, u_int max_delay, bool force);
+int pcie_down_port_link_reset(device_t dev);
int pcie_get_max_completion_timeout(device_t dev);
bool pcie_wait_for_pending_transactions(device_t dev, u_int max_delay);

File Metadata

Mime Type
text/plain
Expires
Sat, Jan 17, 5:57 PM (12 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27697400
Default Alt Text
D19646.id55238.diff (5 KB)

Event Timeline