From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.0 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 977A0C43381 for ; Fri, 22 Feb 2019 15:31:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5A50220818 for ; Fri, 22 Feb 2019 15:31:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727389AbfBVPb3 (ORCPT ); Fri, 22 Feb 2019 10:31:29 -0500 Received: from bastet.se.axis.com ([195.60.68.11]:45368 "EHLO bastet.se.axis.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726214AbfBVPb1 (ORCPT ); Fri, 22 Feb 2019 10:31:27 -0500 Received: from localhost (localhost [127.0.0.1]) by bastet.se.axis.com (Postfix) with ESMTP id 52E6B18684; Fri, 22 Feb 2019 16:31:25 +0100 (CET) X-Axis-User: NO X-Axis-NonUser: YES X-Virus-Scanned: Debian amavisd-new at bastet.se.axis.com Received: from bastet.se.axis.com ([IPv6:::ffff:127.0.0.1]) by localhost (bastet.se.axis.com [::ffff:127.0.0.1]) (amavisd-new, port 10024) with LMTP id Sn4KA-qAsBu8; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: from boulder03.se.axis.com (boulder03.se.axis.com [10.0.8.17]) by bastet.se.axis.com (Postfix) with ESMTPS id 96EBF1868B; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: from boulder03.se.axis.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 6376C1E06C; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: from boulder03.se.axis.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 574841E064; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: from seth.se.axis.com (unknown [10.0.2.172]) by boulder03.se.axis.com (Postfix) with ESMTP; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: from lnxartpec.se.axis.com (lnxartpec.se.axis.com [10.88.4.9]) by seth.se.axis.com (Postfix) with ESMTP id 4AA5D312F; Fri, 22 Feb 2019 16:31:21 +0100 (CET) Received: by lnxartpec.se.axis.com (Postfix, from userid 10564) id 4900A80DC0; Fri, 22 Feb 2019 16:31:21 +0100 (CET) From: Vincent Whitchurch To: sudeep.dutt@intel.com, ashutosh.dixit@intel.com, gregkh@linuxfoundation.org, arnd@arndb.de Cc: linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org, Vincent Whitchurch Subject: [PATCH v2 char-misc-next 4/7] mic: vop: Add loopback driver Date: Fri, 22 Feb 2019 16:30:52 +0100 Message-Id: <20190222153056.18878-7-vincent.whitchurch@axis.com> X-Mailer: git-send-email 2.20.0 In-Reply-To: <20190222153056.18878-1-vincent.whitchurch@axis.com> References: <20190222153056.18878-1-vincent.whitchurch@axis.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-TM-AS-GCONF: 00 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a loopback driver to allow testing and evaluation of the VOP framework without special hardware. The host and the guest will run under the same kernel. Signed-off-by: Vincent Whitchurch --- drivers/misc/mic/Kconfig | 10 + drivers/misc/mic/vop/Makefile | 2 + drivers/misc/mic/vop/vop_loopback.c | 382 ++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+) create mode 100644 drivers/misc/mic/vop/vop_loopback.c diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 242dcee14689..2e2f745afb3a 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -148,6 +148,16 @@ config VOP OS and tools for MIC to use with this driver are available from . +config VOP_LOOPBACK + tristate "VOP loopback driver" + depends on VOP + help + This enables a loopback driver to test and evaluate the VOP + infrastructure without actual PCIe hardware. The host and the guest + sides run under the same kernel. + + If unsure, say N. + if VOP source "drivers/vhost/Kconfig.vringh" endif diff --git a/drivers/misc/mic/vop/Makefile b/drivers/misc/mic/vop/Makefile index 78819c8999f1..a6ead25c4418 100644 --- a/drivers/misc/mic/vop/Makefile +++ b/drivers/misc/mic/vop/Makefile @@ -7,3 +7,5 @@ obj-m := vop.o vop-objs += vop_main.o vop-objs += vop_debugfs.o vop-objs += vop_vringh.o + +obj-$(CONFIG_VOP_LOOPBACK) += vop_loopback.o diff --git a/drivers/misc/mic/vop/vop_loopback.c b/drivers/misc/mic/vop/vop_loopback.c new file mode 100644 index 000000000000..76d787db3d3e --- /dev/null +++ b/drivers/misc/mic/vop/vop_loopback.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Axis Communications AB + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../bus/vop_bus.h" + +struct mic_irq { + struct list_head list; + int irq; + irqreturn_t (*func)(int irq, void *data); + void *data; +}; + +struct vop_loopback_end { + struct vop_loopback *loopback; + const char *name; + struct vop_device *vop; + struct list_head irqs; + struct mutex mutex; + struct work_struct work; +}; + +struct vop_loopback { + struct device *dev; + void *dp; + struct vop_loopback_end host; + struct vop_loopback_end guest; +}; + +static inline struct vop_loopback *vop_to_loopback(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static dma_addr_t +vop_loopback_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + return page_to_phys(page) + offset; +} + +static void vop_loopback_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ +} + +static int vop_loopback_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs) +{ + return remap_pfn_range(vma, vma->vm_start, + PHYS_PFN(dma_addr), size, + vma->vm_page_prot); +} + +static void *vop_loopback_dma_alloc(struct device *dev, size_t size, + dma_addr_t *handle, gfp_t gfp, + unsigned long attrs) +{ + void *p = (void *) __get_free_pages(gfp, get_order(size)); + + if (p) + *handle = virt_to_phys(p); + + return p; +} + +static void vop_loopback_dma_free(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t handle, + unsigned long attrs) +{ + free_pages((unsigned long) (uintptr_t) cpu_addr, get_order(size)); +} + +static const struct dma_map_ops vop_loopback_dma_ops = { + .map_page = vop_loopback_dma_map_page, + .unmap_page = vop_loopback_dma_unmap_page, + .mmap = vop_loopback_dma_mmap, + .alloc = vop_loopback_dma_alloc, + .free = vop_loopback_dma_free, +}; + + +static void vop_loopback_ack_interrupt(struct vop_device *vop, int num) +{ +} + +static int vop_loopback_next_db(struct vop_device *vop) +{ + return 0; +} + +static void *vop_loopback_get_dp(struct vop_device *vop) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + return loopback->dp; +} + +static void __iomem *vop_loopback_get_remote_dp(struct vop_device *vop) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + return (void __iomem *) loopback->dp; +} + +static struct mic_irq * +vop_loopback_request_irq(struct vop_loopback_end *end, + irqreturn_t (*func)(int irq, void *data), + void *data, int intr_src) +{ + struct mic_irq *mic_irq; + + mic_irq = kzalloc(sizeof(*mic_irq), GFP_KERNEL); + if (!mic_irq) + return ERR_PTR(-ENOMEM); + + mic_irq->irq = intr_src; + mic_irq->func = func; + mic_irq->data = data; + + mutex_lock(&end->mutex); + list_add(&mic_irq->list, &end->irqs); + mutex_unlock(&end->mutex); + + return mic_irq; +} + +static struct mic_irq * +vop_loopback_request_irq_host(struct vop_device *vop, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + return vop_loopback_request_irq(&loopback->host, func, data, intr_src); +} + +static struct mic_irq * +vop_loopback_request_irq_guest(struct vop_device *vop, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + return vop_loopback_request_irq(&loopback->guest, func, data, intr_src); +} + +static void vop_loopback_free_irq(struct vop_loopback_end *end, + struct mic_irq *cookie) +{ + mutex_lock(&end->mutex); + list_del(&cookie->list); + mutex_unlock(&end->mutex); + + kfree(cookie); +} + +static void vop_loopback_free_irq_host(struct vop_device *vop, + struct mic_irq *cookie, void *data) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + vop_loopback_free_irq(&loopback->host, cookie); +} + +static void vop_loopback_free_irq_guest(struct vop_device *vop, + struct mic_irq *cookie, void *data) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + vop_loopback_free_irq(&loopback->guest, cookie); +} + +static void vop_loopback_send_intr_host(struct vop_device *vop, int db) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + schedule_work(&loopback->guest.work); +} + +static void vop_loopback_send_intr_guest(struct vop_device *vop, int db) +{ + struct vop_loopback *loopback = vop_to_loopback(&vop->dev); + + schedule_work(&loopback->host.work); +} + +static void __iomem *vop_loopback_ioremap(struct vop_device *vop, + dma_addr_t pa, size_t len) +{ + return (void __iomem *) memremap(pa, len, MEMREMAP_WB); +} + +static void vop_loopback_iounmap(struct vop_device *vop, void __iomem *va) +{ + memunmap((void __force *) va); +} + +static struct vop_hw_ops vop_loopback_host_ops = { + .request_irq = vop_loopback_request_irq_host, + .free_irq = vop_loopback_free_irq_host, + .ack_interrupt = vop_loopback_ack_interrupt, + .next_db = vop_loopback_next_db, + .get_dp = vop_loopback_get_dp, + .get_remote_dp = vop_loopback_get_remote_dp, + .send_intr = vop_loopback_send_intr_host, + .remap = vop_loopback_ioremap, + .unmap = vop_loopback_iounmap, +}; + +static struct vop_hw_ops vop_loopback_guest_ops = { + .request_irq = vop_loopback_request_irq_guest, + .free_irq = vop_loopback_free_irq_guest, + .ack_interrupt = vop_loopback_ack_interrupt, + .next_db = vop_loopback_next_db, + .get_dp = vop_loopback_get_dp, + .get_remote_dp = vop_loopback_get_remote_dp, + .send_intr = vop_loopback_send_intr_guest, + .remap = vop_loopback_ioremap, + .unmap = vop_loopback_iounmap, +}; + +static void vop_loopback_irq(struct work_struct *work) +{ + struct vop_loopback_end *end = container_of(work, struct vop_loopback_end, work); + struct vop_loopback *loopback = end->loopback; + struct mic_irq *mic_irq; + + dev_dbg(loopback->dev, "%s irq\n", end->name); + + mutex_lock(&end->mutex); + list_for_each_entry(mic_irq, &end->irqs, list) { + irqreturn_t ret; + + dev_dbg(loopback->dev, "calling %pS\n", mic_irq->func); + ret = mic_irq->func(mic_irq->irq, mic_irq->data); + dev_dbg(loopback->dev, "%pS ret %d\n", mic_irq->func, ret); + } + mutex_unlock(&end->mutex); +} + +static void vop_loopback_bootparam_init(struct vop_loopback *loopback) +{ + struct mic_bootparam *bootparam = loopback->dp; + + bootparam->magic = cpu_to_le32(MIC_MAGIC); + bootparam->h2c_config_db = -1; + bootparam->node_id = 1; + bootparam->scif_host_dma_addr = 0x0; + bootparam->scif_card_dma_addr = 0x0; + bootparam->c2h_scif_db = -1; + bootparam->h2c_scif_db = -1; +} + +static void vop_loopback_end_init(struct vop_loopback *loopback, + struct vop_loopback_end *end, + const char *name) +{ + end->loopback = loopback; + end->name = name; + + INIT_WORK(&end->work, vop_loopback_irq); + + INIT_LIST_HEAD(&end->irqs); + mutex_init(&end->mutex); +} + +static int vop_loopback_probe(struct platform_device *pdev) +{ + struct vop_loopback *loopback; + int ret; + + loopback = devm_kzalloc(&pdev->dev, sizeof(*loopback), GFP_KERNEL); + if (!loopback) + return -ENOMEM; + + loopback->dp = (void *) devm_get_free_pages(&pdev->dev, + GFP_KERNEL | __GFP_ZERO, + get_order(MIC_DP_SIZE)); + if (!loopback->dp) + return -ENOMEM; + + loopback->dev = &pdev->dev; + + vop_loopback_end_init(loopback, &loopback->host, "host"); + vop_loopback_end_init(loopback, &loopback->guest, "guest"); + vop_loopback_bootparam_init(loopback); + + platform_set_drvdata(pdev, loopback); + + loopback->host.vop = vop_register_device(&pdev->dev, VOP_DEV_TRNSP, + &vop_loopback_dma_ops, + &vop_loopback_host_ops, 1, + NULL, NULL); + if (IS_ERR(loopback->host.vop)) + return PTR_ERR(loopback->host.vop); + + loopback->guest.vop = vop_register_device(&pdev->dev, VOP_DEV_TRNSP, + &vop_loopback_dma_ops, + &vop_loopback_guest_ops, + 0, NULL, NULL); + if (IS_ERR(loopback->guest.vop)) { + ret = PTR_ERR(loopback->guest.vop); + goto err_unregister_host; + } + + schedule_work(&loopback->guest.work); + + return 0; + +err_unregister_host: + vop_unregister_device(loopback->host.vop); + return ret; +} + +static int vop_loopback_remove(struct platform_device *pdev) +{ + struct vop_loopback *loopback = platform_get_drvdata(pdev); + + vop_unregister_device(loopback->guest.vop); + vop_unregister_device(loopback->host.vop); + + return 0; +} + +static struct platform_driver vop_loopback = { + .probe = vop_loopback_probe, + .remove = vop_loopback_remove, + .driver = { + .name = "vop-loopback", + }, +}; + +static struct platform_device *loopback_dev; + +static int __init vop_loopback_init(void) +{ + int ret; + + loopback_dev = platform_device_register_simple("vop-loopback", 0, + NULL, 0); + if (IS_ERR(loopback_dev)) + return PTR_ERR(loopback_dev); + + ret = platform_driver_register(&vop_loopback); + if (ret) + goto err_remove_dev; + + return 0; + +err_remove_dev: + platform_device_unregister(loopback_dev); + return ret; +} + +static void __exit vop_loopback_exit(void) +{ + platform_driver_unregister(&vop_loopback); + platform_device_unregister(loopback_dev); +} + +module_init(vop_loopback_init); +module_exit(vop_loopback_exit); + +MODULE_LICENSE("GPL v2"); -- 2.20.0