Re: [PATCH v2] PCI: Reset PCIe devices to stop ongoing DMA

From: Takao Indoh
Date: Wed Jul 31 2013 - 01:51:34 EST


(2013/07/31 12:11), Alex Williamson wrote:
> On Wed, 2013-07-31 at 09:35 +0900, Takao Indoh wrote:
>> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
>> index e37fea6..c595997 100644
>> --- a/drivers/pci/pci.c
>> +++ b/drivers/pci/pci.c
>> @@ -3392,6 +3392,59 @@ int pci_reset_function(struct pci_dev *dev)
>> EXPORT_SYMBOL_GPL(pci_reset_function);
>>
>> /**
>> + * pci_reset_bus - reset a PCI bus
>> + * @bus: PCI bus to reset
>> + *
>> + * Returns 0 if the bus was successfully reset or negative if failed.
>> + */
>> +int pci_reset_bus(struct pci_bus *bus)
>> +{
>> + struct pci_dev *pdev;
>> + u16 ctrl;
>> +
>> + if (!bus->self)
>> + return -ENOTTY;
>> +
>> + list_for_each_entry(pdev, &bus->devices, bus_list)
>> + if (pdev->subordinate)
>> + return -ENOTTY;
>> +
>> + /* Save config registers of children */
>> + list_for_each_entry(pdev, &bus->devices, bus_list) {
>> + dev_info(&pdev->dev, "Save state\n");
>> + pci_save_state(pdev);
>> + }
>> +
>> + dev_info(&bus->self->dev, "Reset Secondary bus\n");
>> +
>> + /* Assert Secondary Bus Reset */
>> + pci_read_config_word(bus->self, PCI_BRIDGE_CONTROL, &ctrl);
>> + ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
>> + pci_write_config_word(bus->self, PCI_BRIDGE_CONTROL, ctrl);
>> +
>> + /* Read config again to flush previous write */
>> + pci_read_config_word(bus->self, PCI_BRIDGE_CONTROL, &ctrl);
>> +
>> + msleep(2);
>> +
>> + /* De-assert Secondary Bus Reset */
>> + ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
>> + pci_write_config_word(bus->self, PCI_BRIDGE_CONTROL, ctrl);
>> +
>> + /* Wait for completion */
>> + msleep(1000);
>
>
> We already have secondary bus reset code in this file, why are we
> duplicating it here? Also, why are these delays different from the
> existing code? I'm also in need of a bus reset interface for when we
> assign all of the devices on a bus to userspace and do not have working
> function level resets per device. I'll post my patch series and perhaps
> we can collaborate on a pci bus reset interface. Thanks,

Good point. Yes, we have already similar functions.

pci_parent_bus_reset()
1. Assert secondary bus reset
2. msleep(100)
3. De-assert secondary bus reset
4. msleep(100)

aer_do_secondary_bus_reset()
1. Assert secondary bus reset
2. msleep(2)
3. De-assert secondary bus reset,
4. msleep(200)

To be honest, I wrote my reset code almost one years ago, so I forgot
the reason why I separated them.

Basically my reset code is based on aer_do_secondary_bus_reset(). The
different is waiting time after reset. My patch has 1000msec waiting
time.

At first my reset code is almost same as aer_do_secondary_bus_reset().
But when I tested the reset code, I found that on certain machine
restoring config registers failed after reset. It failed because 200msec
waiting time was too short. And I found the following description in
PCIe spec. According to this, I thought we should wait at least 1000msec.

6.6.1. Conventional Reset

* The Root Complex and/or system software must allow at least 1.0s
after a Conventional Reset of a device, before it may determine that a
device which fails to return a Successful Completion status for a
valid Configuration Request is a broken device. This period is
independent of how quickly Link training completes.

Note: This delay is analogous to the Trhfa parameter specified for
PCI/PCI-X, and is intended to allow an adequate amount of time for
devices which require self initialization.

* When attempting a Configuration access to devices on a PCI or PCI-X
bus segment behind a PCI Express/PCI(-X) Bridge, the timing parameter
Trhfa must be respected.

And I saw patches you posted today, yes, your patch looks helpful for
my purpose:-)

Thanks,
Takao Indoh

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/