Skip to content

Commit b3c5841

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 a08eeb2 commit b3c5841

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
@@ -2202,6 +2202,26 @@ static bool __can_merge(struct drm_gpuvm *gpuvm, const struct drm_gpuva_op_map *
22022202
if (drm_WARN_ON(gpuvm->drm, b->va.addr > a->va.addr + a->va.range))
22032203
return false;
22042204

2205+
if (a->flags & DRM_GPUVA_REPEAT) {
2206+
u64 va_diff = b->va.addr - a->va.addr;
2207+
2208+
/* If this is a repeated mapping, both the GEM range
2209+
* and offset must match.
2210+
*/
2211+
if (a->gem.range != b->gem.range ||
2212+
a->gem.offset != b->gem.offset)
2213+
return false;
2214+
2215+
/* The difference between the VA addresses must be a
2216+
* multiple of the repeated range, otherwise there's
2217+
* a shift.
2218+
*/
2219+
if (do_div(va_diff, a->gem.range))
2220+
return false;
2221+
2222+
return true;
2223+
}
2224+
22052225
/* We intentionally ignore u64 underflows because all we care about
22062226
* here is whether the VA diff matches the GEM offset diff.
22072227
*/
@@ -2222,6 +2242,27 @@ static bool can_merge(struct drm_gpuvm *gpuvm, const struct drm_gpuva *a,
22222242
return __can_merge(gpuvm, &tmp, b);
22232243
}
22242244

2245+
static int validate_map_request(struct drm_gpuvm *gpuvm,
2246+
const struct drm_gpuva_op_map *req)
2247+
{
2248+
if (unlikely(!drm_gpuvm_range_valid(gpuvm, req->va.addr, req->va.range)))
2249+
return -EINVAL;
2250+
2251+
if (req->flags & DRM_GPUVA_REPEAT) {
2252+
u64 va_range = req->va.range;
2253+
2254+
/* For a repeated mapping, GEM range must be > 0
2255+
* and a multiple of the VA range.
2256+
*/
2257+
if (unlikely(!req->gem.range ||
2258+
va_range < req->gem.range ||
2259+
do_div(va_range, req->gem.range)))
2260+
return -EINVAL;
2261+
}
2262+
2263+
return 0;
2264+
}
2265+
22252266
static int
22262267
__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
22272268
const struct drm_gpuvm_ops *ops, void *priv,
@@ -2237,7 +2278,8 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
22372278
u64 req_end = req_addr + req_range;
22382279
int ret;
22392280

2240-
if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range)))
2281+
ret = validate_map_request(gpuvm, &req->map);
2282+
if (unlikely(ret))
22412283
return -EINVAL;
22422284

22432285
drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) {
@@ -2274,7 +2316,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
22742316
.va.addr = req_end,
22752317
.va.range = range - req_range,
22762318
.gem.obj = obj,
2277-
.gem.offset = offset + req_range,
2319+
.gem.range = va->gem.range,
2320+
.gem.offset = offset +
2321+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_range),
22782322
.flags = va->flags,
22792323
};
22802324
struct drm_gpuva_op_unmap u = {
@@ -2296,6 +2340,7 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
22962340
.va.addr = addr,
22972341
.va.range = ls_range,
22982342
.gem.obj = obj,
2343+
.gem.range = va->gem.range,
22992344
.gem.offset = offset,
23002345
.flags = va->flags,
23012346
};
@@ -2339,8 +2384,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
23392384
.va.addr = req_end,
23402385
.va.range = end - req_end,
23412386
.gem.obj = obj,
2342-
.gem.offset = offset + ls_range +
2343-
req_range,
2387+
.gem.range = va->gem.range,
2388+
.gem.offset = offset +
2389+
(va->flags & DRM_GPUVA_REPEAT ? 0 : ls_range + req_range),
23442390
.flags = va->flags,
23452391
};
23462392

@@ -2378,7 +2424,9 @@ __drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm,
23782424
.va.addr = req_end,
23792425
.va.range = end - req_end,
23802426
.gem.obj = obj,
2381-
.gem.offset = offset + req_end - addr,
2427+
.gem.range = va->gem.range,
2428+
.gem.offset = offset +
2429+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_end - addr),
23822430
.flags = va->flags,
23832431
};
23842432
struct drm_gpuva_op_unmap u = {
@@ -2430,6 +2478,7 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
24302478
prev.va.addr = addr;
24312479
prev.va.range = req_addr - addr;
24322480
prev.gem.obj = obj;
2481+
prev.gem.range = va->gem.range;
24332482
prev.gem.offset = offset;
24342483
prev.flags = va->flags;
24352484

@@ -2440,7 +2489,9 @@ __drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm,
24402489
next.va.addr = req_end;
24412490
next.va.range = end - req_end;
24422491
next.gem.obj = obj;
2443-
next.gem.offset = offset + (req_end - addr);
2492+
prev.gem.range = va->gem.range;
2493+
next.gem.offset = offset +
2494+
(va->flags & DRM_GPUVA_REPEAT ? 0 : req_end - addr);
24442495
next.flags = va->flags;
24452496

24462497
next_split = true;

include/drm/drm_gpuvm.h

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

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

6574
/**
@@ -111,6 +120,18 @@ struct drm_gpuva {
111120
*/
112121
u64 offset;
113122

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

890+
/*
891+
* @gem.range: the range of the GEM that is mapped
892+
*
893+
* When dealing with normal mappings, this must be zero.
894+
* When flags has DRM_GPUVA_REPEAT set, it must be a multiple
895+
* of va.range. This is a u32 not a u64 because we expect
896+
* repeated mappings to be pointing to a relatively small
897+
* portion of a GEM object.
898+
*/
899+
u32 range;
900+
869901
/**
870902
* @gem.obj: the &drm_gem_object to map
871903
*/

0 commit comments

Comments
 (0)