[PATCH RFC v4 3/9] powerpc/pci: Create pci_dn on demand

Oliver oohall at gmail.com
Tue Mar 5 19:04:19 AEDT 2019


On Sat, Mar 2, 2019 at 3:04 AM Sergey Miroshnichenko
<s.miroshnichenko at yadro.com> wrote:
>
> If a struct pci_dn hasn't yet been created for the PCIe device (there was
> no DT node for it), allocate this structure and fill with info read from
> the device directly.
>
> Signed-off-by: Sergey Miroshnichenko <s.miroshnichenko at yadro.com>
> ---
>  arch/powerpc/kernel/pci_dn.c | 79 ++++++++++++++++++++++++++++++++----
>  1 file changed, 72 insertions(+), 7 deletions(-)
>
> diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c
> index 341ed71250f1..67ccd7be8344 100644
> --- a/arch/powerpc/kernel/pci_dn.c
> +++ b/arch/powerpc/kernel/pci_dn.c
> @@ -33,6 +33,8 @@
>  #include <asm/firmware.h>
>  #include <asm/eeh.h>
>
> +static struct pci_dn *create_pdn(struct pci_dev *pdev, struct pci_dn *parent);
> +
>  /*
>   * The function is used to find the firmware data of one
>   * specific PCI device, which is attached to the indicated
> @@ -65,6 +67,9 @@ struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus)
>         dn = pci_bus_to_OF_node(pbus);
>         pdn = dn ? PCI_DN(dn) : NULL;
>
> +       if (!pdn && pbus->self)
> +               pdn = pbus->self->dev.archdata.pci_data;
> +
>         return pdn;
>  }
>
> @@ -74,10 +79,13 @@ struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
>         struct device_node *dn = NULL;
>         struct pci_dn *parent, *pdn;
>         struct pci_dev *pdev = NULL;
> +       bool pdev_found = false;
>
>         /* Fast path: fetch from PCI device */
>         list_for_each_entry(pdev, &bus->devices, bus_list) {
>                 if (pdev->devfn == devfn) {
> +                       pdev_found = true;
> +
>                         if (pdev->dev.archdata.pci_data)
>                                 return pdev->dev.archdata.pci_data;
>
> @@ -86,6 +94,9 @@ struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
>                 }
>         }
>
> +       if (!pdev_found)
> +               pdev = NULL;
> +
>         /* Fast path: fetch from device node */
>         pdn = dn ? PCI_DN(dn) : NULL;
>         if (pdn)
> @@ -98,9 +109,12 @@ struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
>
>         list_for_each_entry(pdn, &parent->child_list, list) {
>                 if (pdn->busno == bus->number &&
> -                    pdn->devfn == devfn)
> -                        return pdn;
> -        }
> +                   pdn->devfn == devfn) {
> +                       if (pdev)
> +                               pdev->dev.archdata.pci_data = pdn;
> +                       return pdn;
> +               }
> +       }
>
>         return NULL;
>  }
> @@ -130,14 +144,15 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
>
>         list_for_each_entry(pdn, &parent->child_list, list) {
>                 if (pdn->busno == pdev->bus->number &&
> -                   pdn->devfn == pdev->devfn)
> +                   pdn->devfn == pdev->devfn) {
> +                       pdev->dev.archdata.pci_data = pdn;
>                         return pdn;
> +               }
>         }
>
> -       return NULL;
> +       return create_pdn(pdev, parent);
>  }

Eh... If it works I guess it's fine? Killing pdn entirely (or at the
very least greatly restricting it's role) is something we should work
towards though since it doesn't make much sense on PNV.

> -#ifdef CONFIG_PCI_IOV
>  static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent,
>                                            int vf_index,
>                                            int busno, int devfn)
> @@ -156,7 +171,9 @@ static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent,
>         pdn->parent = parent;
>         pdn->busno = busno;
>         pdn->devfn = devfn;
> +       #ifdef CONFIG_PCI_IOV
>         pdn->vf_index = vf_index;
> +       #endif /* CONFIG_PCI_IOV */

This function is currently only used when adding VFs, so I'd move the
VF specific params from this and put them in the call site at
add_dev_pci_data(). Then you can drop the #ifdef hacks. It might be
worth renaming add_one_dev_pci_data() and add_one_dev_pci_data() since
their actual usage is limited to the VF init path, but their name seem
generic.

>         pdn->pe_number = IODA_INVALID_PE;
>         INIT_LIST_HEAD(&pdn->child_list);
>         INIT_LIST_HEAD(&pdn->list);
> @@ -164,7 +181,55 @@ static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent,
>
>         return pdn;
>  }
> -#endif
> +
> +static struct pci_dn *create_pdn(struct pci_dev *pdev, struct pci_dn *parent)
> +{
> +       struct pci_dn *pdn = NULL;
> +
> +       if (!parent)
> +               return NULL;
> +
> +       pdn = add_one_dev_pci_data(parent, 0, pdev->bus->busn_res.start, pdev->devfn);
> +       dev_info(&pdev->dev, "Create a new pdn for devfn %2x\n", pdev->devfn / 8);
> +

> +       if (pdn) {

Check for !pdn and exit early. Stacking indentation levels just gets
kind of ugly.

> +               #ifdef CONFIG_EEH
> +               struct eeh_dev *edev;
> +               #endif /* CONFIG_EEH */
You don't use the variable for anything. You can either switch this to
a void pointer or just check the return value of eeh_dev_init()
directly and drop this.

> +               u32 class_code;
> +               u16 device_id;
> +               u16 vendor_id;
> +

> +               #ifdef CONFIG_EEH
> +               edev = eeh_dev_init(pdn);
> +               if (!edev) {
> +                       kfree(pdn);
> +                       dev_err(&pdev->dev, "%s: Failed to allocate edev\n", __func__);
> +                       return NULL;
> +               }
> +               #endif /* CONFIG_EEH */
> +
> +               pci_bus_read_config_word(pdev->bus, pdev->devfn,
> +                                        PCI_VENDOR_ID, &vendor_id);
> +               pdn->vendor_id = vendor_id;
> +
> +               pci_bus_read_config_word(pdev->bus, pdev->devfn,
> +                                        PCI_DEVICE_ID, &device_id);
> +               pdn->device_id = device_id;
> +
> +               pci_bus_read_config_dword(pdev->bus, pdev->devfn,
> +                                         PCI_CLASS_REVISION, &class_code);
> +               class_code >>= 8;
> +               pdn->class_code = class_code;
> +
> +               pdn->pci_ext_config_space = 0;
Does this need to be explicitly initialised or can we rely on it being
allocated with kzalloc() or similar?

> +               pdev->dev.archdata.pci_data = pdn;
> +       } else {
> +               dev_err(&pdev->dev, "%s: Failed to allocate pdn\n", __func__);
> +       }
> +
> +       return pdn;
> +}
>
>  struct pci_dn *add_dev_pci_data(struct pci_dev *pdev)
>  {
> --
> 2.20.1
>


More information about the Linuxppc-dev mailing list