Skip to content

Commit 045dc3f

Browse files
Caterina Shabliajannau
authored andcommitted
drm/gpuvm: Add DRM_GPUVA_REPEAT flag and logic
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 (or same range of pages). Doing this through individual 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 page mapping, which is repeated all over the entire requested VA range. This tweaks the sm_map() logic to treat the GEM offsets differently when mappings are a repeated ones so they are not incremented as they would be with regular mappings. The size of the GEM portion to repeat is passed through drm_gpuva::gem::range. Most of the time it will be a page size, but it can be bigger as long as it's less than drm_gpuva::va::range, and drm_gpuva::va::range is a multiple of drm_gpuva::gem::range. Signed-off-by: Asahi Lina <lina+kernel@asahilina.net> Signed-off-by: Caterina Shablia <caterina.shablia@collabora.com>
1 parent e949de5 commit 045dc3f

2 files changed

Lines changed: 90 additions & 7 deletions

File tree

drivers/gpu/drm/drm_gpuvm.c

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,26 @@ static bool __can_merge(struct drm_gpuvm *gpuvm, const struct drm_gpuva_op_map *
24202420
if (drm_WARN_ON(gpuvm->drm, b->va.addr > a->va.addr + a->va.range))
24212421
return false;
24222422

2423+
if (a->flags & DRM_GPUVA_REPEAT) {
2424+
u64 va_diff = b->va.addr - a->va.addr;
2425+
2426+
/* If this is a repeated mapping, both the GEM range
2427+
* and offset must match.
2428+
*/
2429+
if (a->gem.range != b->gem.range ||
2430+
a->gem.offset != b->gem.offset)
2431+
return false;
2432+
2433+
/* The difference between the VA addresses must be a
2434+
* multiple of the repeated range, otherwise there's
2435+
* a shift.
2436+
*/
2437+
if (do_div(va_diff, a->gem.range))
2438+
return false;
2439+
2440+
return true;
2441+
}
2442+
24232443
/* We intentionally ignore u64 underflows because all we care about
24242444
* here is whether the VA diff matches the GEM offset diff.
24252445
*/
@@ -2440,6 +2460,27 @@ static bool can_merge(struct drm_gpuvm *gpuvm, const struct drm_gpuva *a,
24402460
return __can_merge(gpuvm, &tmp, b);
24412461
}
24422462

2463+
static int validate_map_request(struct drm_gpuvm *gpuvm,
2464+
const struct drm_gpuva_op_map *req)
2465+
{
2466+
if (unlikely(!drm_gpuvm_range_valid(gpuvm, req->va.addr, req->va.range)))
2467+
return -EINVAL;
2468+
2469+
if (req->flags & DRM_GPUVA_REPEAT) {
2470+
u64 va_range = req->va.range;
2471+
2472+
/* For a repeated mapping, GEM range must be > 0
2473+
* and a multiple of the VA range.
2474+
*/
2475+
if (unlikely(!req->gem.range ||
2476+
va_range < req->gem.range ||
2477+
do_div(va_range, req->gem.range)))
2478+
return -EINVAL;
2479+
}
2480+
2481+
return 0;
2482+
}
2483+
24432484
static int
24442485
__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
24452486
const struct drm_gpuvm_ops *ops, void *priv,
@@ -2455,7 +2496,8 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
24552496
u64 req_end = req_addr + req_range;
24562497
int ret;
24572498

2458-
if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range)))
2499+
ret = validate_map_request(gpuvm, &req->map);
2500+
if (unlikely(ret))
24592501
return -EINVAL;
24602502

