diff options
author | Maarten Lankhorst <maarten.lankhorst@canonical.com> | 2013-10-09 08:36:57 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2013-11-01 15:25:54 -0400 |
commit | 28a326c592e3e444c59f28b3e60c3b07692928d6 (patch) | |
tree | 591325bce244d2d5644f17199d48343bdde985fc /drivers/gpu/drm/radeon/radeon_cs.c | |
parent | db96bd25868c19d71c25cafefed7d0b00c4be641 (diff) |
drm/radeon: fixup locking inversion between, mmap_sem and reservations
op 08-10-13 18:58, Thomas Hellstrom schreef:
> On 10/08/2013 06:47 PM, Jerome Glisse wrote:
>> On Tue, Oct 08, 2013 at 06:29:35PM +0200, Thomas Hellstrom wrote:
>>> On 10/08/2013 04:55 PM, Jerome Glisse wrote:
>>>> On Tue, Oct 08, 2013 at 04:45:18PM +0200, Christian König wrote:
>>>>> Am 08.10.2013 16:33, schrieb Jerome Glisse:
>>>>>> On Tue, Oct 08, 2013 at 04:14:40PM +0200, Maarten Lankhorst wrote:
>>>>>>> Allocate and copy all kernel memory before doing reservations. This prevents a locking
>>>>>>> inversion between mmap_sem and reservation_class, and allows us to drop the trylocking
>>>>>>> in ttm_bo_vm_fault without upsetting lockdep.
>>>>>>>
>>>>>>> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
>>>>>> I would say NAK. Current code only allocate temporary page in AGP case.
>>>>>> So AGP case is userspace -> temp page -> cs checker -> radeon ib.
>>>>>>
>>>>>> Non AGP is directly memcpy to radeon IB.
>>>>>>
>>>>>> Your patch allocate memory memcpy userspace to it and it will then be
>>>>>> memcpy to IB. Which means you introduce an extra memcpy in the process
>>>>>> not something we want.
>>>>> Totally agree. Additional to that there is no good reason to provide
>>>>> anything else than anonymous system memory to the CS ioctl, so the
>>>>> dependency between the mmap_sem and reservations are not really
>>>>> clear to me.
>>>>>
>>>>> Christian.
>>>> I think is that in other code path you take mmap_sem first then reserve
>>>> bo. But here we reserve bo and then we take mmap_sem because of copy
>>> >from user.
>>>> Cheers,
>>>> Jerome
>>>>
>>> Actually the log message is a little confusing. I think the mmap_sem
>>> locking inversion problem is orthogonal to what's being fixed here.
>>>
>>> This patch fixes the possible recursive bo::reserve caused by
>>> malicious user-space handing a pointer to ttm memory so that the ttm
>>> fault handler is called when bos are already reserved. That may
>>> cause a (possibly interruptible) livelock.
>>>
>>> Once that is fixed, we are free to choose the mmap_sem ->
>>> bo::reserve locking order. Currently it's bo::reserve->mmap_sem(),
>>> but the hack required in the ttm fault handler is admittedly a bit
>>> ugly. The plan is to change the locking order to
>>> mmap_sem->bo::reserve
>>>
>>> I'm not sure if it applies to this particular case, but it should be
>>> possible to make sure that copy_from_user_inatomic() will always
>>> succeed, by making sure the pages are present using
>>> get_user_pages(), and release the pages after
>>> copy_from_user_inatomic() is done. That way there's no need for a
>>> double memcpy slowpath, but if the copied data is very fragmented I
>>> guess the resulting code may look ugly. The get_user_pages()
>>> function will return an error if it hits TTM pages.
>>>
>>> /Thomas
>> get_user_pages + copy_from_user_inatomic is overkill. We should just
>> do get_user_pages which fails with ttm memory and then use copy_highpage
>> helper.
>>
>> Cheers,
>> Jerome
> Yeah, it may well be that that's the preferred solution.
>
> /Thomas
>
I still disagree, and shuffled radeon_ib_get around to be called sooner.
How does the patch below look?
8<-------
Allocate and copy all kernel memory before doing reservations. This prevents a locking
inversion between mmap_sem and reservation_class, and allows us to drop the trylocking
in ttm_bo_vm_fault without upsetting lockdep.
Changes since v1:
- Kill extra memcpy for !AGP case.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Reviewed-by: Jerome Glisse <jglisse@redhat.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_cs.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_cs.c | 298 |
1 files changed, 92 insertions, 206 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 66c222836631..c8ab019a980e 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c | |||
@@ -213,9 +213,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) | |||
213 | return -EFAULT; | 213 | return -EFAULT; |
214 | } | 214 | } |
215 | p->chunks[i].length_dw = user_chunk.length_dw; | 215 | p->chunks[i].length_dw = user_chunk.length_dw; |
216 | p->chunks[i].kdata = NULL; | ||
217 | p->chunks[i].chunk_id = user_chunk.chunk_id; | 216 | p->chunks[i].chunk_id = user_chunk.chunk_id; |
218 | p->chunks[i].user_ptr = (void __user *)(unsigned long)user_chunk.chunk_data; | ||
219 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) { | 217 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) { |
220 | p->chunk_relocs_idx = i; | 218 | p->chunk_relocs_idx = i; |
221 | } | 219 | } |
@@ -238,25 +236,31 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) | |||
238 | return -EINVAL; | 236 | return -EINVAL; |
239 | } | 237 | } |
240 | 238 | ||
241 | cdata = (uint32_t *)(unsigned long)user_chunk.chunk_data; | 239 | size = p->chunks[i].length_dw; |
242 | if ((p->chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS) || | 240 | cdata = (void __user *)(unsigned long)user_chunk.chunk_data; |
243 | (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS)) { | 241 | p->chunks[i].user_ptr = cdata; |
244 | size = p->chunks[i].length_dw * sizeof(uint32_t); | 242 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_CONST_IB) |
245 | p->chunks[i].kdata = kmalloc(size, GFP_KERNEL); | 243 | continue; |
246 | if (p->chunks[i].kdata == NULL) { | 244 | |
247 | return -ENOMEM; | 245 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_IB) { |
248 | } | 246 | if (!p->rdev || !(p->rdev->flags & RADEON_IS_AGP)) |
249 | if (DRM_COPY_FROM_USER(p->chunks[i].kdata, | 247 | continue; |
250 | p->chunks[i].user_ptr, size)) { | 248 | } |
251 | return -EFAULT; | 249 | |
252 | } | 250 | p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t)); |
253 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) { | 251 | size *= sizeof(uint32_t); |
254 | p->cs_flags = p->chunks[i].kdata[0]; | 252 | if (p->chunks[i].kdata == NULL) { |
255 | if (p->chunks[i].length_dw > 1) | 253 | return -ENOMEM; |
256 | ring = p->chunks[i].kdata[1]; | 254 | } |
257 | if (p->chunks[i].length_dw > 2) | 255 | if (DRM_COPY_FROM_USER(p->chunks[i].kdata, cdata, size)) { |
258 | priority = (s32)p->chunks[i].kdata[2]; | 256 | return -EFAULT; |
259 | } | 257 | } |
258 | if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) { | ||
259 | p->cs_flags = p->chunks[i].kdata[0]; | ||
260 | if (p->chunks[i].length_dw > 1) | ||
261 | ring = p->chunks[i].kdata[1]; | ||
262 | if (p->chunks[i].length_dw > 2) | ||
263 | priority = (s32)p->chunks[i].kdata[2]; | ||
260 | } | 264 | } |
261 | } | 265 | } |
262 | 266 | ||
@@ -279,34 +283,6 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) | |||
279 | } | 283 | } |
280 | } | 284 | } |
281 | 285 | ||
282 | /* deal with non-vm */ | ||
283 | if ((p->chunk_ib_idx != -1) && | ||
284 | ((p->cs_flags & RADEON_CS_USE_VM) == 0) && | ||
285 | (p->chunks[p->chunk_ib_idx].chunk_id == RADEON_CHUNK_ID_IB)) { | ||
286 | if (p->chunks[p->chunk_ib_idx].length_dw > (16 * 1024)) { | ||
287 | DRM_ERROR("cs IB too big: %d\n", | ||
288 | p->chunks[p->chunk_ib_idx].length_dw); | ||
289 | return -EINVAL; | ||
290 | } | ||
291 | if (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) { | ||
292 | p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
293 | p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
294 | if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL || | ||
295 | p->chunks[p->chunk_ib_idx].kpage[1] == NULL) { | ||
296 | kfree(p->chunks[p->chunk_ib_idx].kpage[0]); | ||
297 | kfree(p->chunks[p->chunk_ib_idx].kpage[1]); | ||
298 | p->chunks[p->chunk_ib_idx].kpage[0] = NULL; | ||
299 | p->chunks[p->chunk_ib_idx].kpage[1] = NULL; | ||
300 | return -ENOMEM; | ||
301 | } | ||
302 | } | ||
303 | p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1; | ||
304 | p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1; | ||
305 | p->chunks[p->chunk_ib_idx].last_copied_page = -1; | ||
306 | p->chunks[p->chunk_ib_idx].last_page_index = | ||
307 | ((p->chunks[p->chunk_ib_idx].length_dw * 4) - 1) / PAGE_SIZE; | ||
308 | } | ||
309 | |||
310 | return 0; | 286 | return 0; |
311 | } | 287 | } |
312 | 288 | ||
@@ -340,13 +316,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo | |||
340 | kfree(parser->track); | 316 | kfree(parser->track); |
341 | kfree(parser->relocs); | 317 | kfree(parser->relocs); |
342 | kfree(parser->relocs_ptr); | 318 | kfree(parser->relocs_ptr); |
343 | for (i = 0; i < parser->nchunks; i++) { | 319 | for (i = 0; i < parser->nchunks; i++) |
344 | kfree(parser->chunks[i].kdata); | 320 | drm_free_large(parser->chunks[i].kdata); |
345 | if ((parser->rdev->flags & RADEON_IS_AGP)) { | ||
346 | kfree(parser->chunks[i].kpage[0]); | ||
347 | kfree(parser->chunks[i].kpage[1]); | ||
348 | } | ||
349 | } | ||
350 | kfree(parser->chunks); | 321 | kfree(parser->chunks); |
351 | kfree(parser->chunks_array); | 322 | kfree(parser->chunks_array); |
352 | radeon_ib_free(parser->rdev, &parser->ib); | 323 | radeon_ib_free(parser->rdev, &parser->ib); |
@@ -356,7 +327,6 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo | |||
356 | static int radeon_cs_ib_chunk(struct radeon_device *rdev, | 327 | static int radeon_cs_ib_chunk(struct radeon_device *rdev, |
357 | struct radeon_cs_parser *parser) | 328 | struct radeon_cs_parser *parser) |
358 | { | 329 | { |
359 | struct radeon_cs_chunk *ib_chunk; | ||
360 | int r; | 330 | int r; |
361 | 331 | ||
362 | if (parser->chunk_ib_idx == -1) | 332 | if (parser->chunk_ib_idx == -1) |
@@ -365,28 +335,11 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, | |||
365 | if (parser->cs_flags & RADEON_CS_USE_VM) | 335 | if (parser->cs_flags & RADEON_CS_USE_VM) |
366 | return 0; | 336 | return 0; |
367 | 337 | ||
368 | ib_chunk = &parser->chunks[parser->chunk_ib_idx]; | ||
369 | /* Copy the packet into the IB, the parser will read from the | ||
370 | * input memory (cached) and write to the IB (which can be | ||
371 | * uncached). | ||
372 | */ | ||
373 | r = radeon_ib_get(rdev, parser->ring, &parser->ib, | ||
374 | NULL, ib_chunk->length_dw * 4); | ||
375 | if (r) { | ||
376 | DRM_ERROR("Failed to get ib !\n"); | ||
377 | return r; | ||
378 | } | ||
379 | parser->ib.length_dw = ib_chunk->length_dw; | ||
380 | r = radeon_cs_parse(rdev, parser->ring, parser); | 338 | r = radeon_cs_parse(rdev, parser->ring, parser); |
381 | if (r || parser->parser_error) { | 339 | if (r || parser->parser_error) { |
382 | DRM_ERROR("Invalid command stream !\n"); | 340 | DRM_ERROR("Invalid command stream !\n"); |
383 | return r; | 341 | return r; |
384 | } | 342 | } |
385 | r = radeon_cs_finish_pages(parser); | ||
386 | if (r) { | ||
387 | DRM_ERROR("Invalid command stream !\n"); | ||
388 | return r; | ||
389 | } | ||
390 | 343 | ||
391 | if (parser->ring == R600_RING_TYPE_UVD_INDEX) | 344 | if (parser->ring == R600_RING_TYPE_UVD_INDEX) |
392 | radeon_uvd_note_usage(rdev); | 345 | radeon_uvd_note_usage(rdev); |
@@ -424,7 +377,6 @@ static int radeon_bo_vm_update_pte(struct radeon_cs_parser *parser, | |||
424 | static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, | 377 | static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, |
425 | struct radeon_cs_parser *parser) | 378 | struct radeon_cs_parser *parser) |
426 | { | 379 | { |
427 | struct radeon_cs_chunk *ib_chunk; | ||
428 | struct radeon_fpriv *fpriv = parser->filp->driver_priv; | 380 | struct radeon_fpriv *fpriv = parser->filp->driver_priv; |
429 | struct radeon_vm *vm = &fpriv->vm; | 381 | struct radeon_vm *vm = &fpriv->vm; |
430 | int r; | 382 | int r; |
@@ -434,49 +386,13 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, | |||
434 | if ((parser->cs_flags & RADEON_CS_USE_VM) == 0) | 386 | if ((parser->cs_flags & RADEON_CS_USE_VM) == 0) |
435 | return 0; | 387 | return 0; |
436 | 388 | ||
437 | if ((rdev->family >= CHIP_TAHITI) && | 389 | if (parser->const_ib.length_dw) { |
438 | (parser->chunk_const_ib_idx != -1)) { | ||
439 | ib_chunk = &parser->chunks[parser->chunk_const_ib_idx]; | ||
440 | if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { | ||
441 | DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw); | ||
442 | return -EINVAL; | ||
443 | } | ||
444 | r = radeon_ib_get(rdev, parser->ring, &parser->const_ib, | ||
445 | vm, ib_chunk->length_dw * 4); | ||
446 | if (r) { | ||
447 | DRM_ERROR("Failed to get const ib !\n"); | ||
448 | return r; | ||
449 | } | ||
450 | parser->const_ib.is_const_ib = true; | ||
451 | parser->const_ib.length_dw = ib_chunk->length_dw; | ||
452 | /* Copy the packet into the IB */ | ||
453 | if (DRM_COPY_FROM_USER(parser->const_ib.ptr, ib_chunk->user_ptr, | ||
454 | ib_chunk->length_dw * 4)) { | ||
455 | return -EFAULT; | ||
456 | } | ||
457 | r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib); | 390 | r = radeon_ring_ib_parse(rdev, parser->ring, &parser->const_ib); |
458 | if (r) { | 391 | if (r) { |
459 | return r; | 392 | return r; |
460 | } | 393 | } |
461 | } | 394 | } |
462 | 395 | ||
463 | ib_chunk = &parser->chunks[parser->chunk_ib_idx]; | ||
464 | if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { | ||
465 | DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw); | ||
466 | return -EINVAL; | ||
467 | } | ||
468 | r = radeon_ib_get(rdev, parser->ring, &parser->ib, | ||
469 | vm, ib_chunk->length_dw * 4); | ||
470 | if (r) { | ||
471 | DRM_ERROR("Failed to get ib !\n"); | ||
472 | return r; | ||
473 | } | ||
474 | parser->ib.length_dw = ib_chunk->length_dw; | ||
475 | /* Copy the packet into the IB */ | ||
476 | if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr, | ||
477 | ib_chunk->length_dw * 4)) { | ||
478 | return -EFAULT; | ||
479 | } | ||
480 | r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib); | 396 | r = radeon_ring_ib_parse(rdev, parser->ring, &parser->ib); |
481 | if (r) { | 397 | if (r) { |
482 | return r; | 398 | return r; |
@@ -528,6 +444,62 @@ static int radeon_cs_handle_lockup(struct radeon_device *rdev, int r) | |||
528 | return r; | 444 | return r; |
529 | } | 445 | } |
530 | 446 | ||
447 | static int radeon_cs_ib_fill(struct radeon_device *rdev, struct radeon_cs_parser *parser) | ||
448 | { | ||
449 | struct radeon_cs_chunk *ib_chunk; | ||
450 | struct radeon_vm *vm = NULL; | ||
451 | int r; | ||
452 | |||
453 | if (parser->chunk_ib_idx == -1) | ||
454 | return 0; | ||
455 | |||
456 | if (parser->cs_flags & RADEON_CS_USE_VM) { | ||
457 | struct radeon_fpriv *fpriv = parser->filp->driver_priv; | ||
458 | vm = &fpriv->vm; | ||
459 | |||
460 | if ((rdev->family >= CHIP_TAHITI) && | ||
461 | (parser->chunk_const_ib_idx != -1)) { | ||
462 | ib_chunk = &parser->chunks[parser->chunk_const_ib_idx]; | ||
463 | if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { | ||
464 | DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw); | ||
465 | return -EINVAL; | ||
466 | } | ||
467 | r = radeon_ib_get(rdev, parser->ring, &parser->const_ib, | ||
468 | vm, ib_chunk->length_dw * 4); | ||
469 | if (r) { | ||
470 | DRM_ERROR("Failed to get const ib !\n"); | ||
471 | return r; | ||
472 | } | ||
473 | parser->const_ib.is_const_ib = true; | ||
474 | parser->const_ib.length_dw = ib_chunk->length_dw; | ||
475 | if (DRM_COPY_FROM_USER(parser->const_ib.ptr, | ||
476 | ib_chunk->user_ptr, | ||
477 | ib_chunk->length_dw * 4)) | ||
478 | return -EFAULT; | ||
479 | } | ||
480 | |||
481 | ib_chunk = &parser->chunks[parser->chunk_ib_idx]; | ||
482 | if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) { | ||
483 | DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw); | ||
484 | return -EINVAL; | ||
485 | } | ||
486 | } | ||
487 | ib_chunk = &parser->chunks[parser->chunk_ib_idx]; | ||
488 | |||
489 | r = radeon_ib_get(rdev, parser->ring, &parser->ib, | ||
490 | vm, ib_chunk->length_dw * 4); | ||
491 | if (r) { | ||
492 | DRM_ERROR("Failed to get ib !\n"); | ||
493 | return r; | ||
494 | } | ||
495 | parser->ib.length_dw = ib_chunk->length_dw; | ||
496 | if (ib_chunk->kdata) | ||
497 | memcpy(parser->ib.ptr, ib_chunk->kdata, ib_chunk->length_dw * 4); | ||
498 | else if (DRM_COPY_FROM_USER(parser->ib.ptr, ib_chunk->user_ptr, ib_chunk->length_dw * 4)) | ||
499 | return -EFAULT; | ||
500 | return 0; | ||
501 | } | ||
502 | |||
531 | int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) | 503 | int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) |
532 | { | 504 | { |
533 | struct radeon_device *rdev = dev->dev_private; | 505 | struct radeon_device *rdev = dev->dev_private; |
@@ -553,10 +525,15 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) | |||
553 | r = radeon_cs_handle_lockup(rdev, r); | 525 | r = radeon_cs_handle_lockup(rdev, r); |
554 | return r; | 526 | return r; |
555 | } | 527 | } |
556 | r = radeon_cs_parser_relocs(&parser); | 528 | |
557 | if (r) { | 529 | r = radeon_cs_ib_fill(rdev, &parser); |
558 | if (r != -ERESTARTSYS) | 530 | if (!r) { |
531 | r = radeon_cs_parser_relocs(&parser); | ||
532 | if (r && r != -ERESTARTSYS) | ||
559 | DRM_ERROR("Failed to parse relocation %d!\n", r); | 533 | DRM_ERROR("Failed to parse relocation %d!\n", r); |
534 | } | ||
535 | |||
536 | if (r) { | ||
560 | radeon_cs_parser_fini(&parser, r, false); | 537 | radeon_cs_parser_fini(&parser, r, false); |
561 | up_read(&rdev->exclusive_lock); | 538 | up_read(&rdev->exclusive_lock); |
562 | r = radeon_cs_handle_lockup(rdev, r); | 539 | r = radeon_cs_handle_lockup(rdev, r); |
@@ -580,97 +557,6 @@ out: | |||
580 | return r; | 557 | return r; |
581 | } | 558 | } |
582 | 559 | ||
583 | int radeon_cs_finish_pages(struct radeon_cs_parser *p) | ||
584 | { | ||
585 | struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; | ||
586 | int i; | ||
587 | int size = PAGE_SIZE; | ||
588 | |||
589 | for (i = ibc->last_copied_page + 1; i <= ibc->last_page_index; i++) { | ||
590 | if (i == ibc->last_page_index) { | ||
591 | size = (ibc->length_dw * 4) % PAGE_SIZE; | ||
592 | if (size == 0) | ||
593 | size = PAGE_SIZE; | ||
594 | } | ||
595 | |||
596 | if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), | ||
597 | ibc->user_ptr + (i * PAGE_SIZE), | ||
598 | size)) | ||
599 | return -EFAULT; | ||
600 | } | ||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) | ||
605 | { | ||
606 | int new_page; | ||
607 | struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; | ||
608 | int i; | ||
609 | int size = PAGE_SIZE; | ||
610 | bool copy1 = (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) ? | ||
611 | false : true; | ||
612 | |||
613 | for (i = ibc->last_copied_page + 1; i < pg_idx; i++) { | ||
614 | if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), | ||
615 | ibc->user_ptr + (i * PAGE_SIZE), | ||
616 | PAGE_SIZE)) { | ||
617 | p->parser_error = -EFAULT; | ||
618 | return 0; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | if (pg_idx == ibc->last_page_index) { | ||
623 | size = (ibc->length_dw * 4) % PAGE_SIZE; | ||
624 | if (size == 0) | ||
625 | size = PAGE_SIZE; | ||
626 | } | ||
627 | |||
628 | new_page = ibc->kpage_idx[0] < ibc->kpage_idx[1] ? 0 : 1; | ||
629 | if (copy1) | ||
630 | ibc->kpage[new_page] = p->ib.ptr + (pg_idx * (PAGE_SIZE / 4)); | ||
631 | |||
632 | if (DRM_COPY_FROM_USER(ibc->kpage[new_page], | ||
633 | ibc->user_ptr + (pg_idx * PAGE_SIZE), | ||
634 | size)) { | ||
635 | p->parser_error = -EFAULT; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | /* copy to IB for non single case */ | ||
640 | if (!copy1) | ||
641 | memcpy((void *)(p->ib.ptr+(pg_idx*(PAGE_SIZE/4))), ibc->kpage[new_page], size); | ||
642 | |||
643 | ibc->last_copied_page = pg_idx; | ||
644 | ibc->kpage_idx[new_page] = pg_idx; | ||
645 | |||
646 | return new_page; | ||
647 | } | ||
648 | |||
649 | u32 radeon_get_ib_value(struct radeon_cs_parser *p, int idx) | ||
650 | { | ||
651 | struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; | ||
652 | u32 pg_idx, pg_offset; | ||
653 | u32 idx_value = 0; | ||
654 | int new_page; | ||
655 | |||
656 | pg_idx = (idx * 4) / PAGE_SIZE; | ||
657 | pg_offset = (idx * 4) % PAGE_SIZE; | ||
658 | |||
659 | if (ibc->kpage_idx[0] == pg_idx) | ||
660 | return ibc->kpage[0][pg_offset/4]; | ||
661 | if (ibc->kpage_idx[1] == pg_idx) | ||
662 | return ibc->kpage[1][pg_offset/4]; | ||
663 | |||
664 | new_page = radeon_cs_update_pages(p, pg_idx); | ||
665 | if (new_page < 0) { | ||
666 | p->parser_error = new_page; | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | idx_value = ibc->kpage[new_page][pg_offset/4]; | ||
671 | return idx_value; | ||
672 | } | ||
673 | |||
674 | /** | 560 | /** |
675 | * radeon_cs_packet_parse() - parse cp packet and point ib index to next packet | 561 | * radeon_cs_packet_parse() - parse cp packet and point ib index to next packet |
676 | * @parser: parser structure holding parsing context. | 562 | * @parser: parser structure holding parsing context. |