Received: by 2002:a25:1506:0:0:0:0:0 with SMTP id 6csp3289756ybv; Sun, 9 Feb 2020 20:03:41 -0800 (PST) X-Google-Smtp-Source: APXvYqxRXhNnuegHKOVxBv2p0Eh1oNqDPTxQnFS/Wo5SE0QgBJisg1a2tTxgolho7yPb1SLq2Ano X-Received: by 2002:aca:5486:: with SMTP id i128mr9099695oib.12.1581307421126; Sun, 09 Feb 2020 20:03:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1581307421; cv=none; d=google.com; s=arc-20160816; b=D1qCy4PuQ0arP4/vuymLN4tYFqTCpeJrfneCKbsppmn+fhh0pg2OIyeRk7BlpkXhHp xm1YzKgAo+wXHbyT66vp0B1f9lqpnY8tY0WAJOeKrXka7HgImXD/2if68pLIshQiY0Lr tPoZEaCVIbIGsXCpZwMg7n7oVyLDHGi3rQ9N9CKS8PCCxaLH98FRkaLWqEaldDlz4Az0 pb+CuMcIoaFYn5ocxCkGZo0NcrZd9nxGihP7T9KURduCyw0uDGXJA9RJ9dAfyonOYEVX fOD6MRSTPU+6RYGVwBAXZoX2jy04z7gTXEApx/WpdXQ7dmKKRYZOZY2io6WWpg7e0j/U LF5g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=YYpCIAb8Dnx5YRxMRLxdMcQtNe0CQWuTzyP4m4OnCAw=; b=k6mDurZeDXSCvLnKQUQD/PQ1zOMG41Hl6ZGRD3lvQHLYzBxXRP5j+aqqEzIObRYDr9 gBJo/X7kjwylgCzNRaGVkpWfXajOWyRT4vmwHhhHqOWNyqA4tAXwNPiOZqo/b3SxNRPx wCAzjMNRnrjZOp4i2Eg5XXeB3yyj1YpdJVg+Rp2rw02kxQqkfaBE+SRykjEQ4KI2zptm N6wA+EglBOnZEQkNT4nwW8hbaxKaL9wgg0NrJyUudboVdt+Vztmg7oTQKeWzrIklSQe/ I6yoyD11pNpIxzB5ELXsV4R/TUUCVlHgOl2xCXRxUxdxftP8FztfU0tKazJBlh3CmILo xkmw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=J0Pm4tDt; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u1si4190221otg.113.2020.02.09.20.03.29; Sun, 09 Feb 2020 20:03:41 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=J0Pm4tDt; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727429AbgBJECo (ORCPT + 99 others); Sun, 9 Feb 2020 23:02:44 -0500 Received: from us-smtp-delivery-1.mimecast.com ([207.211.31.120]:60853 "EHLO us-smtp-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726961AbgBJECo (ORCPT ); Sun, 9 Feb 2020 23:02:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1581307362; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YYpCIAb8Dnx5YRxMRLxdMcQtNe0CQWuTzyP4m4OnCAw=; b=J0Pm4tDtPfFeCT1BjFbncGHicqCBBXta6fndtIfg/09oa0qP+2VDBomi/m5Q/ovZXdZkUs VB/vcscPKHUw+bpywaC7odf+qCrA7ZyRKVb0/FuXU3dc8W2SpKshbuVyq3NYvBw60oWMR+ 6OZRFRZt6Wjrednqths3jwKQPck++aU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-293-k9XK-t3UMQW1OjHIdcbG2w-1; Sun, 09 Feb 2020 23:02:40 -0500 X-MC-Unique: k9XK-t3UMQW1OjHIdcbG2w-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D164D18CA240; Mon, 10 Feb 2020 04:02:37 +0000 (UTC) Received: from jason-ThinkPad-X1-Carbon-6th.redhat.com (ovpn-13-219.pek2.redhat.com [10.72.13.219]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5D4471001B23; Mon, 10 Feb 2020 04:01:48 +0000 (UTC) From: Jason Wang To: mst@redhat.com, linux-kernel@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, netdev@vger.kernel.org Cc: tiwei.bie@intel.com, jgg@mellanox.com, maxime.coquelin@redhat.com, cunming.liang@intel.com, zhihong.wang@intel.com, rob.miller@broadcom.com, xiao.w.wang@intel.com, haotian.wang@sifive.com, lingshan.zhu@intel.com, eperezma@redhat.com, lulu@redhat.com, parav@mellanox.com, kevin.tian@intel.com, stefanha@redhat.com, rdunlap@infradead.org, hch@infradead.org, aadam@redhat.com, jiri@mellanox.com, shahafs@mellanox.com, hanand@xilinx.com, mhabets@solarflare.com, Jason Wang Subject: [PATCH V2 5/5] vdpasim: vDPA device simulator Date: Mon, 10 Feb 2020 11:56:08 +0800 Message-Id: <20200210035608.10002-6-jasowang@redhat.com> In-Reply-To: <20200210035608.10002-1-jasowang@redhat.com> References: <20200210035608.10002-1-jasowang@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Content-Transfer-Encoding: quoted-printable Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch implements a software vDPA networking device. The datapath is implemented through vringh and workqueue. The device has an on-chip IOMMU which translates IOVA to PA. For kernel virtio drivers, vDPA simulator driver provides dma_ops. For vhost driers, set_map() methods of vdpa_config_ops is implemented to accept mappings from vhost. Currently, vDPA device simulator will loopback TX traffic to RX. So the main use case for the device is vDPA feature testing, prototyping and development. Note, there's no management API implemented, a vDPA device will be registered once the module is probed. We need to handle this in the future development. Signed-off-by: Jason Wang --- drivers/virtio/vdpa/Kconfig | 17 + drivers/virtio/vdpa/Makefile | 1 + drivers/virtio/vdpa/vdpa_sim.c | 678 +++++++++++++++++++++++++++++++++ 3 files changed, 696 insertions(+) create mode 100644 drivers/virtio/vdpa/vdpa_sim.c diff --git a/drivers/virtio/vdpa/Kconfig b/drivers/virtio/vdpa/Kconfig index 7a99170e6c30..a7888974dda8 100644 --- a/drivers/virtio/vdpa/Kconfig +++ b/drivers/virtio/vdpa/Kconfig @@ -7,3 +7,20 @@ config VDPA datapath which complies with virtio specifications with vendor specific control path. =20 +menuconfig VDPA_MENU + bool "VDPA drivers" + default n + +if VDPA_MENU + +config VDPA_SIM + tristate "vDPA device simulator" + select VDPA + default n + help + vDPA networking device simulator which loop TX traffic back + to RX. This device is used for testing, prototyping and + development of vDPA. + +endif # VDPA_MENU + diff --git a/drivers/virtio/vdpa/Makefile b/drivers/virtio/vdpa/Makefile index ee6a35e8a4fb..5ec0e6ae3c57 100644 --- a/drivers/virtio/vdpa/Makefile +++ b/drivers/virtio/vdpa/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VDPA) +=3D vdpa.o +obj-$(CONFIG_VDPA_SIM) +=3D vdpa_sim.o diff --git a/drivers/virtio/vdpa/vdpa_sim.c b/drivers/virtio/vdpa/vdpa_si= m.c new file mode 100644 index 000000000000..89783a2b9773 --- /dev/null +++ b/drivers/virtio/vdpa/vdpa_sim.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VDPA networking device simulator. + * + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * Author: Jason Wang + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_VERSION "0.1" +#define DRV_AUTHOR "Jason Wang " +#define DRV_DESC "vDPA Device Simulator" +#define DRV_LICENSE "GPL v2" + +struct vdpasim_dev { + struct device dev; +}; + +struct vdpasim_dev *vdpasim_dev; + +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 =3D (1ULL << VIRTIO_F_ANY_LAYOUT) | + (1ULL << VIRTIO_F_VERSION_1) | + (1ULL << VIRTIO_F_IOMMU_PLATFORM); + +/* State of each vdpasim device */ +struct vdpasim { + struct vdpasim_virtqueue vqs[2]; + struct work_struct work; + /* spinlock to synchronize virtqueue state */ + spinlock_t lock; + struct vdpa_device vdpa; + struct virtio_net_config config; + struct vhost_iotlb *iommu; + void *buffer; + u32 status; + u32 generation; + u64 features; +}; + +struct vdpasim *vdpa_sim; + +static struct vdpasim *vdpa_to_sim(struct vdpa_device *vdpa) +{ + return container_of(vdpa, struct vdpasim, vdpa); +} + +static void vdpasim_queue_ready(struct vdpasim *vdpasim, unsigned int id= x) +{ + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + int ret; + + ret =3D vringh_init_iotlb(&vq->vring, vdpasim_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); +} + +static void vdpasim_vq_reset(struct vdpasim_virtqueue *vq) +{ + vq->ready =3D 0; + vq->desc_addr =3D 0; + vq->driver_addr =3D 0; + vq->device_addr =3D 0; + vq->cb =3D NULL; + vq->private =3D NULL; + vringh_init_iotlb(&vq->vring, vdpasim_features, VDPASIM_QUEUE_MAX, + false, 0, 0, 0); +} + +static void vdpasim_reset(struct vdpasim *vdpasim) +{ + int i; + + for (i =3D 0; i < VDPASIM_VQ_NUM; i++) + vdpasim_vq_reset(&vdpasim->vqs[i]); + + vhost_iotlb_reset(vdpasim->iommu); + + vdpasim->features =3D 0; + vdpasim->status =3D 0; + ++vdpasim->generation; +} + +static void vdpasim_work(struct work_struct *work) +{ + struct vdpasim *vdpasim =3D container_of(work, struct + vdpasim, work); + struct vdpasim_virtqueue *txq =3D &vdpasim->vqs[1]; + struct vdpasim_virtqueue *rxq =3D &vdpasim->vqs[0]; + size_t read, write, total_write; + int err; + int pkts =3D 0; + + 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 =3D 0; + err =3D vringh_getdesc_iotlb(&txq->vring, &txq->iov, NULL, + &txq->head, GFP_ATOMIC); + if (err <=3D 0) + break; + + err =3D vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->iov, + &rxq->head, GFP_ATOMIC); + if (err <=3D 0) { + vringh_complete_iotlb(&txq->vring, txq->head, 0); + break; + } + + while (true) { + read =3D vringh_iov_pull_iotlb(&txq->vring, &txq->iov, + vdpasim->buffer, + PAGE_SIZE); + if (read <=3D 0) + break; + + write =3D vringh_iov_push_iotlb(&rxq->vring, &rxq->iov, + vdpasim->buffer, read); + if (write <=3D 0) + break; + + total_write +=3D 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 =3D -EFAULT; + + switch (dir) { + case DMA_FROM_DEVICE: + perm =3D VHOST_MAP_WO; + break; + case DMA_TO_DEVICE: + perm =3D VHOST_MAP_RO; + break; + case DMA_BIDIRECTIONAL: + perm =3D VHOST_MAP_RW; + break; + default: + break; + } + + return perm; +} + +static dma_addr_t vdpasim_map_page(struct device *dev, struct page *page= , + unsigned long offset, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(dev); + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vhost_iotlb *iommu =3D vdpasim->iommu; + u64 pa =3D (page_to_pfn(page) << PAGE_SHIFT) + offset; + int ret, perm =3D dir_to_perm(dir); + + if (perm < 0) + return DMA_MAPPING_ERROR; + + /* For simplicity, use identical mapping to avoid e.g iova + * allocator. + */ + ret =3D vhost_iotlb_add_range(iommu, pa, pa + size - 1, + pa, dir_to_perm(dir)); + if (ret) + return DMA_MAPPING_ERROR; + + return (dma_addr_t)(pa); +} + +static void vdpasim_unmap_page(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(dev); + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vhost_iotlb *iommu =3D vdpasim->iommu; + + vhost_iotlb_del_range(iommu, (u64)dma_addr, + (u64)dma_addr + size - 1); +} + +static void *vdpasim_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_addr, gfp_t flag, + unsigned long attrs) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(dev); + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vhost_iotlb *iommu =3D vdpasim->iommu; + void *addr =3D kmalloc(size, flag); + int ret; + + if (!addr) + *dma_addr =3D DMA_MAPPING_ERROR; + else { + u64 pa =3D virt_to_phys(addr); + + ret =3D vhost_iotlb_add_range(iommu, (u64)pa, + (u64)pa + size - 1, + pa, VHOST_MAP_RW); + if (ret) { + *dma_addr =3D DMA_MAPPING_ERROR; + kfree(addr); + addr =3D NULL; + } else + *dma_addr =3D (dma_addr_t)pa; + } + + return addr; +} + +static void vdpasim_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_addr, + unsigned long attrs) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(dev); + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vhost_iotlb *iommu =3D vdpasim->iommu; + + vhost_iotlb_del_range(iommu, (u64)dma_addr, + (u64)dma_addr + size - 1); + kfree(phys_to_virt((uintptr_t)dma_addr)); +} + +static const struct dma_map_ops vdpasim_dma_ops =3D { + .map_page =3D vdpasim_map_page, + .unmap_page =3D vdpasim_unmap_page, + .alloc =3D vdpasim_alloc_coherent, + .free =3D vdpasim_free_coherent, +}; + +static void vdpasim_release_dev(struct device *_d) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(_d); + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + kfree(vdpasim->buffer); + kfree(vdpasim); +} + +static const struct vdpa_config_ops vdpasim_net_config_ops; + +static struct vdpasim *vdpasim_create(void) +{ + struct vdpasim *vdpasim; + struct virtio_net_config *config; + struct vdpa_device *vdpa; + struct device *dev; + int ret =3D -ENOMEM; + + vdpasim =3D kzalloc(sizeof(*vdpasim), GFP_KERNEL); + if (!vdpasim) + goto err_vdpa_alloc; + + vdpasim->buffer =3D kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!vdpasim->buffer) + goto err_buffer_alloc; + + vdpasim->iommu =3D vhost_iotlb_alloc(2048, 0); + if (!vdpasim->iommu) + goto err_iotlb; + + config =3D &vdpasim->config; + config->mtu =3D 1500; + config->status =3D VIRTIO_NET_S_LINK_UP; + eth_random_addr(config->mac); + + INIT_WORK(&vdpasim->work, vdpasim_work); + spin_lock_init(&vdpasim->lock); + + vdpa =3D &vdpasim->vdpa; + vdpa->dev.release =3D vdpasim_release_dev; + + vringh_set_iotlb(&vdpasim->vqs[0].vring, vdpasim->iommu); + vringh_set_iotlb(&vdpasim->vqs[1].vring, vdpasim->iommu); + + dev =3D &vdpa->dev; + dev->coherent_dma_mask =3D DMA_BIT_MASK(64); + set_dma_ops(dev, &vdpasim_dma_ops); + + ret =3D vdpa_init_device(vdpa, &vdpasim_dev->dev, dev, + &vdpasim_net_config_ops); + if (ret) + goto err_init; + + ret =3D vdpa_register_device(vdpa); + if (ret) + goto err_register; + + return vdpasim; + +err_register: + put_device(&vdpa->dev); +err_init: + vhost_iotlb_free(vdpasim->iommu); +err_iotlb: + kfree(vdpasim->buffer); +err_buffer_alloc: + kfree(vdpasim); +err_vdpa_alloc: + return ERR_PTR(ret); +} + +static int vdpasim_set_vq_address(struct vdpa_device *vdpa, u16 idx, + u64 desc_area, u64 driver_area, + u64 device_area) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + vq->desc_addr =3D desc_area; + vq->driver_addr =3D driver_area; + vq->device_addr =3D device_area; + + return 0; +} + +static void vdpasim_set_vq_num(struct vdpa_device *vdpa, u16 idx, u32 nu= m) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + vq->num =3D num; +} + +static void vdpasim_kick_vq(struct vdpa_device *vdpa, u16 idx) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + if (vq->ready) + schedule_work(&vdpasim->work); +} + +static void vdpasim_set_vq_cb(struct vdpa_device *vdpa, u16 idx, + struct vdpa_callback *cb) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + vq->cb =3D cb->callback; + vq->private =3D cb->private; +} + +static void vdpasim_set_vq_ready(struct vdpa_device *vdpa, u16 idx, bool= ready) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + spin_lock(&vdpasim->lock); + vq->ready =3D ready; + if (vq->ready) + vdpasim_queue_ready(vdpasim, idx); + spin_unlock(&vdpasim->lock); +} + +static bool vdpasim_get_vq_ready(struct vdpa_device *vdpa, u16 idx) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + + return vq->ready; +} + +static int vdpasim_set_vq_state(struct vdpa_device *vdpa, u16 idx, u64 s= tate) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + struct vringh *vrh =3D &vq->vring; + + spin_lock(&vdpasim->lock); + vrh->last_avail_idx =3D state; + spin_unlock(&vdpasim->lock); + + return 0; +} + +static u64 vdpasim_get_vq_state(struct vdpa_device *vdpa, u16 idx) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vdpasim_virtqueue *vq =3D &vdpasim->vqs[idx]; + struct vringh *vrh =3D &vq->vring; + + return vrh->last_avail_idx; +} + +static u16 vdpasim_get_vq_align(struct vdpa_device *vdpa) +{ + return VDPASIM_QUEUE_ALIGN; +} + +static u64 vdpasim_get_features(struct vdpa_device *vdpa) +{ + return vdpasim_features; +} + +static int vdpasim_set_features(struct vdpa_device *vdpa, u64 features) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + /* DMA mapping must be done by driver */ + if (!(features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))) + return -EINVAL; + + vdpasim->features =3D features & vdpasim_features; + + return 0; +} + +static void vdpasim_set_config_cb(struct vdpa_device *vdpa, + struct vdpa_callback *cb) +{ + /* We don't support config interrupt */ +} + +static u16 vdpasim_get_vq_num_max(struct vdpa_device *vdpa) +{ + return VDPASIM_QUEUE_MAX; +} + +static u32 vdpasim_get_device_id(struct vdpa_device *vdpa) +{ + return VDPASIM_DEVICE_ID; +} + +static u32 vdpasim_get_vendor_id(struct vdpa_device *vdpa) +{ + return VDPASIM_VENDOR_ID; +} + +static u8 vdpasim_get_status(struct vdpa_device *vdpa) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + u8 status; + + spin_lock(&vdpasim->lock); + status =3D vdpasim->status; + spin_unlock(&vdpasim->lock); + + return vdpasim->status; +} + +static void vdpasim_set_status(struct vdpa_device *vdpa, u8 status) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + spin_lock(&vdpasim->lock); + vdpasim->status =3D status; + if (status =3D=3D 0) + vdpasim_reset(vdpasim); + spin_unlock(&vdpasim->lock); +} + +static void vdpasim_get_config(struct vdpa_device *vdpa, unsigned int of= fset, + void *buf, unsigned int len) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + if (offset + len < sizeof(struct virtio_net_config)) + memcpy(buf, &vdpasim->config + offset, len); +} + +static void vdpasim_set_config(struct vdpa_device *vdpa, unsigned int of= fset, + const void *buf, unsigned int len) +{ + /* No writable config supportted by vdpasim */ +} + +static u32 vdpasim_get_generation(struct vdpa_device *vdpa) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + return vdpasim->generation; +} + +static int vdpasim_set_map(struct vdpa_device *vdpa, + struct vhost_iotlb *iotlb) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + struct vhost_iotlb_map *map; + u64 start =3D 0ULL, last =3D 0ULL - 1; + int ret; + + vhost_iotlb_reset(vdpasim->iommu); + + for (map =3D vhost_iotlb_itree_first(iotlb, start, last); map; + map =3D vhost_iotlb_itree_next(map, start, last)) { + ret =3D vhost_iotlb_add_range(vdpasim->iommu, map->start, + map->last, map->addr, map->perm); + if (ret) + goto err; + } + return 0; + +err: + vhost_iotlb_reset(vdpasim->iommu); + return ret; +} + +static int vdpasim_dma_map(struct vdpa_device *vdpa, u64 iova, u64 size, + u64 pa, u32 perm) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + return vhost_iotlb_add_range(vdpasim->iommu, iova, + iova + size - 1, pa, perm); +} + +static int vdpasim_dma_unmap(struct vdpa_device *vdpa, u64 iova, u64 siz= e) +{ + struct vdpasim *vdpasim =3D vdpa_to_sim(vdpa); + + vhost_iotlb_del_range(vdpasim->iommu, iova, iova + size - 1); + + return 0; +} + +static const struct vdpa_config_ops vdpasim_net_config_ops =3D { + .set_vq_address =3D vdpasim_set_vq_address, + .set_vq_num =3D vdpasim_set_vq_num, + .kick_vq =3D vdpasim_kick_vq, + .set_vq_cb =3D vdpasim_set_vq_cb, + .set_vq_ready =3D vdpasim_set_vq_ready, + .get_vq_ready =3D vdpasim_get_vq_ready, + .set_vq_state =3D vdpasim_set_vq_state, + .get_vq_state =3D vdpasim_get_vq_state, + .get_vq_align =3D vdpasim_get_vq_align, + .get_features =3D vdpasim_get_features, + .set_features =3D vdpasim_set_features, + .set_config_cb =3D vdpasim_set_config_cb, + .get_vq_num_max =3D vdpasim_get_vq_num_max, + .get_device_id =3D vdpasim_get_device_id, + .get_vendor_id =3D vdpasim_get_vendor_id, + .get_status =3D vdpasim_get_status, + .set_status =3D vdpasim_set_status, + .get_config =3D vdpasim_get_config, + .set_config =3D vdpasim_set_config, + .get_generation =3D vdpasim_get_generation, + .set_map =3D vdpasim_set_map, + .dma_map =3D vdpasim_dma_map, + .dma_unmap =3D vdpasim_dma_unmap, +}; + +static void vdpasim_device_release(struct device *dev) +{ + struct vdpasim_dev *vdpasim_dev =3D + container_of(dev, struct vdpasim_dev, dev); + + vdpasim_dev->dev.bus =3D NULL; + kfree(vdpasim_dev); +} + +static int __init vdpasim_dev_init(void) +{ + struct device *dev; + int ret =3D 0; + + vdpasim_dev =3D kzalloc(sizeof(*vdpasim_dev), GFP_KERNEL); + if (!vdpasim_dev) + return -ENOMEM; + + dev =3D &vdpasim_dev->dev; + dev->release =3D vdpasim_device_release; + dev_set_name(dev, "%s", VDPASIM_NAME); + + ret =3D device_register(&vdpasim_dev->dev); + if (ret) + goto err_register; + + if (!vdpasim_create()) + goto err_register; + + return 0; + +err_register: + kfree(vdpasim_dev); + vdpasim_dev =3D NULL; + return ret; +} + +static int vdpasim_device_remove_cb(struct device *dev, void *data) +{ + struct vdpa_device *vdpa =3D dev_to_vdpa(dev); + + vdpa_unregister_device(vdpa); + + return 0; +} + +static void __exit vdpasim_dev_exit(void) +{ + device_for_each_child(&vdpasim_dev->dev, NULL, + vdpasim_device_remove_cb); + device_unregister(&vdpasim_dev->dev); +} + +module_init(vdpasim_dev_init) +module_exit(vdpasim_dev_exit) + +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE(DRV_LICENSE); +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESC); --=20 2.19.1