xref: /linux/drivers/vdpa/virtio_pci/vp_vdpa.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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;
163*4d685629SYuxue Liu 	int vectors = 1;
164*4d685629SYuxue Liu 	int msix_vec = 0;
165*4d685629SYuxue Liu 
166*4d685629SYuxue Liu 	for (i = 0; i < queues; i++) {
167*4d685629SYuxue Liu 		if (vp_vdpa->vring[i].cb.callback)
168*4d685629SYuxue Liu 			vectors++;
169*4d685629SYuxue Liu 	}
17064b9f64fSJason Wang 
17164b9f64fSJason Wang 	ret = pci_alloc_irq_vectors(pdev, vectors, vectors, PCI_IRQ_MSIX);
17264b9f64fSJason Wang 	if (ret != vectors) {
17364b9f64fSJason Wang 		dev_err(&pdev->dev,
17464b9f64fSJason Wang 			"vp_vdpa: fail to allocate irq vectors want %d but %d\n",
17564b9f64fSJason Wang 			vectors, ret);
17664b9f64fSJason Wang 		return ret;
17764b9f64fSJason Wang 	}
17864b9f64fSJason Wang 
17964b9f64fSJason Wang 	vp_vdpa->vectors = vectors;
18064b9f64fSJason Wang 
18164b9f64fSJason Wang 	for (i = 0; i < queues; i++) {
182*4d685629SYuxue Liu 		if (!vp_vdpa->vring[i].cb.callback)
183*4d685629SYuxue Liu 			continue;
184*4d685629SYuxue Liu 
18564b9f64fSJason Wang 		snprintf(vp_vdpa->vring[i].msix_name, VP_VDPA_NAME_SIZE,
18664b9f64fSJason Wang 			"vp-vdpa[%s]-%d\n", pci_name(pdev), i);
187*4d685629SYuxue Liu 		irq = pci_irq_vector(pdev, msix_vec);
18864b9f64fSJason Wang 		ret = devm_request_irq(&pdev->dev, irq,
18964b9f64fSJason Wang 				       vp_vdpa_vq_handler,
19064b9f64fSJason Wang 				       0, vp_vdpa->vring[i].msix_name,
19164b9f64fSJason Wang 				       &vp_vdpa->vring[i]);
19264b9f64fSJason Wang 		if (ret) {
19364b9f64fSJason Wang 			dev_err(&pdev->dev,
19464b9f64fSJason Wang 				"vp_vdpa: fail to request irq for vq %d\n", i);
19564b9f64fSJason Wang 			goto err;
19664b9f64fSJason Wang 		}
197*4d685629SYuxue Liu 		vp_modern_queue_vector(mdev, i, msix_vec);
19864b9f64fSJason Wang 		vp_vdpa->vring[i].irq = irq;
199*4d685629SYuxue Liu 		msix_vec++;
20064b9f64fSJason Wang 	}
20164b9f64fSJason Wang 
20264b9f64fSJason Wang 	snprintf(vp_vdpa->msix_name, VP_VDPA_NAME_SIZE, "vp-vdpa[%s]-config\n",
20364b9f64fSJason Wang 		 pci_name(pdev));
204*4d685629SYuxue Liu 	irq = pci_irq_vector(pdev, msix_vec);
20564b9f64fSJason Wang 	ret = devm_request_irq(&pdev->dev, irq,	vp_vdpa_config_handler, 0,
20664b9f64fSJason Wang 			       vp_vdpa->msix_name, vp_vdpa);
20764b9f64fSJason Wang 	if (ret) {
20864b9f64fSJason Wang 		dev_err(&pdev->dev,
209*4d685629SYuxue Liu 			"vp_vdpa: fail to request irq for config: %d\n", ret);
21064b9f64fSJason Wang 			goto err;
21164b9f64fSJason Wang 	}
212*4d685629SYuxue Liu 	vp_modern_config_vector(mdev, msix_vec);
21364b9f64fSJason Wang 	vp_vdpa->config_irq = irq;
21464b9f64fSJason Wang 
21564b9f64fSJason Wang 	return 0;
21664b9f64fSJason Wang err:
21764b9f64fSJason Wang 	vp_vdpa_free_irq(vp_vdpa);
21864b9f64fSJason Wang 	return ret;
21964b9f64fSJason Wang }
22064b9f64fSJason Wang 
22164b9f64fSJason Wang static void vp_vdpa_set_status(struct vdpa_device *vdpa, u8 status)
22264b9f64fSJason Wang {
22364b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
224ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
22564b9f64fSJason Wang 	u8 s = vp_vdpa_get_status(vdpa);
22664b9f64fSJason Wang 
22764b9f64fSJason Wang 	if (status & VIRTIO_CONFIG_S_DRIVER_OK &&
22864b9f64fSJason Wang 	    !(s & VIRTIO_CONFIG_S_DRIVER_OK)) {
229f181a373SYuxue Liu 		if (vp_vdpa_request_irq(vp_vdpa)) {
230f181a373SYuxue Liu 			WARN_ON(1);
231f181a373SYuxue Liu 			return;
232f181a373SYuxue Liu 		}
23364b9f64fSJason Wang 	}
23464b9f64fSJason Wang 
23564b9f64fSJason Wang 	vp_modern_set_status(mdev, status);
2360686082dSXie Yongji }
23764b9f64fSJason Wang 
2380686082dSXie Yongji static int vp_vdpa_reset(struct vdpa_device *vdpa)
2390686082dSXie Yongji {
2400686082dSXie Yongji 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
241ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
2420686082dSXie Yongji 	u8 s = vp_vdpa_get_status(vdpa);
2430686082dSXie Yongji 
2440686082dSXie Yongji 	vp_modern_set_status(mdev, 0);
2450686082dSXie Yongji 
2460686082dSXie Yongji 	if (s & VIRTIO_CONFIG_S_DRIVER_OK)
24764b9f64fSJason Wang 		vp_vdpa_free_irq(vp_vdpa);
2480686082dSXie Yongji 
2490686082dSXie Yongji 	return 0;
25064b9f64fSJason Wang }
25164b9f64fSJason Wang 
25264b9f64fSJason Wang static u16 vp_vdpa_get_vq_num_max(struct vdpa_device *vdpa)
25364b9f64fSJason Wang {
25464b9f64fSJason Wang 	return VP_VDPA_QUEUE_MAX;
25564b9f64fSJason Wang }
25664b9f64fSJason Wang 
25764b9f64fSJason Wang static int vp_vdpa_get_vq_state(struct vdpa_device *vdpa, u16 qid,
25864b9f64fSJason Wang 				struct vdpa_vq_state *state)
25964b9f64fSJason Wang {
26064b9f64fSJason Wang 	/* Note that this is not supported by virtio specification, so
26164b9f64fSJason Wang 	 * we return -EOPNOTSUPP here. This means we can't support live
26264b9f64fSJason Wang 	 * migration, vhost device start/stop.
26364b9f64fSJason Wang 	 */
26464b9f64fSJason Wang 	return -EOPNOTSUPP;
26564b9f64fSJason Wang }
26664b9f64fSJason Wang 
2671225c216SJason Wang static int vp_vdpa_set_vq_state_split(struct vdpa_device *vdpa,
2681225c216SJason Wang 				      const struct vdpa_vq_state *state)
2691225c216SJason Wang {
2701225c216SJason Wang 	const struct vdpa_vq_state_split *split = &state->split;
2711225c216SJason Wang 
2721225c216SJason Wang 	if (split->avail_index == 0)
2731225c216SJason Wang 		return 0;
2741225c216SJason Wang 
2751225c216SJason Wang 	return -EOPNOTSUPP;
2761225c216SJason Wang }
2771225c216SJason Wang 
2781225c216SJason Wang static int vp_vdpa_set_vq_state_packed(struct vdpa_device *vdpa,
2791225c216SJason Wang 				       const struct vdpa_vq_state *state)
2801225c216SJason Wang {
2811225c216SJason Wang 	const struct vdpa_vq_state_packed *packed = &state->packed;
2821225c216SJason Wang 
2831225c216SJason Wang 	if (packed->last_avail_counter == 1 &&
2841225c216SJason Wang 	    packed->last_avail_idx == 0 &&
2851225c216SJason Wang 	    packed->last_used_counter == 1 &&
2861225c216SJason Wang 	    packed->last_used_idx == 0)
2871225c216SJason Wang 		return 0;
2881225c216SJason Wang 
2891225c216SJason Wang 	return -EOPNOTSUPP;
2901225c216SJason Wang }
2911225c216SJason Wang 
29264b9f64fSJason Wang static int vp_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 qid,
29364b9f64fSJason Wang 				const struct vdpa_vq_state *state)
29464b9f64fSJason Wang {
2951225c216SJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
2961225c216SJason Wang 
2971225c216SJason Wang 	/* Note that this is not supported by virtio specification.
2981225c216SJason Wang 	 * But if the state is by chance equal to the device initial
2991225c216SJason Wang 	 * state, we can let it go.
30064b9f64fSJason Wang 	 */
3011225c216SJason Wang 	if ((vp_modern_get_status(mdev) & VIRTIO_CONFIG_S_FEATURES_OK) &&
3021225c216SJason Wang 	    !vp_modern_get_queue_enable(mdev, qid)) {
3031225c216SJason Wang 		if (vp_modern_get_driver_features(mdev) &
3041225c216SJason Wang 		    BIT_ULL(VIRTIO_F_RING_PACKED))
3051225c216SJason Wang 			return vp_vdpa_set_vq_state_packed(vdpa, state);
3061225c216SJason Wang 		else
3071225c216SJason Wang 			return vp_vdpa_set_vq_state_split(vdpa,	state);
3081225c216SJason Wang 	}
3091225c216SJason Wang 
31064b9f64fSJason Wang 	return -EOPNOTSUPP;
31164b9f64fSJason Wang }
31264b9f64fSJason Wang 
31364b9f64fSJason Wang static void vp_vdpa_set_vq_cb(struct vdpa_device *vdpa, u16 qid,
31464b9f64fSJason Wang 			      struct vdpa_callback *cb)
31564b9f64fSJason Wang {
31664b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
31764b9f64fSJason Wang 
31864b9f64fSJason Wang 	vp_vdpa->vring[qid].cb = *cb;
31964b9f64fSJason Wang }
32064b9f64fSJason Wang 
32164b9f64fSJason Wang static void vp_vdpa_set_vq_ready(struct vdpa_device *vdpa,
32264b9f64fSJason Wang 				 u16 qid, bool ready)
32364b9f64fSJason Wang {
32464b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
32564b9f64fSJason Wang 
32664b9f64fSJason Wang 	vp_modern_set_queue_enable(mdev, qid, ready);
32764b9f64fSJason Wang }
32864b9f64fSJason Wang 
32964b9f64fSJason Wang static bool vp_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 qid)
33064b9f64fSJason Wang {
33164b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
33264b9f64fSJason Wang 
33364b9f64fSJason Wang 	return vp_modern_get_queue_enable(mdev, qid);
33464b9f64fSJason Wang }
33564b9f64fSJason Wang 
33664b9f64fSJason Wang static void vp_vdpa_set_vq_num(struct vdpa_device *vdpa, u16 qid,
33764b9f64fSJason Wang 			       u32 num)
33864b9f64fSJason Wang {
33964b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
34064b9f64fSJason Wang 
34164b9f64fSJason Wang 	vp_modern_set_queue_size(mdev, qid, num);
34264b9f64fSJason Wang }
34364b9f64fSJason Wang 
344a97f9c8fSZhu Lingshan static u16 vp_vdpa_get_vq_size(struct vdpa_device *vdpa, u16 qid)
345a97f9c8fSZhu Lingshan {
346a97f9c8fSZhu Lingshan 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
347a97f9c8fSZhu Lingshan 
348a97f9c8fSZhu Lingshan 	return vp_modern_get_queue_size(mdev, qid);
349a97f9c8fSZhu Lingshan }
350a97f9c8fSZhu Lingshan 
35164b9f64fSJason Wang static int vp_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 qid,
35264b9f64fSJason Wang 				  u64 desc_area, u64 driver_area,
35364b9f64fSJason Wang 				  u64 device_area)
35464b9f64fSJason Wang {
35564b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
35664b9f64fSJason Wang 
35764b9f64fSJason Wang 	vp_modern_queue_address(mdev, qid, desc_area,
35864b9f64fSJason Wang 				driver_area, device_area);
35964b9f64fSJason Wang 
36064b9f64fSJason Wang 	return 0;
36164b9f64fSJason Wang }
36264b9f64fSJason Wang 
36364b9f64fSJason Wang static void vp_vdpa_kick_vq(struct vdpa_device *vdpa, u16 qid)
36464b9f64fSJason Wang {
36564b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
36664b9f64fSJason Wang 
36764b9f64fSJason Wang 	vp_iowrite16(qid, vp_vdpa->vring[qid].notify);
36864b9f64fSJason Wang }
36964b9f64fSJason Wang 
37064b9f64fSJason Wang static u32 vp_vdpa_get_generation(struct vdpa_device *vdpa)
37164b9f64fSJason Wang {
37264b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
37364b9f64fSJason Wang 
37464b9f64fSJason Wang 	return vp_modern_generation(mdev);
37564b9f64fSJason Wang }
37664b9f64fSJason Wang 
37764b9f64fSJason Wang static u32 vp_vdpa_get_device_id(struct vdpa_device *vdpa)
37864b9f64fSJason Wang {
37964b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
38064b9f64fSJason Wang 
38164b9f64fSJason Wang 	return mdev->id.device;
38264b9f64fSJason Wang }
38364b9f64fSJason Wang 
38464b9f64fSJason Wang static u32 vp_vdpa_get_vendor_id(struct vdpa_device *vdpa)
38564b9f64fSJason Wang {
38664b9f64fSJason Wang 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
38764b9f64fSJason Wang 
38864b9f64fSJason Wang 	return mdev->id.vendor;
38964b9f64fSJason Wang }
39064b9f64fSJason Wang 
39164b9f64fSJason Wang static u32 vp_vdpa_get_vq_align(struct vdpa_device *vdpa)
39264b9f64fSJason Wang {
39364b9f64fSJason Wang 	return PAGE_SIZE;
39464b9f64fSJason Wang }
39564b9f64fSJason Wang 
396442706f9SStefano Garzarella static size_t vp_vdpa_get_config_size(struct vdpa_device *vdpa)
397442706f9SStefano Garzarella {
398442706f9SStefano Garzarella 	struct virtio_pci_modern_device *mdev = vdpa_to_mdev(vdpa);
399442706f9SStefano Garzarella 
400442706f9SStefano Garzarella 	return mdev->device_len;
401442706f9SStefano Garzarella }
402442706f9SStefano Garzarella 
40364b9f64fSJason Wang static void vp_vdpa_get_config(struct vdpa_device *vdpa,
40464b9f64fSJason Wang 			       unsigned int offset,
40564b9f64fSJason Wang 			       void *buf, 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 	u8 old, new;
41064b9f64fSJason Wang 	u8 *p;
41164b9f64fSJason Wang 	int i;
41264b9f64fSJason Wang 
41364b9f64fSJason Wang 	do {
41464b9f64fSJason Wang 		old = vp_ioread8(&mdev->common->config_generation);
41564b9f64fSJason Wang 		p = buf;
41664b9f64fSJason Wang 		for (i = 0; i < len; i++)
41764b9f64fSJason Wang 			*p++ = vp_ioread8(mdev->device + offset + i);
41864b9f64fSJason Wang 
41964b9f64fSJason Wang 		new = vp_ioread8(&mdev->common->config_generation);
42064b9f64fSJason Wang 	} while (old != new);
42164b9f64fSJason Wang }
42264b9f64fSJason Wang 
42364b9f64fSJason Wang static void vp_vdpa_set_config(struct vdpa_device *vdpa,
42464b9f64fSJason Wang 			       unsigned int offset, const void *buf,
42564b9f64fSJason Wang 			       unsigned int len)
42664b9f64fSJason Wang {
42764b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
428ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
42964b9f64fSJason Wang 	const u8 *p = buf;
43064b9f64fSJason Wang 	int i;
43164b9f64fSJason Wang 
43264b9f64fSJason Wang 	for (i = 0; i < len; i++)
43364b9f64fSJason Wang 		vp_iowrite8(*p++, mdev->device + offset + i);
43464b9f64fSJason Wang }
43564b9f64fSJason Wang 
43664b9f64fSJason Wang static void vp_vdpa_set_config_cb(struct vdpa_device *vdpa,
43764b9f64fSJason Wang 				  struct vdpa_callback *cb)
43864b9f64fSJason Wang {
43964b9f64fSJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
44064b9f64fSJason Wang 
44164b9f64fSJason Wang 	vp_vdpa->config_cb = *cb;
44264b9f64fSJason Wang }
44364b9f64fSJason Wang 
444526cb858SJason Wang static struct vdpa_notification_area
445526cb858SJason Wang vp_vdpa_get_vq_notification(struct vdpa_device *vdpa, u16 qid)
446526cb858SJason Wang {
447526cb858SJason Wang 	struct vp_vdpa *vp_vdpa = vdpa_to_vp(vdpa);
448ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_to_mdev(vp_vdpa);
449526cb858SJason Wang 	struct vdpa_notification_area notify;
450526cb858SJason Wang 
451526cb858SJason Wang 	notify.addr = vp_vdpa->vring[qid].notify_pa;
452526cb858SJason Wang 	notify.size = mdev->notify_offset_multiplier;
453526cb858SJason Wang 
454526cb858SJason Wang 	return notify;
455526cb858SJason Wang }
456526cb858SJason Wang 
45764b9f64fSJason Wang static const struct vdpa_config_ops vp_vdpa_ops = {
458a64917bcSEli Cohen 	.get_device_features = vp_vdpa_get_device_features,
459a64917bcSEli Cohen 	.set_driver_features = vp_vdpa_set_driver_features,
460a64917bcSEli Cohen 	.get_driver_features = vp_vdpa_get_driver_features,
46164b9f64fSJason Wang 	.get_status	= vp_vdpa_get_status,
46264b9f64fSJason Wang 	.set_status	= vp_vdpa_set_status,
4630686082dSXie Yongji 	.reset		= vp_vdpa_reset,
46464b9f64fSJason Wang 	.get_vq_num_max	= vp_vdpa_get_vq_num_max,
46564b9f64fSJason Wang 	.get_vq_state	= vp_vdpa_get_vq_state,
466526cb858SJason Wang 	.get_vq_notification = vp_vdpa_get_vq_notification,
46764b9f64fSJason Wang 	.set_vq_state	= vp_vdpa_set_vq_state,
46864b9f64fSJason Wang 	.set_vq_cb	= vp_vdpa_set_vq_cb,
46964b9f64fSJason Wang 	.set_vq_ready	= vp_vdpa_set_vq_ready,
47064b9f64fSJason Wang 	.get_vq_ready	= vp_vdpa_get_vq_ready,
47164b9f64fSJason Wang 	.set_vq_num	= vp_vdpa_set_vq_num,
472a97f9c8fSZhu Lingshan 	.get_vq_size	= vp_vdpa_get_vq_size,
47364b9f64fSJason Wang 	.set_vq_address	= vp_vdpa_set_vq_address,
47464b9f64fSJason Wang 	.kick_vq	= vp_vdpa_kick_vq,
47564b9f64fSJason Wang 	.get_generation	= vp_vdpa_get_generation,
47664b9f64fSJason Wang 	.get_device_id	= vp_vdpa_get_device_id,
47764b9f64fSJason Wang 	.get_vendor_id	= vp_vdpa_get_vendor_id,
47864b9f64fSJason Wang 	.get_vq_align	= vp_vdpa_get_vq_align,
479442706f9SStefano Garzarella 	.get_config_size = vp_vdpa_get_config_size,
48064b9f64fSJason Wang 	.get_config	= vp_vdpa_get_config,
48164b9f64fSJason Wang 	.set_config	= vp_vdpa_set_config,
48264b9f64fSJason Wang 	.set_config_cb  = vp_vdpa_set_config_cb,
4835bbfea1eSWu Zongyong 	.get_vq_irq	= vp_vdpa_get_vq_irq,
48464b9f64fSJason Wang };
48564b9f64fSJason Wang 
48664b9f64fSJason Wang static void vp_vdpa_free_irq_vectors(void *data)
48764b9f64fSJason Wang {
48864b9f64fSJason Wang 	pci_free_irq_vectors(data);
48964b9f64fSJason Wang }
49064b9f64fSJason Wang 
491ffbda8e9SCindy Lu static int vp_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
492ffbda8e9SCindy Lu 			   const struct vdpa_dev_set_config *add_config)
49364b9f64fSJason Wang {
494ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev =
495ffbda8e9SCindy Lu 		container_of(v_mdev, struct vp_vdpa_mgmtdev, mgtdev);
496ffbda8e9SCindy Lu 
497ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = vp_vdpa_mgtdev->mdev;
498ffbda8e9SCindy Lu 	struct pci_dev *pdev = mdev->pci_dev;
49964b9f64fSJason Wang 	struct device *dev = &pdev->dev;
500ffbda8e9SCindy Lu 	struct vp_vdpa *vp_vdpa = NULL;
501c1ca352dSJason Wang 	u64 device_features;
50264b9f64fSJason Wang 	int ret, i;
50364b9f64fSJason Wang 
50464b9f64fSJason Wang 	vp_vdpa = vdpa_alloc_device(struct vp_vdpa, vdpa,
505ffbda8e9SCindy Lu 				    dev, &vp_vdpa_ops, 1, 1, name, false);
506ffbda8e9SCindy Lu 
5079632e78eSXie Yongji 	if (IS_ERR(vp_vdpa)) {
50864b9f64fSJason Wang 		dev_err(dev, "vp_vdpa: Failed to allocate vDPA structure\n");
5099632e78eSXie Yongji 		return PTR_ERR(vp_vdpa);
51064b9f64fSJason Wang 	}
51164b9f64fSJason Wang 
512ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->vp_vdpa = vp_vdpa;
51364b9f64fSJason Wang 
51464b9f64fSJason Wang 	vp_vdpa->vdpa.dma_dev = &pdev->dev;
51564b9f64fSJason Wang 	vp_vdpa->queues = vp_modern_get_num_queues(mdev);
516ffbda8e9SCindy Lu 	vp_vdpa->mdev = mdev;
51764b9f64fSJason Wang 
518c1ca352dSJason Wang 	device_features = vp_modern_get_features(mdev);
519c1ca352dSJason Wang 	if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
520c1ca352dSJason Wang 		if (add_config->device_features & ~device_features) {
521c1ca352dSJason Wang 			ret = -EINVAL;
522c1ca352dSJason Wang 			dev_err(&pdev->dev, "Try to provision features "
523c1ca352dSJason Wang 				"that are not supported by the device: "
524c1ca352dSJason Wang 				"device_features 0x%llx provisioned 0x%llx\n",
525c1ca352dSJason Wang 				device_features, add_config->device_features);
526c1ca352dSJason Wang 			goto err;
527c1ca352dSJason Wang 		}
528c1ca352dSJason Wang 		device_features = add_config->device_features;
529c1ca352dSJason Wang 	}
530c1ca352dSJason Wang 	vp_vdpa->device_features = device_features;
531c1ca352dSJason Wang 
53264b9f64fSJason Wang 	ret = devm_add_action_or_reset(dev, vp_vdpa_free_irq_vectors, pdev);
53364b9f64fSJason Wang 	if (ret) {
53464b9f64fSJason Wang 		dev_err(&pdev->dev,
53564b9f64fSJason Wang 			"Failed for adding devres for freeing irq vectors\n");
53664b9f64fSJason Wang 		goto err;
53764b9f64fSJason Wang 	}
53864b9f64fSJason Wang 
53964b9f64fSJason Wang 	vp_vdpa->vring = devm_kcalloc(&pdev->dev, vp_vdpa->queues,
54064b9f64fSJason Wang 				      sizeof(*vp_vdpa->vring),
54164b9f64fSJason Wang 				      GFP_KERNEL);
54264b9f64fSJason Wang 	if (!vp_vdpa->vring) {
54364b9f64fSJason Wang 		ret = -ENOMEM;
54464b9f64fSJason Wang 		dev_err(&pdev->dev, "Fail to allocate virtqueues\n");
54564b9f64fSJason Wang 		goto err;
54664b9f64fSJason Wang 	}
54764b9f64fSJason Wang 
54864b9f64fSJason Wang 	for (i = 0; i < vp_vdpa->queues; i++) {
54964b9f64fSJason Wang 		vp_vdpa->vring[i].irq = VIRTIO_MSI_NO_VECTOR;
5509e311bcaSJason Wang 		vp_vdpa->vring[i].notify =
551526cb858SJason Wang 			vp_modern_map_vq_notify(mdev, i,
552526cb858SJason Wang 						&vp_vdpa->vring[i].notify_pa);
55311d8ffedSJason Wang 		if (!vp_vdpa->vring[i].notify) {
55494e48d6aSJason Wang 			ret = -EINVAL;
55511d8ffedSJason Wang 			dev_warn(&pdev->dev, "Fail to map vq notify %d\n", i);
55611d8ffedSJason Wang 			goto err;
55711d8ffedSJason Wang 		}
55864b9f64fSJason Wang 	}
55964b9f64fSJason Wang 	vp_vdpa->config_irq = VIRTIO_MSI_NO_VECTOR;
56064b9f64fSJason Wang 
561ffbda8e9SCindy Lu 	vp_vdpa->vdpa.mdev = &vp_vdpa_mgtdev->mgtdev;
562ffbda8e9SCindy Lu 	ret = _vdpa_register_device(&vp_vdpa->vdpa, vp_vdpa->queues);
56364b9f64fSJason Wang 	if (ret) {
56464b9f64fSJason Wang 		dev_err(&pdev->dev, "Failed to register to vdpa bus\n");
56564b9f64fSJason Wang 		goto err;
56664b9f64fSJason Wang 	}
56764b9f64fSJason Wang 
56864b9f64fSJason Wang 	return 0;
56964b9f64fSJason Wang 
57064b9f64fSJason Wang err:
57164b9f64fSJason Wang 	put_device(&vp_vdpa->vdpa.dev);
57264b9f64fSJason Wang 	return ret;
57364b9f64fSJason Wang }
57464b9f64fSJason Wang 
575ffbda8e9SCindy Lu static void vp_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev,
576ffbda8e9SCindy Lu 			    struct vdpa_device *dev)
577ffbda8e9SCindy Lu {
578ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev =
579ffbda8e9SCindy Lu 		container_of(v_mdev, struct vp_vdpa_mgmtdev, mgtdev);
580ffbda8e9SCindy Lu 
581ffbda8e9SCindy Lu 	struct vp_vdpa *vp_vdpa = vp_vdpa_mgtdev->vp_vdpa;
582ffbda8e9SCindy Lu 
583ffbda8e9SCindy Lu 	_vdpa_unregister_device(&vp_vdpa->vdpa);
584ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->vp_vdpa = NULL;
585ffbda8e9SCindy Lu }
586ffbda8e9SCindy Lu 
587ffbda8e9SCindy Lu static const struct vdpa_mgmtdev_ops vp_vdpa_mdev_ops = {
588ffbda8e9SCindy Lu 	.dev_add = vp_vdpa_dev_add,
589ffbda8e9SCindy Lu 	.dev_del = vp_vdpa_dev_del,
590ffbda8e9SCindy Lu };
591ffbda8e9SCindy Lu 
592ffbda8e9SCindy Lu static int vp_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id)
593ffbda8e9SCindy Lu {
594ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev = NULL;
595ffbda8e9SCindy Lu 	struct vdpa_mgmt_dev *mgtdev;
596ffbda8e9SCindy Lu 	struct device *dev = &pdev->dev;
597ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = NULL;
598ffbda8e9SCindy Lu 	struct virtio_device_id *mdev_id = NULL;
599ffbda8e9SCindy Lu 	int err;
600ffbda8e9SCindy Lu 
601ffbda8e9SCindy Lu 	vp_vdpa_mgtdev = kzalloc(sizeof(*vp_vdpa_mgtdev), GFP_KERNEL);
602ffbda8e9SCindy Lu 	if (!vp_vdpa_mgtdev)
603ffbda8e9SCindy Lu 		return -ENOMEM;
604ffbda8e9SCindy Lu 
605ffbda8e9SCindy Lu 	mgtdev = &vp_vdpa_mgtdev->mgtdev;
606ffbda8e9SCindy Lu 	mgtdev->ops = &vp_vdpa_mdev_ops;
607ffbda8e9SCindy Lu 	mgtdev->device = dev;
608ffbda8e9SCindy Lu 
609ffbda8e9SCindy Lu 	mdev = kzalloc(sizeof(struct virtio_pci_modern_device), GFP_KERNEL);
610ffbda8e9SCindy Lu 	if (!mdev) {
611ffbda8e9SCindy Lu 		err = -ENOMEM;
612ffbda8e9SCindy Lu 		goto mdev_err;
613ffbda8e9SCindy Lu 	}
614ffbda8e9SCindy Lu 
615ffbda8e9SCindy Lu 	mdev_id = kzalloc(sizeof(struct virtio_device_id), GFP_KERNEL);
616ffbda8e9SCindy Lu 	if (!mdev_id) {
617ffbda8e9SCindy Lu 		err = -ENOMEM;
618ffbda8e9SCindy Lu 		goto mdev_id_err;
619ffbda8e9SCindy Lu 	}
620ffbda8e9SCindy Lu 
621ffbda8e9SCindy Lu 	vp_vdpa_mgtdev->mdev = mdev;
622ffbda8e9SCindy Lu 	mdev->pci_dev = pdev;
623ffbda8e9SCindy Lu 
624ffbda8e9SCindy Lu 	err = pcim_enable_device(pdev);
625ffbda8e9SCindy Lu 	if (err) {
626ffbda8e9SCindy Lu 		goto probe_err;
627ffbda8e9SCindy Lu 	}
628ffbda8e9SCindy Lu 
629ffbda8e9SCindy Lu 	err = vp_modern_probe(mdev);
630ffbda8e9SCindy Lu 	if (err) {
631ffbda8e9SCindy Lu 		dev_err(&pdev->dev, "Failed to probe modern PCI device\n");
632ffbda8e9SCindy Lu 		goto probe_err;
633ffbda8e9SCindy Lu 	}
634ffbda8e9SCindy Lu 
635ffbda8e9SCindy Lu 	mdev_id->device = mdev->id.device;
636ffbda8e9SCindy Lu 	mdev_id->vendor = mdev->id.vendor;
637ffbda8e9SCindy Lu 	mgtdev->id_table = mdev_id;
638ffbda8e9SCindy Lu 	mgtdev->max_supported_vqs = vp_modern_get_num_queues(mdev);
639ffbda8e9SCindy Lu 	mgtdev->supported_features = vp_modern_get_features(mdev);
640c1ca352dSJason Wang 	mgtdev->config_attr_mask = (1 << VDPA_ATTR_DEV_FEATURES);
641ffbda8e9SCindy Lu 	pci_set_master(pdev);
642ffbda8e9SCindy Lu 	pci_set_drvdata(pdev, vp_vdpa_mgtdev);
643ffbda8e9SCindy Lu 
644ffbda8e9SCindy Lu 	err = vdpa_mgmtdev_register(mgtdev);
645ffbda8e9SCindy Lu 	if (err) {
646ffbda8e9SCindy Lu 		dev_err(&pdev->dev, "Failed to register vdpa mgmtdev device\n");
647ffbda8e9SCindy Lu 		goto register_err;
648ffbda8e9SCindy Lu 	}
649ffbda8e9SCindy Lu 
650ffbda8e9SCindy Lu 	return 0;
651ffbda8e9SCindy Lu 
652ffbda8e9SCindy Lu register_err:
653ffbda8e9SCindy Lu 	vp_modern_remove(vp_vdpa_mgtdev->mdev);
654ffbda8e9SCindy Lu probe_err:
655ffbda8e9SCindy Lu 	kfree(mdev_id);
656ffbda8e9SCindy Lu mdev_id_err:
657ffbda8e9SCindy Lu 	kfree(mdev);
658ffbda8e9SCindy Lu mdev_err:
659ffbda8e9SCindy Lu 	kfree(vp_vdpa_mgtdev);
660ffbda8e9SCindy Lu 	return err;
661ffbda8e9SCindy Lu }
662ffbda8e9SCindy Lu 
66364b9f64fSJason Wang static void vp_vdpa_remove(struct pci_dev *pdev)
66464b9f64fSJason Wang {
665ffbda8e9SCindy Lu 	struct vp_vdpa_mgmtdev *vp_vdpa_mgtdev = pci_get_drvdata(pdev);
666ffbda8e9SCindy Lu 	struct virtio_pci_modern_device *mdev = NULL;
66764b9f64fSJason Wang 
668ffbda8e9SCindy Lu 	mdev = vp_vdpa_mgtdev->mdev;
669ffbda8e9SCindy Lu 	vdpa_mgmtdev_unregister(&vp_vdpa_mgtdev->mgtdev);
670aed8efddSCindy Lu 	vp_modern_remove(mdev);
671ed843d6eSRong Wang 	kfree(vp_vdpa_mgtdev->mgtdev.id_table);
672ffbda8e9SCindy Lu 	kfree(mdev);
673ffbda8e9SCindy Lu 	kfree(vp_vdpa_mgtdev);
67464b9f64fSJason Wang }
67564b9f64fSJason Wang 
67664b9f64fSJason Wang static struct pci_driver vp_vdpa_driver = {
67764b9f64fSJason Wang 	.name		= "vp-vdpa",
67864b9f64fSJason Wang 	.id_table	= NULL, /* only dynamic ids */
67964b9f64fSJason Wang 	.probe		= vp_vdpa_probe,
68064b9f64fSJason Wang 	.remove		= vp_vdpa_remove,
68164b9f64fSJason Wang };
68264b9f64fSJason Wang 
68364b9f64fSJason Wang module_pci_driver(vp_vdpa_driver);
68464b9f64fSJason Wang 
68564b9f64fSJason Wang MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>");
68664b9f64fSJason Wang MODULE_DESCRIPTION("vp-vdpa");
68764b9f64fSJason Wang MODULE_LICENSE("GPL");
68864b9f64fSJason Wang MODULE_VERSION("1");
689