lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1578408399-20092-2-git-send-email-weiqi4@huawei.com>
Date:   Tue, 7 Jan 2020 22:46:38 +0800
From:   weiqi <weiqi4@...wei.com>
To:     <alexander.h.duyck@...ux.intel.com>, <alex.williamson@...hat.com>
CC:     <kvm@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
        <pbonzini@...hat.com>, <x86@...nel.org>, wei qi <weiqi4@...wei.com>
Subject: [PATCH 1/2] vfio: add mmap/munmap API for page hinting

From: wei qi <weiqi4@...wei.com>

add mmap/munmap API for page hinting.

Signed-off-by: wei qi <weiqi4@...wei.com>
---
 drivers/vfio/vfio.c             | 109 ++++++++++++++++++++++++++++
 drivers/vfio/vfio_iommu_type1.c | 157 +++++++++++++++++++++++++++++++++++++++-
 include/linux/vfio.h            |  17 ++++-
 3 files changed, 280 insertions(+), 3 deletions(-)

diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index c848262..c7e9103 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -1866,6 +1866,115 @@ int vfio_set_irqs_validate_and_prepare(struct vfio_irq_set *hdr, int num_irqs,
 }
 EXPORT_SYMBOL(vfio_set_irqs_validate_and_prepare);
 
+int vfio_mmap_pages(struct device *dev, unsigned long user_pfn,
+			unsigned long page_size, int prot,
+			unsigned long pfn)
+{
+	struct vfio_container *container;
+	struct vfio_group *group;
+	struct vfio_iommu_driver *driver;
+	int ret;
+
+	if (!dev || !user_pfn || !page_size)
+		return -EINVAL;
+
+	group = vfio_group_get_from_dev(dev);
+	if (!group)
+		return -ENODEV;
+
+	ret = vfio_group_add_container_user(group);
+	if (ret)
+		goto err_pin_pages;
+
+	container = group->container;
+	driver = container->iommu_driver;
+	if (likely(driver && driver->ops->mmap_pages))
+		ret = driver->ops->mmap_pages(container->iommu_data, user_pfn,
+					page_size, prot, pfn);
+	else
+		ret = -ENOTTY;
+
+	vfio_group_try_dissolve_container(group);
+
+err_pin_pages:
+	vfio_group_put(group);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_mmap_pages);
+
+int vfio_munmap_pages(struct device *dev, unsigned long user_pfn,
+			unsigned long page_size)
+{
+	struct vfio_container *container;
+	struct vfio_group *group;
+	struct vfio_iommu_driver *driver;
+	int ret;
+
+	if (!dev || !user_pfn || !page_size)
+		return -EINVAL;
+
+	group = vfio_group_get_from_dev(dev);
+	if (!group)
+		return -ENODEV;
+
+	ret = vfio_group_add_container_user(group);
+	if (ret)
+		goto err_pin_pages;
+
+	container = group->container;
+	driver = container->iommu_driver;
+	if (likely(driver && driver->ops->munmap_pages))
+		ret = driver->ops->munmap_pages(container->iommu_data, user_pfn,
+						page_size);
+	else
+		ret = -ENOTTY;
+
+	vfio_group_try_dissolve_container(group);
+
+err_pin_pages:
+	vfio_group_put(group);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vfio_munmap_pages);
+
+int vfio_dma_find(struct device *dev, unsigned long user_pfn, int npage,
+		unsigned long *phys_pfn)
+{
+	struct vfio_container *container;
+	struct vfio_group *group;
+	struct vfio_iommu_driver *driver;
+	int ret;
+
+	if (!dev || !user_pfn || !npage || !phys_pfn)
+		return -EINVAL;
+
+	if (npage > VFIO_PIN_PAGES_MAX_ENTRIES)
+		return -E2BIG;
+
+	group = vfio_group_get_from_dev(dev);
+	if (!group)
+		return -ENODEV;
+
+	ret = vfio_group_add_container_user(group);
+	if (ret)
+		goto err_pin_pages;
+
+	container = group->container;
+	driver = container->iommu_driver;
+	if (driver && driver->ops->dma_find)
+		ret = driver->ops->dma_find(container->iommu_data, user_pfn,
+					npage, phys_pfn);
+	else
+		ret = -ENOTTY;
+
+	vfio_group_try_dissolve_container(group);
+
+err_pin_pages:
+	vfio_group_put(group);
+	return ret;
+}
+EXPORT_SYMBOL(vfio_dma_find);
+
 /*
  * Pin a set of guest PFNs and return their associated host PFNs for local
  * domain only.
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 2ada8e6..df115dc 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -414,7 +414,7 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
 		goto out;
 
 	/* Lock all the consecutive pages from pfn_base */
