[PATCH 3/4] drm/gpuvm: Add DRM_GPUVA_SINGLE_PAGE flag and logic
From: Asahi Lina
Date: Sun Feb 02 2025 - 08:45:47 EST
To be able to support "fake sparse" mappings without relying on GPU page
fault handling, drivers may need to create large (e.g. 4GiB) mappings of
the same page repeatedly. Doing this through individual single-page
mappings would be very wasteful. This can be handled better by using a
flag on map creation, but to do it safely, drm_gpuvm needs to be aware
of this special case.
Add a flag that signals that a given mapping is a single page mapping,
which is repeated all over the entire requested VA range. This does two
things in drm_gpuvm:
- Restricts the merge logic, so only drm_gpuvas with the same flag state
are considered "mergeable" (both SINGLE_PAGE or both not)
- Removes the GEM buffer offset logic for SINGLE_PAGE mappings. Since
a single page from the buffer is repeatedly mapped across the entire
VA range, the offset never needs to have an offset added to it when
mappings are split.
Note that this does not require drm_gpuva to know anything about the
page size.
Signed-off-by: Asahi Lina <lina@xxxxxxxxxxxxx>
---
drivers/gpu/drm/drm_gpuvm.c | 44 ++++++++++++++++++++++++++++++++++----------
include/drm/drm_gpuvm.h | 9 ++++++++-
2 files changed, 42 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index 7443be1fe4de4653ec40ca4b874df30297af7faf..51429d90875e3f2370c4f8975d6fd813842b8976 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -670,6 +670,12 @@
* }
*/
+/**
+ * Mask of flags which must match to consider a drm_gpuva eligible for merging
+ * with a new overlaid mapping.
+ */
+#define DRM_GPUVA_UNMERGEABLE_FLAGS DRM_GPUVA_SINGLE_PAGE
+
/**
* get_next_vm_bo_from_list() - get the next vm_bo element
* @__gpuvm: the &drm_gpuvm
@@ -2121,6 +2127,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
u64 range = va->va.range;
u64 end = addr + range;
bool merge = !!va->gem.obj;
+ bool single_page = va->flags & DRM_GPUVA_SINGLE_PAGE;
+
+ merge &= !((va->flags ^ req_flags) & DRM_GPUVA_UNMERGEABLE_FLAGS);
if (addr == req_addr) {
merge &= obj == req_obj &&
@@ -2145,7 +2154,8 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
.va.addr = req_end,
.va.range = range - req_range,
.gem.obj = obj,
- .gem.offset = offset + req_range,
+ .gem.offset = offset +
+ (single_page ? 0 : req_range),
.flags = va->flags,
};
struct drm_gpuva_op_unmap u = {
@@ -2169,8 +2179,12 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
};
struct drm_gpuva_op_unmap u = { .va = va };
- merge &= obj == req_obj &&
- offset + ls_range == req_offset;
+ merge &= obj == req_obj;
+ if (single_page)
+ merge &= offset == req_offset;
+ else
+ merge &= offset + ls_range == req_offset;
+
u.keep = merge;
if (end == req_end) {
@@ -2192,8 +2206,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
.va.addr = req_end,
.va.range = end - req_end,
.gem.obj = obj,
- .gem.offset = offset + ls_range +
- req_range,
+ .gem.offset = offset +
+ (single_page ? 0 :
+ ls_range + req_range),
.flags = va->flags,
};
@@ -2203,9 +2218,13 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
break;
}
} else if (addr > req_addr) {
- merge &= obj == req_obj &&
- offset == req_offset +
- (addr - req_addr);
+ merge &= obj == req_obj;
+
+ if (single_page)
+ merge &= offset == req_offset;
+ else
+ merge &= offset == req_offset +
+ (addr - req_addr);
if (end == req_end) {
ret = op_unmap_cb(ops, priv, va, merge);
@@ -2226,7 +2245,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
.va.addr = req_end,
.va.range = end - req_end,
.gem.obj = obj,
- .gem.offset = offset + req_end - addr,
+ .gem.offset = offset +
+ (single_page ? 0 :
+ req_end - addr),
.flags = va->flags,
};
struct drm_gpuva_op_unmap u = {
@@ -2268,6 +2289,7 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
u64 addr = va->va.addr;
u64 range = va->va.range;
u64 end = addr + range;
+ bool single_page = va->flags & DRM_GPUVA_SINGLE_PAGE;
if (addr < req_addr) {
prev.va.addr = addr;
@@ -2283,7 +2305,9 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
next.va.addr = req_end;
next.va.range = end - req_end;
next.gem.obj = obj;
- next.gem.offset = offset + (req_end - addr);
+ next.gem.offset = offset;
+ if (!single_page)
+ next.gem.offset += req_end - addr;
next.flags = va->flags;
next_split = true;
diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h
index 42b29adfabdaf193b1e1a02f9ab48ab0dd0e60d4..dfeec61908b1a8295ae08b26bef211d3d4fda85b 100644
--- a/include/drm/drm_gpuvm.h
+++ b/include/drm/drm_gpuvm.h
@@ -56,10 +56,17 @@ enum drm_gpuva_flags {
*/
DRM_GPUVA_SPARSE = (1 << 1),
+ /**
+ * @DRM_GPUVA_SINGLE_PAGE:
+ *
+ * Flag indicating that the &drm_gpuva is a single-page mapping.
+ */
+ DRM_GPUVA_SINGLE_PAGE = (1 << 2),
+
/**
* @DRM_GPUVA_USERBITS: user defined bits
*/
- DRM_GPUVA_USERBITS = (1 << 2),
+ DRM_GPUVA_USERBITS = (1 << 3),
};
/**
--
2.47.1