xref: /linux/drivers/vfio/pci/pds/pci_drv.c (revision 7dabb1bcd1779bda021544671011e97611c819c4)
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