|
6 | 6 |
|
7 | 7 | #include "i915_scatterlist.h"
|
8 | 8 |
|
| 9 | +#include "i915_buddy.h" |
| 10 | +#include "i915_ttm_buddy_manager.h" |
| 11 | + |
9 | 12 | #include <drm/drm_mm.h>
|
10 | 13 |
|
11 | 14 | #include <linux/slab.h>
|
@@ -104,6 +107,83 @@ struct sg_table *i915_sg_from_mm_node(const struct drm_mm_node *node,
|
104 | 107 | return st;
|
105 | 108 | }
|
106 | 109 |
|
| 110 | +/** |
| 111 | + * i915_sg_from_buddy_resource - Create an sg_table from a struct |
| 112 | + * i915_buddy_block list |
| 113 | + * @res: The struct i915_ttm_buddy_resource. |
| 114 | + * @region_start: An offset to add to the dma addresses of the sg list. |
| 115 | + * |
| 116 | + * Create a struct sg_table, initializing it from struct i915_buddy_block list, |
| 117 | + * taking a maximum segment length into account, splitting into segments |
| 118 | + * if necessary. |
| 119 | + * |
| 120 | + * Return: A pointer to a kmalloced struct sg_table on success, negative |
| 121 | + * error code cast to an error pointer on failure. |
| 122 | + */ |
| 123 | +struct sg_table *i915_sg_from_buddy_resource(struct ttm_resource *res, |
| 124 | + u64 region_start) |
| 125 | +{ |
| 126 | + struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); |
| 127 | + const u64 size = res->num_pages << PAGE_SHIFT; |
| 128 | + const u64 max_segment = rounddown(UINT_MAX, PAGE_SIZE); |
| 129 | + struct i915_buddy_mm *mm = bman_res->mm; |
| 130 | + struct list_head *blocks = &bman_res->blocks; |
| 131 | + struct i915_buddy_block *block; |
| 132 | + struct scatterlist *sg; |
| 133 | + struct sg_table *st; |
| 134 | + resource_size_t prev_end; |
| 135 | + |
| 136 | + GEM_BUG_ON(list_empty(blocks)); |
| 137 | + |
| 138 | + st = kmalloc(sizeof(*st), GFP_KERNEL); |
| 139 | + if (!st) |
| 140 | + return ERR_PTR(-ENOMEM); |
| 141 | + |
| 142 | + if (sg_alloc_table(st, res->num_pages, GFP_KERNEL)) { |
| 143 | + kfree(st); |
| 144 | + return ERR_PTR(-ENOMEM); |
| 145 | + } |
| 146 | + |
| 147 | + sg = st->sgl; |
| 148 | + st->nents = 0; |
| 149 | + prev_end = (resource_size_t)-1; |
| 150 | + |
| 151 | + list_for_each_entry(block, blocks, link) { |
| 152 | + u64 block_size, offset; |
| 153 | + |
| 154 | + block_size = min_t(u64, size, i915_buddy_block_size(mm, block)); |
| 155 | + offset = i915_buddy_block_offset(block); |
| 156 | + |
| 157 | + while (block_size) { |
| 158 | + u64 len; |
| 159 | + |
| 160 | + if (offset != prev_end || sg->length >= max_segment) { |
| 161 | + if (st->nents) |
| 162 | + sg = __sg_next(sg); |
| 163 | + |
| 164 | + sg_dma_address(sg) = region_start + offset; |
| 165 | + sg_dma_len(sg) = 0; |
| 166 | + sg->length = 0; |
| 167 | + st->nents++; |
| 168 | + } |
| 169 | + |
| 170 | + len = min(block_size, max_segment - sg->length); |
| 171 | + sg->length += len; |
| 172 | + sg_dma_len(sg) += len; |
| 173 | + |
| 174 | + offset += len; |
| 175 | + block_size -= len; |
| 176 | + |
| 177 | + prev_end = offset; |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + sg_mark_end(sg); |
| 182 | + i915_sg_trim(st); |
| 183 | + |
| 184 | + return st; |
| 185 | +} |
| 186 | + |
107 | 187 | #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
|
108 | 188 | #include "selftests/scatterlist.c"
|
109 | 189 | #endif
|
0 commit comments