Skip to content

Commit bc99f12

Browse files
committed
drm/i915/ttm: fix sg_table construction
If we encounter some monster sized local-memory page that exceeds the maximum sg length (UINT32_MAX), ensure that don't end up with some misaligned address in the entry that follows, leading to fireworks later. Also ensure we have some coverage of this in the selftests. v2(Chris): - Use round_down consistently to avoid udiv errors v3(Nirmoy): - Also update the max_segment in the selftest Fixes: f701b16 ("drm/i915/ttm: add i915_sg_from_buddy_resource") Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/6379 Signed-off-by: Matthew Auld <[email protected]> Cc: Thomas Hellström <[email protected]> Cc: Nirmoy Das <[email protected]> Reviewed-by: Nirmoy Das <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent d50f5a1 commit bc99f12

File tree

7 files changed

+58
-15
lines changed

7 files changed

+58
-15
lines changed

drivers/gpu/drm/i915/gem/i915_gem_ttm.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,15 @@ i915_ttm_resource_get_st(struct drm_i915_gem_object *obj,
602602
struct ttm_resource *res)
603603
{
604604
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
605+
u64 page_alignment;
605606

606607
if (!i915_ttm_gtt_binds_lmem(res))
607608
return i915_ttm_tt_get_st(bo->ttm);
608609

610+
page_alignment = bo->page_alignment << PAGE_SHIFT;
611+
if (!page_alignment)
612+
page_alignment = obj->mm.region->min_page_size;
613+
609614
/*
610615
* If CPU mapping differs, we need to add the ttm_tt pages to
611616
* the resulting st. Might make sense for GGTT.
@@ -616,7 +621,8 @@ i915_ttm_resource_get_st(struct drm_i915_gem_object *obj,
616621
struct i915_refct_sgt *rsgt;
617622

618623
rsgt = intel_region_ttm_resource_to_rsgt(obj->mm.region,
619-
res);
624+
res,
625+
page_alignment);
620626
if (IS_ERR(rsgt))
621627
return rsgt;
622628

@@ -625,7 +631,8 @@ i915_ttm_resource_get_st(struct drm_i915_gem_object *obj,
625631
return i915_refct_sgt_get(obj->ttm.cached_io_rsgt);
626632
}
627633

628-
return intel_region_ttm_resource_to_rsgt(obj->mm.region, res);
634+
return intel_region_ttm_resource_to_rsgt(obj->mm.region, res,
635+
page_alignment);
629636
}
630637

631638
static int i915_ttm_truncate(struct drm_i915_gem_object *obj)

drivers/gpu/drm/i915/i915_scatterlist.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ void i915_refct_sgt_init(struct i915_refct_sgt *rsgt, size_t size)
6868
* drm_mm_node
6969
* @node: The drm_mm_node.
7070
* @region_start: An offset to add to the dma addresses of the sg list.
71+
* @page_alignment: Required page alignment for each sg entry. Power of two.
7172
*
7273
* Create a struct sg_table, initializing it from a struct drm_mm_node,
7374
* taking a maximum segment length into account, splitting into segments
@@ -77,15 +78,18 @@ void i915_refct_sgt_init(struct i915_refct_sgt *rsgt, size_t size)
7778
* error code cast to an error pointer on failure.
7879
*/
7980
struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
80-
u64 region_start)
81+
u64 region_start,
82+
u64 page_alignment)
8183
{
82-
const u64 max_segment = SZ_1G; /* Do we have a limit on this? */
84+
const u64 max_segment = round_down(UINT_MAX, page_alignment);
8385
u64 segment_pages = max_segment >> PAGE_SHIFT;
8486
u64 block_size, offset, prev_end;
8587
struct i915_refct_sgt *rsgt;
8688
struct sg_table *st;
8789
struct scatterlist *sg;
8890

91+
GEM_BUG_ON(!max_segment);
92+
8993
rsgt = kmalloc(sizeof(*rsgt), GFP_KERNEL);
9094
if (!rsgt)
9195
return ERR_PTR(-ENOMEM);
@@ -112,6 +116,8 @@ struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
112116
sg = __sg_next(sg);
113117

114118
sg_dma_address(sg) = region_start + offset;
119+
GEM_BUG_ON(!IS_ALIGNED(sg_dma_address(sg),
120+
page_alignment));
115121
sg_dma_len(sg) = 0;
116122
sg->length = 0;
117123
st->nents++;
@@ -138,6 +144,7 @@ struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
138144
* i915_buddy_block list
139145
* @res: The struct i915_ttm_buddy_resource.
140146
* @region_start: An offset to add to the dma addresses of the sg list.
147+
* @page_alignment: Required page alignment for each sg entry. Power of two.
141148
*
142149
* Create a struct sg_table, initializing it from struct i915_buddy_block list,
143150
* taking a maximum segment length into account, splitting into segments
@@ -147,11 +154,12 @@ struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
147154
* error code cast to an error pointer on failure.
148155
*/
149156
struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
150-
u64 region_start)
157+
u64 region_start,
158+
u64 page_alignment)
151159
{
152160
struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
153161
const u64 size = res->num_pages << PAGE_SHIFT;
154-
const u64 max_segment = rounddown(UINT_MAX, PAGE_SIZE);
162+
const u64 max_segment = round_down(UINT_MAX, page_alignment);
155163
struct drm_buddy *mm = bman_res->mm;
156164
struct list_head *blocks = &bman_res->blocks;
157165
struct drm_buddy_block *block;
@@ -161,6 +169,7 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
161169
resource_size_t prev_end;
162170

163171
GEM_BUG_ON(list_empty(blocks));
172+
GEM_BUG_ON(!max_segment);
164173

165174
rsgt = kmalloc(sizeof(*rsgt), GFP_KERNEL);
166175
if (!rsgt)
@@ -191,6 +200,8 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
191200
sg = __sg_next(sg);
192201

193202
sg_dma_address(sg) = region_start + offset;
203+
GEM_BUG_ON(!IS_ALIGNED(sg_dma_address(sg),
204+
page_alignment));
194205
sg_dma_len(sg) = 0;
195206
sg->length = 0;
196207
st->nents++;

drivers/gpu/drm/i915/i915_scatterlist.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ static inline void __i915_refct_sgt_init(struct i915_refct_sgt *rsgt,
213213
void i915_refct_sgt_init(struct i915_refct_sgt *rsgt, size_t size);
214214

215215
struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
216-
u64 region_start);
216+
u64 region_start,
217+
u64 page_alignment);
217218

