Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[linux-2.6-microblaze.git] / drivers / vdpa / vdpa_sim / vdpa_sim.c
index 923f290..b3fcc67 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * VDPA networking device simulator.
+ * VDPA device simulator core.
  *
  * Copyright (c) 2020, Red Hat Inc. All rights reserved.
  *     Author: Jason Wang <jasowang@redhat.com>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/dma-map-ops.h>
-#include <linux/etherdevice.h>
 #include <linux/vringh.h>
 #include <linux/vdpa.h>
-#include <linux/virtio_byteorder.h>
 #include <linux/vhost_iotlb.h>
-#include <uapi/linux/virtio_config.h>
-#include <uapi/linux/virtio_net.h>
+
+#include "vdpa_sim.h"
 
 #define DRV_VERSION  "0.1"
 #define DRV_AUTHOR   "Jason Wang <jasowang@redhat.com>"
-#define DRV_DESC     "vDPA Device Simulator"
+#define DRV_DESC     "vDPA Device Simulator core"
 #define DRV_LICENSE  "GPL v2"
 
 static int batch_mapping = 1;
@@ -36,71 +34,9 @@ module_param(max_iotlb_entries, int, 0444);
 MODULE_PARM_DESC(max_iotlb_entries,
                 "Maximum number of iotlb entries. 0 means unlimited. (default: 2048)");
 
-static char *macaddr;
-module_param(macaddr, charp, 0);
-MODULE_PARM_DESC(macaddr, "Ethernet MAC address");
-
-struct vdpasim_virtqueue {
-       struct vringh vring;
-       struct vringh_kiov iov;
-       unsigned short head;
-       bool ready;
-       u64 desc_addr;
-       u64 device_addr;
-       u64 driver_addr;
-       u32 num;
-       void *private;
-       irqreturn_t (*cb)(void *data);
-};
-
 #define VDPASIM_QUEUE_ALIGN PAGE_SIZE
 #define VDPASIM_QUEUE_MAX 256
-#define VDPASIM_DEVICE_ID 0x1
 #define VDPASIM_VENDOR_ID 0
-#define VDPASIM_VQ_NUM 0x2
-#define VDPASIM_NAME "vdpasim-netdev"
-
-static u64 vdpasim_features = (1ULL << VIRTIO_F_ANY_LAYOUT) |
-                             (1ULL << VIRTIO_F_VERSION_1)  |
-                             (1ULL << VIRTIO_F_ACCESS_PLATFORM) |
-                             (1ULL << VIRTIO_NET_F_MAC);
-
-/* State of each vdpasim device */
-struct vdpasim {
-       struct vdpa_device vdpa;
-       struct vdpasim_virtqueue *vqs;
-       struct work_struct work;
-       /* spinlock to synchronize virtqueue state */
-       spinlock_t lock;
-       struct virtio_net_config config;
-       struct vhost_iotlb *iommu;
-       void *buffer;
-       u32 status;
-       u32 generation;
-       u64 features;
-       int nvqs;
-       /* spinlock to synchronize iommu table */
-       spinlock_t iommu_lock;
-};
-
-/* TODO: cross-endian support */
-static inline bool vdpasim_is_little_endian(struct vdpasim *vdpasim)
-{
-       return virtio_legacy_is_little_endian() ||
-               (vdpasim->features & (1ULL << VIRTIO_F_VERSION_1));
-}
-
-static inline u16 vdpasim16_to_cpu(struct vdpasim *vdpasim, __virtio16 val)
-{
-       return __virtio16_to_cpu(vdpasim_is_little_endian(vdpasim), val);
-}
-
-static inline __virtio16 cpu_to_vdpasim16(struct vdpasim *vdpasim, u16 val)
-{
-       return __cpu_to_virtio16(vdpasim_is_little_endian(vdpasim), val);
-}
-
-static struct vdpasim *vdpasim_dev;
 
 static struct vdpasim *vdpa_to_sim(struct vdpa_device *vdpa)
 {
@@ -114,20 +50,34 @@ static struct vdpasim *dev_to_sim(struct device *dev)
        return vdpa_to_sim(vdpa);
 }
 
