Received: by 2002:a25:31c3:0:0:0:0:0 with SMTP id x186csp512150ybx; Tue, 29 Oct 2019 23:55:08 -0700 (PDT) X-Google-Smtp-Source: APXvYqyW9hvqqUY2RC1YIZtDpPvSdqWcoBFxCrtCqd8F6h+PXS0EvPQzURU8T5oGJhrabGNo2XrG X-Received: by 2002:a05:6402:2d6:: with SMTP id b22mr22495825edx.155.1572418508245; Tue, 29 Oct 2019 23:55:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1572418508; cv=none; d=google.com; s=arc-20160816; b=AX2XAXmyISl2do+yGGpAE53F24XqLkraXgGRnFPvjgORmZ/818ACDosHVW4duwNdZX 6RIgWbOOEXbytj11D5P3CpbjYqN2d60qvZ02LoS/Pj1XeZiw1sgtcjWO2rQOHxV31BaT k0WsaLss2mQjyQymGiCtUX4setlLPP7QOcYRY7AwXQexYz972i/EsWIZhEAEBmZrwUiW ZNuQo97ji0dtLw4/o5oLqCV4EOV34M+J5yOVEdb/X4jLleTUvjC39TYrX8fdtJtTAxtA 3S5fQWoCyXYhJYPfjO2GP6afXsHov1seaw02RNn6g81yIItbAqs+X1hcmMXOwLkZ+Xb6 FY4w== 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=+QwaX1cTcMJpIPw4pGz+8aksK5AS2TfxLcxj423/inA=; b=QvcY6Emzz+X7YrY2+nFTTNEtLOuaoKbVXvGfJXHXL9vQph/FwLhIe5I7qzwXV6wMmx dtfmEwNlaekNYKBsMpoipBnFpEEcg2lCgefJAHPw6ZW8VHoHpdUeQuSI8yL6y8aSIvvi 1/aMU6UvlncbcgMtJSlIjGzeAcCN3nkbBRQkiynUJucmt4ff8lnEHaIAj67vVh6d1mT+ RlcxF+sZuyyyVs6xgPhoD5thpWlUNEpuV+Kas1SSHruj2nn6V3xOAZ61HkJPdg8Ks2Ij hqNebihmPaRXJ69esREerMzNUkDaR8ogjq2bT+JeBESALRzfpsIv480VAGQguuG7doRF 9PnA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=LspXz30O; 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 t49si790523edd.198.2019.10.29.23.54.44; Tue, 29 Oct 2019 23:55:08 -0700 (PDT) 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=LspXz30O; 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 S1727437AbfJ3GxD (ORCPT + 99 others); Wed, 30 Oct 2019 02:53:03 -0400 Received: from us-smtp-1.mimecast.com ([205.139.110.61]:60448 "EHLO us-smtp-delivery-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727119AbfJ3GxD (ORCPT ); Wed, 30 Oct 2019 02:53:03 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1572418380; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+QwaX1cTcMJpIPw4pGz+8aksK5AS2TfxLcxj423/inA=; b=LspXz30OHePO3mUAltnXyVvnnAkYKzucVIqck6smuAiX5G5ULmy10/a9tf6GOOF3afN1VO fRflbBVHcMJYFVpPVe8Xsu+JzaJ1o/96G2n8gi+VctJ2CNmn3Ye7WoWxYcMkjpfYSmeB70 N15+V1mhYqfmBF2BVuokP1l1zqbnoO0= 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-352-KmHGW5_5Ok-Y0CObsnv6oA-1; Wed, 30 Oct 2019 02:52:56 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D93F6180496F; Wed, 30 Oct 2019 06:52:52 +0000 (UTC) Received: from jason-ThinkPad-X1-Carbon-6th.redhat.com (ovpn-12-223.pek2.redhat.com [10.72.12.223]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1EE775D9C3; Wed, 30 Oct 2019 06:51:07 +0000 (UTC) From: Jason Wang To: kvm@vger.kernel.org, linux-s390@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org, intel-gvt-dev@lists.freedesktop.org, kwankhede@nvidia.com, alex.williamson@redhat.com, mst@redhat.com, tiwei.bie@intel.com Cc: virtualization@lists.linux-foundation.org, netdev@vger.kernel.org, cohuck@redhat.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, zhenyuw@linux.intel.com, zhi.a.wang@intel.com, jani.nikula@linux.intel.com, joonas.lahtinen@linux.intel.com, rodrigo.vivi@intel.com, airlied@linux.ie, daniel@ffwll.ch, farman@linux.ibm.com, pasic@linux.ibm.com, sebott@linux.ibm.com, oberpar@linux.ibm.com, heiko.carstens@de.ibm.com, gor@linux.ibm.com, borntraeger@de.ibm.com, akrowiak@linux.ibm.com, freude@linux.ibm.com, lingshan.zhu@intel.com, idos@mellanox.com, eperezma@redhat.com, lulu@redhat.com, parav@mellanox.com, christophe.de.dinechin@gmail.com, kevin.tian@intel.com, stefanha@redhat.com, Jason Wang Subject: [PATCH V6 6/6] docs: sample driver to demonstrate how to implement virtio-mdev framework Date: Wed, 30 Oct 2019 14:44:44 +0800 Message-Id: <20191030064444.21166-7-jasowang@redhat.com> In-Reply-To: <20191030064444.21166-1-jasowang@redhat.com> References: <20191030064444.21166-1-jasowang@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-MC-Unique: KmHGW5_5Ok-Y0CObsnv6oA-1 X-Mimecast-Spam-Score: 0 Content-Type: text/plain; charset=WINDOWS-1252 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 sample driver creates mdev device that simulate virtio net device over virtio mdev transport. The device is implemented through vringh and workqueue. A device specific dma ops is to make sure HVA is used directly as the IOVA. This should be sufficient for kernel virtio driver to work. Only 'virtio' type is supported right now. I plan to add 'vhost' type on top which requires some virtual IOMMU implemented in this sample driver. Signed-off-by: Jason Wang --- MAINTAINERS | 1 + samples/Kconfig | 7 + samples/vfio-mdev/Makefile | 1 + samples/vfio-mdev/mvnet.c | 691 +++++++++++++++++++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 samples/vfio-mdev/mvnet.c diff --git a/MAINTAINERS b/MAINTAINERS index 9e10ae9c2b4d..8b17927a81fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17248,6 +17248,7 @@ F:=09include/linux/virtio*.h F:=09include/uapi/linux/virtio_*.h F:=09drivers/crypto/virtio/ F:=09mm/balloon_compaction.c +F:=09samples/vfio-mdev/mvnet.c =20 VIRTIO BLOCK AND SCSI DRIVERS M:=09"Michael S. Tsirkin" diff --git a/samples/Kconfig b/samples/Kconfig index c8dacb4dda80..a1a1ca2c00b7 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -131,6 +131,13 @@ config SAMPLE_VFIO_MDEV_MDPY =09 mediated device. It is a simple framebuffer and supports =09 the region display interface (VFIO_GFX_PLANE_TYPE_REGION). =20 +config SAMPLE_VIRTIO_MDEV_NET + tristate "Build virtio mdev net example mediated device sample cod= e -- loadable modules only" +=09depends on VIRTIO_MDEV_DEVICE && VHOST_RING && m +=09help +=09 Build a networking sample device for use as a virtio +=09 mediated device. + config SAMPLE_VFIO_MDEV_MDPY_FB =09tristate "Build VFIO mdpy example guest fbdev driver -- loadable module= only" =09depends on FB && m diff --git a/samples/vfio-mdev/Makefile b/samples/vfio-mdev/Makefile index 10d179c4fdeb..f34af90ed0a0 100644 --- a/samples/vfio-mdev/Makefile +++ b/samples/vfio-mdev/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) +=3D mtty.o obj-$(CONFIG_SAMPLE_VFIO_MDEV_MDPY) +=3D mdpy.o obj-$(CONFIG_SAMPLE_VFIO_MDEV_MDPY_FB) +=3D mdpy-fb.o obj-$(CONFIG_SAMPLE_VFIO_MDEV_MBOCHS) +=3D mbochs.o +obj-$(CONFIG_SAMPLE_VIRTIO_MDEV_NET) +=3D mvnet.o diff --git a/samples/vfio-mdev/mvnet.c b/samples/vfio-mdev/mvnet.c new file mode 100644 index 000000000000..a2a902d59fb7 --- /dev/null +++ b/samples/vfio-mdev/mvnet.c @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Mediated virtual virtio-net device driver. + * + * Copyright (c) 2019, Red Hat Inc. All rights reserved. + * Author: Jason Wang + * + * Sample driver that creates mdev device that simulates ethernet loopback + * device. + * + * Usage: + * + * # modprobe virtio_mdev + * # modprobe mvnet + * # cd /sys/devices/virtual/mvnet/mvnet/mdev_supported_types/mvnet-virtio + * # echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" > ./create + * # cd devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001 + * # ls -d virtio0 + * virtio0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "0.1" +#define DRIVER_AUTHOR "Red Hat Corporation" + +#define MVNET_CLASS_NAME "mvnet" +#define MVNET_NAME "mvnet" + +/* + * Global Structures + */ + +static struct mvnet_dev { +=09struct class=09*vd_class; +=09struct idr=09vd_idr; +=09struct device=09dev; +} mvnet_dev; + +struct mvnet_virtqueue { +=09struct vringh vring; +=09struct vringh_kiov iov; +=09unsigned short head; +=09bool ready; +=09u64 desc_addr; +=09u64 device_addr; +=09u64 driver_addr; +=09u32 num; +=09void *private; +=09irqreturn_t (*cb)(void *data); +}; + +#define MVNET_QUEUE_ALIGN PAGE_SIZE +#define MVNET_QUEUE_MAX 256 +#define MVNET_DEVICE_ID 0x1 +#define MVNET_VENDOR_ID 0 + +u64 mvnet_features =3D (1ULL << VIRTIO_F_ANY_LAYOUT) | +=09=09 (1ULL << VIRTIO_F_VERSION_1) | +=09=09 (1ULL << VIRTIO_F_IOMMU_PLATFORM); + +/* State of each mdev device */ +struct mvnet_state { +=09struct mvnet_virtqueue vqs[2]; +=09struct work_struct work; +=09spinlock_t lock; +=09struct mdev_device *mdev; +=09struct virtio_net_config config; +=09void *buffer; +=09u32 status; +=09u32 generation; +=09u64 features; +=09struct list_head next; +}; + +static struct mutex mdev_list_lock; +static struct list_head mdev_devices_list; + +static void mvnet_queue_ready(struct mvnet_state *mvnet, unsigned int idx) +{ +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; +=09int ret; + +=09ret =3D vringh_init_kern(&vq->vring, mvnet_features, MVNET_QUEUE_MAX, +=09=09=09 false, (struct vring_desc *)vq->desc_addr, +=09=09=09 (struct vring_avail *)vq->driver_addr, +=09=09=09 (struct vring_used *)vq->device_addr); +} + +static void mvnet_vq_reset(struct mvnet_virtqueue *vq) +{ +=09vq->ready =3D 0; +=09vq->desc_addr =3D 0; +=09vq->driver_addr =3D 0; +=09vq->device_addr =3D 0; +=09vq->cb =3D NULL; +=09vq->private =3D NULL; +=09vringh_init_kern(&vq->vring, mvnet_features, MVNET_QUEUE_MAX, +=09=09=09false, 0, 0, 0); +} + +static void mvnet_reset(struct mvnet_state *mvnet) +{ +=09int i; + +=09for (i =3D 0; i < 2; i++) +=09=09mvnet_vq_reset(&mvnet->vqs[i]); + +=09mvnet->features =3D 0; +=09mvnet->status =3D 0; +=09++mvnet->generation; +} + +static void mvnet_work(struct work_struct *work) +{ +=09struct mvnet_state *mvnet =3D container_of(work, struct +=09=09=09=09=09=09 mvnet_state, work); +=09struct mvnet_virtqueue *txq =3D &mvnet->vqs[1]; +=09struct mvnet_virtqueue *rxq =3D &mvnet->vqs[0]; +=09size_t read, write, total_write; +=09int err; +=09int pkts =3D 0; + +=09spin_lock(&mvnet->lock); + +=09if (!txq->ready || !rxq->ready) +=09=09goto out; + +=09while (true) { +=09=09total_write =3D 0; +=09=09err =3D vringh_getdesc_kern(&txq->vring, &txq->iov, NULL, +=09=09=09=09=09 &txq->head, GFP_ATOMIC); +=09=09if (err <=3D 0) +=09=09=09break; + +=09=09err =3D vringh_getdesc_kern(&rxq->vring, NULL, &rxq->iov, +=09=09=09=09=09 &rxq->head, GFP_ATOMIC); +=09=09if (err <=3D 0) { +=09=09=09vringh_complete_kern(&txq->vring, txq->head, 0); +=09=09=09break; +=09=09} + +=09=09while (true) { +=09=09=09read =3D vringh_iov_pull_kern(&txq->iov, mvnet->buffer, +=09=09=09=09=09=09 PAGE_SIZE); +=09=09=09if (read <=3D 0) +=09=09=09=09break; + +=09=09=09write =3D vringh_iov_push_kern(&rxq->iov, mvnet->buffer, +=09=09=09=09=09=09 read); +=09=09=09if (write <=3D 0) +=09=09=09=09break; + +=09=09=09total_write +=3D write; +=09=09} + +=09=09/* Make sure data is wrote before advancing index */ +=09=09smp_wmb(); + +=09=09vringh_complete_kern(&txq->vring, txq->head, 0); +=09=09vringh_complete_kern(&rxq->vring, rxq->head, total_write); + +=09=09/* Make sure used is visible before rasing the interrupt. */ +=09=09smp_wmb(); + +=09=09local_bh_disable(); +=09=09if (txq->cb) +=09=09=09txq->cb(txq->private); +=09=09if (rxq->cb) +=09=09=09rxq->cb(rxq->private); +=09=09local_bh_enable(); + +=09=09if (++pkts > 4) { +=09=09=09schedule_work(&mvnet->work); +=09=09=09goto out; +=09=09} +=09} + +out: +=09spin_unlock(&mvnet->lock); +} + +static dma_addr_t mvnet_map_page(struct device *dev, struct page *page, +=09=09=09=09 unsigned long offset, size_t size, +=09=09=09=09 enum dma_data_direction dir, +=09=09=09=09 unsigned long attrs) +{ +=09/* Vringh can only use HVA */ +=09return (dma_addr_t)(page_address(page) + offset); +} + +static void mvnet_unmap_page(struct device *dev, dma_addr_t dma_addr, +=09=09=09 size_t size, enum dma_data_direction dir, +=09=09=09 unsigned long attrs) +{ +} + +static void *mvnet_alloc_coherent(struct device *dev, size_t size, +=09=09=09=09 dma_addr_t *dma_addr, gfp_t flag, +=09=09=09=09 unsigned long attrs) +{ +=09void *addr =3D kmalloc(size, flag); + +=09if (addr =3D=3D NULL) +=09=09*dma_addr =3D DMA_MAPPING_ERROR; +=09else +=09=09*dma_addr =3D (dma_addr_t) addr; + +=09return addr; +} + +static void mvnet_free_coherent(struct device *dev, size_t size, +=09=09=09=09void *vaddr, dma_addr_t dma_addr, +=09=09=09=09unsigned long attrs) +{ +=09kfree((void *)dma_addr); +} + +static const struct dma_map_ops mvnet_dma_ops =3D { +=09.map_page =3D mvnet_map_page, +=09.unmap_page =3D mvnet_unmap_page, +=09.alloc =3D mvnet_alloc_coherent, +=09.free =3D mvnet_free_coherent, +}; + +static const struct virtio_mdev_device_ops virtio_mdev_ops; + +static int mvnet_create(struct kobject *kobj, struct mdev_device *mdev) +{ +=09struct mvnet_state *mvnet; +=09struct virtio_net_config *config; +=09struct device *dev =3D mdev_dev(mdev); + +=09if (!mdev) +=09=09return -EINVAL; + +=09mvnet =3D kzalloc(sizeof(struct mvnet_state), GFP_KERNEL); +=09if (mvnet =3D=3D NULL) +=09=09return -ENOMEM; + +=09mvnet->buffer =3D kmalloc(PAGE_SIZE, GFP_KERNEL); +=09if (!mvnet->buffer) { +=09=09kfree(mvnet); +=09=09return -ENOMEM; +=09} + +=09config =3D &mvnet->config; +=09config->mtu =3D 1500; +=09config->status =3D VIRTIO_NET_S_LINK_UP; +=09eth_random_addr(config->mac); + +=09INIT_WORK(&mvnet->work, mvnet_work); + +=09spin_lock_init(&mvnet->lock); +=09mvnet->mdev =3D mdev; +=09mdev_set_drvdata(mdev, mvnet); + +=09mutex_lock(&mdev_list_lock); +=09list_add(&mvnet->next, &mdev_devices_list); +=09mutex_unlock(&mdev_list_lock); + +=09dev->coherent_dma_mask =3D DMA_BIT_MASK(64); +=09set_dma_ops(dev, &mvnet_dma_ops); + +=09mdev_set_virtio_ops(mdev, &virtio_mdev_ops); + +=09return 0; +} + +static int mvnet_remove(struct mdev_device *mdev) +{ +=09struct mvnet_state *mds, *tmp_mds; +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09int ret =3D -EINVAL; + +=09mutex_lock(&mdev_list_lock); +=09list_for_each_entry_safe(mds, tmp_mds, &mdev_devices_list, next) { +=09=09if (mvnet =3D=3D mds) { +=09=09=09list_del(&mvnet->next); +=09=09=09mdev_set_drvdata(mdev, NULL); +=09=09=09kfree(mvnet->buffer); +=09=09=09kfree(mvnet); +=09=09=09ret =3D 0; +=09=09=09break; +=09=09} +=09} +=09mutex_unlock(&mdev_list_lock); + +=09return ret; +} + +static ssize_t +sample_mvnet_dev_show(struct device *dev, struct device_attribute *attr, +=09=09 char *buf) +{ +=09if (mdev_from_dev(dev)) +=09=09return sprintf(buf, "This is MDEV %s\n", dev_name(dev)); + +=09return sprintf(buf, "\n"); +} + +static DEVICE_ATTR_RO(sample_mvnet_dev); + +static struct attribute *mvnet_dev_attrs[] =3D { +=09&dev_attr_sample_mvnet_dev.attr, +=09NULL, +}; + +static const struct attribute_group mvnet_dev_group =3D { +=09.name =3D "mvnet_dev", +=09.attrs =3D mvnet_dev_attrs, +}; + +static const struct attribute_group *mvnet_dev_groups[] =3D { +=09&mvnet_dev_group, +=09NULL, +}; + +static ssize_t +sample_mdev_dev_show(struct device *dev, struct device_attribute *attr, +=09=09 char *buf) +{ +=09if (mdev_from_dev(dev)) +=09=09return sprintf(buf, "This is MDEV %s\n", dev_name(dev)); + +=09return sprintf(buf, "\n"); +} + +static DEVICE_ATTR_RO(sample_mdev_dev); + +static struct attribute *mdev_dev_attrs[] =3D { +=09&dev_attr_sample_mdev_dev.attr, +=09NULL, +}; + +static const struct attribute_group mdev_dev_group =3D { +=09.name =3D "vendor", +=09.attrs =3D mdev_dev_attrs, +}; + +static const struct attribute_group *mdev_dev_groups[] =3D { +=09&mdev_dev_group, +=09NULL, +}; + +#define MVNET_STRING_LEN 16 + +static ssize_t +name_show(struct kobject *kobj, struct device *dev, char *buf) +{ +=09char name[MVNET_STRING_LEN]; +=09const char *name_str =3D "virtio-net"; + +=09snprintf(name, MVNET_STRING_LEN, "%s", dev_driver_string(dev)); +=09if (!strcmp(kobj->name, name)) +=09=09return sprintf(buf, "%s\n", name_str); + +=09return -EINVAL; +} + +static MDEV_TYPE_ATTR_RO(name); + +static ssize_t +available_instances_show(struct kobject *kobj, struct device *dev, char *b= uf) +{ +=09return sprintf(buf, "%d\n", INT_MAX); +} + +static MDEV_TYPE_ATTR_RO(available_instances); + +static ssize_t device_api_show(struct kobject *kobj, struct device *dev, +=09=09=09 char *buf) +{ +=09return sprintf(buf, "%s\n", VIRTIO_MDEV_DEVICE_API_STRING); +} + +static MDEV_TYPE_ATTR_RO(device_api); + +static struct attribute *mdev_types_attrs[] =3D { +=09&mdev_type_attr_name.attr, +=09&mdev_type_attr_device_api.attr, +=09&mdev_type_attr_available_instances.attr, +=09NULL, +}; + +static struct attribute_group mdev_type_group =3D { +=09.name =3D "virtio", +=09.attrs =3D mdev_types_attrs, +}; + +/* TBD: "vhost" type */ + +static struct attribute_group *mdev_type_groups[] =3D { +=09&mdev_type_group, +=09NULL, +}; + +static int mvnet_set_vq_address(struct mdev_device *mdev, u16 idx, +=09=09=09=09u64 desc_area, u64 driver_area, u64 device_area) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09vq->desc_addr =3D desc_area; +=09vq->driver_addr =3D driver_area; +=09vq->device_addr =3D device_area; + +=09return 0; +} + +static void mvnet_set_vq_num(struct mdev_device *mdev, u16 idx, u32 num) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09vq->num =3D num; +} + +static void mvnet_kick_vq(struct mdev_device *mdev, u16 idx) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09if (vq->ready) +=09=09schedule_work(&mvnet->work); +} + +static void mvnet_set_vq_cb(struct mdev_device *mdev, u16 idx, +=09=09=09 struct virtio_mdev_callback *cb) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09vq->cb =3D cb->callback; +=09vq->private =3D cb->private; +} + +static void mvnet_set_vq_ready(struct mdev_device *mdev, u16 idx, bool rea= dy) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09spin_lock(&mvnet->lock); +=09vq->ready =3D ready; +=09if (vq->ready) +=09=09mvnet_queue_ready(mvnet, idx); +=09spin_unlock(&mvnet->lock); +} + +static bool mvnet_get_vq_ready(struct mdev_device *mdev, u16 idx) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; + +=09return vq->ready; +} + +static int mvnet_set_vq_state(struct mdev_device *mdev, u16 idx, u64 state= ) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; +=09struct vringh *vrh =3D &vq->vring; + +=09spin_lock(&mvnet->lock); +=09vrh->last_avail_idx =3D state; +=09spin_unlock(&mvnet->lock); + +=09return 0; +} + +static u64 mvnet_get_vq_state(struct mdev_device *mdev, u16 idx) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); +=09struct mvnet_virtqueue *vq =3D &mvnet->vqs[idx]; +=09struct vringh *vrh =3D &vq->vring; + +=09return vrh->last_avail_idx; +} + +static u16 mvnet_get_vq_align(struct mdev_device *mdev) +{ +=09return MVNET_QUEUE_ALIGN; +} + +static u64 mvnet_get_features(struct mdev_device *mdev) +{ +=09return mvnet_features; +} + +static int mvnet_set_features(struct mdev_device *mdev, u64 features) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); + +=09/* DMA mapping must be done by driver */ +=09if (!(features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))) +=09=09return -EINVAL; + +=09mvnet->features =3D features & mvnet_features; + +=09return 0; +} + +static void mvnet_set_config_cb(struct mdev_device *mdev, +=09=09=09=09struct virtio_mdev_callback *cb) +{ +=09/* We don't support config interrupt */ +} + +static u16 mvnet_get_vq_num_max(struct mdev_device *mdev) +{ +=09return MVNET_QUEUE_MAX; +} + +static u32 mvnet_get_device_id(struct mdev_device *mdev) +{ +=09return MVNET_DEVICE_ID; +} + +static u32 mvnet_get_vendor_id(struct mdev_device *mdev) +{ +=09return MVNET_VENDOR_ID; +} + +static u8 mvnet_get_status(struct mdev_device *mdev) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); + +=09return mvnet->status; +} + +static void mvnet_set_status(struct mdev_device *mdev, u8 status) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); + +=09mvnet->status =3D status; + +=09if (status =3D=3D 0) { +=09=09spin_lock(&mvnet->lock); +=09=09mvnet_reset(mvnet); +=09=09spin_unlock(&mvnet->lock); +=09} +} + +static void mvnet_get_config(struct mdev_device *mdev, unsigned int offset= , +=09=09=09 void *buf, unsigned int len) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); + +=09if (offset + len < sizeof(struct virtio_net_config)) +=09=09memcpy(buf, &mvnet->config + offset, len); +} + +static void mvnet_set_config(struct mdev_device *mdev, unsigned int offset= , +=09=09=09 const void *buf, unsigned int len) +{ +=09/* No writable config supportted by mvnet */ +} + +static u64 mvnet_get_mdev_features(struct mdev_device *mdev) +{ +=09return VIRTIO_MDEV_F_VERSION_1; +} + +static u32 mvnet_get_generation(struct mdev_device *mdev) +{ +=09struct mvnet_state *mvnet =3D mdev_get_drvdata(mdev); + +=09return mvnet->generation; +} + +static const struct virtio_mdev_device_ops virtio_mdev_ops =3D { +=09.set_vq_address =3D mvnet_set_vq_address, +=09.set_vq_num =3D mvnet_set_vq_num, +=09.kick_vq =3D mvnet_kick_vq, +=09.set_vq_cb =3D mvnet_set_vq_cb, +=09.set_vq_ready =3D mvnet_set_vq_ready, +=09.get_vq_ready =3D mvnet_get_vq_ready, +=09.set_vq_state =3D mvnet_set_vq_state, +=09.get_vq_state =3D mvnet_get_vq_state, +=09.get_vq_align =3D mvnet_get_vq_align, +=09.get_features =3D mvnet_get_features, +=09.set_features =3D mvnet_set_features, +=09.set_config_cb =3D mvnet_set_config_cb, +=09.get_vq_num_max =3D mvnet_get_vq_num_max, +=09.get_device_id =3D mvnet_get_device_id, +=09.get_vendor_id =3D mvnet_get_vendor_id, +=09.get_status =3D mvnet_get_status, +=09.set_status =3D mvnet_set_status, +=09.get_config =3D mvnet_get_config, +=09.set_config =3D mvnet_set_config, +=09.get_mdev_features =3D mvnet_get_mdev_features, +=09.get_generation =3D mvnet_get_generation, +}; + +static const struct mdev_parent_ops mdev_fops =3D { +=09.owner =3D THIS_MODULE, +=09.dev_attr_groups =3D mvnet_dev_groups, +=09.mdev_attr_groups =3D mdev_dev_groups, +=09.supported_type_groups =3D mdev_type_groups, +=09.create =3D mvnet_create, +=09.remove=09=09=09=3D mvnet_remove, +}; + +static void mvnet_device_release(struct device *dev) +{ +=09dev_dbg(dev, "mvnet: released\n"); +} + +static int __init mvnet_dev_init(void) +{ +=09int ret =3D 0; + +=09pr_info("mvnet_dev: %s\n", __func__); + +=09memset(&mvnet_dev, 0, sizeof(mvnet_dev)); + +=09idr_init(&mvnet_dev.vd_idr); + +=09mvnet_dev.vd_class =3D class_create(THIS_MODULE, MVNET_CLASS_NAME); + +=09if (IS_ERR(mvnet_dev.vd_class)) { +=09=09pr_err("Error: failed to register mvnet_dev class\n"); +=09=09ret =3D PTR_ERR(mvnet_dev.vd_class); +=09=09goto failed1; +=09} + +=09mvnet_dev.dev.class =3D mvnet_dev.vd_class; +=09mvnet_dev.dev.release =3D mvnet_device_release; +=09dev_set_name(&mvnet_dev.dev, "%s", MVNET_NAME); + +=09ret =3D device_register(&mvnet_dev.dev); +=09if (ret) +=09=09goto failed2; + +=09ret =3D mdev_register_device(&mvnet_dev.dev, &mdev_fops); +=09if (ret) +=09=09goto failed3; + +=09mutex_init(&mdev_list_lock); +=09INIT_LIST_HEAD(&mdev_devices_list); + +=09goto all_done; + +failed3: + +=09device_unregister(&mvnet_dev.dev); +failed2: +=09class_destroy(mvnet_dev.vd_class); + +failed1: +all_done: +=09return ret; +} + +static void __exit mvnet_dev_exit(void) +{ +=09mvnet_dev.dev.bus =3D NULL; +=09mdev_unregister_device(&mvnet_dev.dev); + +=09device_unregister(&mvnet_dev.dev); +=09idr_destroy(&mvnet_dev.vd_idr); +=09class_destroy(mvnet_dev.vd_class); +=09mvnet_dev.vd_class =3D NULL; +=09pr_info("mvnet_dev: Unloaded!\n"); +} + +module_init(mvnet_dev_init) +module_exit(mvnet_dev_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_INFO(supported, "Simulate loopback ethernet device over mdev"); +MODULE_VERSION(VERSION_STRING); +MODULE_AUTHOR(DRIVER_AUTHOR); --=20 2.19.1