218219
struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
219-
u64 region_start);
220+
u64 region_start,
221+
u64 page_alignment);
220222

221223
#endif

drivers/gpu/drm/i915/intel_region_ttm.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ int intel_region_ttm_fini(struct intel_memory_region *mem)
152152
* Convert an opaque TTM resource manager resource to a refcounted sg_table.
153153
* @mem: The memory region.
154154
* @res: The resource manager resource obtained from the TTM resource manager.
155+
* @page_alignment: Required page alignment for each sg entry. Power of two.
155156
*
156157
* The gem backends typically use sg-tables for operations on the underlying
157158
* io_memory. So provide a way for the backends to translate the
@@ -161,16 +162,19 @@ int intel_region_ttm_fini(struct intel_memory_region *mem)
161162
*/
162163
struct i915_refct_sgt *
163164
intel_region_ttm_resource_to_rsgt(struct intel_memory_region *mem,
164-
struct ttm_resource *res)
165+
struct ttm_resource *res,
166+
u64 page_alignment)
165167
{
166168
if (mem->is_range_manager) {
167169
struct ttm_range_mgr_node *range_node =
168170
to_ttm_range_mgr_node(res);
169171

170172
return i915_rsgt_from_mm_node(&range_node->mm_nodes[0],
171-
mem->region.start);
173+
mem->region.start,
174+
page_alignment);
172175
} else {
173-
return i915_rsgt_from_buddy_resource(res, mem->region.start);
176+
return i915_rsgt_from_buddy_resource(res, mem->region.start,
177+
page_alignment);
174178
}
175179
}
176180