24612503
drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) {
@@ -2492,7 +2534,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
24922534
.va.addr = req_end,
24932535
.va.range = range - req_range,
24942536
.gem.obj = obj,
2495-
.gem.offset = offset + req_range,
2537+
.gem.range = va->gem.range,
2538+
.gem.offset = offset +
2539+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_range),
24962540
.flags = va->flags,
24972541
};
24982542
struct drm_gpuva_op_unmap u = {
@@ -2514,6 +2558,7 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
25142558
.va.addr = addr,
25152559
.va.range = ls_range,
25162560
.gem.obj = obj,
2561+
.gem.range = va->gem.range,
25172562
.gem.offset = offset,
25182563
.flags = va->flags,
25192564
};
@@ -2557,8 +2602,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
25572602
.va.addr = req_end,
25582603
.va.range = end - req_end,
25592604
.gem.obj = obj,
2560-
.gem.offset = offset + ls_range +
2561-
req_range,
2605+
.gem.range = va->gem.range,
2606+
.gem.offset = offset +
2607+
(va->flags & DRM_GPUVA_REPEAT ? 0 : ls_range + req_range),
25622608
.flags = va->flags,
25632609
};
25642610

@@ -2596,7 +2642,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
25962642
.va.addr = req_end,
25972643
.va.range = end - req_end,
25982644
.gem.obj = obj,
2599-
.gem.offset = offset + req_end - addr,
2645+
.gem.range = va->gem.range,
2646+
.gem.offset = offset +
2647+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_end - addr),
26002648
.flags = va->flags,
26012649
};
26022650
struct drm_gpuva_op_unmap u = {
@@ -2648,6 +2696,7 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
26482696
prev.va.addr = addr;
26492697
prev.va.range = req_addr - addr;
26502698
prev.gem.obj = obj;
2699+
prev.gem.range = va->gem.range;
26512700
prev.gem.offset = offset;
26522701
prev.flags = va->flags;
26532702

@@ -2658,7 +2707,9 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
26582707
next.va.addr = req_end;
26592708
next.va.range = end - req_end;
26602709
next.gem.obj = obj;
2661-
next.gem.offset = offset + (req_end - addr);
2710+
prev.gem.range = va->gem.range;
2711+
next.gem.offset = offset +
2712+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_end - addr);
26622713
next.flags = va->flags;
26632714

26642715
next_split = true;

include/drm/drm_gpuvm.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,19 @@ enum drm_gpuva_flags {
5757
*/
5858
DRM_GPUVA_SPARSE = (1 << 1),
5959

60+
/**
61+
* @DRM_GPUVA_REPEAT:
62+
*
63+
* Flag indicating that the &drm_gpuva is a mapping of a GEM
64+
* object with a certain range that is repeated multiple times to
65+
* fill the virtual address range.
66+
*/
67+
DRM_GPUVA_REPEAT = (1 << 2),
68+
6069
/**
6170
* @DRM_GPUVA_USERBITS: user defined bits
6271
*/
63-
DRM_GPUVA_USERBITS = (1 << 2),
72+
DRM_GPUVA_USERBITS = (1 << 3),
6473
};
6574

6675
/**
@@ -112,6 +121,18 @@ struct drm_gpuva {
112121
*/
113122
u64 offset;
114123

124+
/*
125+
* @gem.range: the range of the GEM that is mapped
126+
*
127+
* When dealing with normal mappings, this must be zero.
128+
* When flags has DRM_GPUVA_REPEAT set, this field must be
129+
* smaller than va.range and va.range must be a multiple of
130+
* gem.range.
131+
* This is a u32 not a u64 because we expect repeated mappings
132+
* to be pointing to relatively small portions of a GEM object.
133+
*/
134+
u32 range;
135+
115136
/**
116137
* @gem.obj: the mapped &drm_gem_object
117138
*/
@@ -882,6 +903,17 @@ struct drm_gpuva_op_map {
882903
*/
883904
u64 offset;
884905

906+
/*
907+
* @gem.range: the range of the GEM that is mapped
908+
*
909+
* When dealing with normal mappings, this must be zero.
910+
* When flags has DRM_GPUVA_REPEAT set, it must be a multiple
911+
* of va.range. This is a u32 not a u64 because we expect
912+
* repeated mappings to be pointing to a relatively small
913+
* portion of a GEM object.
914+
*/
915+
u32 range;
916+
885917
/**
886918
* @gem.obj: the &drm_gem_object to map
887919
*/

0 commit comments

Comments
 (0)