xref: /linux/drivers/vfio/pci/pds/pci_drv.c (revision 13578d4ebe8be1c16146f37c0c91f2579611cff2)
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 
227dabb1bcSBrett Creeley static void pds_vfio_recovery(struct pds_vfio_pci_device *pds_vfio)
237dabb1bcSBrett Creeley {
247dabb1bcSBrett Creeley 	bool deferred_reset_needed = false;
257dabb1bcSBrett Creeley 
267dabb1bcSBrett Creeley 	/*
277dabb1bcSBrett Creeley 	 * Documentation states that the kernel migration driver must not
287dabb1bcSBrett Creeley 	 * generate asynchronous device state transitions outside of
297dabb1bcSBrett Creeley 	 * manipulation by the user or the VFIO_DEVICE_RESET ioctl.
307dabb1bcSBrett Creeley 	 *
317dabb1bcSBrett Creeley 	 * Since recovery is an asynchronous event received from the device,
327dabb1bcSBrett Creeley 	 * initiate a deferred reset. Issue a deferred reset in the following
337dabb1bcSBrett Creeley 	 * situations:
347dabb1bcSBrett Creeley 	 *   1. Migration is in progress, which will cause the next step of
357dabb1bcSBrett Creeley 	 *	the migration to fail.
367dabb1bcSBrett Creeley 	 *   2. If the device is in a state that will be set to
377dabb1bcSBrett Creeley 	 *	VFIO_DEVICE_STATE_RUNNING on the next action (i.e. VM is
387dabb1bcSBrett Creeley 	 *	shutdown and device is in VFIO_DEVICE_STATE_STOP).
397dabb1bcSBrett Creeley 	 */
407dabb1bcSBrett Creeley 	mutex_lock(&pds_vfio->state_mutex);
417dabb1bcSBrett Creeley 	if ((pds_vfio->state != VFIO_DEVICE_STATE_RUNNING &&
427dabb1bcSBrett Creeley 	     pds_vfio->state != VFIO_DEVICE_STATE_ERROR) ||
437dabb1bcSBrett Creeley 	    (pds_vfio->state == VFIO_DEVICE_STATE_RUNNING &&
447dabb1bcSBrett Creeley 	     pds_vfio_dirty_is_enabled(pds_vfio)))
457dabb1bcSBrett Creeley 		deferred_reset_needed = true;
467dabb1bcSBrett Creeley 	mutex_unlock(&pds_vfio->state_mutex);
477dabb1bcSBrett Creeley 
487dabb1bcSBrett Creeley 	/*
497dabb1bcSBrett Creeley 	 * On the next user initiated state transition, the device will
507dabb1bcSBrett Creeley 	 * transition to the VFIO_DEVICE_STATE_ERROR. At this point it's the user's
517dabb1bcSBrett Creeley 	 * responsibility to reset the device.
527dabb1bcSBrett Creeley 	 *
537dabb1bcSBrett Creeley 	 * If a VFIO_DEVICE_RESET is requested post recovery and before the next
547dabb1bcSBrett Creeley 	 * state transition, then the deferred reset state will be set to
557dabb1bcSBrett Creeley 	 * VFIO_DEVICE_STATE_RUNNING.
567dabb1bcSBrett Creeley 	 */
577dabb1bcSBrett Creeley 	if (deferred_reset_needed) {
587dabb1bcSBrett Creeley 		spin_lock(&pds_vfio->reset_lock);
597dabb1bcSBrett Creeley 		pds_vfio->deferred_reset = true;
607dabb1bcSBrett Creeley 		pds_vfio->deferred_reset_state = VFIO_DEVICE_STATE_ERROR;
617dabb1bcSBrett Creeley 		spin_unlock(&pds_vfio->reset_lock);
627dabb1bcSBrett Creeley 	}
637dabb1bcSBrett Creeley }
647dabb1bcSBrett Creeley 
657dabb1bcSBrett Creeley static int pds_vfio_pci_notify_handler(struct notifier_block *nb,
667dabb1bcSBrett Creeley 				       unsigned long ecode, void *data)
677dabb1bcSBrett Creeley {
687dabb1bcSBrett Creeley 	struct pds_vfio_pci_device *pds_vfio =
697dabb1bcSBrett Creeley 		container_of(nb, struct pds_vfio_pci_device, nb);
707dabb1bcSBrett Creeley 	struct device *dev = pds_vfio_to_dev(pds_vfio);
717dabb1bcSBrett Creeley 	union pds_core_notifyq_comp *event = data;
727dabb1bcSBrett Creeley 
737dabb1bcSBrett Creeley 	dev_dbg(dev, "%s: event code %lu\n", __func__, ecode);
747dabb1bcSBrett Creeley 
757dabb1bcSBrett Creeley 	/*
767dabb1bcSBrett Creeley 	 * We don't need to do anything for RESET state==0 as there is no notify
777dabb1bcSBrett Creeley 	 * or feedback mechanism available, and it is possible that we won't
787dabb1bcSBrett Creeley 	 * even see a state==0 event since the pds_core recovery is pending.
797dabb1bcSBrett Creeley 	 *
807dabb1bcSBrett Creeley 	 * Any requests from VFIO while state==0 will fail, which will return
817dabb1bcSBrett Creeley 	 * error and may cause migration to fail.
827dabb1bcSBrett Creeley 	 */
837dabb1bcSBrett Creeley 	if (ecode == PDS_EVENT_RESET) {
847dabb1bcSBrett Creeley 		dev_info(dev, "%s: PDS_EVENT_RESET event received, state==%d\n",
857dabb1bcSBrett Creeley 			 __func__, event->reset.state);
867dabb1bcSBrett Creeley 		/*
877dabb1bcSBrett Creeley 		 * pds_core device finished recovery and sent us the
887dabb1bcSBrett Creeley 		 * notification (state == 1) to allow us to recover
897dabb1bcSBrett Creeley 		 */
907dabb1bcSBrett Creeley 		if (event->reset.state == 1)
917dabb1bcSBrett Creeley 			pds_vfio_recovery(pds_vfio);
927dabb1bcSBrett Creeley 	}
937dabb1bcSBrett Creeley 
947dabb1bcSBrett Creeley 	return 0;
957dabb1bcSBrett Creeley }
967dabb1bcSBrett Creeley 
977dabb1bcSBrett Creeley static int
987dabb1bcSBrett Creeley pds_vfio_pci_register_event_handler(struct pds_vfio_pci_device *pds_vfio)
997dabb1bcSBrett Creeley {
1007dabb1bcSBrett Creeley 	struct device *dev = pds_vfio_to_dev(pds_vfio);
1017dabb1bcSBrett Creeley 	struct notifier_block *nb = &pds_vfio->nb;
1027dabb1bcSBrett Creeley 	int err;
1037dabb1bcSBrett Creeley 
1047dabb1bcSBrett Creeley 	if (!nb->notifier_call) {
1057dabb1bcSBrett Creeley 		nb->notifier_call = pds_vfio_pci_notify_handler;
1067dabb1bcSBrett Creeley 		err = pdsc_register_notify(nb);
1077dabb1bcSBrett Creeley 		if (err) {
1087dabb1bcSBrett Creeley 			nb->notifier_call = NULL;
1097dabb1bcSBrett Creeley 			dev_err(dev,
1107dabb1bcSBrett Creeley 				"failed to register pds event handler: %pe\n",
1117dabb1bcSBrett Creeley 				ERR_PTR(err));
1127dabb1bcSBrett Creeley 			return -EINVAL;
1137dabb1bcSBrett Creeley 		}
1147dabb1bcSBrett Creeley 		dev_dbg(dev, "pds event handler registered\n");
1157dabb1bcSBrett Creeley 	}
1167dabb1bcSBrett Creeley 
1177dabb1bcSBrett Creeley 	return 0;
1187dabb1bcSBrett Creeley }
1197dabb1bcSBrett Creeley 
1207dabb1bcSBrett Creeley static void
1217dabb1bcSBrett Creeley pds_vfio_pci_unregister_event_handler(struct pds_vfio_pci_device *pds_vfio)
1227dabb1bcSBrett Creeley {
1237dabb1bcSBrett Creeley 	if (pds_vfio->nb.notifier_call) {
1247dabb1bcSBrett Creeley 		pdsc_unregister_notify(&pds_vfio->nb);
1257dabb1bcSBrett Creeley 		pds_vfio->nb.notifier_call = NULL;
1267dabb1bcSBrett Creeley 	}
1277dabb1bcSBrett Creeley }
1287dabb1bcSBrett 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 
1537dabb1bcSBrett Creeley 	err = pds_vfio_pci_register_event_handler(pds_vfio);
1547dabb1bcSBrett Creeley 	if (err)
1557dabb1bcSBrett Creeley 		goto out_unregister_client;
1567dabb1bcSBrett Creeley 
15738fe3975SBrett Creeley 	return 0;
15838fe3975SBrett Creeley 
1597dabb1bcSBrett Creeley out_unregister_client:
1607dabb1bcSBrett 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 
1727dabb1bcSBrett 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 
207*13578d4eSJoao Martins MODULE_IMPORT_NS(IOMMUFD);
20838fe3975SBrett Creeley MODULE_DESCRIPTION(PDS_VFIO_DRV_DESCRIPTION);
20938fe3975SBrett Creeley MODULE_AUTHOR("Brett Creeley <brett.creeley@amd.com>");
21038fe3975SBrett Creeley MODULE_LICENSE("GPL");
211