diff options
author | Dave Airlie <airlied@redhat.com> | 2009-09-23 02:56:27 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2009-09-24 23:08:18 -0400 |
commit | 513bcb4655e68706594e45dfa1d4b181500110ba (patch) | |
tree | ed457db4cfb202015866a131ad4e742503728fad /drivers/gpu/drm/radeon/radeon_cs.c | |
parent | 35e4b7af21d77933abda3d41d1672589eb6c960c (diff) |
drm/radeon/kms: don't require up to 64k allocations. (v2)
This avoids needing to do a kmalloc > PAGE_SIZE for the main
indirect buffer chunk, it adds an accessor for all reads from
the chunk and caches a single page at a time for subsequent
reads.
changes since v1:
Use a two page pool which should be the most common case
a single packet spanning > PAGE_SIZE will be hit, but I'm
having trouble seeing anywhere we currently generate anything like that.
hopefully proper short page copying at end
added parser_error flag to set deep errors instead of having to test
every ib value fetch.
fixed bug in patch that went to list.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_cs.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_cs.c | 105 |
1 files changed, 96 insertions, 9 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 12f5990c2d2a..dea8acf88865 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c | |||
@@ -142,15 +142,31 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) | |||
142 | } | 142 | } |
143 | 143 | ||
144 | p->chunks[i].length_dw = user_chunk.length_dw; | 144 | p->chunks[i].length_dw = user_chunk.length_dw; |
145 | cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; | 145 | p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data; |
146 | 146 | ||
147 | size = p->chunks[i].length_dw * sizeof(uint32_t); | 147 | cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; |
148 | p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); | 148 | if (p->chunks[i].chunk_id != RADEON_CHUNK_ID_IB) { |
149 | if (p->chunks[i].kdata == NULL) { | 149 | size = p->chunks[i].length_dw * sizeof(uint32_t); |
150 | return -ENOMEM; | 150 | p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); |
151 | } | 151 | if (p->chunks[i].kdata == NULL) { |
152 | if (DRM_COPY_FROM_USER(p->chunks[i].kdata, cdata, size)) { | 152 | return -ENOMEM; |
153 | return -EFAULT; | 153 | } |
154 | if (DRM_COPY_FROM_USER(p->chunks[i].kdata, | ||
155 | p->chunks[i].user_ptr, size)) { | ||
156 | return -EFAULT; | ||
157 | } | ||
158 | } else { | ||
159 | p->chunks[i].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
160 | p->chunks[i].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
161 | if (p->chunks[i].kpage[0] == NULL || p->chunks[i].kpage[1] == NULL) { | ||
162 | kfree(p->chunks[i].kpage[0]); | ||
163 | kfree(p->chunks[i].kpage[1]); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | p->chunks[i].kpage_idx[0] = -1; | ||
167 | p->chunks[i].kpage_idx[1] = -1; | ||
168 | p->chunks[i].last_copied_page = -1; | ||
169 | p->chunks[i].last_page_index = ((p->chunks[i].length_dw * 4) - 1) / PAGE_SIZE; | ||
154 | } | 170 | } |
155 | } | 171 | } |
156 | if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { | 172 | if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { |
@@ -190,6 +206,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error) | |||
190 | kfree(parser->relocs_ptr); | 206 | kfree(parser->relocs_ptr); |
191 | for (i = 0; i < parser->nchunks; i++) { | 207 | for (i = 0; i < parser->nchunks; i++) { |
192 | kfree(parser->chunks[i].kdata); | 208 | kfree(parser->chunks[i].kdata); |
209 | kfree(parser->chunks[i].kpage[0]); | ||
210 | kfree(parser->chunks[i].kpage[1]); | ||
193 | } | 211 | } |
194 | kfree(parser->chunks); | 212 | kfree(parser->chunks); |
195 | kfree(parser->chunks_array); | 213 | kfree(parser->chunks_array); |
@@ -238,8 +256,14 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) | |||
238 | * uncached). */ | 256 | * uncached). */ |
239 | ib_chunk = &parser.chunks[parser.chunk_ib_idx]; | 257 | ib_chunk = &parser.chunks[parser.chunk_ib_idx]; |
240 | parser.ib->length_dw = ib_chunk->length_dw; | 258 | parser.ib->length_dw = ib_chunk->length_dw; |
241 | memcpy((void *)parser.ib->ptr, ib_chunk->kdata, ib_chunk->length_dw*4); | ||
242 | r = radeon_cs_parse(&parser); | 259 | r = radeon_cs_parse(&parser); |
260 | if (r || parser.parser_error) { | ||
261 | DRM_ERROR("Invalid command stream !\n"); | ||
262 | radeon_cs_parser_fini(&parser, r); | ||
263 | mutex_unlock(&rdev->cs_mutex); | ||
264 | return r; | ||
265 | } | ||
266 | r = radeon_cs_finish_pages(&parser); | ||
243 | if (r) { | 267 | if (r) { |
244 | DRM_ERROR("Invalid command stream !\n"); | 268 | DRM_ERROR("Invalid command stream !\n"); |
245 | radeon_cs_parser_fini(&parser, r); | 269 | radeon_cs_parser_fini(&parser, r); |
@@ -254,3 +278,66 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) | |||
254 | mutex_unlock(&rdev->cs_mutex); | 278 | mutex_unlock(&rdev->cs_mutex); |
255 | return r; | 279 | return r; |
256 | } | 280 | } |
281 | |||
282 | int radeon_cs_finish_pages(struct radeon_cs_parser *p) | ||
283 | { | ||
284 | struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; | ||
285 | int i; | ||
286 | int size = PAGE_SIZE; | ||
287 | |||
288 | for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) { | ||
289 | if (i == ibc->last_page_index) { | ||
290 | size = (ibc->length_dw * 4) % PAGE_SIZE; | ||
291 | if (size == 0) | ||
292 | size = PAGE_SIZE; | ||
293 | } | ||
294 | |||
295 | if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), | ||
296 | ibc->user_ptr + (i * PAGE_SIZE), | ||
297 | size)) | ||
298 | return -EFAULT; | ||
299 | } | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) | ||
304 | { | ||
305 | int new_page; | ||
306 | int num_extra_pages; | ||
307 | struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; | ||
308 | int i; | ||
309 | int size = PAGE_SIZE; | ||
310 | |||
311 | num_extra_pages = (pg_idx - ibc->last_copied_page - 1); | ||
312 | for (i = ibc->last_copied_page + 1; i < ibc->last_copied_page + num_extra_pages; i++) { | ||
313 | if (DRM_COPY_FROM_USER(p->ib->ptr + (i * (PAGE_SIZE/4)), | ||
314 | ibc->user_ptr + (i * PAGE_SIZE), | ||
315 | PAGE_SIZE)) { | ||
316 | p->parser_error = -EFAULT; | ||
317 | return 0; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1; | ||
322 | |||
323 | if (pg_idx == ibc->last_page_index) { | ||
324 | size = (ibc->length_dw * 4) % PAGE_SIZE; | ||
325 | if (size == 0) | ||
326 | size = PAGE_SIZE; | ||
327 | } | ||
328 | |||
329 | if (DRM_COPY_FROM_USER(ibc->kpage[new_page], | ||
330 | ibc->user_ptr + (pg_idx * PAGE_SIZE), | ||
331 | size)) { | ||
332 | p->parser_error = -EFAULT; | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | /* copy to IB here */ | ||
337 | memcpy((void *)(p->ib->ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); | ||
338 | |||
339 | ibc->last_copied_page = pg_idx; | ||
340 | ibc->kpage_idx[new_page] = pg_idx; | ||
341 | |||
342 | return new_page; | ||
343 | } | ||