aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/radeon/radeon_cs.c
diff options
context:
space:
mode:
authorMaarten Lankhorst <maarten.lankhorst@canonical.com>2013-10-09 08:36:57 -0400
committerAlex Deucher <alexander.deucher@amd.com>2013-11-01 15:25:54 -0400
commit28a326c592e3e444c59f28b3e60c3b07692928d6 (patch)
tree591325bce244d2d5644f17199d48343bdde985fc /drivers/gpu/drm/radeon/radeon_cs.c
parentdb96bd25868c19d71c25cafefed7d0b00c4be641 (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.c298
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
356static int radeon_cs_ib_chunk(struct radeon_device *rdev, 327static 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,
424static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, 377static 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
447static 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
531int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) 503int 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
583int 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
604static 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
649u32 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.