+static void vdpasim_vq_notify(struct vringh *vring)
+{
+       struct vdpasim_virtqueue *vq =
+               container_of(vring, struct vdpasim_virtqueue, vring);
+
+       if (!vq->cb)
+               return;
+
+       vq->cb(vq->private);
+}
+
 static void vdpasim_queue_ready(struct vdpasim *vdpasim, unsigned int idx)
 {
        struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx];
 
-       vringh_init_iotlb(&vq->vring, vdpasim_features,
+       vringh_init_iotlb(&vq->vring, vdpasim->dev_attr.supported_features,
                          VDPASIM_QUEUE_MAX, false,
                          (struct vring_desc *)(uintptr_t)vq->desc_addr,
                          (struct vring_avail *)
                          (uintptr_t)vq->driver_addr,
                          (struct vring_used *)
                          (uintptr_t)vq->device_addr);
+
+       vq->vring.notify = vdpasim_vq_notify;
 }
 
-static void vdpasim_vq_reset(struct vdpasim_virtqueue *vq)
+static void vdpasim_vq_reset(struct vdpasim *vdpasim,
+                            struct vdpasim_virtqueue *vq)
 {
        vq->ready = false;
        vq->desc_addr = 0;
@@ -135,16 +85,18 @@ static void vdpasim_vq_reset(struct vdpasim_virtqueue *vq)
        vq->device_addr = 0;
        vq->cb = NULL;
        vq->private = NULL;
-       vringh_init_iotlb(&vq->vring, vdpasim_features, VDPASIM_QUEUE_MAX,
-                         false, NULL, NULL, NULL);
+       vringh_init_iotlb(&vq->vring, vdpasim->dev_attr.supported_features,
+                         VDPASIM_QUEUE_MAX, false, NULL, NULL, NULL);
+
+       vq->vring.notify = NULL;
 }
 
 static void vdpasim_reset(struct vdpasim *vdpasim)
 {
        int i;
 
-       for (i = 0; i < vdpasim->nvqs; i++)
-               vdpasim_vq_reset(&vdpasim->vqs[i]);
+       for (i = 0; i < vdpasim->dev_attr.nvqs; i++)
+               vdpasim_vq_reset(vdpasim, &vdpasim->vqs[i]);
 
        spin_lock(&vdpasim->iommu_lock);
        vhost_iotlb_reset(vdpasim->iommu);
@@ -155,80 +107,6 @@ static void vdpasim_reset(struct vdpasim *vdpasim)
        ++vdpasim->generation;
 }
 
