On Wed, Aug 21, 2019 at 05:37:51PM +0300, Mika Westerberg wrote: > On Wed, Aug 21, 2019 at 10:28:37AM +0300, Mika Westerberg wrote: > > > If we see a type of PCI_EXP_TYPE_ROOT_PORT or > > > PCI_EXP_TYPE_PCIE_BRIDGE, I think we have to assume that's accurate > > > (which we already do today -- for those types, we assume the device > > > has a secondary link). > > > > > > For a device that claims to be PCI_EXP_TYPE_DOWNSTREAM, if a parent > > > device exists and is a Downstream Port (Root Port, Switch Downstream > > > Port, and I suppose a PCI-to-PCIe bridge (this is basically > > > pcie_downstream_port()), this device must actually be acting as a > > > PCI_EXP_TYPE_UPSTREAM device. > > > > > > If a device claiming to be PCI_EXP_TYPE_UPSTREAM has a parent that is > > > PCI_EXP_TYPE_UPSTREAM, this device must actually be a > > > PCI_EXP_TYPE_DOWNSTREAM port. > > > > > > For PCI_EXP_TYPE_DOWNSTREAM and PCI_EXP_TYPE_UPSTREAM devices that > > > don't have parents, we just have to assume they advertise the correct > > > type (as we do today). There are sparc and virtualization configs > > > like this. > > > > OK, thanks for the details. I'll try to make patch based on the above. > > Something like the below patch? Only compile tested for now but I will > split it into a proper patch series and give it some testing if this is > what you were after. This is great, thanks a lot for cleaning this up! I feel bad for having introduced the has_secondary_link hack in the first place, so I'm really glad you're fixing it up. A couple minor nits below. > diff --git a/drivers/pci/access.c b/drivers/pci/access.c > index 544922f097c0..2fccb5762c76 100644 > --- a/drivers/pci/access.c > +++ b/drivers/pci/access.c > @@ -336,15 +336,6 @@ static inline int pcie_cap_version(const struct pci_dev *dev) > return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS; > } > > -static bool pcie_downstream_port(const struct pci_dev *dev) > -{ > - int type = pci_pcie_type(dev); > - > - return type == PCI_EXP_TYPE_ROOT_PORT || > - type == PCI_EXP_TYPE_DOWNSTREAM || > - type == PCI_EXP_TYPE_PCIE_BRIDGE; > -} > - > bool pcie_cap_has_lnkctl(const struct pci_dev *dev) > { > int type = pci_pcie_type(dev); > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c > index 9ac50710f1d4..3c0672f1dfe7 100644 > --- a/drivers/pci/pci.c > +++ b/drivers/pci/pci.c > @@ -3577,7 +3577,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) > } > > /* Ensure upstream ports don't block AtomicOps on egress */ > - if (!bridge->has_secondary_link) { > + if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) { > pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2, > &ctl2); > if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK) > diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h > index 9a83fcf612ca..ae8d839dca4f 100644 > --- a/drivers/pci/pci.h > +++ b/drivers/pci/pci.h > @@ -118,6 +118,15 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev) > return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3; > } > > +static inline bool pcie_downstream_port(const struct pci_dev *dev) > +{ > + int type = pci_pcie_type(dev); > + > + return type == PCI_EXP_TYPE_ROOT_PORT || > + type == PCI_EXP_TYPE_DOWNSTREAM || > + type == PCI_EXP_TYPE_PCIE_BRIDGE; > +} > + > int pci_vpd_init(struct pci_dev *dev); > void pci_vpd_release(struct pci_dev *dev); > void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev); > diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c > index 464f8f92653f..db2d40e44c08 100644 > --- a/drivers/pci/pcie/aspm.c > +++ b/drivers/pci/pcie/aspm.c > @@ -904,6 +904,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) > { > struct pcie_link_state *link; > int blacklist = !!pcie_aspm_sanity_check(pdev); > + int type = pci_pcie_type(pdev); > > if (!aspm_support_enabled) > return; > @@ -913,15 +914,14 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) > > /* > * We allocate pcie_link_state for the component on the upstream > - * end of a Link, so there's nothing to do unless this device has a > - * Link on its secondary side. > + * end of a Link, so there's nothing to do unless this device is > + * downstream or root port. > */ > - if (!pdev->has_secondary_link) > + if (type != PCI_EXP_TYPE_ROOT_PORT && type != PCI_EXP_TYPE_DOWNSTREAM) I think this should use pcie_downstream_port(), since PCI_EXP_TYPE_PCIE_BRIDGE is a Downstream Port, and set_pcie_port_type() did set dev->has_secondary_link for PCIE_BRIDGE. > return; > > /* VIA has a strange chipset, root port is under a bridge */ > - if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT && > - pdev->bus->self) > + if (type == PCI_EXP_TYPE_ROOT_PORT && pdev->bus->self) > return; > > down_read(&pci_bus_sem); > @@ -1070,7 +1070,8 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) > if (!pci_is_pcie(pdev)) > return 0; > > - if (pdev->has_secondary_link) > + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || > + pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) Same here. > parent = pdev; > if (!parent || !parent->link_state) > return -EINVAL; > diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c > index 773197a12568..b0e6048a9208 100644 > --- a/drivers/pci/pcie/err.c > +++ b/drivers/pci/pcie/err.c > @@ -166,7 +166,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service) > driver = pcie_port_find_service(dev, service); > if (driver && driver->reset_link) { > status = driver->reset_link(dev); > - } else if (dev->has_secondary_link) { > + } else if (pcie_downstream_port(dev)) { > status = default_reset_link(dev); > } else { > pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n", > diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c > index a3c7338fad86..983a5612c548 100644 > --- a/drivers/pci/probe.c > +++ b/drivers/pci/probe.c > @@ -1431,26 +1431,40 @@ void set_pcie_port_type(struct pci_dev *pdev) > pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16); > pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; > > + parent = pci_upstream_bridge(pdev); > + if (!parent) > + return; > + > /* > - * A Root Port or a PCI-to-PCIe bridge is always the upstream end > - * of a Link. No PCIe component has two Links. Two Links are > - * connected by a Switch that has a Port on each Link and internal > - * logic to connect the two Ports. > + * Some systems do not identify their upstream/downstream ports > + * correctly so detect impossible configurations here and correct > + * the port type accordingly. > */ > type = pci_pcie_type(pdev); > - if (type == PCI_EXP_TYPE_ROOT_PORT || > - type == PCI_EXP_TYPE_PCIE_BRIDGE) > - pdev->has_secondary_link = 1; > - else if (type == PCI_EXP_TYPE_UPSTREAM || > - type == PCI_EXP_TYPE_DOWNSTREAM) { > - parent = pci_upstream_bridge(pdev); > - > + if (type == PCI_EXP_TYPE_DOWNSTREAM) { > /* > - * Usually there's an upstream device (Root Port or Switch > - * Downstream Port), but we can't assume one exists. > + * If pdev claims to be downstream port but the parent > + * device is also downstream port assume pdev is actually > + * upstream port. > */ > - if (parent && !parent->has_secondary_link) > - pdev->has_secondary_link = 1; > + if (pcie_downstream_port(parent)) { > + dev_info(&pdev->dev, > + "claims to be downstream port but is acting as upstream port, correcting type\n"); You can use pci_info() here. Since the text wraps anyway, I'd just remove the line break and put it all on one line. > + pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE; > + pdev->pcie_flags_reg |= PCI_EXP_TYPE_UPSTREAM; > + } > + } else if (type == PCI_EXP_TYPE_UPSTREAM) { > + /* > + * If pdev claims to be upstream port but the parent > + * device is also upstream port assume pdev is actually > + * downstream port. > + */ > + if (pci_pcie_type(parent) == PCI_EXP_TYPE_UPSTREAM) { > + dev_info(&pdev->dev, > + "claims to be upstream port but is acting as downstream port, correcting type\n"); And here. > + pdev->pcie_flags_reg &= ~PCI_EXP_FLAGS_TYPE; > + pdev->pcie_flags_reg |= PCI_EXP_TYPE_DOWNSTREAM; > + } > } > } > > @@ -2764,12 +2778,8 @@ static int only_one_child(struct pci_bus *bus) > * A PCIe Downstream Port normally leads to a Link with only Device > * 0 on it (PCIe spec r3.1, sec 7.3.1). As an optimization, scan > * only for Device 0 in that situation. > - * > - * Checking has_secondary_link is a hack to identify Downstream > - * Ports because sometimes Switches are configured such that the > - * PCIe Port Type labels are backwards. > */ > - if (bridge && pci_is_pcie(bridge) && bridge->has_secondary_link) > + if (bridge && pci_is_pcie(bridge) && pcie_downstream_port(bridge)) > return 1; > > return 0; > diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c > index 5acd9c02683a..9ae9fb9339e8 100644 > --- a/drivers/pci/vc.c > +++ b/drivers/pci/vc.c > @@ -13,6 +13,8 @@ > #include <linux/pci_regs.h> > #include <linux/types.h> > > +#include "pci.h" > + > /** > * pci_vc_save_restore_dwords - Save or restore a series of dwords > * @dev: device > @@ -105,7 +107,7 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res) > struct pci_dev *link = NULL; > > /* Enable VCs from the downstream device */ > - if (!dev->has_secondary_link) > + if (!pci_is_pcie(dev) || !pcie_downstream_port(dev)) > return; > > ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 82e4cd1b7ac3..2f8990246316 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -418,7 +418,6 @@ struct pci_dev { > unsigned int broken_intx_masking:1; /* INTx masking can't be used */ > unsigned int io_window_1k:1; /* Intel bridge 1K I/O windows */ > unsigned int irq_managed:1; > - unsigned int has_secondary_link:1; > unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */ > unsigned int is_probed:1; /* Device probing in progress */ > unsigned int link_active_reporting:1;/* Device capable of reporting link active */