From patchwork Wed Aug 1 10:22:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kenneth Lee X-Patchwork-Id: 143268 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp680819ljj; Wed, 1 Aug 2018 03:24:16 -0700 (PDT) X-Google-Smtp-Source: AAOMgpefhafw8fv8Lls3r3S60h+LomQLQIBrg6/NQj3rnRe4QeUnpQh0wPbJ7XK4fzI6sO1gePjo X-Received: by 2002:a65:4107:: with SMTP id w7-v6mr23097569pgp.302.1533119056602; Wed, 01 Aug 2018 03:24:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533119056; cv=none; d=google.com; s=arc-20160816; b=rbf3rUyn7M0wvdJAi9WCuIKc6l34eAW63bvMQfLJ/5qF7nvb2apsoBkc6TRnZ1TqRx SoydjIU39bu2nL9MuJJW9nKxv5eyIcKIYOqdlvHn2xYsVObtZ2cmDuvn3ozW9EDKZ61R CItZH5YTxUU+D//ZeGlJ2fW2vHcwYSP3OV/kPPJvn/Jn4HlgNbAM0q9kIplKFT1I3Skj PFMOWoHAPF3txvG0Ya4FCgeCjafjEcfxcSecWULTFluV3pqt1XD/wwhIgvBQ2rbsJrIe QscZd2RpcWJbmoaO7J1V45x1lrUkGGiv/h2PwHGSmrPtFnzrxzvsysrdCloS4W8+BE9b qMRQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=Lpt+samP8CQqQ79TXRngQuvM7gOQ3mc3raE4OeDPodM=; b=KZisR8eMhwiH/WLjlJsZkw4GzmpoubeIEtA96g4mcsFJCCG1gAmawcD6rOj0iAUZTz znq0SScF4yKguB/vM6RdSPKFRi+uNRdyVHnHskFBoGhVUzN2FK+13nLxBkz+nyJTjgD1 IRzshVGtdpCcglCw38X5JXsyDuHpTpa9rBgeaos9sRtjxNjQZo/JdhWH1j+EowBYpV1J t2YXLIeyINItjx1ypyyParhgUiecEuY/xPcXvOwxj2skRGyUpJdR00SbuW/3lIbUUeMi 4KRUuo1bsfjSM6RGdIrv+qgWOzoFqQ4NsOfM3+ssJNUvCoSS1t1RX1Esh6tc7FiwPKWd sCIQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=MlLX4P99; spf=pass (google.com: best guess record for domain of linux-crypto-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-crypto-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id n8-v6si15447494pgl.101.2018.08.01.03.24.16; Wed, 01 Aug 2018 03:24:16 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-crypto-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=@gmail.com header.s=20161025 header.b=MlLX4P99; spf=pass (google.com: best guess record for domain of linux-crypto-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-crypto-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387528AbeHAMJU (ORCPT + 1 other); Wed, 1 Aug 2018 08:09:20 -0400 Received: from mail-qk0-f193.google.com ([209.85.220.193]:46407 "EHLO mail-qk0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387723AbeHAMJT (ORCPT ); Wed, 1 Aug 2018 08:09:19 -0400 Received: by mail-qk0-f193.google.com with SMTP id 191-v6so6324000qki.13; Wed, 01 Aug 2018 03:24:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Lpt+samP8CQqQ79TXRngQuvM7gOQ3mc3raE4OeDPodM=; b=MlLX4P9941JVGEFAS72ot4s4njBHa4NwMk0XSZV5wEUyB+Vy6TnoTn2MDdNxJvy6+F 2JYlp49GF/Z07hEdBzgdoaKYwVpVZm393xIwLXN5LpGZ//Myj8QZrHGNjPdTb27UaZ6V X+iF/I7kgXixN1lVxaF1wjAkV/dP82JL60HWysNtY79HIIOq2aXM7Q0dlsDD8gdvtm51 3WVKyIl3aufd+ZIPgU02nWSkkPGeqcUSvqRLKY6loAAxa6nwUV869R9aQVj413XDOqua 6RrN2NYxV0idZzDRstvh/0Wn0Y56HQOoYHE0Cf/f3fTIF2C9d7AtYmxEzgJpBnBoMNww +mVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Lpt+samP8CQqQ79TXRngQuvM7gOQ3mc3raE4OeDPodM=; b=XIoX9USIX2vSdooV/EkkPpl33w73iWRvmCU1nHtalJ8KLRAJv/CnMow7+0yvnNbAGs 7l91PItR/HUtk2SixLYFTkYm+We46cM5DhNfqAwd631RBOuRydeMZuV1nEK962HorB+N /LNcVouG4b7X8l6DVI6hbB2t49/ZIzfm98SLFMcoQP99TaT8juo8idxwguT2YQJpkJ4V 3PHUpsc6x+CQ0DmU6UcfD/UsFa+XoaAXjHbHsoqvWqddbaz8kTzA3L/1a4plQANSk9Vk tCxhZUY+M492maFhXyE6VN27SS5UvR0qWg9QjvQwQBb3PwnW8qZSTo4ArTa8yEcuCHKh rMdw== X-Gm-Message-State: AOUpUlEAc51dqSk6EFVId/tH1yfsOQSsoxsxiM3wAc0hxVYPahJcUADi 1iR1XQ2Zy8CyWPzW2YEvtFg= X-Received: by 2002:a37:5b83:: with SMTP id p125-v6mr23348358qkb.196.1533119053182; Wed, 01 Aug 2018 03:24:13 -0700 (PDT) Received: from localhost.localdomain ([104.237.86.144]) by smtp.gmail.com with ESMTPSA id s19-v6sm11176890qtj.61.2018.08.01.03.24.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 01 Aug 2018 03:24:12 -0700 (PDT) From: Kenneth Lee To: Jonathan Corbet , Herbert Xu , "David S . Miller" , Joerg Roedel , Alex Williamson , Kenneth Lee , Hao Fang , Zhou Wang , Zaibo Xu , Philippe Ombredanne , Greg Kroah-Hartman , Thomas Gleixner , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org, iommu@lists.linux-foundation.org, kvm@vger.kernel.org, linux-accelerators@lists.ozlabs.org, Lu Baolu , Sanjay Kumar Cc: linuxarm@huawei.com Subject: [RFC PATCH 3/7] vfio: add spimdev support Date: Wed, 1 Aug 2018 18:22:17 +0800 Message-Id: <20180801102221.5308-4-nek.in.cn@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180801102221.5308-1-nek.in.cn@gmail.com> References: <20180801102221.5308-1-nek.in.cn@gmail.com> Sender: linux-crypto-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org From: Kenneth Lee SPIMDEV is "Share Parent IOMMU Mdev". It is a vfio-mdev. But differ from the general vfio-mdev: 1. It shares its parent's IOMMU. 2. There is no hardware resource attached to the mdev is created. The hardware resource (A `queue') is allocated only when the mdev is opened. Currently only the vfio type-1 driver is updated to make it to be aware of. Signed-off-by: Kenneth Lee Signed-off-by: Zaibo Xu Signed-off-by: Zhou Wang --- drivers/vfio/Kconfig | 1 + drivers/vfio/Makefile | 1 + drivers/vfio/spimdev/Kconfig | 10 + drivers/vfio/spimdev/Makefile | 3 + drivers/vfio/spimdev/vfio_spimdev.c | 421 ++++++++++++++++++++++++++++ drivers/vfio/vfio_iommu_type1.c | 136 ++++++++- include/linux/vfio_spimdev.h | 95 +++++++ include/uapi/linux/vfio_spimdev.h | 28 ++ 8 files changed, 689 insertions(+), 6 deletions(-) create mode 100644 drivers/vfio/spimdev/Kconfig create mode 100644 drivers/vfio/spimdev/Makefile create mode 100644 drivers/vfio/spimdev/vfio_spimdev.c create mode 100644 include/linux/vfio_spimdev.h create mode 100644 include/uapi/linux/vfio_spimdev.h -- 2.17.1 diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index c84333eb5eb5..3719eba72ef1 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -47,4 +47,5 @@ menuconfig VFIO_NOIOMMU source "drivers/vfio/pci/Kconfig" source "drivers/vfio/platform/Kconfig" source "drivers/vfio/mdev/Kconfig" +source "drivers/vfio/spimdev/Kconfig" source "virt/lib/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index de67c4725cce..28f3ef0cdce1 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o obj-$(CONFIG_VFIO_PCI) += pci/ obj-$(CONFIG_VFIO_PLATFORM) += platform/ obj-$(CONFIG_VFIO_MDEV) += mdev/ +obj-$(CONFIG_VFIO_SPIMDEV) += spimdev/ diff --git a/drivers/vfio/spimdev/Kconfig b/drivers/vfio/spimdev/Kconfig new file mode 100644 index 000000000000..1226301f9d0e --- /dev/null +++ b/drivers/vfio/spimdev/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +config VFIO_SPIMDEV + tristate "Support for Share Parent IOMMU MDEV" + depends on VFIO_MDEV_DEVICE + help + Support for VFIO Share Parent IOMMU MDEV, which enable the kernel to + support for the light weight hardware accelerator framework, WrapDrive. + + To compile this as a module, choose M here: the module will be called + spimdev. diff --git a/drivers/vfio/spimdev/Makefile b/drivers/vfio/spimdev/Makefile new file mode 100644 index 000000000000..d02fb69c37e4 --- /dev/null +++ b/drivers/vfio/spimdev/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +spimdev-y := spimdev.o +obj-$(CONFIG_VFIO_SPIMDEV) += vfio_spimdev.o diff --git a/drivers/vfio/spimdev/vfio_spimdev.c b/drivers/vfio/spimdev/vfio_spimdev.c new file mode 100644 index 000000000000..1b6910c9d27d --- /dev/null +++ b/drivers/vfio/spimdev/vfio_spimdev.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include + +struct spimdev_mdev_state { + struct vfio_spimdev *spimdev; +}; + +static struct class *spimdev_class; +static DEFINE_IDR(spimdev_idr); + +static int vfio_spimdev_dev_exist(struct device *dev, void *data) +{ + return !strcmp(dev_name(dev), dev_name((struct device *)data)); +} + +#ifdef CONFIG_IOMMU_SVA +static bool vfio_spimdev_is_valid_pasid(int pasid) +{ + struct mm_struct *mm; + + mm = iommu_sva_find(pasid); + if (mm) { + mmput(mm); + return mm == current->mm; + } + + return false; +} +#endif + +/* Check if the device is a mediated device belongs to vfio_spimdev */ +int vfio_spimdev_is_spimdev(struct device *dev) +{ + struct mdev_device *mdev; + struct device *pdev; + + mdev = mdev_from_dev(dev); + if (!mdev) + return 0; + + pdev = mdev_parent_dev(mdev); + if (!pdev) + return 0; + + return class_for_each_device(spimdev_class, NULL, pdev, + vfio_spimdev_dev_exist); +} +EXPORT_SYMBOL_GPL(vfio_spimdev_is_spimdev); + +struct vfio_spimdev *vfio_spimdev_pdev_spimdev(struct device *dev) +{ + struct device *class_dev; + + if (!dev) + return ERR_PTR(-EINVAL); + + class_dev = class_find_device(spimdev_class, NULL, dev, + (int(*)(struct device *, const void *))vfio_spimdev_dev_exist); + if (!class_dev) + return ERR_PTR(-ENODEV); + + return container_of(class_dev, struct vfio_spimdev, cls_dev); +} +EXPORT_SYMBOL_GPL(vfio_spimdev_pdev_spimdev); + +struct vfio_spimdev *mdev_spimdev(struct mdev_device *mdev) +{ + struct device *pdev = mdev_parent_dev(mdev); + + return vfio_spimdev_pdev_spimdev(pdev); +} +EXPORT_SYMBOL_GPL(mdev_spimdev); + +static ssize_t iommu_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); + + if (!spimdev) + return -ENODEV; + + return sprintf(buf, "%d\n", spimdev->iommu_type); +} + +static DEVICE_ATTR_RO(iommu_type); + +static ssize_t dma_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); + + if (!spimdev) + return -ENODEV; + + return sprintf(buf, "%d\n", spimdev->dma_flag); +} + +static DEVICE_ATTR_RO(dma_flag); + +/* mdev->dev_attr_groups */ +static struct attribute *vfio_spimdev_attrs[] = { + &dev_attr_iommu_type.attr, + &dev_attr_dma_flag.attr, + NULL, +}; +static const struct attribute_group vfio_spimdev_group = { + .name = VFIO_SPIMDEV_PDEV_ATTRS_GRP_NAME, + .attrs = vfio_spimdev_attrs, +}; +const struct attribute_group *vfio_spimdev_groups[] = { + &vfio_spimdev_group, + NULL, +}; + +/* default attributes for mdev->supported_type_groups, used by registerer*/ +#define MDEV_TYPE_ATTR_RO_EXPORT(name) \ + MDEV_TYPE_ATTR_RO(name); \ + EXPORT_SYMBOL_GPL(mdev_type_attr_##name); + +#define DEF_SIMPLE_SPIMDEV_ATTR(_name, spimdev_member, format) \ +static ssize_t _name##_show(struct kobject *kobj, struct device *dev, \ + char *buf) \ +{ \ + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); \ + if (!spimdev) \ + return -ENODEV; \ + return sprintf(buf, format, spimdev->spimdev_member); \ +} \ +MDEV_TYPE_ATTR_RO_EXPORT(_name) + +DEF_SIMPLE_SPIMDEV_ATTR(flags, flags, "%d"); +DEF_SIMPLE_SPIMDEV_ATTR(name, name, "%s"); /* this should be algorithm name, */ + /* but you would not care if you have only one algorithm */ +DEF_SIMPLE_SPIMDEV_ATTR(device_api, api_ver, "%s"); + +/* this return total queue left, not mdev left */ +static ssize_t +available_instances_show(struct kobject *kobj, struct device *dev, char *buf) +{ + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); + + return sprintf(buf, "%d", + spimdev->ops->get_available_instances(spimdev)); +} +MDEV_TYPE_ATTR_RO_EXPORT(available_instances); + +static int vfio_spimdev_mdev_create(struct kobject *kobj, + struct mdev_device *mdev) +{ + struct device *dev = mdev_dev(mdev); + struct device *pdev = mdev_parent_dev(mdev); + struct spimdev_mdev_state *mdev_state; + struct vfio_spimdev *spimdev = mdev_spimdev(mdev); + + if (!spimdev->ops->get_queue) + return -ENODEV; + + mdev_state = devm_kzalloc(dev, sizeof(struct spimdev_mdev_state), + GFP_KERNEL); + if (!mdev_state) + return -ENOMEM; + mdev_set_drvdata(mdev, mdev_state); + mdev_state->spimdev = spimdev; + dev->iommu_fwspec = pdev->iommu_fwspec; + get_device(pdev); + __module_get(spimdev->owner); + + return 0; +} + +static int vfio_spimdev_mdev_remove(struct mdev_device *mdev) +{ + struct device *dev = mdev_dev(mdev); + struct device *pdev = mdev_parent_dev(mdev); + struct vfio_spimdev *spimdev = mdev_spimdev(mdev); + + put_device(pdev); + module_put(spimdev->owner); + dev->iommu_fwspec = NULL; + mdev_set_drvdata(mdev, NULL); + + return 0; +} + +/* Wake up the process who is waiting this queue */ +void vfio_spimdev_wake_up(struct vfio_spimdev_queue *q) +{ + wake_up(&q->wait); +} +EXPORT_SYMBOL_GPL(vfio_spimdev_wake_up); + +static int vfio_spimdev_q_file_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int vfio_spimdev_q_file_release(struct inode *inode, struct file *file) +{ + struct vfio_spimdev_queue *q = + (struct vfio_spimdev_queue *)file->private_data; + struct vfio_spimdev *spimdev = q->spimdev; + int ret; + + ret = spimdev->ops->put_queue(q); + if (ret) { + dev_err(spimdev->dev, "drv put queue fail (%d)!\n", ret); + return ret; + } + + put_device(mdev_dev(q->mdev)); + + return 0; +} + +static long vfio_spimdev_q_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct vfio_spimdev_queue *q = + (struct vfio_spimdev_queue *)file->private_data; + struct vfio_spimdev *spimdev = q->spimdev; + + if (spimdev->ops->ioctl) + return spimdev->ops->ioctl(q, cmd, arg); + + dev_err(spimdev->dev, "ioctl cmd (%d) is not supported!\n", cmd); + + return -EINVAL; +} + +static int vfio_spimdev_q_file_mmap(struct file *file, + struct vm_area_struct *vma) +{ + struct vfio_spimdev_queue *q = + (struct vfio_spimdev_queue *)file->private_data; + struct vfio_spimdev *spimdev = q->spimdev; + + if (spimdev->ops->mmap) + return spimdev->ops->mmap(q, vma); + + dev_err(spimdev->dev, "no driver mmap!\n"); + return -EINVAL; +} + +static __poll_t vfio_spimdev_q_file_poll(struct file *file, poll_table *wait) +{ + struct vfio_spimdev_queue *q = + (struct vfio_spimdev_queue *)file->private_data; + struct vfio_spimdev *spimdev = q->spimdev; + + poll_wait(file, &q->wait, wait); + if (spimdev->ops->is_q_updated(q)) + return EPOLLIN | EPOLLRDNORM; + + return 0; +} + +static const struct file_operations spimdev_q_file_ops = { + .owner = THIS_MODULE, + .open = vfio_spimdev_q_file_open, + .unlocked_ioctl = vfio_spimdev_q_file_ioctl, + .release = vfio_spimdev_q_file_release, + .poll = vfio_spimdev_q_file_poll, + .mmap = vfio_spimdev_q_file_mmap, +}; + +static long vfio_spimdev_mdev_get_queue(struct mdev_device *mdev, + struct vfio_spimdev *spimdev, unsigned long arg) +{ + struct vfio_spimdev_queue *q; + int ret; + +#ifdef CONFIG_IOMMU_SVA + int pasid = arg; + + if (!vfio_spimdev_is_valid_pasid(pasid)) + return -EINVAL; +#endif + + if (!spimdev->ops->get_queue) + return -EINVAL; + + ret = spimdev->ops->get_queue(spimdev, arg, &q); + if (ret < 0) { + dev_err(spimdev->dev, "get_queue failed\n"); + return -ENODEV; + } + + ret = anon_inode_getfd("spimdev_q", &spimdev_q_file_ops, + q, O_CLOEXEC | O_RDWR); + if (ret < 0) { + dev_err(spimdev->dev, "getfd fail %d\n", ret); + goto err_with_queue; + } + + q->fd = ret; + q->spimdev = spimdev; + q->mdev = mdev; + q->container = arg; + init_waitqueue_head(&q->wait); + get_device(mdev_dev(mdev)); + + return ret; + +err_with_queue: + spimdev->ops->put_queue(q); + return ret; +} + +static long vfio_spimdev_mdev_ioctl(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg) +{ + struct spimdev_mdev_state *mdev_state; + struct vfio_spimdev *spimdev; + + if (!mdev) + return -ENODEV; + + mdev_state = mdev_get_drvdata(mdev); + if (!mdev_state) + return -ENODEV; + + spimdev = mdev_state->spimdev; + if (!spimdev) + return -ENODEV; + + if (cmd == VFIO_SPIMDEV_CMD_GET_Q) + return vfio_spimdev_mdev_get_queue(mdev, spimdev, arg); + + dev_err(spimdev->dev, + "%s, ioctl cmd (0x%x) is not supported!\n", __func__, cmd); + return -EINVAL; +} + +static void vfio_spimdev_release(struct device *dev) { } +static void vfio_spimdev_mdev_release(struct mdev_device *mdev) { } +static int vfio_spimdev_mdev_open(struct mdev_device *mdev) { return 0; } + +/** + * vfio_spimdev_register - register a spimdev + * @spimdev: device structure + */ +int vfio_spimdev_register(struct vfio_spimdev *spimdev) +{ + int ret; + const char *drv_name; + + if (!spimdev->dev) + return -ENODEV; + + drv_name = dev_driver_string(spimdev->dev); + if (strstr(drv_name, "-")) { + pr_err("spimdev: parent driver name cannot include '-'!\n"); + return -EINVAL; + } + + spimdev->dev_id = idr_alloc(&spimdev_idr, spimdev, 0, 0, GFP_KERNEL); + if (spimdev->dev_id < 0) + return spimdev->dev_id; + + atomic_set(&spimdev->ref, 0); + spimdev->cls_dev.parent = spimdev->dev; + spimdev->cls_dev.class = spimdev_class; + spimdev->cls_dev.release = vfio_spimdev_release; + dev_set_name(&spimdev->cls_dev, "%s", dev_name(spimdev->dev)); + ret = device_register(&spimdev->cls_dev); + if (ret) + return ret; + + spimdev->mdev_fops.owner = spimdev->owner; + spimdev->mdev_fops.dev_attr_groups = vfio_spimdev_groups; + WARN_ON(!spimdev->mdev_fops.supported_type_groups); + spimdev->mdev_fops.create = vfio_spimdev_mdev_create; + spimdev->mdev_fops.remove = vfio_spimdev_mdev_remove; + spimdev->mdev_fops.ioctl = vfio_spimdev_mdev_ioctl; + spimdev->mdev_fops.open = vfio_spimdev_mdev_open; + spimdev->mdev_fops.release = vfio_spimdev_mdev_release; + + ret = mdev_register_device(spimdev->dev, &spimdev->mdev_fops); + if (ret) + device_unregister(&spimdev->cls_dev); + + return ret; +} +EXPORT_SYMBOL_GPL(vfio_spimdev_register); + +/** + * vfio_spimdev_unregister - unregisters a spimdev + * @spimdev: device to unregister + * + * Unregister a miscellaneous device that wat previously successully registered + * with vfio_spimdev_register(). + */ +void vfio_spimdev_unregister(struct vfio_spimdev *spimdev) +{ + mdev_unregister_device(spimdev->dev); + device_unregister(&spimdev->cls_dev); +} +EXPORT_SYMBOL_GPL(vfio_spimdev_unregister); + +static int __init vfio_spimdev_init(void) +{ + spimdev_class = class_create(THIS_MODULE, VFIO_SPIMDEV_CLASS_NAME); + return PTR_ERR_OR_ZERO(spimdev_class); +} + +static __exit void vfio_spimdev_exit(void) +{ + class_destroy(spimdev_class); + idr_destroy(&spimdev_idr); +} + +module_init(vfio_spimdev_init); +module_exit(vfio_spimdev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hisilicon Tech. Co., Ltd."); +MODULE_DESCRIPTION("VFIO Share Parent's IOMMU Mediated Device"); diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 3e5b17710a4f..0ec38a17c98c 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -41,6 +41,7 @@ #include #include #include +#include #define DRIVER_VERSION "0.2" #define DRIVER_AUTHOR "Alex Williamson " @@ -89,6 +90,8 @@ struct vfio_dma { }; struct vfio_group { + /* iommu_group of mdev's parent device */ + struct iommu_group *parent_group; struct iommu_group *iommu_group; struct list_head next; }; @@ -1327,6 +1330,109 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base) return ret; } +/* return 0 if the device is not spimdev. + * return 1 if the device is spimdev, the data will be updated with parent + * device's group. + * return -errno if other error. + */ +static int vfio_spimdev_type(struct device *dev, void *data) +{ + struct iommu_group **group = data; + struct iommu_group *pgroup; + int (*spimdev_mdev)(struct device *dev); + struct device *pdev; + int ret = 1; + + /* vfio_spimdev module is not configurated */ + spimdev_mdev = symbol_get(vfio_spimdev_is_spimdev); + if (!spimdev_mdev) + return 0; + + /* check if it belongs to vfio_spimdev device */ + if (!spimdev_mdev(dev)) { + ret = 0; + goto get_exit; + } + + pdev = dev->parent; + pgroup = iommu_group_get(pdev); + if (!pgroup) { + ret = -ENODEV; + goto get_exit; + } + + if (group) { + /* check if all parent devices is the same */ + if (*group && *group != pgroup) + ret = -ENODEV; + else + *group = pgroup; + } + + iommu_group_put(pgroup); + +get_exit: + symbol_put(vfio_spimdev_is_spimdev); + + return ret; +} + +/* return 0 or -errno */ +static int vfio_spimdev_bus(struct device *dev, void *data) +{ + struct bus_type **bus = data; + + if (!dev->bus) + return -ENODEV; + + /* ensure all devices has the same bus_type */ + if (*bus && *bus != dev->bus) + return -EINVAL; + + *bus = dev->bus; + return 0; +} + +/* return 0 means it is not spi group, 1 means it is, or -EXXX for error */ +static int vfio_iommu_type1_attach_spigroup(struct vfio_domain *domain, + struct vfio_group *group, + struct iommu_group *iommu_group) +{ + int ret; + struct bus_type *pbus = NULL; + struct iommu_group *pgroup = NULL; + + ret = iommu_group_for_each_dev(iommu_group, &pgroup, + vfio_spimdev_type); + if (ret < 0) + goto out; + else if (ret > 0) { + domain->domain = iommu_group_share_domain(pgroup); + if (IS_ERR(domain->domain)) + goto out; + ret = iommu_group_for_each_dev(pgroup, &pbus, + vfio_spimdev_bus); + if (ret < 0) + goto err_with_share_domain; + + if (pbus && iommu_capable(pbus, IOMMU_CAP_CACHE_COHERENCY)) + domain->prot |= IOMMU_CACHE; + + group->parent_group = pgroup; + INIT_LIST_HEAD(&domain->group_list); + list_add(&group->next, &domain->group_list); + + return 1; + } + + return 0; + +err_with_share_domain: + iommu_group_unshare_domain(pgroup); +out: + return ret; +} + static int vfio_iommu_type1_attach_group(void *iommu_data, struct iommu_group *iommu_group) { @@ -1335,8 +1441,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, struct vfio_domain *domain, *d; struct bus_type *bus = NULL, *mdev_bus; int ret; - bool resv_msi, msi_remap; - phys_addr_t resv_msi_base; + bool resv_msi = false, msi_remap; + phys_addr_t resv_msi_base = 0; mutex_lock(&iommu->lock); @@ -1373,6 +1479,14 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, if (mdev_bus) { if ((bus == mdev_bus) && !iommu_present(bus)) { symbol_put(mdev_bus_type); + + ret = vfio_iommu_type1_attach_spigroup(domain, group, + iommu_group); + if (ret < 0) + goto out_free; + else if (ret > 0) + goto replay_check; + if (!iommu->external_domain) { INIT_LIST_HEAD(&domain->group_list); iommu->external_domain = domain; @@ -1451,12 +1565,13 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, vfio_test_domain_fgsp(domain); +replay_check: /* replay mappings on new domains */ ret = vfio_iommu_replay(iommu, domain); if (ret) goto out_detach; - if (resv_msi) { + if (!group->parent_group && resv_msi) { ret = iommu_get_msi_cookie(domain->domain, resv_msi_base); if (ret) goto out_detach; @@ -1471,7 +1586,10 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, out_detach: iommu_detach_group(domain->domain, iommu_group); out_domain: - iommu_domain_free(domain->domain); + if (group->parent_group) + iommu_group_unshare_domain(group->parent_group); + else + iommu_domain_free(domain->domain); out_free: kfree(domain); kfree(group); @@ -1533,6 +1651,7 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, struct vfio_iommu *iommu = iommu_data; struct vfio_domain *domain; struct vfio_group *group; + int ret; mutex_lock(&iommu->lock); @@ -1560,7 +1679,11 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, if (!group) continue; - iommu_detach_group(domain->domain, iommu_group); + if (group->parent_group) + iommu_group_unshare_domain(group->parent_group); + else + iommu_detach_group(domain->domain, iommu_group); + list_del(&group->next); kfree(group); /* @@ -1577,7 +1700,8 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, else vfio_iommu_unmap_unpin_reaccount(iommu); } - iommu_domain_free(domain->domain); + if (!ret) + iommu_domain_free(domain->domain); list_del(&domain->next); kfree(domain); } diff --git a/include/linux/vfio_spimdev.h b/include/linux/vfio_spimdev.h new file mode 100644 index 000000000000..f7e7d90013e1 --- /dev/null +++ b/include/linux/vfio_spimdev.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __VFIO_SPIMDEV_H +#define __VFIO_SPIMDEV_H + +#include +#include +#include +#include +#include + +struct vfio_spimdev_queue; +struct vfio_spimdev; + +/** + * struct vfio_spimdev_ops - WD device operations + * @get_queue: get a queue from the device according to algorithm + * @put_queue: free a queue to the device + * @is_q_updated: check whether the task is finished + * @mask_notify: mask the task irq of queue + * @mmap: mmap addresses of queue to user space + * @reset: reset the WD device + * @reset_queue: reset the queue + * @ioctl: ioctl for user space users of the queue + * @get_available_instances: get numbers of the queue remained + */ +struct vfio_spimdev_ops { + int (*get_queue)(struct vfio_spimdev *spimdev, unsigned long arg, + struct vfio_spimdev_queue **q); + int (*put_queue)(struct vfio_spimdev_queue *q); + int (*is_q_updated)(struct vfio_spimdev_queue *q); + void (*mask_notify)(struct vfio_spimdev_queue *q, int event_mask); + int (*mmap)(struct vfio_spimdev_queue *q, struct vm_area_struct *vma); + int (*reset)(struct vfio_spimdev *spimdev); + int (*reset_queue)(struct vfio_spimdev_queue *q); + long (*ioctl)(struct vfio_spimdev_queue *q, unsigned int cmd, + unsigned long arg); + int (*get_available_instances)(struct vfio_spimdev *spimdev); +}; + +struct vfio_spimdev_queue { + struct mutex mutex; + struct vfio_spimdev *spimdev; + int qid; + __u32 flags; + void *priv; + wait_queue_head_t wait; + struct mdev_device *mdev; + int fd; + int container; +#ifdef CONFIG_IOMMU_SVA + int pasid; +#endif +}; + +struct vfio_spimdev { + const char *name; + int status; + atomic_t ref; + struct module *owner; + const struct vfio_spimdev_ops *ops; + struct device *dev; + struct device cls_dev; + bool is_vf; + u32 iommu_type; + u32 dma_flag; + u32 dev_id; + void *priv; + int flags; + const char *api_ver; + struct mdev_parent_ops mdev_fops; +}; + +int vfio_spimdev_register(struct vfio_spimdev *spimdev); +void vfio_spimdev_unregister(struct vfio_spimdev *spimdev); +void vfio_spimdev_wake_up(struct vfio_spimdev_queue *q); +int vfio_spimdev_is_spimdev(struct device *dev); +struct vfio_spimdev *vfio_spimdev_pdev_spimdev(struct device *dev); +int vfio_spimdev_pasid_pri_check(int pasid); +int vfio_spimdev_get(struct device *dev); +int vfio_spimdev_put(struct device *dev); +struct vfio_spimdev *mdev_spimdev(struct mdev_device *mdev); + +extern struct mdev_type_attribute mdev_type_attr_flags; +extern struct mdev_type_attribute mdev_type_attr_name; +extern struct mdev_type_attribute mdev_type_attr_device_api; +extern struct mdev_type_attribute mdev_type_attr_available_instances; +#define VFIO_SPIMDEV_DEFAULT_MDEV_TYPE_ATTRS \ + &mdev_type_attr_name.attr, \ + &mdev_type_attr_device_api.attr, \ + &mdev_type_attr_available_instances.attr, \ + &mdev_type_attr_flags.attr + +#define _VFIO_SPIMDEV_REGION(vm_pgoff) (vm_pgoff & 0xf) + +#endif diff --git a/include/uapi/linux/vfio_spimdev.h b/include/uapi/linux/vfio_spimdev.h new file mode 100644 index 000000000000..3435e5c345b4 --- /dev/null +++ b/include/uapi/linux/vfio_spimdev.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _UAPIVFIO_SPIMDEV_H +#define _UAPIVFIO_SPIMDEV_H + +#include + +#define VFIO_SPIMDEV_CLASS_NAME "spimdev" + +/* Device ATTRs in parent dev SYSFS DIR */ +#define VFIO_SPIMDEV_PDEV_ATTRS_GRP_NAME "params" + +/* Parent device attributes */ +#define SPIMDEV_IOMMU_TYPE "iommu_type" +#define SPIMDEV_DMA_FLAG "dma_flag" + +/* Maximum length of algorithm name string */ +#define VFIO_SPIMDEV_ALG_NAME_SIZE 64 + +/* the bits used in SPIMDEV_DMA_FLAG attributes */ +#define VFIO_SPIMDEV_DMA_INVALID 0 +#define VFIO_SPIMDEV_DMA_SINGLE_PROC_MAP 1 +#define VFIO_SPIMDEV_DMA_MULTI_PROC_MAP 2 +#define VFIO_SPIMDEV_DMA_SVM 4 +#define VFIO_SPIMDEV_DMA_SVM_NO_FAULT 8 +#define VFIO_SPIMDEV_DMA_PHY 16 + +#define VFIO_SPIMDEV_CMD_GET_Q _IO('W', 1) +#endif