drivers/gpu/drm/i915/intel_region_ttm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ int intel_region_ttm_fini(struct intel_memory_region *mem);
2424

2525
struct i915_refct_sgt *
2626
intel_region_ttm_resource_to_rsgt(struct intel_memory_region *mem,
27-
struct ttm_resource *res);
27+
struct ttm_resource *res,
28+
u64 page_alignment);
2829

2930
void intel_region_ttm_resource_free(struct intel_memory_region *mem,
3031
struct ttm_resource *res);

drivers/gpu/drm/i915/selftests/intel_memory_region.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,6 @@ static int igt_mock_splintered_region(void *arg)
451451

452452
static int igt_mock_max_segment(void *arg)
453453
{
454-
const unsigned int max_segment = rounddown(UINT_MAX, PAGE_SIZE);
455454
struct intel_memory_region *mem = arg;
456455
struct drm_i915_private *i915 = mem->i915;
457456
struct i915_ttm_buddy_resource *res;
@@ -460,7 +459,10 @@ static int igt_mock_max_segment(void *arg)
460459
struct drm_buddy *mm;
461460
struct list_head *blocks;
462461
struct scatterlist *sg;
462+
I915_RND_STATE(prng);
463463
LIST_HEAD(objects);
464+
unsigned int max_segment;
465+
unsigned int ps;
464466
u64 size;
465467
int err = 0;
466468

@@ -472,7 +474,13 @@ static int igt_mock_max_segment(void *arg)
472474
*/
473475

474476
size = SZ_8G;
475-
mem = mock_region_create(i915, 0, size, PAGE_SIZE, 0, 0);
477+
ps = PAGE_SIZE;
478+
if (i915_prandom_u64_state(&prng) & 1)
479+
ps = SZ_64K; /* For something like DG2 */
480+
481+
max_segment = round_down(UINT_MAX, ps);
482+
483+
mem = mock_region_create(i915, 0, size, ps, 0, 0);
476484
if (IS_ERR(mem))
477485
return PTR_ERR(mem);
478486

@@ -498,12 +506,21 @@ static int igt_mock_max_segment(void *arg)
498506
}
499507

500508
for (sg = obj->mm.pages->sgl; sg; sg = sg_next(sg)) {
509+
dma_addr_t daddr = sg_dma_address(sg);
510+
501511
if (sg->length > max_segment) {
502512
pr_err("%s: Created an oversized scatterlist entry, %u > %u\n",
503513
__func__, sg->length, max_segment);
504514
err = -EINVAL;
505515
goto out_close;
506516
}
517+
518+
if (!IS_ALIGNED(daddr, ps)) {
519+
pr_err("%s: Created an unaligned scatterlist entry, addr=%pa, ps=%u\n",
520+
__func__, &daddr, ps);
521+
err = -EINVAL;
522+
goto out_close;
523+
}
507524
}
508525

509526
out_close:

drivers/gpu/drm/i915/selftests/mock_region.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ static int mock_region_get_pages(struct drm_i915_gem_object *obj)
3333
return PTR_ERR(obj->mm.res);
3434

3535
obj->mm.rsgt = intel_region_ttm_resource_to_rsgt(obj->mm.region,
36-
obj->mm.res);
36+
obj->mm.res,
37+
obj->mm.region->min_page_size);
3738
if (IS_ERR(obj->mm.rsgt)) {
3839
err = PTR_ERR(obj->mm.rsgt);
3940
goto err_free_resource;

0 commit comments

Comments
 (0)