aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2009-03-12 14:23:52 -0400
committerEric Anholt <eric@anholt.net>2009-03-27 17:47:55 -0400
commit40a5f0decdf050785ebd62b36ad48c869ee4b384 (patch)
tree3e5937b1c1455c1d33ad73f13c32b8b285c84862 /drivers/gpu
parent201361a54ed187d8595a283e3a4ddb213bc8323b (diff)
drm/i915: Fix lock order reversal in GEM relocation entry copying.
Signed-off-by: Eric Anholt <eric@anholt.net> Reviewed-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c187
1 files changed, 133 insertions, 54 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 2bda15197cce..f135c903305f 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2713,12 +2713,11 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
2713static int 2713static int
2714i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, 2714i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
2715 struct drm_file *file_priv, 2715 struct drm_file *file_priv,
2716 struct drm_i915_gem_exec_object *entry) 2716 struct drm_i915_gem_exec_object *entry,
2717 struct drm_i915_gem_relocation_entry *relocs)
2717{ 2718{
2718 struct drm_device *dev = obj->dev; 2719 struct drm_device *dev = obj->dev;
2719 drm_i915_private_t *dev_priv = dev->dev_private; 2720 drm_i915_private_t *dev_priv = dev->dev_private;
2720 struct drm_i915_gem_relocation_entry reloc;
2721 struct drm_i915_gem_relocation_entry __user *relocs;
2722 struct drm_i915_gem_object *obj_priv = obj->driver_private; 2721 struct drm_i915_gem_object *obj_priv = obj->driver_private;
2723 int i, ret; 2722 int i, ret;
2724 void __iomem *reloc_page; 2723 void __iomem *reloc_page;
@@ -2730,25 +2729,18 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
2730 2729
2731 entry->offset = obj_priv->gtt_offset; 2730 entry->offset = obj_priv->gtt_offset;
2732 2731
2733 relocs = (struct drm_i915_gem_relocation_entry __user *)
2734 (uintptr_t) entry->relocs_ptr;
2735 /* Apply the relocations, using the GTT aperture to avoid cache 2732 /* Apply the relocations, using the GTT aperture to avoid cache
2736 * flushing requirements. 2733 * flushing requirements.
2737 */ 2734 */
2738 for (i = 0; i < entry->relocation_count; i++) { 2735 for (i = 0; i < entry->relocation_count; i++) {
2736 struct drm_i915_gem_relocation_entry *reloc= &relocs[i];
2739 struct drm_gem_object *target_obj; 2737 struct drm_gem_object *target_obj;
2740 struct drm_i915_gem_object *target_obj_priv; 2738 struct drm_i915_gem_object *target_obj_priv;
2741 uint32_t reloc_val, reloc_offset; 2739 uint32_t reloc_val, reloc_offset;
2742 uint32_t __iomem *reloc_entry; 2740 uint32_t __iomem *reloc_entry;
2743 2741
2744 ret = copy_from_user(&reloc, relocs + i, sizeof(reloc));
2745 if (ret != 0) {
2746 i915_gem_object_unpin(obj);
2747 return ret;
2748 }
2749
2750 target_obj = drm_gem_object_lookup(obj->dev, file_priv, 2742 target_obj = drm_gem_object_lookup(obj->dev, file_priv,
2751 reloc.target_handle); 2743 reloc->target_handle);
2752 if (target_obj == NULL) { 2744 if (target_obj == NULL) {
2753 i915_gem_object_unpin(obj); 2745 i915_gem_object_unpin(obj);
2754 return -EBADF; 2746 return -EBADF;
@@ -2760,53 +2752,53 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
2760 */ 2752 */
2761 if (target_obj_priv->gtt_space == NULL) { 2753 if (target_obj_priv->gtt_space == NULL) {
2762 DRM_ERROR("No GTT space found for object %d\n", 2754 DRM_ERROR("No GTT space found for object %d\n",
2763 reloc.target_handle); 2755 reloc->target_handle);
2764 drm_gem_object_unreference(target_obj); 2756 drm_gem_object_unreference(target_obj);
2765 i915_gem_object_unpin(obj); 2757 i915_gem_object_unpin(obj);
2766 return -EINVAL; 2758 return -EINVAL;
2767 } 2759 }
2768 2760
2769 if (reloc.offset > obj->size - 4) { 2761 if (reloc->offset > obj->size - 4) {
2770 DRM_ERROR("Relocation beyond object bounds: " 2762 DRM_ERROR("Relocation beyond object bounds: "
2771 "obj %p target %d offset %d size %d.\n", 2763 "obj %p target %d offset %d size %d.\n",
2772 obj, reloc.target_handle, 2764 obj, reloc->target_handle,
2773 (int) reloc.offset, (int) obj->size); 2765 (int) reloc->offset, (int) obj->size);
2774 drm_gem_object_unreference(target_obj); 2766 drm_gem_object_unreference(target_obj);
2775 i915_gem_object_unpin(obj); 2767 i915_gem_object_unpin(obj);
2776 return -EINVAL; 2768 return -EINVAL;
2777 } 2769 }
2778 if (reloc.offset & 3) { 2770 if (reloc->offset & 3) {
2779 DRM_ERROR("Relocation not 4-byte aligned: " 2771 DRM_ERROR("Relocation not 4-byte aligned: "
2780 "obj %p target %d offset %d.\n", 2772 "obj %p target %d offset %d.\n",
2781 obj, reloc.target_handle, 2773 obj, reloc->target_handle,
2782 (int) reloc.offset); 2774 (int) reloc->offset);
2783 drm_gem_object_unreference(target_obj); 2775 drm_gem_object_unreference(target_obj);
2784 i915_gem_object_unpin(obj); 2776 i915_gem_object_unpin(obj);
2785 return -EINVAL; 2777 return -EINVAL;
2786 } 2778 }
2787 2779
2788 if (reloc.write_domain & I915_GEM_DOMAIN_CPU || 2780 if (reloc->write_domain & I915_GEM_DOMAIN_CPU ||
2789 reloc.read_domains & I915_GEM_DOMAIN_CPU) { 2781 reloc->read_domains & I915_GEM_DOMAIN_CPU) {
2790 DRM_ERROR("reloc with read/write CPU domains: " 2782 DRM_ERROR("reloc with read/write CPU domains: "
2791 "obj %p target %d offset %d " 2783 "obj %p target %d offset %d "
2792 "read %08x write %08x", 2784 "read %08x write %08x",
2793 obj, reloc.target_handle, 2785 obj, reloc->target_handle,
2794 (int) reloc.offset, 2786 (int) reloc->offset,
2795 reloc.read_domains, 2787 reloc->read_domains,
2796 reloc.write_domain); 2788 reloc->write_domain);
2797 drm_gem_object_unreference(target_obj); 2789 drm_gem_object_unreference(target_obj);
2798 i915_gem_object_unpin(obj); 2790 i915_gem_object_unpin(obj);
2799 return -EINVAL; 2791 return -EINVAL;
2800 } 2792 }
2801 2793
2802 if (reloc.write_domain && target_obj->pending_write_domain && 2794 if (reloc->write_domain && target_obj->pending_write_domain &&
2803 reloc.write_domain != target_obj->pending_write_domain) { 2795 reloc->write_domain != target_obj->pending_write_domain) {
2804 DRM_ERROR("Write domain conflict: " 2796 DRM_ERROR("Write domain conflict: "
2805 "obj %p target %d offset %d " 2797 "obj %p target %d offset %d "
2806 "new %08x old %08x\n", 2798 "new %08x old %08x\n",
2807 obj, reloc.target_handle, 2799 obj, reloc->target_handle,
2808 (int) reloc.offset, 2800 (int) reloc->offset,
2809 reloc.write_domain, 2801 reloc->write_domain,
2810 target_obj->pending_write_domain); 2802 target_obj->pending_write_domain);
2811 drm_gem_object_unreference(target_obj); 2803 drm_gem_object_unreference(target_obj);
2812 i915_gem_object_unpin(obj); 2804 i915_gem_object_unpin(obj);
@@ -2819,22 +2811,22 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
2819 "presumed %08x delta %08x\n", 2811 "presumed %08x delta %08x\n",
2820 __func__, 2812 __func__,
2821 obj, 2813 obj,
2822 (int) reloc.offset, 2814 (int) reloc->offset,
2823 (int) reloc.target_handle, 2815 (int) reloc->target_handle,
2824 (int) reloc.read_domains, 2816 (int) reloc->read_domains,
2825 (int) reloc.write_domain, 2817 (int) reloc->write_domain,
2826 (int) target_obj_priv->gtt_offset, 2818 (int) target_obj_priv->gtt_offset,
2827 (int) reloc.presumed_offset, 2819 (int) reloc->presumed_offset,
2828 reloc.delta); 2820 reloc->delta);
2829#endif 2821#endif
2830 2822
2831 target_obj->pending_read_domains |= reloc.read_domains; 2823 target_obj->pending_read_domains |= reloc->read_domains;
2832 target_obj->pending_write_domain |= reloc.write_domain; 2824 target_obj->pending_write_domain |= reloc->write_domain;
2833 2825
2834 /* If the relocation already has the right value in it, no 2826 /* If the relocation already has the right value in it, no
2835 * more work needs to be done. 2827 * more work needs to be done.
2836 */ 2828 */
2837 if (target_obj_priv->gtt_offset == reloc.presumed_offset) { 2829 if (target_obj_priv->gtt_offset == reloc->presumed_offset) {
2838 drm_gem_object_unreference(target_obj); 2830 drm_gem_object_unreference(target_obj);
2839 continue; 2831 continue;
2840 } 2832 }
@@ -2849,32 +2841,26 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
2849 /* Map the page containing the relocation we're going to 2841 /* Map the page containing the relocation we're going to
2850 * perform. 2842 * perform.
2851 */ 2843 */
2852 reloc_offset = obj_priv->gtt_offset + reloc.offset; 2844 reloc_offset = obj_priv->gtt_offset + reloc->offset;
2853 reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, 2845 reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
2854 (reloc_offset & 2846 (reloc_offset &
2855 ~(PAGE_SIZE - 1))); 2847 ~(PAGE_SIZE - 1)));
2856 reloc_entry = (uint32_t __iomem *)(reloc_page + 2848 reloc_entry = (uint32_t __iomem *)(reloc_page +
2857 (reloc_offset & (PAGE_SIZE - 1))); 2849 (reloc_offset & (PAGE_SIZE - 1)));
2858 reloc_val = target_obj_priv->gtt_offset + reloc.delta; 2850 reloc_val = target_obj_priv->gtt_offset + reloc->delta;
2859 2851
2860#if WATCH_BUF 2852#if WATCH_BUF
2861 DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n", 2853 DRM_INFO("Applied relocation: %p@0x%08x %08x -> %08x\n",
2862 obj, (unsigned int) reloc.offset, 2854 obj, (unsigned int) reloc->offset,
2863 readl(reloc_entry), reloc_val); 2855 readl(reloc_entry), reloc_val);
2864#endif 2856#endif
2865 writel(reloc_val, reloc_entry); 2857 writel(reloc_val, reloc_entry);
2866 io_mapping_unmap_atomic(reloc_page); 2858 io_mapping_unmap_atomic(reloc_page);
2867 2859
2868 /* Write the updated presumed offset for this entry back out 2860 /* The updated presumed offset for this entry will be
2869 * to the user. 2861 * copied back out to the user.
2870 */ 2862 */
2871 reloc.presumed_offset = target_obj_priv->gtt_offset; 2863 reloc->presumed_offset = target_obj_priv->gtt_offset;
2872 ret = copy_to_user(relocs + i, &reloc, sizeof(reloc));
2873 if (ret != 0) {
2874 drm_gem_object_unreference(target_obj);
2875 i915_gem_object_unpin(obj);
2876 return ret;
2877 }
2878 2864
2879 drm_gem_object_unreference(target_obj); 2865 drm_gem_object_unreference(target_obj);
2880 } 2866 }
@@ -2971,6 +2957,75 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
2971 return ret; 2957 return ret;
2972} 2958}
2973 2959
2960static int
2961i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
2962 uint32_t buffer_count,
2963 struct drm_i915_gem_relocation_entry **relocs)
2964{
2965 uint32_t reloc_count = 0, reloc_index = 0, i;
2966 int ret;
2967
2968 *relocs = NULL;
2969 for (i = 0; i < buffer_count; i++) {
2970 if (reloc_count + exec_list[i].relocation_count < reloc_count)
2971 return -EINVAL;
2972 reloc_count += exec_list[i].relocation_count;
2973 }
2974
2975 *relocs = drm_calloc(reloc_count, sizeof(**relocs), DRM_MEM_DRIVER);
2976 if (*relocs == NULL)
2977 return -ENOMEM;
2978
2979 for (i = 0; i < buffer_count; i++) {
2980 struct drm_i915_gem_relocation_entry __user *user_relocs;
2981
2982 user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
2983
2984 ret = copy_from_user(&(*relocs)[reloc_index],
2985 user_relocs,
2986 exec_list[i].relocation_count *
2987 sizeof(**relocs));
2988 if (ret != 0) {
2989 drm_free(*relocs, reloc_count * sizeof(**relocs),
2990 DRM_MEM_DRIVER);
2991 *relocs = NULL;
2992 return ret;
2993 }
2994
2995 reloc_index += exec_list[i].relocation_count;
2996 }
2997
2998 return ret;
2999}
3000
3001static int
3002i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
3003 uint32_t buffer_count,
3004 struct drm_i915_gem_relocation_entry *relocs)
3005{
3006 uint32_t reloc_count = 0, i;
3007 int ret;
3008
3009 for (i = 0; i < buffer_count; i++) {
3010 struct drm_i915_gem_relocation_entry __user *user_relocs;
3011
3012 user_relocs = (void __user *)(uintptr_t)exec_list[i].relocs_ptr;
3013
3014 if (ret == 0) {
3015 ret = copy_to_user(user_relocs,
3016 &relocs[reloc_count],
3017 exec_list[i].relocation_count *
3018 sizeof(*relocs));
3019 }
3020
3021 reloc_count += exec_list[i].relocation_count;
3022 }
3023
3024 drm_free(relocs, reloc_count * sizeof(*relocs), DRM_MEM_DRIVER);
3025
3026 return ret;
3027}
3028
2974int 3029int
2975i915_gem_execbuffer(struct drm_device *dev, void *data, 3030i915_gem_execbuffer(struct drm_device *dev, void *data,
2976 struct drm_file *file_priv) 3031 struct drm_file *file_priv)
@@ -2983,9 +3038,10 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
2983 struct drm_gem_object *batch_obj; 3038 struct drm_gem_object *batch_obj;
2984 struct drm_i915_gem_object *obj_priv; 3039 struct drm_i915_gem_object *obj_priv;
2985 struct drm_clip_rect *cliprects = NULL; 3040 struct drm_clip_rect *cliprects = NULL;
2986 int ret, i, pinned = 0; 3041 struct drm_i915_gem_relocation_entry *relocs;
3042 int ret, ret2, i, pinned = 0;
2987 uint64_t exec_offset; 3043 uint64_t exec_offset;
2988 uint32_t seqno, flush_domains; 3044 uint32_t seqno, flush_domains, reloc_index;
2989 int pin_tries; 3045 int pin_tries;
2990 3046
2991#if WATCH_EXEC 3047#if WATCH_EXEC
@@ -3036,6 +3092,11 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
3036 } 3092 }
3037 } 3093 }
3038 3094
3095 ret = i915_gem_get_relocs_from_user(exec_list, args->buffer_count,
3096 &relocs);
3097 if (ret != 0)
3098 goto pre_mutex_err;
3099
3039 mutex_lock(&dev->struct_mutex); 3100 mutex_lock(&dev->struct_mutex);
3040 3101
3041 i915_verify_inactive(dev, __FILE__, __LINE__); 3102 i915_verify_inactive(dev, __FILE__, __LINE__);
@@ -3078,15 +3139,19 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
3078 /* Pin and relocate */ 3139 /* Pin and relocate */
3079 for (pin_tries = 0; ; pin_tries++) { 3140 for (pin_tries = 0; ; pin_tries++) {
3080 ret = 0; 3141 ret = 0;
3142 reloc_index = 0;
3143
3081 for (i = 0; i < args->buffer_count; i++) { 3144 for (i = 0; i < args->buffer_count; i++) {
3082 object_list[i]->pending_read_domains = 0; 3145 object_list[i]->pending_read_domains = 0;
3083 object_list[i]->pending_write_domain = 0; 3146 object_list[i]->pending_write_domain = 0;
3084 ret = i915_gem_object_pin_and_relocate(object_list[i], 3147 ret = i915_gem_object_pin_and_relocate(object_list[i],
3085 file_priv, 3148 file_priv,
3086 &exec_list[i]); 3149 &exec_list[i],
3150 &relocs[reloc_index]);
3087 if (ret) 3151 if (ret)
3088 break; 3152 break;
3089 pinned = i + 1; 3153 pinned = i + 1;
3154 reloc_index += exec_list[i].relocation_count;
3090 } 3155 }
3091 /* success */ 3156 /* success */
3092 if (ret == 0) 3157 if (ret == 0)
@@ -3236,6 +3301,20 @@ err:
3236 args->buffer_count, ret); 3301 args->buffer_count, ret);
3237 } 3302 }
3238 3303
3304 /* Copy the updated relocations out regardless of current error
3305 * state. Failure to update the relocs would mean that the next
3306 * time userland calls execbuf, it would do so with presumed offset
3307 * state that didn't match the actual object state.
3308 */
3309 ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
3310 relocs);
3311 if (ret2 != 0) {
3312 DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
3313
3314 if (ret == 0)
3315 ret = ret2;
3316 }
3317
3239pre_mutex_err: 3318pre_mutex_err:
3240 drm_free(object_list, sizeof(*object_list) * args->buffer_count, 3319 drm_free(object_list, sizeof(*object_list) * args->buffer_count,
3241 DRM_MEM_DRIVER); 3320 DRM_MEM_DRIVER);