mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
virtio_pci: Add support for PCIe Function Level Reset
Implement support for Function Level Reset (FLR) in virtio_pci devices. This change adds reset_prepare and reset_done callbacks, allowing drivers to properly handle FLR operations. Without this patch, performing and recovering from an FLR is not possible for virtio_pci devices. This implementation ensures proper FLR handling and recovery for both physical and virtual functions. The device reset can be triggered in case of error or manually via sysfs: echo 1 > /sys/bus/pci/devices/$PCI_ADDR/reset Signed-off-by: Israel Rukshin <israelr@nvidia.com> Reviewed-by: Max Gurtovoy <mgurtovoy@nvidia.com> Message-Id: <1732690652-3065-2-git-send-email-israelr@nvidia.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
a3b9c053d8
commit
a0ec4fb63f
3 changed files with 118 additions and 25 deletions
|
@ -527,29 +527,7 @@ void unregister_virtio_device(struct virtio_device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_virtio_device);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int virtio_device_freeze(struct virtio_device *dev)
|
||||
{
|
||||
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
|
||||
int ret;
|
||||
|
||||
virtio_config_core_disable(dev);
|
||||
|
||||
dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
|
||||
|
||||
if (drv && drv->freeze) {
|
||||
ret = drv->freeze(dev);
|
||||
if (ret) {
|
||||
virtio_config_core_enable(dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_device_freeze);
|
||||
|
||||
int virtio_device_restore(struct virtio_device *dev)
|
||||
static int virtio_device_restore_priv(struct virtio_device *dev, bool restore)
|
||||
{
|
||||
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
|
||||
int ret;
|
||||
|
@ -580,8 +558,14 @@ int virtio_device_restore(struct virtio_device *dev)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (drv->restore) {
|
||||
ret = drv->restore(dev);
|
||||
if (restore) {
|
||||
if (drv->restore) {
|
||||
ret = drv->restore(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
ret = drv->reset_done(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
@ -598,9 +582,69 @@ err:
|
|||
virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int virtio_device_freeze(struct virtio_device *dev)
|
||||
{
|
||||
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
|
||||
int ret;
|
||||
|
||||
virtio_config_core_disable(dev);
|
||||
|
||||
dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
|
||||
|
||||
if (drv && drv->freeze) {
|
||||
ret = drv->freeze(dev);
|
||||
if (ret) {
|
||||
virtio_config_core_enable(dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_device_freeze);
|
||||
|
||||
int virtio_device_restore(struct virtio_device *dev)
|
||||
{
|
||||
return virtio_device_restore_priv(dev, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_device_restore);
|
||||
#endif
|
||||
|
||||
int virtio_device_reset_prepare(struct virtio_device *dev)
|
||||
{
|
||||
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
|
||||
int ret;
|
||||
|
||||
if (!drv || !drv->reset_prepare)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
virtio_config_core_disable(dev);
|
||||
|
||||
dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
|
||||
|
||||
ret = drv->reset_prepare(dev);
|
||||
if (ret) {
|
||||
virtio_config_core_enable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_device_reset_prepare);
|
||||
|
||||
int virtio_device_reset_done(struct virtio_device *dev)
|
||||
{
|
||||
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
|
||||
|
||||
if (!drv || !drv->reset_done)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return virtio_device_restore_priv(dev, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_device_reset_done);
|
||||
|
||||
static int virtio_init(void)
|
||||
{
|
||||
if (bus_register(&virtio_bus) != 0)
|
||||
|
|
|
@ -794,6 +794,46 @@ static int virtio_pci_sriov_configure(struct pci_dev *pci_dev, int num_vfs)
|
|||
return num_vfs;
|
||||
}
|
||||
|
||||
static void virtio_pci_reset_prepare(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = virtio_device_reset_prepare(&vp_dev->vdev);
|
||||
if (ret) {
|
||||
if (ret != -EOPNOTSUPP)
|
||||
dev_warn(&pci_dev->dev, "Reset prepare failure: %d",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pci_is_enabled(pci_dev))
|
||||
pci_disable_device(pci_dev);
|
||||
}
|
||||
|
||||
static void virtio_pci_reset_done(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
|
||||
int ret;
|
||||
|
||||
if (pci_is_enabled(pci_dev))
|
||||
return;
|
||||
|
||||
ret = pci_enable_device(pci_dev);
|
||||
if (!ret) {
|
||||
pci_set_master(pci_dev);
|
||||
ret = virtio_device_reset_done(&vp_dev->vdev);
|
||||
}
|
||||
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
dev_warn(&pci_dev->dev, "Reset done failure: %d", ret);
|
||||
}
|
||||
|
||||
static const struct pci_error_handlers virtio_pci_err_handler = {
|
||||
.reset_prepare = virtio_pci_reset_prepare,
|
||||
.reset_done = virtio_pci_reset_done,
|
||||
};
|
||||
|
||||
static struct pci_driver virtio_pci_driver = {
|
||||
.name = "virtio-pci",
|
||||
.id_table = virtio_pci_id_table,
|
||||
|
@ -803,6 +843,7 @@ static struct pci_driver virtio_pci_driver = {
|
|||
.driver.pm = &virtio_pci_pm_ops,
|
||||
#endif
|
||||
.sriov_configure = virtio_pci_sriov_configure,
|
||||
.err_handler = &virtio_pci_err_handler,
|
||||
};
|
||||
|
||||
struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev)
|
||||
|
|
|
@ -190,6 +190,8 @@ int virtio_device_freeze(struct virtio_device *dev);
|
|||
int virtio_device_restore(struct virtio_device *dev);
|
||||
#endif
|
||||
void virtio_reset_device(struct virtio_device *dev);
|
||||
int virtio_device_reset_prepare(struct virtio_device *dev);
|
||||
int virtio_device_reset_done(struct virtio_device *dev);
|
||||
|
||||
size_t virtio_max_dma_size(const struct virtio_device *vdev);
|
||||
|
||||
|
@ -214,6 +216,10 @@ size_t virtio_max_dma_size(const struct virtio_device *vdev);
|
|||
* changes; may be called in interrupt context.
|
||||
* @freeze: optional function to call during suspend/hibernation.
|
||||
* @restore: optional function to call on resume.
|
||||
* @reset_prepare: optional function to call when a transport specific reset
|
||||
* occurs.
|
||||
* @reset_done: optional function to call after transport specific reset
|
||||
* operation has finished.
|
||||
*/
|
||||
struct virtio_driver {
|
||||
struct device_driver driver;
|
||||
|
@ -229,6 +235,8 @@ struct virtio_driver {
|
|||
void (*config_changed)(struct virtio_device *dev);
|
||||
int (*freeze)(struct virtio_device *dev);
|
||||
int (*restore)(struct virtio_device *dev);
|
||||
int (*reset_prepare)(struct virtio_device *dev);
|
||||
int (*reset_done)(struct virtio_device *dev);
|
||||
};
|
||||
|
||||
#define drv_to_virtio(__drv) container_of_const(__drv, struct virtio_driver, driver)
|
||||
|
|
Loading…
Add table
Reference in a new issue