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 | |
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')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 107 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 27 |
3 files changed, 97 insertions, 39 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, |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 75e33844146b..2c02ce6b2b98 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -520,7 +520,7 @@ extern int i915_driver_device_is_agp(struct drm_device * dev); | |||
520 | extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, | 520 | extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, |
521 | unsigned long arg); | 521 | unsigned long arg); |
522 | extern int i915_emit_box(struct drm_device *dev, | 522 | extern int i915_emit_box(struct drm_device *dev, |
523 | struct drm_clip_rect __user *boxes, | 523 | struct drm_clip_rect *boxes, |
524 | int i, int DR1, int DR4); | 524 | int i, int DR1, int DR4); |
525 | 525 | ||
526 | /* i915_irq.c */ | 526 | /* i915_irq.c */ |
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 010af908bdb6..2bda15197cce 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c | |||
@@ -2891,11 +2891,10 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | |||
2891 | static int | 2891 | static int |
2892 | i915_dispatch_gem_execbuffer(struct drm_device *dev, | 2892 | i915_dispatch_gem_execbuffer(struct drm_device *dev, |
2893 | struct drm_i915_gem_execbuffer *exec, | 2893 | struct drm_i915_gem_execbuffer *exec, |
2894 | struct drm_clip_rect *cliprects, | ||
2894 | uint64_t exec_offset) | 2895 | uint64_t exec_offset) |
2895 | { | 2896 | { |
2896 | drm_i915_private_t *dev_priv = dev->dev_private; | 2897 | drm_i915_private_t *dev_priv = dev->dev_private; |
2897 | struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *) | ||
2898 | (uintptr_t) exec->cliprects_ptr; | ||
2899 | int nbox = exec->num_cliprects; | 2898 | int nbox = exec->num_cliprects; |
2900 | int i = 0, count; | 2899 | int i = 0, count; |
2901 | uint32_t exec_start, exec_len; | 2900 | uint32_t exec_start, exec_len; |
@@ -2916,7 +2915,7 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev, | |||
2916 | 2915 | ||
2917 | for (i = 0; i < count; i++) { | 2916 | for (i = 0; i < count; i++) { |
2918 | if (i < nbox) { | 2917 | if (i < nbox) { |
2919 | int ret = i915_emit_box(dev, boxes, i, | 2918 | int ret = i915_emit_box(dev, cliprects, i, |
2920 | exec->DR1, exec->DR4); | 2919 | exec->DR1, exec->DR4); |
2921 | if (ret) | 2920 | if (ret) |
2922 | return ret; | 2921 | return ret; |
@@ -2983,6 +2982,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
2983 | struct drm_gem_object **object_list = NULL; | 2982 | struct drm_gem_object **object_list = NULL; |
2984 | struct drm_gem_object *batch_obj; | 2983 | struct drm_gem_object *batch_obj; |
2985 | struct drm_i915_gem_object *obj_priv; | 2984 | struct drm_i915_gem_object *obj_priv; |
2985 | struct drm_clip_rect *cliprects = NULL; | ||
2986 | int ret, i, pinned = 0; | 2986 | int ret, i, pinned = 0; |
2987 | uint64_t exec_offset; | 2987 | uint64_t exec_offset; |
2988 | uint32_t seqno, flush_domains; | 2988 | uint32_t seqno, flush_domains; |
@@ -3019,6 +3019,23 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
3019 | goto pre_mutex_err; | 3019 | goto pre_mutex_err; |
3020 | } | 3020 | } |
3021 | 3021 | ||
3022 | if (args->num_cliprects != 0) { | ||
3023 | cliprects = drm_calloc(args->num_cliprects, sizeof(*cliprects), | ||
3024 | DRM_MEM_DRIVER); | ||
3025 | if (cliprects == NULL) | ||
3026 | goto pre_mutex_err; | ||
3027 | |||
3028 | ret = copy_from_user(cliprects, | ||
3029 | (struct drm_clip_rect __user *) | ||
3030 | (uintptr_t) args->cliprects_ptr, | ||
3031 | sizeof(*cliprects) * args->num_cliprects); | ||
3032 | if (ret != 0) { | ||
3033 | DRM_ERROR("copy %d cliprects failed: %d\n", | ||
3034 | args->num_cliprects, ret); | ||
3035 | goto pre_mutex_err; | ||
3036 | } | ||
3037 | } | ||
3038 | |||
3022 | mutex_lock(&dev->struct_mutex); | 3039 | mutex_lock(&dev->struct_mutex); |
3023 | 3040 | ||
3024 | i915_verify_inactive(dev, __FILE__, __LINE__); | 3041 | i915_verify_inactive(dev, __FILE__, __LINE__); |
@@ -3155,7 +3172,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, | |||
3155 | #endif | 3172 | #endif |
3156 | 3173 | ||
3157 | /* Exec the batchbuffer */ | 3174 | /* Exec the batchbuffer */ |
3158 | ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset); | 3175 | ret = i915_dispatch_gem_execbuffer(dev, args, cliprects, exec_offset); |
3159 | if (ret) { | 3176 | if (ret) { |
3160 | DRM_ERROR("dispatch failed %d\n", ret); | 3177 | DRM_ERROR("dispatch failed %d\n", ret); |
3161 | goto err; | 3178 | goto err; |
@@ -3224,6 +3241,8 @@ pre_mutex_err: | |||
3224 | DRM_MEM_DRIVER); | 3241 | DRM_MEM_DRIVER); |
3225 | drm_free(exec_list, sizeof(*exec_list) * args->buffer_count, | 3242 | drm_free(exec_list, sizeof(*exec_list) * args->buffer_count, |
3226 | DRM_MEM_DRIVER); | 3243 | DRM_MEM_DRIVER); |
3244 | drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects, | ||
3245 | DRM_MEM_DRIVER); | ||
3227 | 3246 | ||
3228 | return ret; | 3247 | return ret; |
3229 | } | 3248 | } |