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