Re: [PATCH v15 3/9] PCI: Add new array for keeping track of ordering of reset methods

From: Raphael Norwitz
Date: Mon Aug 09 2021 - 07:18:43 EST


On Thu, Aug 05, 2021 at 09:59:11PM +0530, Amey Narkhede wrote:
> Introduce a new array reset_methods in struct pci_dev to keep track of
> reset mechanisms supported by the device and their ordering.
>
> Also refactor probing and reset functions to take advantage of calling
> convention of reset functions.
>
> Co-developed-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
> Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
> Signed-off-by: Amey Narkhede <ameynarkhede03@xxxxxxxxx>

Reviewed-by: Raphael Norwitz <raphael.norwitz@xxxxxxxxxxx>

> ---
> drivers/pci/pci.c | 95 ++++++++++++++++++++++++++-------------------
> drivers/pci/pci.h | 8 +++-
> drivers/pci/probe.c | 5 +--
> include/linux/pci.h | 7 ++++
> 4 files changed, 71 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 7d1d9671160b..67eab3d29cb3 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -73,6 +73,11 @@ static void pci_dev_d3_sleep(struct pci_dev *dev)
> msleep(delay);
> }
>
> +bool pci_reset_supported(struct pci_dev *dev)
> +{
> + return dev->reset_methods[0] != 0;
> +}
> +
> #ifdef CONFIG_PCI_DOMAINS
> int pci_domains_supported = 1;
> #endif
> @@ -5117,6 +5122,16 @@ static void pci_dev_restore(struct pci_dev *dev)
> err_handler->reset_done(dev);
> }
>
> +/* dev->reset_methods[] is a 0-terminated list of indices into this array */
> +static const struct pci_reset_fn_method pci_reset_fn_methods[] = {
> + { },
> + { pci_dev_specific_reset, .name = "device_specific" },
> + { pcie_reset_flr, .name = "flr" },
> + { pci_af_flr, .name = "af_flr" },
> + { pci_pm_reset, .name = "pm" },
> + { pci_reset_bus_function, .name = "bus" },
> +};
> +
> /**
> * __pci_reset_function_locked - reset a PCI device function while holding
> * the @dev mutex lock.
> @@ -5139,65 +5154,65 @@ static void pci_dev_restore(struct pci_dev *dev)
> */
> int __pci_reset_function_locked(struct pci_dev *dev)
> {
> - int rc;
> + int i, m, rc = -ENOTTY;
>
> might_sleep();
>
> /*
> - * A reset method returns -ENOTTY if it doesn't support this device
> - * and we should try the next method.
> + * A reset method returns -ENOTTY if it doesn't support this device and
> + * we should try the next method.
> *
> - * If it returns 0 (success), we're finished. If it returns any
> - * other error, we're also finished: this indicates that further
> - * reset mechanisms might be broken on the device.
> + * If it returns 0 (success), we're finished. If it returns any other
> + * error, we're also finished: this indicates that further reset
> + * mechanisms might be broken on the device.
> */
> - rc = pci_dev_specific_reset(dev, 0);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pcie_reset_flr(dev, 0);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pci_af_flr(dev, 0);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pci_pm_reset(dev, 0);
> - if (rc != -ENOTTY)
> - return rc;
> - return pci_reset_bus_function(dev, 0);
> + for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
> + m = dev->reset_methods[i];
> + if (!m)
> + return -ENOTTY;
> +
> + rc = pci_reset_fn_methods[m].reset_fn(dev, 0);
> + if (!rc)
> + return 0;
> + if (rc != -ENOTTY)
> + return rc;
> + }
> +
> + return -ENOTTY;
> }
> EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
>
> /**
> - * pci_probe_reset_function - check whether the device can be safely reset
> - * @dev: PCI device to reset
> + * pci_init_reset_methods - check whether device can be safely reset
> + * and store supported reset mechanisms.
> + * @dev: PCI device to check for reset mechanisms
> *
> * Some devices allow an individual function to be reset without affecting
> - * other functions in the same device. The PCI device must be responsive
> - * to PCI config space in order to use this function.
> + * other functions in the same device. The PCI device must be in D0-D3hot
> + * state.
> *
> - * Returns 0 if the device function can be reset or negative if the
> - * device doesn't support resetting a single function.
> + * Stores reset mechanisms supported by device in reset_methods byte array
> + * which is a member of struct pci_dev.
> */
> -int pci_probe_reset_function(struct pci_dev *dev)
> +void pci_init_reset_methods(struct pci_dev *dev)
> {
> - int rc;
> + int m, i, rc;
> +
> + BUILD_BUG_ON(ARRAY_SIZE(pci_reset_fn_methods) != PCI_NUM_RESET_METHODS);
>
> might_sleep();
>
> - rc = pci_dev_specific_reset(dev, 1);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pcie_reset_flr(dev, 1);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pci_af_flr(dev, 1);
> - if (rc != -ENOTTY)
> - return rc;
> - rc = pci_pm_reset(dev, 1);
> - if (rc != -ENOTTY)
> - return rc;
> + i = 0;
> +
> + for (m = 1; m < PCI_NUM_RESET_METHODS; m++) {
> + rc = pci_reset_fn_methods[m].reset_fn(dev, 1);
> + if (!rc)
> + dev->reset_methods[i++] = m;
> + else if (rc != -ENOTTY)
> + break;
> + }
>
> - return pci_reset_bus_function(dev, 1);
> + dev->reset_methods[i] = 0;
> }
>
> /**
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index 37c913bbc6e1..7438953745e0 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -33,7 +33,8 @@ enum pci_mmap_api {
> int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
> enum pci_mmap_api mmap_api);
>
> -int pci_probe_reset_function(struct pci_dev *dev);
> +bool pci_reset_supported(struct pci_dev *dev);
> +void pci_init_reset_methods(struct pci_dev *dev);
> int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
> int pci_bus_error_reset(struct pci_dev *dev);
>
> @@ -606,6 +607,11 @@ struct pci_dev_reset_methods {
> int (*reset)(struct pci_dev *dev, int probe);
> };
>
> +struct pci_reset_fn_method {
> + int (*reset_fn)(struct pci_dev *pdev, int probe);
> + char *name;
> +};
> +
> #ifdef CONFIG_PCI_QUIRKS
> int pci_dev_specific_reset(struct pci_dev *dev, int probe);
> #else
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index df3f9db6e151..5d8ad230f7d0 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -2405,9 +2405,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
> pci_rcec_init(dev); /* Root Complex Event Collector */
>
> pcie_report_downtraining(dev);
> -
> - if (pci_probe_reset_function(dev) == 0)
> - dev->reset_fn = 1;
> + pci_init_reset_methods(dev);
> + dev->reset_fn = pci_reset_supported(dev);
> }
>
> /*
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index aa85e7d3147e..d1a9a232d08e 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -49,6 +49,9 @@
> PCI_STATUS_SIG_TARGET_ABORT | \
> PCI_STATUS_PARITY)
>
> +/* Number of reset methods used in pci_reset_fn_methods array in pci.c */
> +#define PCI_NUM_RESET_METHODS 6
> +
> /*
> * The PCI interface treats multi-function devices as independent
> * devices. The slot/function address of each device is encoded
> @@ -506,6 +509,10 @@ struct pci_dev {
> char *driver_override; /* Driver name to force a match */
>
> unsigned long priv_flags; /* Private flags for the PCI driver */
> + /*
> + * See pci_reset_fn_methods array in pci.c for ordering.
> + */
> + u8 reset_methods[PCI_NUM_RESET_METHODS]; /* Reset methods ordered by priority */
> };
>
> static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
> --
> 2.32.0
>
>