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
pds_vfio_recovery(struct pds_vfio_pci_device * pds_vfio)227dabb1bcSBrett Creeley static void pds_vfio_recovery(struct pds_vfio_pci_device *pds_vfio)
237dabb1bcSBrett Creeley {
247dabb1bcSBrett Creeley /*
257dabb1bcSBrett Creeley * Documentation states that the kernel migration driver must not
267dabb1bcSBrett Creeley * generate asynchronous device state transitions outside of
277dabb1bcSBrett Creeley * manipulation by the user or the VFIO_DEVICE_RESET ioctl.
287dabb1bcSBrett Creeley *
297dabb1bcSBrett Creeley * Since recovery is an asynchronous event received from the device,
30*6a7e448cSBrett Creeley * initiate a reset in the following situations:
317dabb1bcSBrett Creeley * 1. Migration is in progress, which will cause the next step of
327dabb1bcSBrett Creeley * the migration to fail.
337dabb1bcSBrett Creeley * 2. If the device is in a state that will be set to
347dabb1bcSBrett Creeley * VFIO_DEVICE_STATE_RUNNING on the next action (i.e. VM is
357dabb1bcSBrett Creeley * shutdown and device is in VFIO_DEVICE_STATE_STOP).
367dabb1bcSBrett Creeley */
377dabb1bcSBrett Creeley mutex_lock(&pds_vfio->state_mutex);
387dabb1bcSBrett Creeley if ((pds_vfio->state != VFIO_DEVICE_STATE_RUNNING &&
397dabb1bcSBrett Creeley pds_vfio->state != VFIO_DEVICE_STATE_ERROR) ||
407dabb1bcSBrett Creeley (pds_vfio->state == VFIO_DEVICE_STATE_RUNNING &&
417dabb1bcSBrett Creeley pds_vfio_dirty_is_enabled(pds_vfio)))
42*6a7e448cSBrett Creeley pds_vfio_reset(pds_vfio, VFIO_DEVICE_STATE_ERROR);
437dabb1bcSBrett Creeley mutex_unlock(&pds_vfio->state_mutex);
447dabb1bcSBrett Creeley }
457dabb1bcSBrett Creeley
pds_vfio_pci_notify_handler(struct notifier_block * nb,unsigned long ecode,void * data)467dabb1bcSBrett Creeley static int pds_vfio_pci_notify_handler(struct notifier_block *nb,
477dabb1bcSBrett Creeley unsigned long ecode, void *data)
487dabb1bcSBrett Creeley {
497dabb1bcSBrett Creeley struct pds_vfio_pci_device *pds_vfio =
507dabb1bcSBrett Creeley container_of(nb, struct pds_vfio_pci_device, nb);
517dabb1bcSBrett Creeley struct device *dev = pds_vfio_to_dev(pds_vfio);
527dabb1bcSBrett Creeley union pds_core_notifyq_comp *event = data;
537dabb1bcSBrett Creeley
547dabb1bcSBrett Creeley dev_dbg(dev, "%s: event code %lu\n", __func__, ecode);
557dabb1bcSBrett Creeley
567dabb1bcSBrett Creeley /*
577dabb1bcSBrett Creeley * We don't need to do anything for RESET state==0 as there is no notify
587dabb1bcSBrett Creeley * or feedback mechanism available, and it is possible that we won't
597dabb1bcSBrett Creeley * even see a state==0 event since the pds_core recovery is pending.
607dabb1bcSBrett Creeley *
617dabb1bcSBrett Creeley * Any requests from VFIO while state==0 will fail, which will return
627dabb1bcSBrett Creeley * error and may cause migration to fail.
637dabb1bcSBrett Creeley */
647dabb1bcSBrett Creeley if (ecode == PDS_EVENT_RESET) {
657dabb1bcSBrett Creeley dev_info(dev, "%s: PDS_EVENT_RESET event received, state==%d\n",
667dabb1bcSBrett Creeley __func__, event->reset.state);
677dabb1bcSBrett Creeley /*
687dabb1bcSBrett Creeley * pds_core device finished recovery and sent us the
697dabb1bcSBrett Creeley * notification (state == 1) to allow us to recover
707dabb1bcSBrett Creeley */
717dabb1bcSBrett Creeley if (event->reset.state == 1)
727dabb1bcSBrett Creeley pds_vfio_recovery(pds_vfio);
737dabb1bcSBrett Creeley }
747dabb1bcSBrett Creeley
757dabb1bcSBrett Creeley return 0;
767dabb1bcSBrett Creeley }
777dabb1bcSBrett Creeley
787dabb1bcSBrett Creeley static int
pds_vfio_pci_register_event_handler(struct pds_vfio_pci_device * pds_vfio)797dabb1bcSBrett Creeley pds_vfio_pci_register_event_handler(struct pds_vfio_pci_device *pds_vfio)
807dabb1bcSBrett Creeley {
817dabb1bcSBrett Creeley struct device *dev = pds_vfio_to_dev(pds_vfio);
827dabb1bcSBrett Creeley struct notifier_block *nb = &pds_vfio->nb;
837dabb1bcSBrett Creeley int err;
847dabb1bcSBrett Creeley
857dabb1bcSBrett Creeley if (!nb->notifier_call) {
867dabb1bcSBrett Creeley nb->notifier_call = pds_vfio_pci_notify_handler;
877dabb1bcSBrett Creeley err = pdsc_register_notify(nb);
887dabb1bcSBrett Creeley if (err) {
897dabb1bcSBrett Creeley nb->notifier_call = NULL;
907dabb1bcSBrett Creeley dev_err(dev,
917dabb1bcSBrett Creeley "failed to register pds event handler: %pe\n",
927dabb1bcSBrett Creeley ERR_PTR(err));
937dabb1bcSBrett Creeley return -EINVAL;
947dabb1bcSBrett Creeley }
957dabb1bcSBrett Creeley dev_dbg(dev, "pds event handler registered\n");
967dabb1bcSBrett Creeley }
977dabb1bcSBrett Creeley
987dabb1bcSBrett Creeley return 0;
997dabb1bcSBrett Creeley }
1007dabb1bcSBrett Creeley
1017dabb1bcSBrett Creeley static void
pds_vfio_pci_unregister_event_handler(struct pds_vfio_pci_device * pds_vfio)1027dabb1bcSBrett Creeley pds_vfio_pci_unregister_event_handler(struct pds_vfio_pci_device *pds_vfio)
1037dabb1bcSBrett Creeley {
1047dabb1bcSBrett Creeley if (pds_vfio->nb.notifier_call) {
1057dabb1bcSBrett Creeley pdsc_unregister_notify(&pds_vfio->nb);
1067dabb1bcSBrett Creeley pds_vfio->nb.notifier_call = NULL;
1077dabb1bcSBrett Creeley }
1087dabb1bcSBrett Creeley }
1097dabb1bcSBrett Creeley
pds_vfio_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)11038fe3975SBrett Creeley static int pds_vfio_pci_probe(struct pci_dev *pdev,
11138fe3975SBrett Creeley const struct pci_device_id *id)
11238fe3975SBrett Creeley {
11338fe3975SBrett Creeley struct pds_vfio_pci_device *pds_vfio;
11438fe3975SBrett Creeley int err;
11538fe3975SBrett Creeley
11638fe3975SBrett Creeley pds_vfio = vfio_alloc_device(pds_vfio_pci_device, vfio_coredev.vdev,
11738fe3975SBrett Creeley &pdev->dev, pds_vfio_ops_info());
11838fe3975SBrett Creeley if (IS_ERR(pds_vfio))
11938fe3975SBrett Creeley return PTR_ERR(pds_vfio);
12038fe3975SBrett Creeley
12138fe3975SBrett Creeley dev_set_drvdata(&pdev->dev, &pds_vfio->vfio_coredev);
12238fe3975SBrett Creeley
12338fe3975SBrett Creeley err = vfio_pci_core_register_device(&pds_vfio->vfio_coredev);
12438fe3975SBrett Creeley if (err)
12538fe3975SBrett Creeley goto out_put_vdev;
12638fe3975SBrett Creeley
12763f77a71SBrett Creeley err = pds_vfio_register_client_cmd(pds_vfio);
12863f77a71SBrett Creeley if (err) {
12963f77a71SBrett Creeley dev_err(&pdev->dev, "failed to register as client: %pe\n",
13063f77a71SBrett Creeley ERR_PTR(err));
13163f77a71SBrett Creeley goto out_unregister_coredev;
13263f77a71SBrett Creeley }
13363f77a71SBrett Creeley
1347dabb1bcSBrett Creeley err = pds_vfio_pci_register_event_handler(pds_vfio);
1357dabb1bcSBrett Creeley if (err)
1367dabb1bcSBrett Creeley goto out_unregister_client;
1377dabb1bcSBrett Creeley
13838fe3975SBrett Creeley return 0;
13938fe3975SBrett Creeley
1407dabb1bcSBrett Creeley out_unregister_client:
1417dabb1bcSBrett Creeley pds_vfio_unregister_client_cmd(pds_vfio);
14263f77a71SBrett Creeley out_unregister_coredev:
14363f77a71SBrett Creeley vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev);
14438fe3975SBrett Creeley out_put_vdev:
14538fe3975SBrett Creeley vfio_put_device(&pds_vfio->vfio_coredev.vdev);
14638fe3975SBrett Creeley return err;
14738fe3975SBrett Creeley }
14838fe3975SBrett Creeley
pds_vfio_pci_remove(struct pci_dev * pdev)14938fe3975SBrett Creeley static void pds_vfio_pci_remove(struct pci_dev *pdev)
15038fe3975SBrett Creeley {
15138fe3975SBrett Creeley struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev);
15238fe3975SBrett Creeley
1537dabb1bcSBrett Creeley pds_vfio_pci_unregister_event_handler(pds_vfio);
15463f77a71SBrett Creeley pds_vfio_unregister_client_cmd(pds_vfio);
15538fe3975SBrett Creeley vfio_pci_core_unregister_device(&pds_vfio->vfio_coredev);
15638fe3975SBrett Creeley vfio_put_device(&pds_vfio->vfio_coredev.vdev);
15738fe3975SBrett Creeley }
15838fe3975SBrett Creeley
15938fe3975SBrett Creeley static const struct pci_device_id pds_vfio_pci_table[] = {
16038fe3975SBrett Creeley { PCI_DRIVER_OVERRIDE_DEVICE_VFIO(PCI_VENDOR_ID_PENSANDO, 0x1003) }, /* Ethernet VF */
16138fe3975SBrett Creeley { 0, }
16238fe3975SBrett Creeley };
16338fe3975SBrett Creeley MODULE_DEVICE_TABLE(pci, pds_vfio_pci_table);
16438fe3975SBrett Creeley
pds_vfio_pci_aer_reset_done(struct pci_dev * pdev)165bb500dbeSBrett Creeley static void pds_vfio_pci_aer_reset_done(struct pci_dev *pdev)
166bb500dbeSBrett Creeley {
167bb500dbeSBrett Creeley struct pds_vfio_pci_device *pds_vfio = pds_vfio_pci_drvdata(pdev);
168bb500dbeSBrett Creeley
169*6a7e448cSBrett Creeley mutex_lock(&pds_vfio->state_mutex);
170*6a7e448cSBrett Creeley pds_vfio_reset(pds_vfio, VFIO_DEVICE_STATE_RUNNING);
171*6a7e448cSBrett Creeley mutex_unlock(&pds_vfio->state_mutex);
172bb500dbeSBrett Creeley }
173bb500dbeSBrett Creeley
174bb500dbeSBrett Creeley static const struct pci_error_handlers pds_vfio_pci_err_handlers = {
175bb500dbeSBrett Creeley .reset_done = pds_vfio_pci_aer_reset_done,
176bb500dbeSBrett Creeley .error_detected = vfio_pci_core_aer_err_detected,
177bb500dbeSBrett Creeley };
178bb500dbeSBrett Creeley
17938fe3975SBrett Creeley static struct pci_driver pds_vfio_pci_driver = {
18038fe3975SBrett Creeley .name = KBUILD_MODNAME,
18138fe3975SBrett Creeley .id_table = pds_vfio_pci_table,
18238fe3975SBrett Creeley .probe = pds_vfio_pci_probe,
18338fe3975SBrett Creeley .remove = pds_vfio_pci_remove,
184bb500dbeSBrett Creeley .err_handler = &pds_vfio_pci_err_handlers,
18538fe3975SBrett Creeley .driver_managed_dma = true,
18638fe3975SBrett Creeley };
18738fe3975SBrett Creeley
18838fe3975SBrett Creeley module_pci_driver(pds_vfio_pci_driver);
18938fe3975SBrett Creeley
19013578d4eSJoao Martins MODULE_IMPORT_NS(IOMMUFD);
19138fe3975SBrett Creeley MODULE_DESCRIPTION(PDS_VFIO_DRV_DESCRIPTION);
19238fe3975SBrett Creeley MODULE_AUTHOR("Brett Creeley <brett.creeley@amd.com>");
19338fe3975SBrett Creeley MODULE_LICENSE("GPL");
194