162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * VIRTIO based driver for vDPA device
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020, Red Hat. All rights reserved.
662306a36Sopenharmony_ci *     Author: Jason Wang <jasowang@redhat.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/uuid.h>
1662306a36Sopenharmony_ci#include <linux/group_cpus.h>
1762306a36Sopenharmony_ci#include <linux/virtio.h>
1862306a36Sopenharmony_ci#include <linux/vdpa.h>
1962306a36Sopenharmony_ci#include <linux/virtio_config.h>
2062306a36Sopenharmony_ci#include <linux/virtio_ring.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MOD_VERSION  "0.1"
2362306a36Sopenharmony_ci#define MOD_AUTHOR   "Jason Wang <jasowang@redhat.com>"
2462306a36Sopenharmony_ci#define MOD_DESC     "vDPA bus driver for virtio devices"
2562306a36Sopenharmony_ci#define MOD_LICENSE  "GPL v2"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct virtio_vdpa_device {
2862306a36Sopenharmony_ci	struct virtio_device vdev;
2962306a36Sopenharmony_ci	struct vdpa_device *vdpa;
3062306a36Sopenharmony_ci	u64 features;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* The lock to protect virtqueue list */
3362306a36Sopenharmony_ci	spinlock_t lock;
3462306a36Sopenharmony_ci	/* List of virtio_vdpa_vq_info */
3562306a36Sopenharmony_ci	struct list_head virtqueues;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct virtio_vdpa_vq_info {
3962306a36Sopenharmony_ci	/* the actual virtqueue */
4062306a36Sopenharmony_ci	struct virtqueue *vq;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* the list node for the virtqueues list */
4362306a36Sopenharmony_ci	struct list_head node;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic inline struct virtio_vdpa_device *
4762306a36Sopenharmony_cito_virtio_vdpa_device(struct virtio_device *dev)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return container_of(dev, struct virtio_vdpa_device, vdev);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct vdpa_device *vd_get_vdpa(struct virtio_device *vdev)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return to_virtio_vdpa_device(vdev)->vdpa;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void virtio_vdpa_get(struct virtio_device *vdev, unsigned int offset,
5862306a36Sopenharmony_ci			    void *buf, unsigned int len)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	vdpa_get_config(vdpa, offset, buf, len);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void virtio_vdpa_set(struct virtio_device *vdev, unsigned int offset,
6662306a36Sopenharmony_ci			    const void *buf, unsigned int len)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	vdpa_set_config(vdpa, offset, buf, len);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic u32 virtio_vdpa_generation(struct virtio_device *vdev)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
7662306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (ops->get_generation)
7962306a36Sopenharmony_ci		return ops->get_generation(vdpa);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic u8 virtio_vdpa_get_status(struct virtio_device *vdev)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
8762306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return ops->get_status(vdpa);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void virtio_vdpa_set_status(struct virtio_device *vdev, u8 status)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return vdpa_set_status(vdpa, status);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void virtio_vdpa_reset(struct virtio_device *vdev)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	vdpa_reset(vdpa);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic bool virtio_vdpa_notify(struct virtqueue *vq)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vq->vdev);
10962306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	ops->kick_vq(vdpa, vq->index);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return true;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic bool virtio_vdpa_notify_with_data(struct virtqueue *vq)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vq->vdev);
11962306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
12062306a36Sopenharmony_ci	u32 data = vring_notification_data(vq);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ops->kick_vq_with_data(vdpa, data);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return true;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic irqreturn_t virtio_vdpa_config_cb(void *private)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = private;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	virtio_config_changed(&vd_dev->vdev);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return IRQ_HANDLED;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic irqreturn_t virtio_vdpa_virtqueue_cb(void *private)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct virtio_vdpa_vq_info *info = private;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return vring_interrupt(0, info->vq);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic struct virtqueue *
14462306a36Sopenharmony_civirtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index,
14562306a36Sopenharmony_ci		     void (*callback)(struct virtqueue *vq),
14662306a36Sopenharmony_ci		     const char *name, bool ctx)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev);
14962306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
15062306a36Sopenharmony_ci	struct device *dma_dev;
15162306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
15262306a36Sopenharmony_ci	struct virtio_vdpa_vq_info *info;
15362306a36Sopenharmony_ci	bool (*notify)(struct virtqueue *vq) = virtio_vdpa_notify;
15462306a36Sopenharmony_ci	struct vdpa_callback cb;
15562306a36Sopenharmony_ci	struct virtqueue *vq;
15662306a36Sopenharmony_ci	u64 desc_addr, driver_addr, device_addr;
15762306a36Sopenharmony_ci	/* Assume split virtqueue, switch to packed if necessary */
15862306a36Sopenharmony_ci	struct vdpa_vq_state state = {0};
15962306a36Sopenharmony_ci	unsigned long flags;
16062306a36Sopenharmony_ci	u32 align, max_num, min_num = 1;
16162306a36Sopenharmony_ci	bool may_reduce_num = true;
16262306a36Sopenharmony_ci	int err;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!name)
16562306a36Sopenharmony_ci		return NULL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (index >= vdpa->nvqs)
16862306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* We cannot accept VIRTIO_F_NOTIFICATION_DATA without kick_vq_with_data */
17162306a36Sopenharmony_ci	if (__virtio_test_bit(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
17262306a36Sopenharmony_ci		if (ops->kick_vq_with_data)
17362306a36Sopenharmony_ci			notify = virtio_vdpa_notify_with_data;
17462306a36Sopenharmony_ci		else
17562306a36Sopenharmony_ci			__virtio_clear_bit(vdev, VIRTIO_F_NOTIFICATION_DATA);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Queue shouldn't already be set up. */
17962306a36Sopenharmony_ci	if (ops->get_vq_ready(vdpa, index))
18062306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Allocate and fill out our active queue description */
18362306a36Sopenharmony_ci	info = kmalloc(sizeof(*info), GFP_KERNEL);
18462306a36Sopenharmony_ci	if (!info)
18562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	max_num = ops->get_vq_num_max(vdpa);
18862306a36Sopenharmony_ci	if (max_num == 0) {
18962306a36Sopenharmony_ci		err = -ENOENT;
19062306a36Sopenharmony_ci		goto error_new_virtqueue;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (ops->get_vq_num_min)
19462306a36Sopenharmony_ci		min_num = ops->get_vq_num_min(vdpa);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	may_reduce_num = (max_num == min_num) ? false : true;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* Create the vring */
19962306a36Sopenharmony_ci	align = ops->get_vq_align(vdpa);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (ops->get_vq_dma_dev)
20262306a36Sopenharmony_ci		dma_dev = ops->get_vq_dma_dev(vdpa, index);
20362306a36Sopenharmony_ci	else
20462306a36Sopenharmony_ci		dma_dev = vdpa_get_dma_dev(vdpa);
20562306a36Sopenharmony_ci	vq = vring_create_virtqueue_dma(index, max_num, align, vdev,
20662306a36Sopenharmony_ci					true, may_reduce_num, ctx,
20762306a36Sopenharmony_ci					notify, callback, name, dma_dev);
20862306a36Sopenharmony_ci	if (!vq) {
20962306a36Sopenharmony_ci		err = -ENOMEM;
21062306a36Sopenharmony_ci		goto error_new_virtqueue;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	vq->num_max = max_num;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Setup virtqueue callback */
21662306a36Sopenharmony_ci	cb.callback = callback ? virtio_vdpa_virtqueue_cb : NULL;
21762306a36Sopenharmony_ci	cb.private = info;
21862306a36Sopenharmony_ci	cb.trigger = NULL;
21962306a36Sopenharmony_ci	ops->set_vq_cb(vdpa, index, &cb);
22062306a36Sopenharmony_ci	ops->set_vq_num(vdpa, index, virtqueue_get_vring_size(vq));
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	desc_addr = virtqueue_get_desc_addr(vq);
22362306a36Sopenharmony_ci	driver_addr = virtqueue_get_avail_addr(vq);
22462306a36Sopenharmony_ci	device_addr = virtqueue_get_used_addr(vq);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (ops->set_vq_address(vdpa, index,
22762306a36Sopenharmony_ci				desc_addr, driver_addr,
22862306a36Sopenharmony_ci				device_addr)) {
22962306a36Sopenharmony_ci		err = -EINVAL;
23062306a36Sopenharmony_ci		goto err_vq;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* reset virtqueue state index */
23462306a36Sopenharmony_ci	if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) {
23562306a36Sopenharmony_ci		struct vdpa_vq_state_packed *s = &state.packed;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		s->last_avail_counter = 1;
23862306a36Sopenharmony_ci		s->last_avail_idx = 0;
23962306a36Sopenharmony_ci		s->last_used_counter = 1;
24062306a36Sopenharmony_ci		s->last_used_idx = 0;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci	err = ops->set_vq_state(vdpa, index, &state);
24362306a36Sopenharmony_ci	if (err)
24462306a36Sopenharmony_ci		goto err_vq;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ops->set_vq_ready(vdpa, index, 1);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	vq->priv = info;
24962306a36Sopenharmony_ci	info->vq = vq;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	spin_lock_irqsave(&vd_dev->lock, flags);
25262306a36Sopenharmony_ci	list_add(&info->node, &vd_dev->virtqueues);
25362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vd_dev->lock, flags);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return vq;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cierr_vq:
25862306a36Sopenharmony_ci	vring_del_virtqueue(vq);
25962306a36Sopenharmony_cierror_new_virtqueue:
26062306a36Sopenharmony_ci	ops->set_vq_ready(vdpa, index, 0);
26162306a36Sopenharmony_ci	/* VDPA driver should make sure vq is stopeed here */
26262306a36Sopenharmony_ci	WARN_ON(ops->get_vq_ready(vdpa, index));
26362306a36Sopenharmony_ci	kfree(info);
26462306a36Sopenharmony_ci	return ERR_PTR(err);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void virtio_vdpa_del_vq(struct virtqueue *vq)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vq->vdev);
27062306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_dev->vdpa;
27162306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
27262306a36Sopenharmony_ci	struct virtio_vdpa_vq_info *info = vq->priv;
27362306a36Sopenharmony_ci	unsigned int index = vq->index;
27462306a36Sopenharmony_ci	unsigned long flags;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	spin_lock_irqsave(&vd_dev->lock, flags);
27762306a36Sopenharmony_ci	list_del(&info->node);
27862306a36Sopenharmony_ci	spin_unlock_irqrestore(&vd_dev->lock, flags);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Select and deactivate the queue (best effort) */
28162306a36Sopenharmony_ci	ops->set_vq_ready(vdpa, index, 0);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	vring_del_virtqueue(vq);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	kfree(info);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void virtio_vdpa_del_vqs(struct virtio_device *vdev)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct virtqueue *vq, *n;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	list_for_each_entry_safe(vq, n, &vdev->vqs, list)
29362306a36Sopenharmony_ci		virtio_vdpa_del_vq(vq);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	affd->nr_sets = 1;
29962306a36Sopenharmony_ci	affd->set_size[0] = affvecs;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic struct cpumask *
30362306a36Sopenharmony_cicreate_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	unsigned int affvecs = 0, curvec, usedvecs, i;
30662306a36Sopenharmony_ci	struct cpumask *masks = NULL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (nvecs > affd->pre_vectors + affd->post_vectors)
30962306a36Sopenharmony_ci		affvecs = nvecs - affd->pre_vectors - affd->post_vectors;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!affd->calc_sets)
31262306a36Sopenharmony_ci		affd->calc_sets = default_calc_sets;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	affd->calc_sets(affd, affvecs);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!affvecs)
31762306a36Sopenharmony_ci		return NULL;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL);
32062306a36Sopenharmony_ci	if (!masks)
32162306a36Sopenharmony_ci		return NULL;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Fill out vectors at the beginning that don't need affinity */
32462306a36Sopenharmony_ci	for (curvec = 0; curvec < affd->pre_vectors; curvec++)
32562306a36Sopenharmony_ci		cpumask_setall(&masks[curvec]);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
32862306a36Sopenharmony_ci		unsigned int this_vecs = affd->set_size[i];
32962306a36Sopenharmony_ci		int j;
33062306a36Sopenharmony_ci		struct cpumask *result = group_cpus_evenly(this_vecs);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (!result) {
33362306a36Sopenharmony_ci			kfree(masks);
33462306a36Sopenharmony_ci			return NULL;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		for (j = 0; j < this_vecs; j++)
33862306a36Sopenharmony_ci			cpumask_copy(&masks[curvec + j], &result[j]);
33962306a36Sopenharmony_ci		kfree(result);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		curvec += this_vecs;
34262306a36Sopenharmony_ci		usedvecs += this_vecs;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* Fill out vectors at the end that don't need affinity */
34662306a36Sopenharmony_ci	if (usedvecs >= affvecs)
34762306a36Sopenharmony_ci		curvec = affd->pre_vectors + affvecs;
34862306a36Sopenharmony_ci	else
34962306a36Sopenharmony_ci		curvec = affd->pre_vectors + usedvecs;
35062306a36Sopenharmony_ci	for (; curvec < nvecs; curvec++)
35162306a36Sopenharmony_ci		cpumask_setall(&masks[curvec]);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return masks;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
35762306a36Sopenharmony_ci				struct virtqueue *vqs[],
35862306a36Sopenharmony_ci				vq_callback_t *callbacks[],
35962306a36Sopenharmony_ci				const char * const names[],
36062306a36Sopenharmony_ci				const bool *ctx,
36162306a36Sopenharmony_ci				struct irq_affinity *desc)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev);
36462306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
36562306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
36662306a36Sopenharmony_ci	struct irq_affinity default_affd = { 0 };
36762306a36Sopenharmony_ci	struct cpumask *masks;
36862306a36Sopenharmony_ci	struct vdpa_callback cb;
36962306a36Sopenharmony_ci	bool has_affinity = desc && ops->set_vq_affinity;
37062306a36Sopenharmony_ci	int i, err, queue_idx = 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (has_affinity) {
37362306a36Sopenharmony_ci		masks = create_affinity_masks(nvqs, desc ? desc : &default_affd);
37462306a36Sopenharmony_ci		if (!masks)
37562306a36Sopenharmony_ci			return -ENOMEM;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	for (i = 0; i < nvqs; ++i) {
37962306a36Sopenharmony_ci		if (!names[i]) {
38062306a36Sopenharmony_ci			vqs[i] = NULL;
38162306a36Sopenharmony_ci			continue;
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++,
38562306a36Sopenharmony_ci					      callbacks[i], names[i], ctx ?
38662306a36Sopenharmony_ci					      ctx[i] : false);
38762306a36Sopenharmony_ci		if (IS_ERR(vqs[i])) {
38862306a36Sopenharmony_ci			err = PTR_ERR(vqs[i]);
38962306a36Sopenharmony_ci			goto err_setup_vq;
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		if (has_affinity)
39362306a36Sopenharmony_ci			ops->set_vq_affinity(vdpa, i, &masks[i]);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	cb.callback = virtio_vdpa_config_cb;
39762306a36Sopenharmony_ci	cb.private = vd_dev;
39862306a36Sopenharmony_ci	ops->set_config_cb(vdpa, &cb);
39962306a36Sopenharmony_ci	if (has_affinity)
40062306a36Sopenharmony_ci		kfree(masks);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cierr_setup_vq:
40562306a36Sopenharmony_ci	virtio_vdpa_del_vqs(vdev);
40662306a36Sopenharmony_ci	if (has_affinity)
40762306a36Sopenharmony_ci		kfree(masks);
40862306a36Sopenharmony_ci	return err;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic u64 virtio_vdpa_get_features(struct virtio_device *vdev)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
41462306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return ops->get_device_features(vdpa);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int virtio_vdpa_finalize_features(struct virtio_device *vdev)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* Give virtio_ring a chance to accept features. */
42462306a36Sopenharmony_ci	vring_transport_features(vdev);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return vdpa_set_features(vdpa, vdev->features);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic const char *virtio_vdpa_bus_name(struct virtio_device *vdev)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev);
43262306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_dev->vdpa;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return dev_name(&vdpa->dev);
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int virtio_vdpa_set_vq_affinity(struct virtqueue *vq,
43862306a36Sopenharmony_ci				       const struct cpumask *cpu_mask)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vq->vdev);
44162306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_dev->vdpa;
44262306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
44362306a36Sopenharmony_ci	unsigned int index = vq->index;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (ops->set_vq_affinity)
44662306a36Sopenharmony_ci		return ops->set_vq_affinity(vdpa, index, cpu_mask);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic const struct cpumask *
45262306a36Sopenharmony_civirtio_vdpa_get_vq_affinity(struct virtio_device *vdev, int index)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct vdpa_device *vdpa = vd_get_vdpa(vdev);
45562306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (ops->get_vq_affinity)
45862306a36Sopenharmony_ci		return ops->get_vq_affinity(vdpa, index);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return NULL;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic const struct virtio_config_ops virtio_vdpa_config_ops = {
46462306a36Sopenharmony_ci	.get		= virtio_vdpa_get,
46562306a36Sopenharmony_ci	.set		= virtio_vdpa_set,
46662306a36Sopenharmony_ci	.generation	= virtio_vdpa_generation,
46762306a36Sopenharmony_ci	.get_status	= virtio_vdpa_get_status,
46862306a36Sopenharmony_ci	.set_status	= virtio_vdpa_set_status,
46962306a36Sopenharmony_ci	.reset		= virtio_vdpa_reset,
47062306a36Sopenharmony_ci	.find_vqs	= virtio_vdpa_find_vqs,
47162306a36Sopenharmony_ci	.del_vqs	= virtio_vdpa_del_vqs,
47262306a36Sopenharmony_ci	.get_features	= virtio_vdpa_get_features,
47362306a36Sopenharmony_ci	.finalize_features = virtio_vdpa_finalize_features,
47462306a36Sopenharmony_ci	.bus_name	= virtio_vdpa_bus_name,
47562306a36Sopenharmony_ci	.set_vq_affinity = virtio_vdpa_set_vq_affinity,
47662306a36Sopenharmony_ci	.get_vq_affinity = virtio_vdpa_get_vq_affinity,
47762306a36Sopenharmony_ci};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void virtio_vdpa_release_dev(struct device *_d)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct virtio_device *vdev =
48262306a36Sopenharmony_ci	       container_of(_d, struct virtio_device, dev);
48362306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev =
48462306a36Sopenharmony_ci	       container_of(vdev, struct virtio_vdpa_device, vdev);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	kfree(vd_dev);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int virtio_vdpa_probe(struct vdpa_device *vdpa)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdpa->config;
49262306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev, *reg_dev = NULL;
49362306a36Sopenharmony_ci	int ret = -EINVAL;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	vd_dev = kzalloc(sizeof(*vd_dev), GFP_KERNEL);
49662306a36Sopenharmony_ci	if (!vd_dev)
49762306a36Sopenharmony_ci		return -ENOMEM;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	vd_dev->vdev.dev.parent = vdpa_get_dma_dev(vdpa);
50062306a36Sopenharmony_ci	vd_dev->vdev.dev.release = virtio_vdpa_release_dev;
50162306a36Sopenharmony_ci	vd_dev->vdev.config = &virtio_vdpa_config_ops;
50262306a36Sopenharmony_ci	vd_dev->vdpa = vdpa;
50362306a36Sopenharmony_ci	INIT_LIST_HEAD(&vd_dev->virtqueues);
50462306a36Sopenharmony_ci	spin_lock_init(&vd_dev->lock);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	vd_dev->vdev.id.device = ops->get_device_id(vdpa);
50762306a36Sopenharmony_ci	if (vd_dev->vdev.id.device == 0)
50862306a36Sopenharmony_ci		goto err;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	vd_dev->vdev.id.vendor = ops->get_vendor_id(vdpa);
51162306a36Sopenharmony_ci	ret = register_virtio_device(&vd_dev->vdev);
51262306a36Sopenharmony_ci	reg_dev = vd_dev;
51362306a36Sopenharmony_ci	if (ret)
51462306a36Sopenharmony_ci		goto err;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	vdpa_set_drvdata(vdpa, vd_dev);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cierr:
52162306a36Sopenharmony_ci	if (reg_dev)
52262306a36Sopenharmony_ci		put_device(&vd_dev->vdev.dev);
52362306a36Sopenharmony_ci	else
52462306a36Sopenharmony_ci		kfree(vd_dev);
52562306a36Sopenharmony_ci	return ret;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void virtio_vdpa_remove(struct vdpa_device *vdpa)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct virtio_vdpa_device *vd_dev = vdpa_get_drvdata(vdpa);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	unregister_virtio_device(&vd_dev->vdev);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic struct vdpa_driver virtio_vdpa_driver = {
53662306a36Sopenharmony_ci	.driver = {
53762306a36Sopenharmony_ci		.name	= "virtio_vdpa",
53862306a36Sopenharmony_ci	},
53962306a36Sopenharmony_ci	.probe	= virtio_vdpa_probe,
54062306a36Sopenharmony_ci	.remove = virtio_vdpa_remove,
54162306a36Sopenharmony_ci};
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cimodule_vdpa_driver(virtio_vdpa_driver);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ciMODULE_VERSION(MOD_VERSION);
54662306a36Sopenharmony_ciMODULE_LICENSE(MOD_LICENSE);
54762306a36Sopenharmony_ciMODULE_AUTHOR(MOD_AUTHOR);
54862306a36Sopenharmony_ciMODULE_DESCRIPTION(MOD_DESC);
549