On 2018-01-12 01:22, Gustavo Lima Chaves wrote:
> Try our best to follow more closely what the specification mandates for
> function-level reset (section 6.6.2 of PCI Express Base Specification,
> Revision 3.0). The previous approach was writing blindly on the command
> register (setting int. disable bit), but that would lead to an
> unsupported request on functions that do not expect that right away.
> 
> We now do checks for FLR support, proceed to the previous command
> register write, wait for any pending transactions for some cycles (used
> as a placeholder for the suggested 100ms timeout), raise the 'initiate
> FLR' bit on the right register, wait on the same kind of loop again and,
> finally, clear the transactions pending bit forcefully, as mandated.
> 
> This should leave us with better support for PCI devices out there.
> 
> Signed-off-by: Gustavo Lima Chaves <gustavo.lima.cha...@intel.com>
> ---
>  hypervisor/include/jailhouse/pci.h |  5 +++
>  hypervisor/pci.c                   | 82 
> +++++++++++++++++++++++++++++++++++---
>  2 files changed, 82 insertions(+), 5 deletions(-)
> 
> diff --git a/hypervisor/include/jailhouse/pci.h 
> b/hypervisor/include/jailhouse/pci.h
> index 6d256613..00d52099 100644
> --- a/hypervisor/include/jailhouse/pci.h
> +++ b/hypervisor/include/jailhouse/pci.h
> @@ -40,7 +40,12 @@
>  #define PCI_CAP_EXPRESS      0x10
>  
>  #define PCI_CAP_PCIE         (0x10 | JAILHOUSE_PCI_EXT_CAP)
> +#define PCIE_DEV_CAPS_REG    0x04
> +# define PCI_DEV_CAPS_FLR    (1 << 28)
>  #define PCIE_CONTROL_REG     0x08
> +# define PCI_CONTROL_INIT_FLR        (1 << 15)
> +#define PCIE_STATUS_REG              0x0a
> +# define PCI_STATUS_PENDING  (1 << 5)
>  
>  #define PCIE_AER_FLAGS       (PCIE_DEVCTL_CERE | PCIE_DEVCTL_NFERE | \
>                        PCIE_DEVCTL_FERE | PCIE_DEVCTL_URRE)
> diff --git a/hypervisor/pci.c b/hypervisor/pci.c
> index 30b733c9..f74f6399 100644
> --- a/hypervisor/pci.c
> +++ b/hypervisor/pci.c
> @@ -21,6 +21,8 @@
>  
>  #define MSIX_VECTOR_CTRL_DWORD               3
>  
> +#define PCI_RESET_TIMEOUT 250

Where was this value derived from? What is its unit (it's apparently
"loops" or "cycles")?

Style: indention.

> +
>  #define for_each_configured_pci_device(dev, cell)                    \
>       for ((dev) = (cell)->pci_devices;                               \
>            (dev) - (cell)->pci_devices < (cell)->config->num_pci_devices; \
> @@ -618,10 +620,13 @@ void pci_prepare_handover(void)
>       }
>  }
>  
> -void pci_reset_device(struct pci_device *device)
> +static void pci_reset_device_internal(struct pci_device *device)
>  {
>       const struct jailhouse_pci_capability *cap;
> +     unsigned timeout = PCI_RESET_TIMEOUT;
> +     bool found = false;
>       unsigned int n;
> +     u32 val;
>  
>       memset(&device->msi_registers, 0, sizeof(device->msi_registers));
>       for (n = 0; n < device->info->num_msix_vectors; n++) {
> @@ -635,13 +640,81 @@ void pci_reset_device(struct pci_device *device)
>               return;
>       }
>  
> +     for_each_pci_cap(cap, device, n) {
> +           if (cap->id == PCI_CAP_PCIE) {
> +                 found = true;
> +                 break;
> +           }
> +     }
> +
> +     if (!found)
> +             return;

Another use case for pci_get_cap().

> +
> +     val = pci_read_config(device->info->bdf,
> +                           cap->start + PCIE_DEV_CAPS_REG, 4);
> +
> +     if ((val & PCI_DEV_CAPS_FLR) != PCI_DEV_CAPS_FLR) {
> +             printk("Skipping function level reset for "
> +                    "device %02x:%02x.%x: no support\n",
> +                    PCI_BDF_PARAMS(device->info->bdf));
> +             return;
> +     }
> +
>       /*
> -      * Silence INTx of the physical device by setting the mask bit.
> -      * This is a deviation from the specified reset state.
> +      * "When a 0 is written to this register, the device is
> +      * logically disconnected from the PCI bus for all accesses
> +      * except configuration accesses."
> +      *
> +      * The bit that we set on that mask will silence INTx of the
> +      * physical device.
>        */
>       pci_write_config(device->info->bdf, PCI_CFG_COMMAND,
>                        PCI_CMD_INTX_OFF, 2);
>  
> +     /*
> +      * Wait for any pending transaction to end. Ideally we should
> +      * count for 100ms, but we count arbitrary loops for now
> +      * instead, by lack of timer notion in the hypervisor.
> +      */
> +     do {
> +             val = pci_read_config(device->info->bdf,
> +                                   cap->start + PCIE_STATUS_REG, 2);
> +             cpu_relax();
> +             timeout--;
> +     } while ((val & PCI_STATUS_PENDING) == PCI_STATUS_PENDING
> +              || timeout);

while (val & PCI_STATUS_PENDING || timeout > 0)

But what if timeout reaches 0? Just ignore STATUS_PENDING??

> +
> +     /*
> +      * Set "initiate function level reset" bit and wait a little
> +      * bit more.
> +      */
> +     val = pci_read_config(device->info->bdf,
> +                           cap->start + PCIE_CONTROL_REG, 2);

Redundant, we have the current value already.

> +     pci_write_config(device->info->bdf, cap->start + PCIE_CONTROL_REG,
> +                      val & PCI_CONTROL_INIT_FLR, 2);
> +     timeout = PCI_RESET_TIMEOUT;
> +     while (timeout) {
> +             cpu_relax();
> +             timeout--;
> +     }

These two timeout loops have totally different time spans because one is
throttled by PCI accesses while the other is just a trivial counter. I
suspect we can either drop that second loop completely or have to
properly throttle it as well.

What does the spec say here?

> +
> +     /*
> +      * "The Transactions Pending bit must be cleared upon completion
> +      * of the FLR."
> +      */
> +     val = pci_read_config(device->info->bdf,
> +                           cap->start + PCIE_STATUS_REG, 2);

Again: don't we have the current value already?

> +     pci_write_config(device->info->bdf, cap->start + PCIE_STATUS_REG,
> +                      val & ~PCI_STATUS_PENDING, 2);
> +}
> +
> +void pci_reset_device(struct pci_device *device)
> +{
> +     const struct jailhouse_pci_capability *cap;
> +     unsigned int n;
> +
> +     pci_reset_device_internal(device);
> +
>       for_each_pci_cap(cap, device, n) {
>               if (cap->id == PCI_CAP_MSI || cap->id == PCI_CAP_MSIX)
>                       /* Disable MSI/MSI-X by clearing the control word. */
> @@ -937,7 +1010,6 @@ void pci_shutdown(void)
>                               pci_restore_msix(device, cap);
>  
>               if (device->cell != &root_cell)
> -                     pci_write_config(device->info->bdf, PCI_CFG_COMMAND,
> -                                      PCI_CMD_INTX_OFF, 2);
> +                     pci_reset_device_internal(device);
>       }
>  }
> 

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jailhouse-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to