diff options
author | Eric Anholt <eric@anholt.net> | 2009-03-12 14:23:52 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-03-27 17:47:55 -0400 |
commit | 40a5f0decdf050785ebd62b36ad48c869ee4b384 (patch) | |
tree | 3e5937b1c1455c1d33ad73f13c32b8b285c84862 /drivers/gpu | |
parent | 201361a54ed187d8595a283e3a4ddb213bc8323b (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.c | 187 |
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, | |||
2713 | static int | 2713 | static int |
2714 | i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, | 2714 | i915_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 | ||
2960 | static int | ||
2961 | i915_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 | |||
3001 | static int | ||
3002 | i915_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 | |||
2974 | int | 3029 | int |
2975 | i915_gem_execbuffer(struct drm_device *dev, void *data, | 3030 | i915_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 | |||
3239 | pre_mutex_err: | 3318 | pre_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); |