-	for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; pinned < npage;
+	for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; (pinned < npage && pinned < 512);
 	     pinned++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) {
 		ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, &pfn);
 		if (ret)
@@ -768,7 +768,7 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
 		phys_addr_t phys, next;
 
 		phys = iommu_iova_to_phys(domain->domain, iova);
-		if (WARN_ON(!phys)) {
+		if (!phys) {
 			iova += PAGE_SIZE;
 			continue;
 		}
@@ -1154,6 +1154,156 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
 	return ret;
 }
 
+static int vfio_iommu_type1_munmap_pages(void *iommu_data,
+					unsigned long user_pfn,
+					unsigned long page_size)
+{
+	struct vfio_iommu *iommu = iommu_data;
+	struct vfio_domain *domain;
+	struct vfio_dma *dma;
+	dma_addr_t iova = user_pfn  << PAGE_SHIFT;
+	int ret = 0;
+	phys_addr_t phys;
+	size_t unmapped;
+	long unlocked = 0;
+
+	if (!iommu || !user_pfn || !page_size)
+		return -EINVAL;
+
+	/* Supported for v2 version only */
+	if (!iommu->v2)
+		return -EACCES;
+
+	mutex_lock(&iommu->lock);
+	dma = vfio_find_dma(iommu, iova, page_size);
+	if (!dma) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	domain = list_first_entry(&iommu->domain_list,
+			struct vfio_domain, next);
+	phys = iommu_iova_to_phys(domain->domain, iova);
+	if (!phys) {
+		goto out_unlock;
+	} else {
+		unmapped = iommu_unmap(domain->domain, iova, page_size);
+		unlocked = vfio_unpin_pages_remote(dma, iova,
+					phys >> PAGE_SHIFT,
+					unmapped >> PAGE_SHIFT, true);
+	}
+
+out_unlock:
+	mutex_unlock(&iommu->lock);
+	return ret;
+}
+
+static int vfio_iommu_type1_mmap_pages(void *iommu_data,
+				unsigned long user_pfn,
+				unsigned long page_size, int prot,
+				unsigned long pfn)
+{
+	struct vfio_iommu *iommu = iommu_data;
+	struct vfio_domain *domain;
+	struct vfio_dma *dma;
+	dma_addr_t iova = user_pfn  << PAGE_SHIFT;
+	int ret = 0;
+	size_t unmapped;
+	phys_addr_t phys;
+	long unlocked = 0;
+
+	if (!iommu || !user_pfn || !page_size || !pfn)
+		return -EINVAL;
+
+	/* Supported for v2 version only */
+	if (!iommu->v2)
+		return -EACCES;
+
+	mutex_lock(&iommu->lock);
+
+	if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) {
+		ret = -EACCES;
+		goto out_unlock;
+	}
+
+	dma = vfio_find_dma(iommu, iova, page_size);
+	if (!dma) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	domain = list_first_entry(&iommu->domain_list,
+		struct vfio_domain, next);
+
+	phys = iommu_iova_to_phys(domain->domain, iova);
+	if (phys) {
+		unmapped = iommu_unmap(domain->domain, iova, page_size);
+		unlocked = vfio_unpin_pages_remote(dma, iova,
+					phys >> PAGE_SHIFT,
+					unmapped >> PAGE_SHIFT, false);
+	}
+
+	ret = vfio_iommu_map(iommu, iova, pfn, page_size >> PAGE_SHIFT, prot);
+	if (ret) {
+		pr_warn("%s: gfn: %lx, pfn: %lx, npages:%lu\n", __func__,
+			user_pfn, pfn, page_size >> PAGE_SHIFT);
+	}
+
+out_unlock:
+	mutex_unlock(&iommu->lock);
+	return ret;
+}
+
+u64 vfio_iommu_iova_to_phys(struct vfio_iommu *iommu, dma_addr_t iova)
+{
+	struct vfio_domain *d;
+	u64 phys;
+
+	list_for_each_entry(d, &iommu->domain_list, next) {
+		phys = iommu_iova_to_phys(d->domain, iova);
+		if (phys)
+			return phys;
+	}
+	return 0;
+}
+
+static int vfio_iommu_type1_dma_find(void *iommu_data,
+					unsigned long user_pfn,
+					int npage, unsigned long *phys_pfn)
+{
+	struct vfio_iommu *iommu = iommu_data;
+	int i = 0;
+	struct vfio_dma *dma;
+	u64 phys;
+	dma_addr_t iova;
+
+	if (!iommu || !user_pfn)
+		return -EINVAL;
+
+	/* Supported for v2 version only */
+	if (!iommu->v2)
+		return -EACCES;
+
+	 mutex_lock(&iommu->lock);
+
+	iova = user_pfn << PAGE_SHIFT;
+	dma = vfio_find_dma(iommu, iova, PAGE_SIZE);
+	if (!dma)
+		goto unpin_exit;
+
+	if (((user_pfn + npage) << PAGE_SHIFT) <= (dma->iova + dma->size))
+		i = npage;
+	else
+		goto unpin_exit;
+
+	phys = vfio_iommu_iova_to_phys(iommu, iova);
+	*phys_pfn = phys >> PAGE_SHIFT;
+
+unpin_exit:
+	mutex_unlock(&iommu->lock);
+	return i;
+}
+
 static int vfio_bus_type(struct device *dev, void *data)
 {
 	struct bus_type **bus = data;
@@ -2336,6 +2486,9 @@ static int vfio_iommu_type1_unregister_notifier(void *iommu_data,
 	.detach_group		= vfio_iommu_type1_detach_group,
 	.pin_pages		= vfio_iommu_type1_pin_pages,
 	.unpin_pages		= vfio_iommu_type1_unpin_pages,
+	.mmap_pages             = vfio_iommu_type1_mmap_pages,
+	.munmap_pages           = vfio_iommu_type1_munmap_pages,
+	.dma_find		= vfio_iommu_type1_dma_find,
 	.register_notifier	= vfio_iommu_type1_register_notifier,
 	.unregister_notifier	= vfio_iommu_type1_unregister_notifier,
 };
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index e42a711..d7df495 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -77,6 +77,15 @@ struct vfio_iommu_driver_ops {
 				     unsigned long *phys_pfn);
 	int		(*unpin_pages)(void *iommu_data,
 				       unsigned long *user_pfn, int npage);
+	int		(*mmap_pages)(void *iommu_data,
+					unsigned long user_pfn,
+					unsigned long page_size,
+					int prot, unsigned long pfn);
+	int		(*munmap_pages)(void *iommu_data,
+					unsigned long user_pfn,
+					unsigned long page_size);
+	int		(*dma_find)(void *iommu_data, unsigned long user_pfn,
+					int npage, unsigned long *phys_pfn);
 	int		(*register_notifier)(void *iommu_data,
 					     unsigned long *events,
 					     struct notifier_block *nb);
@@ -106,7 +115,13 @@ extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn,
 			  int npage, int prot, unsigned long *phys_pfn);
 extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn,
 			    int npage);
-
+extern int vfio_dma_find(struct device *dev, unsigned long user_pfn, int npage,
+			unsigned long *phys_pfn);
+extern int vfio_mmap_pages(struct device *dev, unsigned long user_pfn,
+			unsigned long page_size, int prot,
+			unsigned long pfn);
+extern int vfio_munmap_pages(struct device *dev, unsigned long user_pfn,
+			unsigned long page_size);
 /* each type has independent events */
 enum vfio_notify_type {
 	VFIO_IOMMU_NOTIFY = 0,
-- 
1.8.3.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