diff options
| -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); |
