Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: f701b16d4cc5 ("drm/i915/ttm: add i915_sg_from_buddy_resource")
Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/6379
Signed-off-by: Matthew Auld <matthew.auld@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Cc: Nirmoy Das <nirmoy.das@linux.intel.com>
Reviewed-by: Nirmoy Das <nirmoy.das@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220711085859.24198-1-matthew.auld@intel.com

+58 -15
+9 -2
drivers/gpu/drm/i915/gem/i915_gem_ttm.c
··· 602 602 struct ttm_resource *res) 603 603 { 604 604 struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); 605 + u64 page_alignment; 605 606 606 607 if (!i915_ttm_gtt_binds_lmem(res)) 607 608 return i915_ttm_tt_get_st(bo->ttm); 609 + 610 + page_alignment = bo->page_alignment << PAGE_SHIFT; 611 + if (!page_alignment) 612 + page_alignment = obj->mm.region->min_page_size; 608 613 609 614 /* 610 615 * If CPU mapping differs, we need to add the ttm_tt pages to ··· 621 616 struct i915_refct_sgt *rsgt; 622 617 623 618 rsgt = intel_region_ttm_resource_to_rsgt(obj->mm.region, 624 - res); 619 + res, 620 + page_alignment); 625 621 if (IS_ERR(rsgt)) 626 622 return rsgt; 627 623 ··· 631 625 return i915_refct_sgt_get(obj->ttm.cached_io_rsgt); 632 626 } 633 627 634 - return intel_region_ttm_resource_to_rsgt(obj->mm.region, res); 628 + return intel_region_ttm_resource_to_rsgt(obj->mm.region, res, 629 + page_alignment); 635 630 } 636 631 637 632 static int i915_ttm_truncate(struct drm_i915_gem_object *obj)
+15 -4
drivers/gpu/drm/i915/i915_scatterlist.c
··· 68 68 * drm_mm_node 69 69 * @node: The drm_mm_node. 70 70 * @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. 71 72 * 72 73 * Create a struct sg_table, initializing it from a struct drm_mm_node, 73 74 * taking a maximum segment length into account, splitting into segments ··· 78 77 * error code cast to an error pointer on failure. 79 78 */ 80 79 struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node, 81 - u64 region_start) 80 + u64 region_start, 81 + u64 page_alignment) 82 82 { 83 - const u64 max_segment = SZ_1G; /* Do we have a limit on this? */ 83 + const u64 max_segment = round_down(UINT_MAX, page_alignment); 84 84 u64 segment_pages = max_segment >> PAGE_SHIFT; 85 85 u64 block_size, offset, prev_end; 86 86 struct i915_refct_sgt *rsgt; 87 87 struct sg_table *st; 88 88 struct scatterlist *sg; 89 + 90 + GEM_BUG_ON(!max_segment); 89 91 90 92 rsgt = kmalloc(sizeof(*rsgt), GFP_KERNEL); 91 93 if (!rsgt) ··· 116 112 sg = __sg_next(sg); 117 113 118 114 sg_dma_address(sg) = region_start + offset; 115 + GEM_BUG_ON(!IS_ALIGNED(sg_dma_address(sg), 116 + page_alignment)); 119 117 sg_dma_len(sg) = 0; 120 118 sg->length = 0; 121 119 st->nents++; ··· 144 138 * i915_buddy_block list 145 139 * @res: The struct i915_ttm_buddy_resource. 146 140 * @region_start: An offset to add to the dma addresses of the sg list. 141 + * @page_alignment: Required page alignment for each sg entry. Power of two. 147 142 * 148 143 * Create a struct sg_table, initializing it from struct i915_buddy_block list, 149 144 * taking a maximum segment length into account, splitting into segments ··· 154 147 * error code cast to an error pointer on failure. 155 148 */ 156 149 struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res, 157 - u64 region_start) 150 + u64 region_start, 151 + u64 page_alignment) 158 152 { 159 153 struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); 160 154 const u64 size = res->num_pages << PAGE_SHIFT; 161 - const u64 max_segment = rounddown(UINT_MAX, PAGE_SIZE); 155 + const u64 max_segment = round_down(UINT_MAX, page_alignment); 162 156 struct drm_buddy *mm = bman_res->mm; 163 157 struct list_head *blocks = &bman_res->blocks; 164 158 struct drm_buddy_block *block; ··· 169 161 resource_size_t prev_end; 170 162 171 163 GEM_BUG_ON(list_empty(blocks)); 164 + GEM_BUG_ON(!max_segment); 172 165 173 166 rsgt = kmalloc(sizeof(*rsgt), GFP_KERNEL); 174 167 if (!rsgt) ··· 200 191 sg = __sg_next(sg); 201 192 202 193 sg_dma_address(sg) = region_start + offset; 194 + GEM_BUG_ON(!IS_ALIGNED(sg_dma_address(sg), 195 + page_alignment)); 203 196 sg_dma_len(sg) = 0; 204 197 sg->length = 0; 205 198 st->nents++;
+4 -2
drivers/gpu/drm/i915/i915_scatterlist.h
··· 213 213 void i915_refct_sgt_init(struct i915_refct_sgt *rsgt, size_t size); 214 214 215 215 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); 217 218 218 219 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); 220 222 221 223 #endif
+7 -3
drivers/gpu/drm/i915/intel_region_ttm.c
··· 152 152 * Convert an opaque TTM resource manager resource to a refcounted sg_table. 153 153 * @mem: The memory region. 154 154 * @res: The resource manager resource obtained from the TTM resource manager. 155 + * @page_alignment: Required page alignment for each sg entry. Power of two. 155 156 * 156 157 * The gem backends typically use sg-tables for operations on the underlying 157 158 * io_memory. So provide a way for the backends to translate the ··· 162 161 */ 163 162 struct i915_refct_sgt * 164 163 intel_region_ttm_resource_to_rsgt(struct intel_memory_region *mem, 165 - struct ttm_resource *res) 164 + struct ttm_resource *res, 165 + u64 page_alignment) 166 166 { 167 167 if (mem->is_range_manager) { 168 168 struct ttm_range_mgr_node *range_node = 169 169 to_ttm_range_mgr_node(res); 170 170 171 171 return i915_rsgt_from_mm_node(&range_node->mm_nodes[0], 172 - mem->region.start); 172 + mem->region.start, 173 + page_alignment); 173 174 } else { 174 - return i915_rsgt_from_buddy_resource(res, mem->region.start); 175 + return i915_rsgt_from_buddy_resource(res, mem->region.start, 176 + page_alignment); 175 177 } 176 178 } 177 179
+2 -1
drivers/gpu/drm/i915/intel_region_ttm.h
··· 24 24 25 25 struct i915_refct_sgt * 26 26 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); 28 29 29 30 void intel_region_ttm_resource_free(struct intel_memory_region *mem, 30 31 struct ttm_resource *res);
+19 -2
drivers/gpu/drm/i915/selftests/intel_memory_region.c
··· 451 451 452 452 static int igt_mock_max_segment(void *arg) 453 453 { 454 - const unsigned int max_segment = rounddown(UINT_MAX, PAGE_SIZE); 455 454 struct intel_memory_region *mem = arg; 456 455 struct drm_i915_private *i915 = mem->i915; 457 456 struct i915_ttm_buddy_resource *res; ··· 459 460 struct drm_buddy *mm; 460 461 struct list_head *blocks; 461 462 struct scatterlist *sg; 463 + I915_RND_STATE(prng); 462 464 LIST_HEAD(objects); 465 + unsigned int max_segment; 466 + unsigned int ps; 463 467 u64 size; 464 468 int err = 0; 465 469 ··· 474 472 */ 475 473 476 474 size = SZ_8G; 477 - mem = mock_region_create(i915, 0, size, PAGE_SIZE, 0, 0); 475 + ps = PAGE_SIZE; 476 + if (i915_prandom_u64_state(&prng) & 1) 477 + ps = SZ_64K; /* For something like DG2 */ 478 + 479 + max_segment = round_down(UINT_MAX, ps); 480 + 481 + mem = mock_region_create(i915, 0, size, ps, 0, 0); 478 482 if (IS_ERR(mem)) 479 483 return PTR_ERR(mem); 480 484 ··· 506 498 } 507 499 508 500 for (sg = obj->mm.pages->sgl; sg; sg = sg_next(sg)) { 501 + dma_addr_t daddr = sg_dma_address(sg); 502 + 509 503 if (sg->length > max_segment) { 510 504 pr_err("%s: Created an oversized scatterlist entry, %u > %u\n", 511 505 __func__, sg->length, max_segment); 506 + err = -EINVAL; 507 + goto out_close; 508 + } 509 + 510 + if (!IS_ALIGNED(daddr, ps)) { 511 + pr_err("%s: Created an unaligned scatterlist entry, addr=%pa, ps=%u\n", 512 + __func__, &daddr, ps); 512 513 err = -EINVAL; 513 514 goto out_close; 514 515 }
+2 -1
drivers/gpu/drm/i915/selftests/mock_region.c
··· 33 33 return PTR_ERR(obj->mm.res); 34 34 35 35 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); 37 38 if (IS_ERR(obj->mm.rsgt)) { 38 39 err = PTR_ERR(obj->mm.rsgt); 39 40 goto err_free_resource;