diff options
author | Eric Anholt <eric@anholt.net> | 2009-03-11 15:30:04 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-03-27 17:47:34 -0400 |
commit | 201361a54ed187d8595a283e3a4ddb213bc8323b (patch) | |
tree | 12a5d23a45f72f8bd917161735d55985654b52e0 /drivers/gpu/drm/i915/i915_dma.c | |
parent | eb01459fbbccb4ca0b879cbfc97e33ac6eabf975 (diff) |
drm/i915: Fix lock order reversal with cliprects and cmdbuf in non-DRI2 paths.
This introduces allocation in the batch submission path that wasn't there
previously, but these are compatibility paths so we care about simplicity
more than performance.
kernel.org bug #12419.
Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_dma.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 107 |
1 files changed, 73 insertions, 34 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6d21b9e48b89..ae83fe0ab374 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c | |||
@@ -356,7 +356,7 @@ static int validate_cmd(int cmd) | |||
356 | return ret; | 356 | return ret; |
357 | } | 357 | } |
358 | 358 | ||
359 | static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords) | 359 | static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords) |
360 | { | 360 | { |
361 | drm_i915_private_t *dev_priv = dev->dev_private; | 361 | drm_i915_private_t *dev_priv = dev->dev_private; |
362 | int i; | 362 | int i; |
@@ -370,8 +370,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor | |||
370 | for (i = 0; i < dwords;) { | 370 | for (i = 0; i < dwords;) { |
371 | int cmd, sz; | 371 | int cmd, sz; |
372 | 372 | ||
373 | if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) | 373 | cmd = buffer[i]; |
374 | return -EINVAL; | ||
375 | 374 | ||
376 | if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) | 375 | if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) |
377 | return -EINVAL; | 376 | return -EINVAL; |
@@ -379,11 +378,7 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor | |||
379 | OUT_RING(cmd); | 378 | OUT_RING(cmd); |
380 | 379 | ||
381 | while (++i, --sz) { | 380 | while (++i, --sz) { |
382 | if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], | 381 | OUT_RING(buffer[i]); |
383 | sizeof(cmd))) { | ||
384 | return -EINVAL; | ||
385 | } | ||
386 | OUT_RING(cmd); | ||
387 | } | 382 | } |
388 | } | 383 | } |
389 | 384 | ||
@@ -397,17 +392,13 @@ static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwor | |||
397 | 392 | ||
398 | int | 393 | int |
399 | i915_emit_box(struct drm_device *dev, | 394 | i915_emit_box(struct drm_device *dev, |
400 | struct drm_clip_rect __user *boxes, | 395 | struct drm_clip_rect *boxes, |
401 | int i, int DR1, int DR4) | 396 | int i, int DR1, int DR4) |
402 | { | 397 | { |
403 | drm_i915_private_t *dev_priv = dev->dev_private; | 398 | drm_i915_private_t *dev_priv = dev->dev_private; |
404 | struct drm_clip_rect box; | 399 | struct drm_clip_rect box = boxes[i]; |
405 | RING_LOCALS; | 400 | RING_LOCALS; |
406 | 401 | ||
407 | if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { | ||
408 | return -EFAULT; | ||
409 | } | ||
410 | |||
411 | if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { | 402 | if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { |
412 | DRM_ERROR("Bad box %d,%d..%d,%d\n", | 403 | DRM_ERROR("Bad box %d,%d..%d,%d\n", |
413 | box.x1, box.y1, box.x2, box.y2); | 404 | box.x1, box.y1, box.x2, box.y2); |
@@ -460,7 +451,9 @@ static void i915_emit_breadcrumb(struct drm_device *dev) | |||
460 | } | 451 | } |
461 | 452 | ||
462 | static int i915_dispatch_cmdbuffer(struct drm_device * dev, | 453 | static int i915_dispatch_cmdbuffer(struct drm_device * dev, |
463 | drm_i915_cmdbuffer_t * cmd) | 454 | drm_i915_cmdbuffer_t *cmd, |
455 | struct drm_clip_rect *cliprects, | ||
456 | void *cmdbuf) | ||
464 | { | 457 | { |
465 | int nbox = cmd->num_cliprects; | 458 | int nbox = cmd->num_cliprects; |
466 | int i = 0, count, ret; | 459 | int i = 0, count, ret; |
@@ -476,13 +469,13 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev, | |||
476 | 469 | ||
477 | for (i = 0; i < count; i++) { | 470 | for (i = 0; i < count; i++) { |
478 | if (i < nbox) { | 471 | if (i < nbox) { |
479 | ret = i915_emit_box(dev, cmd->cliprects, i, | 472 | ret = i915_emit_box(dev, cliprects, i, |
480 | cmd->DR1, cmd->DR4); | 473 | cmd->DR1, cmd->DR4); |
481 | if (ret) | 474 | if (ret) |
482 | return ret; | 475 | return ret; |
483 | } | 476 | } |
484 | 477 | ||
485 | ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4); | 478 | ret = i915_emit_cmds(dev, cmdbuf, cmd->sz / 4); |
486 | if (ret) | 479 | if (ret) |
487 | return ret; | 480 | return ret; |
488 | } | 481 | } |
@@ -492,10 +485,10 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev, | |||
492 | } | 485 | } |
493 | 486 | ||
494 | static int i915_dispatch_batchbuffer(struct drm_device * dev, | 487 | static int i915_dispatch_batchbuffer(struct drm_device * dev, |
495 | drm_i915_batchbuffer_t * batch) | 488 | drm_i915_batchbuffer_t * batch, |
489 | struct drm_clip_rect *cliprects) | ||
496 | { | 490 | { |
497 | drm_i915_private_t *dev_priv = dev->dev_private; | 491 | drm_i915_private_t *dev_priv = dev->dev_private; |
498 | struct drm_clip_rect __user *boxes = batch->cliprects; | ||
499 | int nbox = batch->num_cliprects; | 492 | int nbox = batch->num_cliprects; |
500 | int i = 0, count; | 493 | int i = 0, count; |
501 | RING_LOCALS; | 494 | RING_LOCALS; |
@@ -511,7 +504,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev, | |||
511 | 504 | ||
512 | for (i = 0; i < count; i++) { | 505 | for (i = 0; i < count; i++) { |
513 | if (i < nbox) { | 506 | if (i < nbox) { |
514 | int ret = i915_emit_box(dev, boxes, i, | 507 | int ret = i915_emit_box(dev, cliprects, i, |
515 | batch->DR1, batch->DR4); | 508 | batch->DR1, batch->DR4); |
516 | if (ret) | 509 | if (ret) |
517 | return ret; | 510 | return ret; |
@@ -626,6 +619,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, | |||
626 | master_priv->sarea_priv; | 619 | master_priv->sarea_priv; |
627 | drm_i915_batchbuffer_t *batch = data; | 620 | drm_i915_batchbuffer_t *batch = data; |
628 | int ret; | 621 | int ret; |
622 | struct drm_clip_rect *cliprects = NULL; | ||
629 | 623 | ||
630 | if (!dev_priv->allow_batchbuffer) { | 624 | if (!dev_priv->allow_batchbuffer) { |
631 | DRM_ERROR("Batchbuffer ioctl disabled\n"); | 625 | DRM_ERROR("Batchbuffer ioctl disabled\n"); |
@@ -637,17 +631,35 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, | |||
637 | 631 | ||
638 | RING_LOCK_TEST_WITH_RETURN(dev, file_priv); | 632 | RING_LOCK_TEST_WITH_RETURN(dev, file_priv); |
639 | 633 | ||
640 | if (batch->num_cliprects && DRM_VERIFYAREA_READ(batch->cliprects, | 634 | if (batch->num_cliprects < 0) |
641 | batch->num_cliprects * | 635 | return -EINVAL; |
642 | sizeof(struct drm_clip_rect))) | 636 | |
643 | return -EFAULT; | 637 | if (batch->num_cliprects) { |
638 | cliprects = drm_calloc(batch->num_cliprects, | ||
639 | sizeof(struct drm_clip_rect), | ||
640 | DRM_MEM_DRIVER); | ||
641 | if (cliprects == NULL) | ||
642 | return -ENOMEM; | ||
643 | |||
644 | ret = copy_from_user(cliprects, batch->cliprects, | ||
645 | batch->num_cliprects * | ||
646 | sizeof(struct drm_clip_rect)); | ||
647 | if (ret != 0) | ||
648 | goto fail_free; | ||
649 | } | ||
644 | 650 | ||
645 | mutex_lock(&dev->struct_mutex); | 651 | mutex_lock(&dev->struct_mutex); |
646 | ret = i915_dispatch_batchbuffer(dev, batch); | 652 | ret = i915_dispatch_batchbuffer(dev, batch, cliprects); |
647 | mutex_unlock(&dev->struct_mutex); | 653 | mutex_unlock(&dev->struct_mutex); |
648 | 654 | ||
649 | if (sarea_priv) | 655 | if (sarea_priv) |
650 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | 656 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); |
657 | |||
658 | fail_free: | ||
659 | drm_free(cliprects, | ||
660 | batch->num_cliprects * sizeof(struct drm_clip_rect), | ||
661 | DRM_MEM_DRIVER); | ||
662 | |||
651 | return ret; | 663 | return ret; |
652 | } | 664 | } |
653 | 665 | ||
@@ -659,6 +671,8 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, | |||
659 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) | 671 | drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) |
660 | master_priv->sarea_priv; | 672 | master_priv->sarea_priv; |
661 | drm_i915_cmdbuffer_t *cmdbuf = data; | 673 | drm_i915_cmdbuffer_t *cmdbuf = data; |
674 | struct drm_clip_rect *cliprects = NULL; | ||
675 | void *batch_data; | ||
662 | int ret; | 676 | int ret; |
663 | 677 | ||
664 | DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", | 678 | DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", |
@@ -666,25 +680,50 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, | |||
666 | 680 | ||
667 | RING_LOCK_TEST_WITH_RETURN(dev, file_priv); | 681 | RING_LOCK_TEST_WITH_RETURN(dev, file_priv); |
668 | 682 | ||
669 | if (cmdbuf->num_cliprects && | 683 | if (cmdbuf->num_cliprects < 0) |
670 | DRM_VERIFYAREA_READ(cmdbuf->cliprects, | 684 | return -EINVAL; |
671 | cmdbuf->num_cliprects * | 685 | |
672 | sizeof(struct drm_clip_rect))) { | 686 | batch_data = drm_alloc(cmdbuf->sz, DRM_MEM_DRIVER); |
673 | DRM_ERROR("Fault accessing cliprects\n"); | 687 | if (batch_data == NULL) |
674 | return -EFAULT; | 688 | return -ENOMEM; |
689 | |||
690 | ret = copy_from_user(batch_data, cmdbuf->buf, cmdbuf->sz); | ||
691 | if (ret != 0) | ||
692 | goto fail_batch_free; | ||
693 | |||
694 | if (cmdbuf->num_cliprects) { | ||
695 | cliprects = drm_calloc(cmdbuf->num_cliprects, | ||
696 | sizeof(struct drm_clip_rect), | ||
697 | DRM_MEM_DRIVER); | ||
698 | if (cliprects == NULL) | ||
699 | goto fail_batch_free; | ||
700 | |||
701 | ret = copy_from_user(cliprects, cmdbuf->cliprects, | ||
702 | cmdbuf->num_cliprects * | ||
703 | sizeof(struct drm_clip_rect)); | ||
704 | if (ret != 0) | ||
705 | goto fail_clip_free; | ||
675 | } | 706 | } |
676 | 707 | ||
677 | mutex_lock(&dev->struct_mutex); | 708 | mutex_lock(&dev->struct_mutex); |
678 | ret = i915_dispatch_cmdbuffer(dev, cmdbuf); | 709 | ret = i915_dispatch_cmdbuffer(dev, cmdbuf, cliprects, batch_data); |
679 | mutex_unlock(&dev->struct_mutex); | 710 | mutex_unlock(&dev->struct_mutex); |
680 | if (ret) { | 711 | if (ret) { |
681 | DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); | 712 | DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); |
682 | return ret; | 713 | goto fail_batch_free; |
683 | } | 714 | } |
684 | 715 | ||
685 | if (sarea_priv) | 716 | if (sarea_priv) |
686 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | 717 | sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); |
687 | return 0; | 718 | |
719 | fail_batch_free: | ||
720 | drm_free(batch_data, cmdbuf->sz, DRM_MEM_DRIVER); | ||
721 | fail_clip_free: | ||
722 | drm_free(cliprects, | ||
723 | cmdbuf->num_cliprects * sizeof(struct drm_clip_rect), | ||
724 | DRM_MEM_DRIVER); | ||
725 | |||
726 | return ret; | ||
688 | } | 727 | } |
689 | 728 | ||
690 | static int i915_flip_bufs(struct drm_device *dev, void *data, | 729 | static int i915_flip_bufs(struct drm_device *dev, void *data, |