162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2023 Advanced Micro Devices, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/vfio.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/pds/pds_common.h> 1262306a36Sopenharmony_ci#include <linux/pds/pds_core_if.h> 1362306a36Sopenharmony_ci#include <linux/pds/pds_adminq.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "vfio_dev.h" 1662306a36Sopenharmony_ci#include "pci_drv.h" 1762306a36Sopenharmony_ci#include "cmds.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define PDS_VFIO_DRV_DESCRIPTION "AMD/Pensando VFIO Device Driver" 2062306a36Sopenharmony_ci#define PCI_VENDOR_ID_PENSANDO 0x1dd8 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic void pds_vfio_recovery(struct pds_vfio_pci_device *pds_vfio) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci bool deferred_reset_needed = false; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * Documentation states that the kernel migration driver must not 2862306a36Sopenharmony_ci * generate asynchronous device state transitions outside of 2962306a36Sopenharmony_ci * manipulation by the user or the VFIO_DEVICE_RESET ioctl. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Since recovery is an asynchronous event received from the device, 3262306a36Sopenharmony_ci * initiate a deferred reset. Issue a deferred reset in the following 3362306a36Sopenharmony_ci * situations: 3462306a36Sopenharmony_ci * 1. Migration is in progress, which will cause the next step of 3562306a36Sopenharmony_ci * the migration to fail. 3662306a36Sopenharmony_ci * 2. If the device is in a state that will be set to 3762306a36Sopenharmony_ci * VFIO_DEVICE_STATE_RUNNING on the next action (i.e. VM is 3862306a36Sopenharmony_ci * shutdown and device is in VFIO_DEVICE_STATE_STOP). 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci mutex_lock(&pds_vfio->state_mutex); 4162306a36Sopenharmony_ci if ((pds_vfio->state != VFIO_DEVICE_STATE_RUNNING && 4262306a36Sopenharmony_ci pds_vfio->state != VFIO_DEVICE_STATE_ERROR) || 4362306a36Sopenharmony_ci (pds_vfio->state == VFIO_DEVICE_STATE_RUNNING && 4462306a36Sopenharmony_ci pds_vfio_dirty_is_enabled(pds_vfio))) 4562306a36Sopenharmony_ci deferred_reset_needed = true; 4662306a36Sopenharmony_ci mutex_unlock(&pds_vfio->state_mutex); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * On the next user initiated state transition, the device will 5062306a36Sopenharmony_ci * transition to the VFIO_DEVICE_STATE_ERROR. At this point it's the user's 5162306a36Sopenharmony_ci * responsibility to reset the device. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * If a VFIO_DEVICE_RESET is requested post recovery and before the next 5462306a36Sopenharmony_ci * state transition, then the deferred reset state will be set to 5562306a36Sopenharmony_ci * VFIO_DEVICE_STATE_RUNNING. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci if (deferred_reset_needed) { 5862306a36Sopenharmony_ci mutex_lock(&pds_vfio->reset_mutex); 5962306a36Sopenharmony_ci pds_vfio->deferred_reset = true; 6062306a36Sopenharmony_ci pds_vfio->deferred_reset_state = VFIO_DEVICE_STATE_ERROR; 6162306a36Sopenharmony_ci mutex_unlock(&pds_vfio->reset_mutex); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int pds_vfio_pci_notify_handler(struct notifier_block *nb, 6662306a36Sopenharmony_ci unsigned long ecode, void *data) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct pds_vfio_pci_device *pds_vfio = 6962306a36Sopenharmony_ci container_of(nb, struct pds_vfio_pci_device, nb); 7062306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 7162306a36Sopenharmony_ci union pds_core_notifyq_comp *event = data; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci dev_dbg(dev, "%s: event code %lu\n", __func__, ecode); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * We don't need to do anything for RESET state==0 as there is no notify 7762306a36Sopenharmony_ci * or feedback mechanism available, and it is possible that we won't 7862306a36Sopenharmony_ci * even see a state==0 event since the pds_core recovery is pending. 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * Any requests from VFIO while state==0 will fail, which will return 8162306a36Sopenharmony_ci * error and may cause migration to fail. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (ecode == PDS_EVENT_RESET) { 8462306a36Sopenharmony_ci dev_info(dev, "%s: PDS_EVENT_RESET event received, state==%d\n", 8562306a36Sopenharmony_ci __func__, event->reset.state); 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * pds_core device finished recovery and sent us the 8862306a36Sopenharmony_ci * notification (state == 1) to allow us to recover 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (event->reset.state == 1) 9162306a36Sopenharmony_ci pds_vfio_recovery(pds_vfio); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int 9862306a36Sopenharmony_cipds_vfio_pci_register_event_handler(struct pds_vfio_pci_device *pds_vfio) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct device *dev = pds_vfio_to_dev(pds_vfio); 10162306a36Sopenharmony_ci struct notifier_block *nb = &pds_vfio->nb; 10262306a36Sopenharmony_ci int err; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!nb->notifier_call) { 10562306a36Sopenharmony_ci nb->notifier_call = pds_vfio_pci_notify_handler; 10662306a36Sopenharmony_ci err = pdsc_register_notify(nb); 10762306a36Sopenharmony_ci if (err) { 10862306a36Sopenharmony_ci nb->notifier_call = NULL; 10962306a36Sopenharmony_ci dev_err(dev, 11062306a36Sopenharmony_ci "failed to register pds event handler: %pe\n", 11162306a36Sopenharmony_ci ERR_PTR(err)); 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci dev_dbg(dev, "pds event handler registered\n"); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void 12162306a36Sopenharmony_cipds_vfio_pci_unregister_event_handler(struct pds_vfio_pci_device *pds_vfio) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (pds_vfio->nb.notifier_call) { 12462306a36Sopenharmony_ci pdsc_unregister_notify(&pds_vfio->nb); 12562306a36Sopenharmony_ci pds_vfio->nb.notifier_call = NULL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int pds_vfio_pci_probe(struct pci_dev *pdev, 13062306a36Sopenharmony_ci const struct pci_device_id *id) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct pds_vfio_pci_device *pds_vfio; 13362306a36Sopenharmony_ci int err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pds_vfio = vfio_alloc_device(pds_vfio_pci_device, vfio_coredev.vdev, 13662306a36Sopenharmony_ci &pdev->dev, pds_vfio_ops_info()); 13762306a36Sopenharmony_ci if (IS_ERR(pds_vfio)) 13862306a36Sopenharmony_ci return PTR_ERR(pds_vfio); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, &pds_vfio->vfio_coredev); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = vfio_pci_core_register_device(&pds_vfio->vfio_coredev); 14362306a36Sopenharmony_ci if (err) 14462306a36Sopenharmony_ci goto out_put_vdev; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci err = pds_vfio_register_client_cmd(pds_vfio); 14762306a36Sopenharmony_ci if (err) { 14862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register as client: %pe\n", 14962306a36Sopenharmony_ci ERR_PTR(err)); 15062306a36Sopenharmony_ci goto out_unregister_coredev; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci err = pds_vfio_pci_register_event_handler(pds_vfio); 15462306a36Sopenharmony_ci if (err) 15562306a36Sopenharmony_ci goto out_unregister_client; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciout_unregister_client: 16062306a36Sopenharmony_ci pds_vfio_unregister_client_cmd(pds_vfio); 16162306a36Sopenharmony_ciout_unregister_coredev: 16262306a36Sopenharmony_ci vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev); 16362306a36Sopenharmony_ciout_put_vdev: 16462306a36Sopenharmony_ci vfio_put_device(&pds_vfio->vfio_coredev.vdev); 16562306a36Sopenharmony_ci return err; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void pds_vfio_pci_remove(struct pci_dev *pdev) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci pds_vfio_pci_unregister_event_handler(pds_vfio); 17362306a36Sopenharmony_ci pds_vfio_unregister_client_cmd(pds_vfio); 17462306a36Sopenharmony_ci vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev); 17562306a36Sopenharmony_ci vfio_put_device(&pds_vfio->vfio_coredev.vdev); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const struct pci_device_id pds_vfio_pci_table[] = { 17962306a36Sopenharmony_ci { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_PENSANDO, 0x1003) }, /* Ethernet VF */ 18062306a36Sopenharmony_ci { 0, } 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pds_vfio_pci_table); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void pds_vfio_pci_aer_reset_done(struct pci_dev *pdev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci pds_vfio_reset(pds_vfio); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const struct pci_error_handlers pds_vfio_pci_err_handlers = { 19262306a36Sopenharmony_ci .reset_done = pds_vfio_pci_aer_reset_done, 19362306a36Sopenharmony_ci .error_detected = vfio_pci_core_aer_err_detected, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic struct pci_driver pds_vfio_pci_driver = { 19762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 19862306a36Sopenharmony_ci .id_table = pds_vfio_pci_table, 19962306a36Sopenharmony_ci .probe = pds_vfio_pci_probe, 20062306a36Sopenharmony_ci .remove = pds_vfio_pci_remove, 20162306a36Sopenharmony_ci .err_handler = &pds_vfio_pci_err_handlers, 20262306a36Sopenharmony_ci .driver_managed_dma = true, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cimodule_pci_driver(pds_vfio_pci_driver); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciMODULE_DESCRIPTION(PDS_VFIO_DRV_DESCRIPTION); 20862306a36Sopenharmony_ciMODULE_AUTHOR("Brett Creeley <brett.creeley@amd.com>"); 20962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 210