forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
iommufd: PFN handling for iopt_pages
The top of the data structure provides an IO Address Space (IOAS) that is similar to a VFIO container. The IOAS allows map/unmap of memory into ranges of IOVA called iopt_areas. Multiple IOMMU domains (IO page tables) and in-kernel accesses (like VFIO mdevs) can be attached to the IOAS to access the PFNs that those IOVA areas cover. The IO Address Space (IOAS) datastructure is composed of: - struct io_pagetable holding the IOVA map - struct iopt_areas representing populated portions of IOVA - struct iopt_pages representing the storage of PFNs - struct iommu_domain representing each IO page table in the system IOMMU - struct iopt_pages_access representing in-kernel accesses of PFNs (ie VFIO mdevs) - struct xarray pinned_pfns holding a list of pages pinned by in-kernel accesses This patch introduces the lowest part of the datastructure - the movement of PFNs in a tiered storage scheme: 1) iopt_pages::pinned_pfns xarray 2) Multiple iommu_domains 3) The origin of the PFNs, i.e. the userspace pointer PFN have to be copied between all combinations of tiers, depending on the configuration. The interface is an iterator called a 'pfn_reader' which determines which tier each PFN is stored and loads it into a list of PFNs held in a struct pfn_batch. Each step of the iterator will fill up the pfn_batch, then the caller can use the pfn_batch to send the PFNs to the required destination. Repeating this loop will read all the PFNs in an IOVA range. The pfn_reader and pfn_batch also keep track of the pinned page accounting. While PFNs are always stored and accessed as full PAGE_SIZE units the iommu_domain tier can store with a sub-page offset/length to support IOMMUs with a smaller IOPTE size than PAGE_SIZE. Link: https://lore.kernel.org/r/8-v6-a196d26f289e+11787-iommufd_jgg@nvidia.com Reviewed-by: Kevin Tian <kevin.tian@intel.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Tested-by: Yi Liu <yi.l.liu@intel.com> Tested-by: Lixiao Yang <lixiao.yang@intel.com> Tested-by: Matthew Rosato <mjrosato@linux.ibm.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
- Loading branch information
1 parent
ce5a23c
commit f394576
Showing
7 changed files
with
1,262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
iommufd-y := \ | ||
main.o | ||
main.o \ | ||
pages.o | ||
|
||
obj-$(CONFIG_IOMMUFD) += iommufd.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. | ||
*/ | ||
#ifndef __IOMMUFD_DOUBLE_SPAN_H | ||
#define __IOMMUFD_DOUBLE_SPAN_H | ||
|
||
#include <linux/interval_tree.h> | ||
|
||
/* | ||
* This is a variation of the general interval_tree_span_iter that computes the | ||
* spans over the union of two different interval trees. Used ranges are broken | ||
* up and reported based on the tree that provides the interval. The first span | ||
* always takes priority. Like interval_tree_span_iter it is greedy and the same | ||
* value of is_used will not repeat on two iteration cycles. | ||
*/ | ||
struct interval_tree_double_span_iter { | ||
struct rb_root_cached *itrees[2]; | ||
struct interval_tree_span_iter spans[2]; | ||
union { | ||
unsigned long start_hole; | ||
unsigned long start_used; | ||
}; | ||
union { | ||
unsigned long last_hole; | ||
unsigned long last_used; | ||
}; | ||
/* 0 = hole, 1 = used span[0], 2 = used span[1], -1 done iteration */ | ||
int is_used; | ||
}; | ||
|
||
void interval_tree_double_span_iter_update( | ||
struct interval_tree_double_span_iter *iter); | ||
void interval_tree_double_span_iter_first( | ||
struct interval_tree_double_span_iter *iter, | ||
struct rb_root_cached *itree1, struct rb_root_cached *itree2, | ||
unsigned long first_index, unsigned long last_index); | ||
void interval_tree_double_span_iter_next( | ||
struct interval_tree_double_span_iter *iter); | ||
|
||
static inline bool | ||
interval_tree_double_span_iter_done(struct interval_tree_double_span_iter *state) | ||
{ | ||
return state->is_used == -1; | ||
} | ||
|
||
#define interval_tree_for_each_double_span(span, itree1, itree2, first_index, \ | ||
last_index) \ | ||
for (interval_tree_double_span_iter_first(span, itree1, itree2, \ | ||
first_index, last_index); \ | ||
!interval_tree_double_span_iter_done(span); \ | ||
interval_tree_double_span_iter_next(span)) | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. | ||
* | ||
*/ | ||
#ifndef __IO_PAGETABLE_H | ||
#define __IO_PAGETABLE_H | ||
|
||
#include <linux/interval_tree.h> | ||
#include <linux/mutex.h> | ||
#include <linux/kref.h> | ||
#include <linux/xarray.h> | ||
|
||
#include "iommufd_private.h" | ||
|
||
struct iommu_domain; | ||
|
||
/* | ||
* Each io_pagetable is composed of intervals of areas which cover regions of | ||
* the iova that are backed by something. iova not covered by areas is not | ||
* populated in the page table. Each area is fully populated with pages. | ||
* | ||
* iovas are in byte units, but must be iopt->iova_alignment aligned. | ||
* | ||
* pages can be NULL, this means some other thread is still working on setting | ||
* up or tearing down the area. When observed under the write side of the | ||
* domain_rwsem a NULL pages must mean the area is still being setup and no | ||
* domains are filled. | ||
* | ||
* storage_domain points at an arbitrary iommu_domain that is holding the PFNs | ||
* for this area. It is locked by the pages->mutex. This simplifies the locking | ||
* as the pages code can rely on the storage_domain without having to get the | ||
* iopt->domains_rwsem. | ||
* | ||
* The io_pagetable::iova_rwsem protects node | ||
* The iopt_pages::mutex protects pages_node | ||
* iopt and immu_prot are immutable | ||
* The pages::mutex protects num_accesses | ||
*/ | ||
struct iopt_area { | ||
struct interval_tree_node node; | ||
struct interval_tree_node pages_node; | ||
struct io_pagetable *iopt; | ||
struct iopt_pages *pages; | ||
struct iommu_domain *storage_domain; | ||
/* How many bytes into the first page the area starts */ | ||
unsigned int page_offset; | ||
/* IOMMU_READ, IOMMU_WRITE, etc */ | ||
int iommu_prot; | ||
unsigned int num_accesses; | ||
}; | ||
|
||
static inline unsigned long iopt_area_index(struct iopt_area *area) | ||
{ | ||
return area->pages_node.start; | ||
} | ||
|
||
static inline unsigned long iopt_area_last_index(struct iopt_area *area) | ||
{ | ||
return area->pages_node.last; | ||
} | ||
|
||
static inline unsigned long iopt_area_iova(struct iopt_area *area) | ||
{ | ||
return area->node.start; | ||
} | ||
|
||
static inline unsigned long iopt_area_last_iova(struct iopt_area *area) | ||
{ | ||
return area->node.last; | ||
} | ||
|
||
enum { | ||
IOPT_PAGES_ACCOUNT_NONE = 0, | ||
IOPT_PAGES_ACCOUNT_USER = 1, | ||
IOPT_PAGES_ACCOUNT_MM = 2, | ||
}; | ||
|
||
/* | ||
* This holds a pinned page list for multiple areas of IO address space. The | ||
* pages always originate from a linear chunk of userspace VA. Multiple | ||
* io_pagetable's, through their iopt_area's, can share a single iopt_pages | ||
* which avoids multi-pinning and double accounting of page consumption. | ||
* | ||
* indexes in this structure are measured in PAGE_SIZE units, are 0 based from | ||
* the start of the uptr and extend to npages. pages are pinned dynamically | ||
* according to the intervals in the access_itree and domains_itree, npinned | ||
* records the current number of pages pinned. | ||
*/ | ||
struct iopt_pages { | ||
struct kref kref; | ||
struct mutex mutex; | ||
size_t npages; | ||
size_t npinned; | ||
size_t last_npinned; | ||
struct task_struct *source_task; | ||
struct mm_struct *source_mm; | ||
struct user_struct *source_user; | ||
void __user *uptr; | ||
bool writable:1; | ||
u8 account_mode; | ||
|
||
struct xarray pinned_pfns; | ||
/* Of iopt_pages_access::node */ | ||
struct rb_root_cached access_itree; | ||
/* Of iopt_area::pages_node */ | ||
struct rb_root_cached domains_itree; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.