138fe3975SBrett Creeley // SPDX-License-Identifier: GPL-2.0 238fe3975SBrett Creeley /* Copyright(c) 2023 Advanced Micro Devices, Inc. */ 338fe3975SBrett Creeley 438fe3975SBrett Creeley #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 538fe3975SBrett Creeley 638fe3975SBrett Creeley #include <linux/module.h> 738fe3975SBrett Creeley #include <linux/pci.h> 838fe3975SBrett Creeley #include <linux/types.h> 938fe3975SBrett Creeley #include <linux/vfio.h> 1038fe3975SBrett Creeley 1163f77a71SBrett Creeley #include <linux/pds/pds_common.h> 1238fe3975SBrett Creeley #include <linux/pds/pds_core_if.h> 1363f77a71SBrett Creeley #include <linux/pds/pds_adminq.h> 1438fe3975SBrett Creeley 1538fe3975SBrett Creeley #include "vfio_dev.h" 1663f77a71SBrett Creeley #include "pci_drv.h" 1763f77a71SBrett Creeley #include "cmds.h" 1838fe3975SBrett Creeley 1938fe3975SBrett Creeley #define PDS_VFIO_DRV_DESCRIPTION "AMD/Pensando VFIO Device Driver" 2038fe3975SBrett Creeley #define PCI_VENDOR_ID_PENSANDO 0x1dd8 2138fe3975SBrett Creeley 22*7dabb1bcSBrett Creeley static void pds_vfio_recovery(struct pds_vfio_pci_device *pds_vfio) 23*7dabb1bcSBrett Creeley { 24*7dabb1bcSBrett Creeley bool deferred_reset_needed = false; 25*7dabb1bcSBrett Creeley 26*7dabb1bcSBrett Creeley /* 27*7dabb1bcSBrett Creeley * Documentation states that the kernel migration driver must not 28*7dabb1bcSBrett Creeley * generate asynchronous device state transitions outside of 29*7dabb1bcSBrett Creeley * manipulation by the user or the VFIO_DEVICE_RESET ioctl. 30*7dabb1bcSBrett Creeley * 31*7dabb1bcSBrett Creeley * Since recovery is an asynchronous event received from the device, 32*7dabb1bcSBrett Creeley * initiate a deferred reset. Issue a deferred reset in the following 33*7dabb1bcSBrett Creeley * situations: 34*7dabb1bcSBrett Creeley * 1. Migration is in progress, which will cause the next step of 35*7dabb1bcSBrett Creeley * the migration to fail. 36*7dabb1bcSBrett Creeley * 2. If the device is in a state that will be set to 37*7dabb1bcSBrett Creeley * VFIO_DEVICE_STATE_RUNNING on the next action (i.e. VM is 38*7dabb1bcSBrett Creeley * shutdown and device is in VFIO_DEVICE_STATE_STOP). 39*7dabb1bcSBrett Creeley */ 40*7dabb1bcSBrett Creeley mutex_lock(&pds_vfio->state_mutex); 41*7dabb1bcSBrett Creeley if ((pds_vfio->state != VFIO_DEVICE_STATE_RUNNING && 42*7dabb1bcSBrett Creeley pds_vfio->state != VFIO_DEVICE_STATE_ERROR) || 43*7dabb1bcSBrett Creeley (pds_vfio->state == VFIO_DEVICE_STATE_RUNNING && 44*7dabb1bcSBrett Creeley pds_vfio_dirty_is_enabled(pds_vfio))) 45*7dabb1bcSBrett Creeley deferred_reset_needed = true; 46*7dabb1bcSBrett Creeley mutex_unlock(&pds_vfio->state_mutex); 47*7dabb1bcSBrett Creeley 48*7dabb1bcSBrett Creeley /* 49*7dabb1bcSBrett Creeley * On the next user initiated state transition, the device will 50*7dabb1bcSBrett Creeley * transition to the VFIO_DEVICE_STATE_ERROR. At this point it's the user's 51*7dabb1bcSBrett Creeley * responsibility to reset the device. 52*7dabb1bcSBrett Creeley * 53*7dabb1bcSBrett Creeley * If a VFIO_DEVICE_RESET is requested post recovery and before the next 54*7dabb1bcSBrett Creeley * state transition, then the deferred reset state will be set to 55*7dabb1bcSBrett Creeley * VFIO_DEVICE_STATE_RUNNING. 56*7dabb1bcSBrett Creeley */ 57*7dabb1bcSBrett Creeley if (deferred_reset_needed) { 58*7dabb1bcSBrett Creeley spin_lock(&pds_vfio->reset_lock); 59*7dabb1bcSBrett Creeley pds_vfio->deferred_reset = true; 60*7dabb1bcSBrett Creeley pds_vfio->deferred_reset_state = VFIO_DEVICE_STATE_ERROR; 61*7dabb1bcSBrett Creeley spin_unlock(&pds_vfio->reset_lock); 62*7dabb1bcSBrett Creeley } 63*7dabb1bcSBrett Creeley } 64*7dabb1bcSBrett Creeley 65*7dabb1bcSBrett Creeley static int pds_vfio_pci_notify_handler(struct notifier_block *nb, 66*7dabb1bcSBrett Creeley unsigned long ecode, void *data) 67*7dabb1bcSBrett Creeley { 68*7dabb1bcSBrett Creeley struct pds_vfio_pci_device *pds_vfio = 69*7dabb1bcSBrett Creeley container_of(nb, struct pds_vfio_pci_device, nb); 70*7dabb1bcSBrett Creeley struct device *dev = pds_vfio_to_dev(pds_vfio); 71*7dabb1bcSBrett Creeley union pds_core_notifyq_comp *event = data; 72*7dabb1bcSBrett Creeley 73*7dabb1bcSBrett Creeley dev_dbg(dev, "%s: event code %lu\n", __func__, ecode); 74*7dabb1bcSBrett Creeley 75*7dabb1bcSBrett Creeley /* 76*7dabb1bcSBrett Creeley * We don't need to do anything for RESET state==0 as there is no notify 77*7dabb1bcSBrett Creeley * or feedback mechanism available, and it is possible that we won't 78*7dabb1bcSBrett Creeley * even see a state==0 event since the pds_core recovery is pending. 79*7dabb1bcSBrett Creeley * 80*7dabb1bcSBrett Creeley * Any requests from VFIO while state==0 will fail, which will return 81*7dabb1bcSBrett Creeley * error and may cause migration to fail. 82*7dabb1bcSBrett Creeley */ 83*7dabb1bcSBrett Creeley if (ecode == PDS_EVENT_RESET) { 84*7dabb1bcSBrett Creeley dev_info(dev, "%s: PDS_EVENT_RESET event received, state==%d\n", 85*7dabb1bcSBrett Creeley __func__, event->reset.state); 86*7dabb1bcSBrett Creeley /* 87*7dabb1bcSBrett Creeley * pds_core device finished recovery and sent us the 88*7dabb1bcSBrett Creeley * notification (state == 1) to allow us to recover 89*7dabb1bcSBrett Creeley */ 90*7dabb1bcSBrett Creeley if (event->reset.state == 1) 91*7dabb1bcSBrett Creeley pds_vfio_recovery(pds_vfio); 92*7dabb1bcSBrett Creeley } 93*7dabb1bcSBrett Creeley 94*7dabb1bcSBrett Creeley return 0; 95*7dabb1bcSBrett Creeley } 96*7dabb1bcSBrett Creeley 97*7dabb1bcSBrett Creeley static int 98*7dabb1bcSBrett Creeley pds_vfio_pci_register_event_handler(struct pds_vfio_pci_device *pds_vfio) 99*7dabb1bcSBrett Creeley { 100*7dabb1bcSBrett Creeley struct device *dev = pds_vfio_to_dev(pds_vfio); 101*7dabb1bcSBrett Creeley struct notifier_block *nb = &pds_vfio->nb; 102*7dabb1bcSBrett Creeley int err; 103*7dabb1bcSBrett Creeley 104*7dabb1bcSBrett Creeley if (!nb->notifier_call) { 105*7dabb1bcSBrett Creeley nb->notifier_call = pds_vfio_pci_notify_handler; 106*7dabb1bcSBrett Creeley err = pdsc_register_notify(nb); 107*7dabb1bcSBrett Creeley if (err) { 108*7dabb1bcSBrett Creeley nb->notifier_call = NULL; 109*7dabb1bcSBrett Creeley dev_err(dev, 110*7dabb1bcSBrett Creeley "failed to register pds event handler: %pe\n", 111*7dabb1bcSBrett Creeley ERR_PTR(err)); 112*7dabb1bcSBrett Creeley return -EINVAL; 113*7dabb1bcSBrett Creeley } 114*7dabb1bcSBrett Creeley dev_dbg(dev, "pds event handler registered\n"); 115*7dabb1bcSBrett Creeley } 116*7dabb1bcSBrett Creeley 117*7dabb1bcSBrett Creeley return 0; 118*7dabb1bcSBrett Creeley } 119*7dabb1bcSBrett Creeley 120*7dabb1bcSBrett Creeley static void 121*7dabb1bcSBrett Creeley pds_vfio_pci_unregister_event_handler(struct pds_vfio_pci_device *pds_vfio) 122*7dabb1bcSBrett Creeley { 123*7dabb1bcSBrett Creeley if (pds_vfio->nb.notifier_call) { 124*7dabb1bcSBrett Creeley pdsc_unregister_notify(&pds_vfio->nb); 125*7dabb1bcSBrett Creeley pds_vfio->nb.notifier_call = NULL; 126*7dabb1bcSBrett Creeley } 127*7dabb1bcSBrett Creeley } 128*7dabb1bcSBrett Creeley 12938fe3975SBrett Creeley static int pds_vfio_pci_probe(struct pci_dev *pdev, 13038fe3975SBrett Creeley const struct pci_device_id *id) 13138fe3975SBrett Creeley { 13238fe3975SBrett Creeley struct pds_vfio_pci_device *pds_vfio; 13338fe3975SBrett Creeley int err; 13438fe3975SBrett Creeley 13538fe3975SBrett Creeley pds_vfio = vfio_alloc_device(pds_vfio_pci_device, vfio_coredev.vdev, 13638fe3975SBrett Creeley &pdev->dev, pds_vfio_ops_info()); 13738fe3975SBrett Creeley if (IS_ERR(pds_vfio)) 13838fe3975SBrett Creeley return PTR_ERR(pds_vfio); 13938fe3975SBrett Creeley 14038fe3975SBrett Creeley dev_set_drvdata(&pdev->dev, &pds_vfio->vfio_coredev); 14138fe3975SBrett Creeley 14238fe3975SBrett Creeley err = vfio_pci_core_register_device(&pds_vfio->vfio_coredev); 14338fe3975SBrett Creeley if (err) 14438fe3975SBrett Creeley goto out_put_vdev; 14538fe3975SBrett Creeley 14663f77a71SBrett Creeley err = pds_vfio_register_client_cmd(pds_vfio); 14763f77a71SBrett Creeley if (err) { 14863f77a71SBrett Creeley dev_err(&pdev->dev, "failed to register as client: %pe\n", 14963f77a71SBrett Creeley ERR_PTR(err)); 15063f77a71SBrett Creeley goto out_unregister_coredev; 15163f77a71SBrett Creeley } 15263f77a71SBrett Creeley 153*7dabb1bcSBrett Creeley err = pds_vfio_pci_register_event_handler(pds_vfio); 154*7dabb1bcSBrett Creeley if (err) 155*7dabb1bcSBrett Creeley goto out_unregister_client; 156*7dabb1bcSBrett Creeley 15738fe3975SBrett Creeley return 0; 15838fe3975SBrett Creeley 159*7dabb1bcSBrett Creeley out_unregister_client: 160*7dabb1bcSBrett Creeley pds_vfio_unregister_client_cmd(pds_vfio); 16163f77a71SBrett Creeley out_unregister_coredev: 16263f77a71SBrett Creeley vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev); 16338fe3975SBrett Creeley out_put_vdev: 16438fe3975SBrett Creeley vfio_put_device(&pds_vfio->vfio_coredev.vdev); 16538fe3975SBrett Creeley return err; 16638fe3975SBrett Creeley } 16738fe3975SBrett Creeley 16838fe3975SBrett Creeley static void pds_vfio_pci_remove(struct pci_dev *pdev) 16938fe3975SBrett Creeley { 17038fe3975SBrett Creeley struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev); 17138fe3975SBrett Creeley 172*7dabb1bcSBrett Creeley pds_vfio_pci_unregister_event_handler(pds_vfio); 17363f77a71SBrett Creeley pds_vfio_unregister_client_cmd(pds_vfio); 17438fe3975SBrett Creeley vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev); 17538fe3975SBrett Creeley vfio_put_device(&pds_vfio->vfio_coredev.vdev); 17638fe3975SBrett Creeley } 17738fe3975SBrett Creeley 17838fe3975SBrett Creeley static const struct pci_device_id pds_vfio_pci_table[] = { 17938fe3975SBrett Creeley { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_PENSANDO, 0x1003) }, /* Ethernet VF */ 18038fe3975SBrett Creeley { 0, } 18138fe3975SBrett Creeley }; 18238fe3975SBrett Creeley MODULE_DEVICE_TABLE(pci, pds_vfio_pci_table); 18338fe3975SBrett Creeley 184bb500dbeSBrett Creeley static void pds_vfio_pci_aer_reset_done(struct pci_dev *pdev) 185bb500dbeSBrett Creeley { 186bb500dbeSBrett Creeley struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev); 187bb500dbeSBrett Creeley 188bb500dbeSBrett Creeley pds_vfio_reset(pds_vfio); 189bb500dbeSBrett Creeley } 190bb500dbeSBrett Creeley 191bb500dbeSBrett Creeley static const struct pci_error_handlers pds_vfio_pci_err_handlers = { 192bb500dbeSBrett Creeley .reset_done = pds_vfio_pci_aer_reset_done, 193bb500dbeSBrett Creeley .error_detected = vfio_pci_core_aer_err_detected, 194bb500dbeSBrett Creeley }; 195bb500dbeSBrett Creeley 19638fe3975SBrett Creeley static struct pci_driver pds_vfio_pci_driver = { 19738fe3975SBrett Creeley .name = KBUILD_MODNAME, 19838fe3975SBrett Creeley .id_table = pds_vfio_pci_table, 19938fe3975SBrett Creeley .probe = pds_vfio_pci_probe, 20038fe3975SBrett Creeley .remove = pds_vfio_pci_remove, 201bb500dbeSBrett Creeley .err_handler = &pds_vfio_pci_err_handlers, 20238fe3975SBrett Creeley .driver_managed_dma = true, 20338fe3975SBrett Creeley }; 20438fe3975SBrett Creeley 20538fe3975SBrett Creeley module_pci_driver(pds_vfio_pci_driver); 20638fe3975SBrett Creeley 20738fe3975SBrett Creeley MODULE_DESCRIPTION(PDS_VFIO_DRV_DESCRIPTION); 20838fe3975SBrett Creeley MODULE_AUTHOR("Brett Creeley <brett.creeley@amd.com>"); 20938fe3975SBrett Creeley MODULE_LICENSE("GPL"); 210