164b9f64fSJason Wang // SPDX-License-Identifier: GPL-2.0-only
264b9f64fSJason Wang /*
364b9f64fSJason Wang  * vDPA bridge driver for modern virtio-pci device
464b9f64fSJason Wang  *
564b9f64fSJason Wang  * Copyright (c) 2020, Red Hat Inc. All rights reserved.
664b9f64fSJason Wang  * Author: Jason Wang <jasowang@redhat.com>
764b9f64fSJason Wang  *
864b9f64fSJason Wang  * Based on virtio_pci_modern.c.
964b9f64fSJason Wang  */
1064b9f64fSJason Wang 
1164b9f64fSJason Wang #include <linux/interrupt.h>
1264b9f64fSJason Wang #include <linux/module.h>
1364b9f64fSJason Wang #include <linux/pci.h>
1464b9f64fSJason Wang #include <linux/vdpa.h>
1564b9f64fSJason Wang #include <linux/virtio.h>
1664b9f64fSJason Wang #include <linux/virtio_config.h>
1764b9f64fSJason Wang #include <linux/virtio_ring.h>
1864b9f64fSJason Wang #include <linux/virtio_pci.h>
1964b9f64fSJason Wang #include <linux/virtio_pci_modern.h>
20c1ca352dSJason Wang #include <uapi/linux/vdpa.h>
2164b9f64fSJason Wang 
2264b9f64fSJason Wang #define VP_VDPA_QUEUE_MAX 256
2364b9f64fSJason Wang #define VP_VDPA_DRIVER_NAME "vp_vdpa"
2464b9f64fSJason Wang #define VP_VDPA_NAME_SIZE 256
2564b9f64fSJason Wang 
2664b9f64fSJason Wang struct vp_vring {
2764b9f64fSJason Wang 	void __iomem *notify;
2864b9f64fSJason Wang 	char msix_name[VP_VDPA_NAME_SIZE];
2964b9f64fSJason Wang 	struct vdpa_callback cb;
30526cb858SJason Wang 	resource_size_t notify_pa;
3164b9f64fSJason Wang 	int irq;
3264b9f64fSJason Wang };
3364b9f64fSJason Wang 
3464b9f64fSJason Wang struct vp_vdpa {
3564b9f64fSJason Wang 	struct vdpa_device vdpa;
36ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev;
3764b9f64fSJason Wang 	struct vp_vring *vring;
3864b9f64fSJason Wang 	struct vdpa_callback config_cb;
39c1ca352dSJason Wang 	u64 device_features;
4064b9f64fSJason Wang 	char msix_name[VP_VDPA_NAME_SIZE];
4164b9f64fSJason Wang 	int config_irq;
4264b9f64fSJason Wang 	int queues;
4364b9f64fSJason Wang 	int vectors;
4464b9f64fSJason Wang };
4564b9f64fSJason Wang 
46ffbda8e9SCindy Lu struct vp_vdpa_mgmtdev {
47ffbda8e9SCindy Lu 	struct vdpa_mgmt_dev mgtdev;
48ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev;
49ffbda8e9SCindy Lu 	struct vp_vdpa *vp_vdpa;
50ffbda8e9SCindy Lu };
51ffbda8e9SCindy Lu 
vdpa_to_vp(struct vdpa_device * vdpa)5264b9f64fSJason Wang static struct vp_vdpa *vdpa_to_vp(struct vdpa_device *vdpa)
5364b9f64fSJason Wang {
5464b9f64fSJason Wang 	return container_of(vdpa, struct vp_vdpa, vdpa);
5564b9f64fSJason Wang }
5664b9f64fSJason Wang 
vdpa_to_mdev(struct vdpa_device * vdpa)5764b9f64fSJason Wang static struct virtio_pci_modern_device *vdpa_to_mdev(struct vdpa_device *vdpa)
5864b9f64fSJason Wang {
5964b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
6064b9f64fSJason Wang 
61ffbda8e9SCindy Lu 	return vp_vdpa->mdev;
62ffbda8e9SCindy Lu }
63ffbda8e9SCindy Lu 
vp_vdpa_to_mdev(struct vp_vdpa * vp_vdpa)64ffbda8e9SCindy Lu static struct virtio_pci_modern_device *vp_vdpa_to_mdev(struct vp_vdpa *vp_vdpa)
65ffbda8e9SCindy Lu {
66ffbda8e9SCindy Lu 	return vp_vdpa->mdev;
6764b9f64fSJason Wang }
6864b9f64fSJason Wang 
vp_vdpa_get_device_features(struct vdpa_device * vdpa)69a64917bcSEli Cohen static u64 vp_vdpa_get_device_features(struct vdpa_device *vdpa)
7064b9f64fSJason Wang {
71c1ca352dSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
7264b9f64fSJason Wang 
73c1ca352dSJason Wang 	return vp_vdpa->device_features;
7464b9f64fSJason Wang }
7564b9f64fSJason Wang 
vp_vdpa_set_driver_features(struct vdpa_device * vdpa,u64 features)76a64917bcSEli Cohen static int vp_vdpa_set_driver_features(struct vdpa_device *vdpa, u64 features)
7764b9f64fSJason Wang {
7864b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
7964b9f64fSJason Wang 
8064b9f64fSJason Wang 	vp_modern_set_features(mdev, features);
8164b9f64fSJason Wang 
8264b9f64fSJason Wang 	return 0;
8364b9f64fSJason Wang }
8464b9f64fSJason Wang 
vp_vdpa_get_driver_features(struct vdpa_device * vdpa)85a64917bcSEli Cohen static u64 vp_vdpa_get_driver_features(struct vdpa_device *vdpa)
86a64917bcSEli Cohen {
87a64917bcSEli Cohen 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
88a64917bcSEli Cohen 
89a64917bcSEli Cohen 	return vp_modern_get_driver_features(mdev);
90a64917bcSEli Cohen }
91a64917bcSEli Cohen 
vp_vdpa_get_status(struct vdpa_device * vdpa)9264b9f64fSJason Wang static u8 vp_vdpa_get_status(struct vdpa_device *vdpa)
9364b9f64fSJason Wang {
9464b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
9564b9f64fSJason Wang 
9664b9f64fSJason Wang 	return vp_modern_get_status(mdev);
9764b9f64fSJason Wang }
9864b9f64fSJason Wang 
vp_vdpa_get_vq_irq(struct vdpa_device * vdpa,u16 idx)995bbfea1eSWu Zongyong static int vp_vdpa_get_vq_irq(struct vdpa_device *vdpa, u16 idx)
1005bbfea1eSWu Zongyong {
1015bbfea1eSWu Zongyong 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
1025bbfea1eSWu Zongyong 	int irq = vp_vdpa->vring[idx].irq;
1035bbfea1eSWu Zongyong 
1045bbfea1eSWu Zongyong 	if (irq == VIRTIO_MSI_NO_VECTOR)
1055bbfea1eSWu Zongyong 		return -EINVAL;
1065bbfea1eSWu Zongyong 
1075bbfea1eSWu Zongyong 	return irq;
1085bbfea1eSWu Zongyong }
1095bbfea1eSWu Zongyong 
vp_vdpa_free_irq(struct vp_vdpa * vp_vdpa)11064b9f64fSJason Wang static void vp_vdpa_free_irq(struct vp_vdpa *vp_vdpa)
11164b9f64fSJason Wang {
112ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
11364b9f64fSJason Wang 	struct pci_dev *pdev = mdev->pci_dev;
11464b9f64fSJason Wang 	int i;
11564b9f64fSJason Wang 
11664b9f64fSJason Wang 	for (i = 0; i < vp_vdpa->queues; i++) {
11764b9f64fSJason Wang 		if (vp_vdpa->vring[i].irq != VIRTIO_MSI_NO_VECTOR) {
11864b9f64fSJason Wang 			vp_modern_queue_vector(mdev, i, VIRTIO_MSI_NO_VECTOR);
11964b9f64fSJason Wang 			devm_free_irq(&pdev->dev, vp_vdpa->vring[i].irq,
12064b9f64fSJason Wang 				      &vp_vdpa->vring[i]);
12164b9f64fSJason Wang 			vp_vdpa->vring[i].irq = VIRTIO_MSI_NO_VECTOR;
12264b9f64fSJason Wang 		}
12364b9f64fSJason Wang 	}
12464b9f64fSJason Wang 
12564b9f64fSJason Wang 	if (vp_vdpa->config_irq != VIRTIO_MSI_NO_VECTOR) {
12664b9f64fSJason Wang 		vp_modern_config_vector(mdev, VIRTIO_MSI_NO_VECTOR);
12764b9f64fSJason Wang 		devm_free_irq(&pdev->dev, vp_vdpa->config_irq, vp_vdpa);
12864b9f64fSJason Wang 		vp_vdpa->config_irq = VIRTIO_MSI_NO_VECTOR;
12964b9f64fSJason Wang 	}
13064b9f64fSJason Wang 
13164b9f64fSJason Wang 	if (vp_vdpa->vectors) {
13264b9f64fSJason Wang 		pci_free_irq_vectors(pdev);
13364b9f64fSJason Wang 		vp_vdpa->vectors = 0;
13464b9f64fSJason Wang 	}
13564b9f64fSJason Wang }
13664b9f64fSJason Wang 
vp_vdpa_vq_handler(int irq,void * arg)13764b9f64fSJason Wang static irqreturn_t vp_vdpa_vq_handler(int irq, void *arg)
13864b9f64fSJason Wang {
13964b9f64fSJason Wang 	struct vp_vring *vring = arg;
14064b9f64fSJason Wang 
14164b9f64fSJason Wang 	if (vring->cb.callback)
14264b9f64fSJason Wang 		return vring->cb.callback(vring->cb.private);
14364b9f64fSJason Wang 
14464b9f64fSJason Wang 	return IRQ_HANDLED;
14564b9f64fSJason Wang }
14664b9f64fSJason Wang 
vp_vdpa_config_handler(int irq,void * arg)14764b9f64fSJason Wang static irqreturn_t vp_vdpa_config_handler(int irq, void *arg)
14864b9f64fSJason Wang {
14964b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = arg;
15064b9f64fSJason Wang 
15164b9f64fSJason Wang 	if (vp_vdpa->config_cb.callback)
15264b9f64fSJason Wang 		return vp_vdpa->config_cb.callback(vp_vdpa->config_cb.private);
15364b9f64fSJason Wang 
15464b9f64fSJason Wang 	return IRQ_HANDLED;
15564b9f64fSJason Wang }
15664b9f64fSJason Wang 
vp_vdpa_request_irq(struct vp_vdpa * vp_vdpa)15764b9f64fSJason Wang static int vp_vdpa_request_irq(struct vp_vdpa *vp_vdpa)
15864b9f64fSJason Wang {
159ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
16064b9f64fSJason Wang 	struct pci_dev *pdev = mdev->pci_dev;
16164b9f64fSJason Wang 	int i, ret, irq;
16264b9f64fSJason Wang 	int queues = vp_vdpa->queues;
16364b9f64fSJason Wang 	int vectors = queues + 1;
16464b9f64fSJason Wang 
16564b9f64fSJason Wang 	ret = pci_alloc_irq_vectors(pdev, vectors, vectors, PCI_IRQ_MSIX);
16664b9f64fSJason Wang 	if (ret != vectors) {
16764b9f64fSJason Wang 		dev_err(&pdev->dev,
16864b9f64fSJason Wang 			"vp_vdpa: fail to allocate irq vectors want %d but %d\n",
16964b9f64fSJason Wang 			vectors, ret);
17064b9f64fSJason Wang 		return ret;
17164b9f64fSJason Wang 	}
17264b9f64fSJason Wang 
17364b9f64fSJason Wang 	vp_vdpa->vectors = vectors;
17464b9f64fSJason Wang 
17564b9f64fSJason Wang 	for (i = 0; i < queues; i++) {
17664b9f64fSJason Wang 		snprintf(vp_vdpa->vring[i].msix_name, VP_VDPA_NAME_SIZE,
17764b9f64fSJason Wang 			"vp-vdpa[%s]-%d\n", pci_name(pdev), i);
17864b9f64fSJason Wang 		irq = pci_irq_vector(pdev, i);
17964b9f64fSJason Wang 		ret = devm_request_irq(&pdev->dev, irq,
18064b9f64fSJason Wang 				       vp_vdpa_vq_handler,
18164b9f64fSJason Wang 				       0, vp_vdpa->vring[i].msix_name,
18264b9f64fSJason Wang 				       &vp_vdpa->vring[i]);
18364b9f64fSJason Wang 		if (ret) {
18464b9f64fSJason Wang 			dev_err(&pdev->dev,
18564b9f64fSJason Wang 				"vp_vdpa: fail to request irq for vq %d\n", i);
18664b9f64fSJason Wang 			goto err;
18764b9f64fSJason Wang 		}
18864b9f64fSJason Wang 		vp_modern_queue_vector(mdev, i, i);
18964b9f64fSJason Wang 		vp_vdpa->vring[i].irq = irq;
19064b9f64fSJason Wang 	}
19164b9f64fSJason Wang 
19264b9f64fSJason Wang 	snprintf(vp_vdpa->msix_name, VP_VDPA_NAME_SIZE, "vp-vdpa[%s]-config\n",
19364b9f64fSJason Wang 		 pci_name(pdev));
19464b9f64fSJason Wang 	irq = pci_irq_vector(pdev, queues);
19564b9f64fSJason Wang 	ret = devm_request_irq(&pdev->dev, irq,	vp_vdpa_config_handler, 0,
19664b9f64fSJason Wang 			       vp_vdpa->msix_name, vp_vdpa);
19764b9f64fSJason Wang 	if (ret) {
19864b9f64fSJason Wang 		dev_err(&pdev->dev,
19964b9f64fSJason Wang 			"vp_vdpa: fail to request irq for vq %d\n", i);
20064b9f64fSJason Wang 			goto err;
20164b9f64fSJason Wang 	}
20264b9f64fSJason Wang 	vp_modern_config_vector(mdev, queues);
20364b9f64fSJason Wang 	vp_vdpa->config_irq = irq;
20464b9f64fSJason Wang 
20564b9f64fSJason Wang 	return 0;
20664b9f64fSJason Wang err:
20764b9f64fSJason Wang 	vp_vdpa_free_irq(vp_vdpa);
20864b9f64fSJason Wang 	return ret;
20964b9f64fSJason Wang }
21064b9f64fSJason Wang 
vp_vdpa_set_status(struct vdpa_device * vdpa,u8 status)21164b9f64fSJason Wang static void vp_vdpa_set_status(struct vdpa_device *vdpa, u8 status)
21264b9f64fSJason Wang {
21364b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
214ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
21564b9f64fSJason Wang 	u8 s = vp_vdpa_get_status(vdpa);
21664b9f64fSJason Wang 
21764b9f64fSJason Wang 	if (status & VIRTIO_CONFIG_S_DRIVER_OK &&
21864b9f64fSJason Wang 	    !(s & VIRTIO_CONFIG_S_DRIVER_OK)) {
21964b9f64fSJason Wang 		vp_vdpa_request_irq(vp_vdpa);
22064b9f64fSJason Wang 	}
22164b9f64fSJason Wang 
22264b9f64fSJason Wang 	vp_modern_set_status(mdev, status);
2230686082dSXie Yongji }
22464b9f64fSJason Wang 
vp_vdpa_reset(struct vdpa_device * vdpa)2250686082dSXie Yongji static int vp_vdpa_reset(struct vdpa_device *vdpa)
2260686082dSXie Yongji {
2270686082dSXie Yongji 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
228ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
2290686082dSXie Yongji 	u8 s = vp_vdpa_get_status(vdpa);
2300686082dSXie Yongji 
2310686082dSXie Yongji 	vp_modern_set_status(mdev, 0);
2320686082dSXie Yongji 
2330686082dSXie Yongji 	if (s & VIRTIO_CONFIG_S_DRIVER_OK)
23464b9f64fSJason Wang 		vp_vdpa_free_irq(vp_vdpa);
2350686082dSXie Yongji 
2360686082dSXie Yongji 	return 0;
23764b9f64fSJason Wang }
23864b9f64fSJason Wang 
vp_vdpa_get_vq_num_max(struct vdpa_device * vdpa)23964b9f64fSJason Wang static u16 vp_vdpa_get_vq_num_max(struct vdpa_device *vdpa)
24064b9f64fSJason Wang {
24164b9f64fSJason Wang 	return VP_VDPA_QUEUE_MAX;
24264b9f64fSJason Wang }
24364b9f64fSJason Wang 
vp_vdpa_get_vq_state(struct vdpa_device * vdpa,u16 qid,struct vdpa_vq_state * state)24464b9f64fSJason Wang static int vp_vdpa_get_vq_state(struct vdpa_device *vdpa, u16 qid,
24564b9f64fSJason Wang 				struct vdpa_vq_state *state)
24664b9f64fSJason Wang {
24764b9f64fSJason Wang 	/* Note that this is not supported by virtio specification, so
24864b9f64fSJason Wang 	 * we return -EOPNOTSUPP here. This means we can't support live
24964b9f64fSJason Wang 	 * migration, vhost device start/stop.
25064b9f64fSJason Wang 	 */
25164b9f64fSJason Wang 	return -EOPNOTSUPP;
25264b9f64fSJason Wang }
25364b9f64fSJason Wang 
vp_vdpa_set_vq_state_split(struct vdpa_device * vdpa,const struct vdpa_vq_state * state)2541225c216SJason Wang static int vp_vdpa_set_vq_state_split(struct vdpa_device *vdpa,
2551225c216SJason Wang 				      const struct vdpa_vq_state *state)
2561225c216SJason Wang {
2571225c216SJason Wang 	const struct vdpa_vq_state_split *split = &state->split;
2581225c216SJason Wang 
2591225c216SJason Wang 	if (split->avail_index == 0)
2601225c216SJason Wang 		return 0;
2611225c216SJason Wang 
2621225c216SJason Wang 	return -EOPNOTSUPP;
2631225c216SJason Wang }
2641225c216SJason Wang 
vp_vdpa_set_vq_state_packed(struct vdpa_device * vdpa,const struct vdpa_vq_state * state)2651225c216SJason Wang static int vp_vdpa_set_vq_state_packed(struct vdpa_device *vdpa,
2661225c216SJason Wang 				       const struct vdpa_vq_state *state)
2671225c216SJason Wang {
2681225c216SJason Wang 	const struct vdpa_vq_state_packed *packed = &state->packed;
2691225c216SJason Wang 
2701225c216SJason Wang 	if (packed->last_avail_counter == 1 &&
2711225c216SJason Wang 	    packed->last_avail_idx == 0 &&
2721225c216SJason Wang 	    packed->last_used_counter == 1 &&
2731225c216SJason Wang 	    packed->last_used_idx == 0)
2741225c216SJason Wang 		return 0;
2751225c216SJason Wang 
2761225c216SJason Wang 	return -EOPNOTSUPP;
2771225c216SJason Wang }
2781225c216SJason Wang 
vp_vdpa_set_vq_state(struct vdpa_device * vdpa,u16 qid,const struct vdpa_vq_state * state)27964b9f64fSJason Wang static int vp_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 qid,
28064b9f64fSJason Wang 				const struct vdpa_vq_state *state)
28164b9f64fSJason Wang {
2821225c216SJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
2831225c216SJason Wang 
2841225c216SJason Wang 	/* Note that this is not supported by virtio specification.
2851225c216SJason Wang 	 * But if the state is by chance equal to the device initial
2861225c216SJason Wang 	 * state, we can let it go.
28764b9f64fSJason Wang 	 */
2881225c216SJason Wang 	if ((vp_modern_get_status(mdev) & VIRTIO_CONFIG_S_FEATURES_OK) &&
2891225c216SJason Wang 	    !vp_modern_get_queue_enable(mdev, qid)) {
2901225c216SJason Wang 		if (vp_modern_get_driver_features(mdev) &
2911225c216SJason Wang 		    BIT_ULL(VIRTIO_F_RING_PACKED))
2921225c216SJason Wang 			return vp_vdpa_set_vq_state_packed(vdpa, state);
2931225c216SJason Wang 		else
2941225c216SJason Wang 			return vp_vdpa_set_vq_state_split(vdpa,	state);
2951225c216SJason Wang 	}
2961225c216SJason Wang 
29764b9f64fSJason Wang 	return -EOPNOTSUPP;
29864b9f64fSJason Wang }
29964b9f64fSJason Wang 
vp_vdpa_set_vq_cb(struct vdpa_device * vdpa,u16 qid,struct vdpa_callback * cb)30064b9f64fSJason Wang static void vp_vdpa_set_vq_cb(struct vdpa_device *vdpa, u16 qid,
30164b9f64fSJason Wang 			      struct vdpa_callback *cb)
30264b9f64fSJason Wang {
30364b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
30464b9f64fSJason Wang 
30564b9f64fSJason Wang 	vp_vdpa->vring[qid].cb = *cb;
30664b9f64fSJason Wang }
30764b9f64fSJason Wang 
vp_vdpa_set_vq_ready(struct vdpa_device * vdpa,u16 qid,bool ready)30864b9f64fSJason Wang static void vp_vdpa_set_vq_ready(struct vdpa_device *vdpa,
30964b9f64fSJason Wang 				 u16 qid, bool ready)
31064b9f64fSJason Wang {
31164b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
31264b9f64fSJason Wang 
31364b9f64fSJason Wang 	vp_modern_set_queue_enable(mdev, qid, ready);
31464b9f64fSJason Wang }
31564b9f64fSJason Wang 
vp_vdpa_get_vq_ready(struct vdpa_device * vdpa,u16 qid)31664b9f64fSJason Wang static bool vp_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 qid)
31764b9f64fSJason Wang {
31864b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
31964b9f64fSJason Wang 
32064b9f64fSJason Wang 	return vp_modern_get_queue_enable(mdev, qid);
32164b9f64fSJason Wang }
32264b9f64fSJason Wang 
vp_vdpa_set_vq_num(struct vdpa_device * vdpa,u16 qid,u32 num)32364b9f64fSJason Wang static void vp_vdpa_set_vq_num(struct vdpa_device *vdpa, u16 qid,
32464b9f64fSJason Wang 			       u32 num)
32564b9f64fSJason Wang {
32664b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
32764b9f64fSJason Wang 
32864b9f64fSJason Wang 	vp_modern_set_queue_size(mdev, qid, num);
32964b9f64fSJason Wang }
33064b9f64fSJason Wang 
vp_vdpa_set_vq_address(struct vdpa_device * vdpa,u16 qid,u64 desc_area,u64 driver_area,u64 device_area)33164b9f64fSJason Wang static int vp_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 qid,
33264b9f64fSJason Wang 				  u64 desc_area, u64 driver_area,
33364b9f64fSJason Wang 				  u64 device_area)
33464b9f64fSJason Wang {
33564b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
33664b9f64fSJason Wang 
33764b9f64fSJason Wang 	vp_modern_queue_address(mdev, qid, desc_area,
33864b9f64fSJason Wang 				driver_area, device_area);
33964b9f64fSJason Wang 
34064b9f64fSJason Wang 	return 0;
34164b9f64fSJason Wang }
34264b9f64fSJason Wang 
vp_vdpa_kick_vq(struct vdpa_device * vdpa,u16 qid)34364b9f64fSJason Wang static void vp_vdpa_kick_vq(struct vdpa_device *vdpa, u16 qid)
34464b9f64fSJason Wang {
34564b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
34664b9f64fSJason Wang 
34764b9f64fSJason Wang 	vp_iowrite16(qid, vp_vdpa->vring[qid].notify);
34864b9f64fSJason Wang }
34964b9f64fSJason Wang 
vp_vdpa_get_generation(struct vdpa_device * vdpa)35064b9f64fSJason Wang static u32 vp_vdpa_get_generation(struct vdpa_device *vdpa)
35164b9f64fSJason Wang {
35264b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
35364b9f64fSJason Wang 
35464b9f64fSJason Wang 	return vp_modern_generation(mdev);
35564b9f64fSJason Wang }
35664b9f64fSJason Wang 
vp_vdpa_get_device_id(struct vdpa_device * vdpa)35764b9f64fSJason Wang static u32 vp_vdpa_get_device_id(struct vdpa_device *vdpa)
35864b9f64fSJason Wang {
35964b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
36064b9f64fSJason Wang 
36164b9f64fSJason Wang 	return mdev->id.device;
36264b9f64fSJason Wang }
36364b9f64fSJason Wang 
vp_vdpa_get_vendor_id(struct vdpa_device * vdpa)36464b9f64fSJason Wang static u32 vp_vdpa_get_vendor_id(struct vdpa_device *vdpa)
36564b9f64fSJason Wang {
36664b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
36764b9f64fSJason Wang 
36864b9f64fSJason Wang 	return mdev->id.vendor;
36964b9f64fSJason Wang }
37064b9f64fSJason Wang 
vp_vdpa_get_vq_align(struct vdpa_device * vdpa)37164b9f64fSJason Wang static u32 vp_vdpa_get_vq_align(struct vdpa_device *vdpa)
37264b9f64fSJason Wang {
37364b9f64fSJason Wang 	return PAGE_SIZE;
37464b9f64fSJason Wang }
37564b9f64fSJason Wang 
vp_vdpa_get_config_size(struct vdpa_device * vdpa)376442706f9SStefano Garzarella static size_t vp_vdpa_get_config_size(struct vdpa_device *vdpa)
377442706f9SStefano Garzarella {
378442706f9SStefano Garzarella 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
379442706f9SStefano Garzarella 
380442706f9SStefano Garzarella 	return mdev->device_len;
381442706f9SStefano Garzarella }
382442706f9SStefano Garzarella 
vp_vdpa_get_config(struct vdpa_device * vdpa,unsigned int offset,void * buf,unsigned int len)38364b9f64fSJason Wang static void vp_vdpa_get_config(struct vdpa_device *vdpa,
38464b9f64fSJason Wang 			       unsigned int offset,
38564b9f64fSJason Wang 			       void *buf, unsigned int len)
38664b9f64fSJason Wang {
38764b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
388ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
38964b9f64fSJason Wang 	u8 old, new;
39064b9f64fSJason Wang 	u8 *p;
39164b9f64fSJason Wang 	int i;
39264b9f64fSJason Wang 
39364b9f64fSJason Wang 	do {
39464b9f64fSJason Wang 		old = vp_ioread8(&mdev->common->config_generation);
39564b9f64fSJason Wang 		p = buf;
39664b9f64fSJason Wang 		for (i = 0; i < len; i++)
39764b9f64fSJason Wang 			*p++ = vp_ioread8(mdev->device + offset + i);
39864b9f64fSJason Wang 
39964b9f64fSJason Wang 		new = vp_ioread8(&mdev->common->config_generation);
40064b9f64fSJason Wang 	} while (old != new);
40164b9f64fSJason Wang }
40264b9f64fSJason Wang 
vp_vdpa_set_config(struct vdpa_device * vdpa,unsigned int offset,const void * buf,unsigned int len)40364b9f64fSJason Wang static void vp_vdpa_set_config(struct vdpa_device *vdpa,
40464b9f64fSJason Wang 			       unsigned int offset, const void *buf,
40564b9f64fSJason Wang 			       unsigned int len)
40664b9f64fSJason Wang {
40764b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
408ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
40964b9f64fSJason Wang 	const u8 *p = buf;
41064b9f64fSJason Wang 	int i;
41164b9f64fSJason Wang 
41264b9f64fSJason Wang 	for (i = 0; i < len; i++)
41364b9f64fSJason Wang 		vp_iowrite8(*p++, mdev->device + offset + i);
41464b9f64fSJason Wang }
41564b9f64fSJason Wang 
vp_vdpa_set_config_cb(struct vdpa_device * vdpa,struct vdpa_callback * cb)41664b9f64fSJason Wang static void vp_vdpa_set_config_cb(struct vdpa_device *vdpa,
41764b9f64fSJason Wang 				  struct vdpa_callback *cb)
41864b9f64fSJason Wang {
41964b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
42064b9f64fSJason Wang 
42164b9f64fSJason Wang 	vp_vdpa->config_cb = *cb;
42264b9f64fSJason Wang }
42364b9f64fSJason Wang 
424526cb858SJason Wang static struct vdpa_notification_area
vp_vdpa_get_vq_notification(struct vdpa_device * vdpa,u16 qid)425526cb858SJason Wang vp_vdpa_get_vq_notification(struct vdpa_device *vdpa, u16 qid)
426526cb858SJason Wang {
427526cb858SJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
428ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
429526cb858SJason Wang 	struct vdpa_notification_area notify;
430526cb858SJason Wang 
431526cb858SJason Wang 	notify.addr = vp_vdpa->vring[qid].notify_pa;
432526cb858SJason Wang 	notify.size = mdev->notify_offset_multiplier;
433526cb858SJason Wang 
434526cb858SJason Wang 	return notify;
435526cb858SJason Wang }
436526cb858SJason Wang 
43764b9f64fSJason Wang static const struct vdpa_config_ops vp_vdpa_ops = {
438a64917bcSEli Cohen 	.get_device_features = vp_vdpa_get_device_features,
439a64917bcSEli Cohen 	.set_driver_features = vp_vdpa_set_driver_features,
440a64917bcSEli Cohen 	.get_driver_features = vp_vdpa_get_driver_features,
44164b9f64fSJason Wang 	.get_status	= vp_vdpa_get_status,
44264b9f64fSJason Wang 	.set_status	= vp_vdpa_set_status,
4430686082dSXie Yongji 	.reset		= vp_vdpa_reset,
44464b9f64fSJason Wang 	.get_vq_num_max	= vp_vdpa_get_vq_num_max,
44564b9f64fSJason Wang 	.get_vq_state	= vp_vdpa_get_vq_state,
446526cb858SJason Wang 	.get_vq_notification = vp_vdpa_get_vq_notification,
44764b9f64fSJason Wang 	.set_vq_state	= vp_vdpa_set_vq_state,
44864b9f64fSJason Wang 	.set_vq_cb	= vp_vdpa_set_vq_cb,
44964b9f64fSJason Wang 	.set_vq_ready	= vp_vdpa_set_vq_ready,
45064b9f64fSJason Wang 	.get_vq_ready	= vp_vdpa_get_vq_ready,
45164b9f64fSJason Wang 	.set_vq_num	= vp_vdpa_set_vq_num,
45264b9f64fSJason Wang 	.set_vq_address	= vp_vdpa_set_vq_address,
45364b9f64fSJason Wang 	.kick_vq	= vp_vdpa_kick_vq,
45464b9f64fSJason Wang 	.get_generation	= vp_vdpa_get_generation,
45564b9f64fSJason Wang 	.get_device_id	= vp_vdpa_get_device_id,
45664b9f64fSJason Wang 	.get_vendor_id	= vp_vdpa_get_vendor_id,
45764b9f64fSJason Wang 	.get_vq_align	= vp_vdpa_get_vq_align,
458442706f9SStefano Garzarella 	.get_config_size = vp_vdpa_get_config_size,
45964b9f64fSJason Wang 	.get_config	= vp_vdpa_get_config,
46064b9f64fSJason Wang 	.set_config	= vp_vdpa_set_config,
46164b9f64fSJason Wang 	.set_config_cb  = vp_vdpa_set_config_cb,
4625bbfea1eSWu Zongyong 	.get_vq_irq	= vp_vdpa_get_vq_irq,
46364b9f64fSJason Wang };
46464b9f64fSJason Wang 
vp_vdpa_free_irq_vectors(void * data)46564b9f64fSJason Wang static void vp_vdpa_free_irq_vectors(void *data)
46664b9f64fSJason Wang {
46764b9f64fSJason Wang 	pci_free_irq_vectors(data);
46864b9f64fSJason Wang }
46964b9f64fSJason Wang 
vp_vdpa_dev_add(struct vdpa_mgmt_dev * v_mdev,const char * name,const struct vdpa_dev_set_config * add_config)470ffbda8e9SCindy Lu static int vp_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
471ffbda8e9SCindy Lu 			   const struct vdpa_dev_set_config *add_config)
47264b9f64fSJason Wang {
473ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev =
474ffbda8e9SCindy Lu 		container_of(v_mdev, struct vp_vdpa_mgmtdev, mgtdev);
475ffbda8e9SCindy Lu 
476ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_mgtdev->mdev;
477ffbda8e9SCindy Lu 	struct pci_dev *pdev = mdev->pci_dev;
47864b9f64fSJason Wang 	struct device *dev = &pdev->dev;
479ffbda8e9SCindy Lu 	struct vp_vdpa *vp_vdpa = NULL;
480c1ca352dSJason Wang 	u64 device_features;
48164b9f64fSJason Wang 	int ret, i;
48264b9f64fSJason Wang 
48364b9f64fSJason Wang 	vp_vdpa = vdpa_alloc_device(struct vp_vdpa, vdpa,
484ffbda8e9SCindy Lu 				    dev, &vp_vdpa_ops, 1, 1, name, false);
485ffbda8e9SCindy Lu 
4869632e78eSXie Yongji 	if (IS_ERR(vp_vdpa)) {
48764b9f64fSJason Wang 		dev_err(dev, "vp_vdpa: Failed to allocate vDPA structure\n");
4889632e78eSXie Yongji 		return PTR_ERR(vp_vdpa);
48964b9f64fSJason Wang 	}
49064b9f64fSJason Wang 
491ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->vp_vdpa = vp_vdpa;
49264b9f64fSJason Wang 
49364b9f64fSJason Wang 	vp_vdpa->vdpa.dma_dev = &pdev->dev;
49464b9f64fSJason Wang 	vp_vdpa->queues = vp_modern_get_num_queues(mdev);
495ffbda8e9SCindy Lu 	vp_vdpa->mdev = mdev;
49664b9f64fSJason Wang 
497c1ca352dSJason Wang 	device_features = vp_modern_get_features(mdev);
498c1ca352dSJason Wang 	if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
499c1ca352dSJason Wang 		if (add_config->device_features & ~device_features) {
500c1ca352dSJason Wang 			ret = -EINVAL;
501c1ca352dSJason Wang 			dev_err(&pdev->dev, "Try to provision features "
502c1ca352dSJason Wang 				"that are not supported by the device: "
503c1ca352dSJason Wang 				"device_features 0x%llx provisioned 0x%llx\n",
504c1ca352dSJason Wang 				device_features, add_config->device_features);
505c1ca352dSJason Wang 			goto err;
506c1ca352dSJason Wang 		}
507c1ca352dSJason Wang 		device_features = add_config->device_features;
508c1ca352dSJason Wang 	}
509c1ca352dSJason Wang 	vp_vdpa->device_features = device_features;
510c1ca352dSJason Wang 
51164b9f64fSJason Wang 	ret = devm_add_action_or_reset(dev, vp_vdpa_free_irq_vectors, pdev);
51264b9f64fSJason Wang 	if (ret) {
51364b9f64fSJason Wang 		dev_err(&pdev->dev,
51464b9f64fSJason Wang 			"Failed for adding devres for freeing irq vectors\n");
51564b9f64fSJason Wang 		goto err;
51664b9f64fSJason Wang 	}
51764b9f64fSJason Wang 
51864b9f64fSJason Wang 	vp_vdpa->vring = devm_kcalloc(&pdev->dev, vp_vdpa->queues,
51964b9f64fSJason Wang 				      sizeof(*vp_vdpa->vring),
52064b9f64fSJason Wang 				      GFP_KERNEL);
52164b9f64fSJason Wang 	if (!vp_vdpa->vring) {
52264b9f64fSJason Wang 		ret = -ENOMEM;
52364b9f64fSJason Wang 		dev_err(&pdev->dev, "Fail to allocate virtqueues\n");
52464b9f64fSJason Wang 		goto err;
52564b9f64fSJason Wang 	}
52664b9f64fSJason Wang 
52764b9f64fSJason Wang 	for (i = 0; i < vp_vdpa->queues; i++) {
52864b9f64fSJason Wang 		vp_vdpa->vring[i].irq = VIRTIO_MSI_NO_VECTOR;
5299e311bcaSJason Wang 		vp_vdpa->vring[i].notify =
530526cb858SJason Wang 			vp_modern_map_vq_notify(mdev, i,
531526cb858SJason Wang 						&vp_vdpa->vring[i].notify_pa);
53211d8ffedSJason Wang 		if (!vp_vdpa->vring[i].notify) {
53394e48d6aSJason Wang 			ret = -EINVAL;
53411d8ffedSJason Wang 			dev_warn(&pdev->dev, "Fail to map vq notify %d\n", i);
53511d8ffedSJason Wang 			goto err;
53611d8ffedSJason Wang 		}
53764b9f64fSJason Wang 	}
53864b9f64fSJason Wang 	vp_vdpa->config_irq = VIRTIO_MSI_NO_VECTOR;
53964b9f64fSJason Wang 
540ffbda8e9SCindy Lu 	vp_vdpa->vdpa.mdev = &vp_vdpa_mgtdev->mgtdev;
541ffbda8e9SCindy Lu 	ret = _vdpa_register_device(&vp_vdpa->vdpa, vp_vdpa->queues);
54264b9f64fSJason Wang 	if (ret) {
54364b9f64fSJason Wang 		dev_err(&pdev->dev, "Failed to register to vdpa bus\n");
54464b9f64fSJason Wang 		goto err;
54564b9f64fSJason Wang 	}
54664b9f64fSJason Wang 
54764b9f64fSJason Wang 	return 0;
54864b9f64fSJason Wang 
54964b9f64fSJason Wang err:
55064b9f64fSJason Wang 	put_device(&vp_vdpa->vdpa.dev);
55164b9f64fSJason Wang 	return ret;
55264b9f64fSJason Wang }
55364b9f64fSJason Wang 
vp_vdpa_dev_del(struct vdpa_mgmt_dev * v_mdev,struct vdpa_device * dev)554ffbda8e9SCindy Lu static void vp_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev,
555ffbda8e9SCindy Lu 			    struct vdpa_device *dev)
556ffbda8e9SCindy Lu {
557ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev =
558ffbda8e9SCindy Lu 		container_of(v_mdev, struct vp_vdpa_mgmtdev, mgtdev);
559ffbda8e9SCindy Lu 
560ffbda8e9SCindy Lu 	struct vp_vdpa *vp_vdpa = vp_vdpa_mgtdev->vp_vdpa;
561ffbda8e9SCindy Lu 
562ffbda8e9SCindy Lu 	_vdpa_unregister_device(&vp_vdpa->vdpa);
563ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->vp_vdpa = NULL;
564ffbda8e9SCindy Lu }
565ffbda8e9SCindy Lu 
566ffbda8e9SCindy Lu static const struct vdpa_mgmtdev_ops vp_vdpa_mdev_ops = {
567ffbda8e9SCindy Lu 	.dev_add = vp_vdpa_dev_add,
568ffbda8e9SCindy Lu 	.dev_del = vp_vdpa_dev_del,
569ffbda8e9SCindy Lu };
570ffbda8e9SCindy Lu 
vp_vdpa_probe(struct pci_dev * pdev,const struct pci_device_id * id)571ffbda8e9SCindy Lu static int vp_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id)
572ffbda8e9SCindy Lu {
573ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev = NULL;
574ffbda8e9SCindy Lu 	struct vdpa_mgmt_dev *mgtdev;
575ffbda8e9SCindy Lu 	struct device *dev = &pdev->dev;
576ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = NULL;
577ffbda8e9SCindy Lu 	struct virtio_device_id *mdev_id = NULL;
578ffbda8e9SCindy Lu 	int err;
579ffbda8e9SCindy Lu 
580ffbda8e9SCindy Lu 	vp_vdpa_mgtdev = kzalloc(sizeof(*vp_vdpa_mgtdev), GFP_KERNEL);
581ffbda8e9SCindy Lu 	if (!vp_vdpa_mgtdev)
582ffbda8e9SCindy Lu 		return -ENOMEM;
583ffbda8e9SCindy Lu 
584ffbda8e9SCindy Lu 	mgtdev = &vp_vdpa_mgtdev->mgtdev;
585ffbda8e9SCindy Lu 	mgtdev->ops = &vp_vdpa_mdev_ops;
586ffbda8e9SCindy Lu 	mgtdev->device = dev;
587ffbda8e9SCindy Lu 
588ffbda8e9SCindy Lu 	mdev = kzalloc(sizeof(struct virtio_pci_modern_device), GFP_KERNEL);
589ffbda8e9SCindy Lu 	if (!mdev) {
590ffbda8e9SCindy Lu 		err = -ENOMEM;
591ffbda8e9SCindy Lu 		goto mdev_err;
592ffbda8e9SCindy Lu 	}
593ffbda8e9SCindy Lu 
594ffbda8e9SCindy Lu 	mdev_id = kzalloc(sizeof(struct virtio_device_id), GFP_KERNEL);
595ffbda8e9SCindy Lu 	if (!mdev_id) {
596ffbda8e9SCindy Lu 		err = -ENOMEM;
597ffbda8e9SCindy Lu 		goto mdev_id_err;
598ffbda8e9SCindy Lu 	}
599ffbda8e9SCindy Lu 
600ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->mdev = mdev;
601ffbda8e9SCindy Lu 	mdev->pci_dev = pdev;
602ffbda8e9SCindy Lu 
603ffbda8e9SCindy Lu 	err = pcim_enable_device(pdev);
604ffbda8e9SCindy Lu 	if (err) {
605ffbda8e9SCindy Lu 		goto probe_err;
606ffbda8e9SCindy Lu 	}
607ffbda8e9SCindy Lu 
608ffbda8e9SCindy Lu 	err = vp_modern_probe(mdev);
609ffbda8e9SCindy Lu 	if (err) {
610ffbda8e9SCindy Lu 		dev_err(&pdev->dev, "Failed to probe modern PCI device\n");
611ffbda8e9SCindy Lu 		goto probe_err;
612ffbda8e9SCindy Lu 	}
613ffbda8e9SCindy Lu 
614ffbda8e9SCindy Lu 	mdev_id->device = mdev->id.device;
615ffbda8e9SCindy Lu 	mdev_id->vendor = mdev->id.vendor;
616ffbda8e9SCindy Lu 	mgtdev->id_table = mdev_id;
617ffbda8e9SCindy Lu 	mgtdev->max_supported_vqs = vp_modern_get_num_queues(mdev);
618ffbda8e9SCindy Lu 	mgtdev->supported_features = vp_modern_get_features(mdev);
619c1ca352dSJason Wang 	mgtdev->config_attr_mask = (1 << VDPA_ATTR_DEV_FEATURES);
620ffbda8e9SCindy Lu 	pci_set_master(pdev);
621ffbda8e9SCindy Lu 	pci_set_drvdata(pdev, vp_vdpa_mgtdev);
622ffbda8e9SCindy Lu 
623ffbda8e9SCindy Lu 	err = vdpa_mgmtdev_register(mgtdev);
624ffbda8e9SCindy Lu 	if (err) {
625ffbda8e9SCindy Lu 		dev_err(&pdev->dev, "Failed to register vdpa mgmtdev device\n");
626ffbda8e9SCindy Lu 		goto register_err;
627ffbda8e9SCindy Lu 	}
628ffbda8e9SCindy Lu 
629ffbda8e9SCindy Lu 	return 0;
630ffbda8e9SCindy Lu 
631ffbda8e9SCindy Lu register_err:
632ffbda8e9SCindy Lu 	vp_modern_remove(vp_vdpa_mgtdev->mdev);
633ffbda8e9SCindy Lu probe_err:
634ffbda8e9SCindy Lu 	kfree(mdev_id);
635ffbda8e9SCindy Lu mdev_id_err:
636ffbda8e9SCindy Lu 	kfree(mdev);
637ffbda8e9SCindy Lu mdev_err:
638ffbda8e9SCindy Lu 	kfree(vp_vdpa_mgtdev);
639ffbda8e9SCindy Lu 	return err;
640ffbda8e9SCindy Lu }
641ffbda8e9SCindy Lu 
vp_vdpa_remove(struct pci_dev * pdev)64264b9f64fSJason Wang static void vp_vdpa_remove(struct pci_dev *pdev)
64364b9f64fSJason Wang {
644ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev = pci_get_drvdata(pdev);
645ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = NULL;
64664b9f64fSJason Wang 
647ffbda8e9SCindy Lu 	mdev = vp_vdpa_mgtdev->mdev;
648ffbda8e9SCindy Lu 	vdpa_mgmtdev_unregister(&vp_vdpa_mgtdev->mgtdev);
649*aed8efddSCindy Lu 	vp_modern_remove(mdev);
650ed843d6eSRong Wang 	kfree(vp_vdpa_mgtdev->mgtdev.id_table);
651ffbda8e9SCindy Lu 	kfree(mdev);
652ffbda8e9SCindy Lu 	kfree(vp_vdpa_mgtdev);
65364b9f64fSJason Wang }
65464b9f64fSJason Wang 
65564b9f64fSJason Wang static struct pci_driver vp_vdpa_driver = {
65664b9f64fSJason Wang 	.name		= "vp-vdpa",
65764b9f64fSJason Wang 	.id_table	= NULL, /* only dynamic ids */
65864b9f64fSJason Wang 	.probe		= vp_vdpa_probe,
65964b9f64fSJason Wang 	.remove		= vp_vdpa_remove,
66064b9f64fSJason Wang };
66164b9f64fSJason Wang 
66264b9f64fSJason Wang module_pci_driver(vp_vdpa_driver);
66364b9f64fSJason Wang 
66464b9f64fSJason Wang MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>");
66564b9f64fSJason Wang MODULE_DESCRIPTION("vp-vdpa");
66664b9f64fSJason Wang MODULE_LICENSE("GPL");
66764b9f64fSJason Wang MODULE_VERSION("1");
668