-static void vdpasim_work(struct work_struct *work)
-{
-       struct vdpasim *vdpasim = container_of(work, struct
-                                                vdpasim, work);
-       struct vdpasim_virtqueue *txq = &vdpasim->vqs[1];
-       struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0];
-       ssize_t read, write;
-       size_t total_write;
-       int pkts = 0;
-       int err;
-
-       spin_lock(&vdpasim->lock);
-
-       if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
-               goto out;
-
-       if (!txq->ready || !rxq->ready)
-               goto out;
-
-       while (true) {
-               total_write = 0;
-               err = vringh_getdesc_iotlb(&txq->vring, &txq->iov, NULL,
-                                          &txq->head, GFP_ATOMIC);
-               if (err <= 0)
-                       break;
-
-               err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->iov,
-                                          &rxq->head, GFP_ATOMIC);
-               if (err <= 0) {
-                       vringh_complete_iotlb(&txq->vring, txq->head, 0);
-                       break;
-               }
-
-               while (true) {
-                       read = vringh_iov_pull_iotlb(&txq->vring, &txq->iov,
-                                                    vdpasim->buffer,
-                                                    PAGE_SIZE);
-                       if (read <= 0)
-                               break;
-
-                       write = vringh_iov_push_iotlb(&rxq->vring, &rxq->iov,
-                                                     vdpasim->buffer, read);
-                       if (write <= 0)
-                               break;
-
-                       total_write += write;
-               }
-
-               /* Make sure data is wrote before advancing index */
-               smp_wmb();
-
-               vringh_complete_iotlb(&txq->vring, txq->head, 0);
-               vringh_complete_iotlb(&rxq->vring, rxq->head, total_write);
-
-               /* Make sure used is visible before rasing the interrupt. */
-               smp_wmb();
-
-               local_bh_disable();
-               if (txq->cb)
-                       txq->cb(txq->private);
-               if (rxq->cb)
-                       rxq->cb(rxq->private);
-               local_bh_enable();
-
-               if (++pkts > 4) {
-                       schedule_work(&vdpasim->work);
-                       goto out;
-               }
-       }
-
-out:
-       spin_unlock(&vdpasim->lock);
-}
-
 static int dir_to_perm(enum dma_data_direction dir)
 {
        int perm = -EFAULT;
@@ -344,7 +222,7 @@ static const struct dma_map_ops vdpasim_dma_ops = {
 static const struct vdpa_config_ops vdpasim_config_ops;
 static const struct vdpa_config_ops vdpasim_batch_config_ops;
 
-static struct vdpasim *vdpasim_create(void)
+struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr)
 {
        const struct vdpa_config_ops *ops;
        struct vdpasim *vdpasim;
@@ -356,12 +234,13 @@ static struct vdpasim *vdpasim_create(void)
        else
                ops = &vdpasim_config_ops;
 
-       vdpasim = vdpa_alloc_device(struct vdpasim, vdpa, NULL, ops, VDPASIM_VQ_NUM);
+       vdpasim = vdpa_alloc_device(struct vdpasim, vdpa, NULL, ops,
+                                   dev_attr->nvqs);
        if (!vdpasim)
                goto err_alloc;
 
-       vdpasim->nvqs = VDPASIM_VQ_NUM;
-       INIT_WORK(&vdpasim->work, vdpasim_work);
+       vdpasim->dev_attr = *dev_attr;
+       INIT_WORK(&vdpasim->work, dev_attr->work_fn);
        spin_lock_init(&vdpasim->lock);
        spin_lock_init(&vdpasim->iommu_lock);
 
@@ -371,7 +250,11 @@ static struct vdpasim *vdpasim_create(void)
                goto err_iommu;
        set_dma_ops(dev, &vdpasim_dma_ops);
 
-       vdpasim->vqs = kcalloc(vdpasim->nvqs, sizeof(struct vdpasim_virtqueue),
+       vdpasim->config = kzalloc(dev_attr->config_size, GFP_KERNEL);
+       if (!vdpasim->config)
+               goto err_iommu;
+
+       vdpasim->vqs = kcalloc(dev_attr->nvqs, sizeof(struct vdpasim_virtqueue),
                               GFP_KERNEL);
        if (!vdpasim->vqs)
                goto err_iommu;
@@ -380,27 +263,14 @@ static struct vdpasim *vdpasim_create(void)
        if (!vdpasim->iommu)
                goto err_iommu;
 
-       vdpasim->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       vdpasim->buffer = kvmalloc(dev_attr->buffer_size, GFP_KERNEL);
        if (!vdpasim->buffer)
                goto err_iommu;
 
-       if (macaddr) {
-               mac_pton(macaddr, vdpasim->config.mac);
-               if (!is_valid_ether_addr(vdpasim->config.mac)) {
-                       ret = -EADDRNOTAVAIL;
-                       goto err_iommu;
-               }
-       } else {
-               eth_random_addr(vdpasim->config.mac);
-       }
-
-       for (i = 0; i < vdpasim->nvqs; i++)
+       for (i = 0; i < dev_attr->nvqs; i++)
                vringh_set_iotlb(&vdpasim->vqs[i].vring, vdpasim->iommu);
 
        vdpasim->vdpa.dma_dev = dev;
-       ret = vdpa_register_device(&vdpasim->vdpa);
-       if (ret)
-               goto err_iommu;
 
        return vdpasim;
 
@@ -409,6 +279,7 @@ err_iommu:
 err_alloc:
        return ERR_PTR(ret);
 }
+EXPORT_SYMBOL_GPL(vdpasim_create);
 
 static int vdpasim_set_vq_address(struct vdpa_device *vdpa, u16 idx,
                                  u64 desc_area, u64 driver_area,
@@ -503,28 +374,21 @@ static u32 vdpasim_get_vq_align(struct vdpa_device *vdpa)
 
 static u64 vdpasim_get_features(struct vdpa_device *vdpa)
 {
-       return vdpasim_features;
+       struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
+
+       return vdpasim->dev_attr.supported_features;
 }
 
 static int vdpasim_set_features(struct vdpa_device *vdpa, u64 features)
 {
        struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
-       struct virtio_net_config *config = &vdpasim->config;
 
        /* DMA mapping must be done by driver */
        if (!(features & (1ULL << VIRTIO_F_ACCESS_PLATFORM)))
                return -EINVAL;
 
-       vdpasim->features = features & vdpasim_features;
-
-       /* We generally only know whether guest is using the legacy interface
-        * here, so generally that's the earliest we can set config fields.
-        * Note: We actually require VIRTIO_F_ACCESS_PLATFORM above which
-        * implies VIRTIO_F_VERSION_1, but let's not try to be clever here.
-        */
+       vdpasim->features = features & vdpasim->dev_attr.supported_features;
 
-       config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
-       config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
        return 0;
 }
 
@@ -541,7 +405,9 @@ static u16 vdpasim_get_vq_num_max(struct vdpa_device *vdpa)
 
 static u32 vdpasim_get_device_id(struct vdpa_device *vdpa)
 {
-       return VDPASIM_DEVICE_ID;
+       struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
+
+       return vdpasim->dev_attr.id;
 }
 
 static u32 vdpasim_get_vendor_id(struct vdpa_device *vdpa)
@@ -577,14 +443,27 @@ static void vdpasim_get_config(struct vdpa_device *vdpa, unsigned int offset,
 {
        struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
 
-       if (offset + len < sizeof(struct virtio_net_config))
-               memcpy(buf, (u8 *)&vdpasim->config + offset, len);
+       if (offset + len > vdpasim->dev_attr.config_size)
+               return;
+
+       if (vdpasim->dev_attr.get_config)
+               vdpasim->dev_attr.get_config(vdpasim, vdpasim->config);
+
+       memcpy(buf, vdpasim->config + offset, len);
 }
 
 static void vdpasim_set_config(struct vdpa_device *vdpa, unsigned int offset,
                             const void *buf, unsigned int len)
 {
-       /* No writable config supportted by vdpasim */
+       struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
+
+       if (offset + len > vdpasim->dev_attr.config_size)
+               return;
+
+       memcpy(vdpasim->config + offset, buf, len);
+
+       if (vdpasim->dev_attr.set_config)
+               vdpasim->dev_attr.set_config(vdpasim, vdpasim->config);
 }
 
 static u32 vdpasim_get_generation(struct vdpa_device *vdpa)
@@ -661,10 +540,11 @@ static void vdpasim_free(struct vdpa_device *vdpa)
        struct vdpasim *vdpasim = vdpa_to_sim(vdpa);
 
        cancel_work_sync(&vdpasim->work);
-       kfree(vdpasim->buffer);
+       kvfree(vdpasim->buffer);
        if (vdpasim->iommu)
                vhost_iotlb_free(vdpasim->iommu);
        kfree(vdpasim->vqs);
+       kfree(vdpasim->config);
 }
 
 static const struct vdpa_config_ops vdpasim_config_ops = {
@@ -720,26 +600,6 @@ static const struct vdpa_config_ops vdpasim_batch_config_ops = {
        .free                   = vdpasim_free,
 };
 
-static int __init vdpasim_dev_init(void)
-{
-       vdpasim_dev = vdpasim_create();
-
-       if (!IS_ERR(vdpasim_dev))
-               return 0;
-
-       return PTR_ERR(vdpasim_dev);
-}
-
-static void __exit vdpasim_dev_exit(void)
-{
-       struct vdpa_device *vdpa = &vdpasim_dev->vdpa;
-
-       vdpa_unregister_device(vdpa);
-}
-
-module_init(vdpasim_dev_init)
-module_exit(vdpasim_dev_exit)
-
 MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE(DRV_LICENSE);
 MODULE_AUTHOR(DRV_AUTHOR);