drm/i915: Avoid undefined behaviour of "u32 >> 32"
authorChris Wilson <chris@chris-wilson.co.uk>
Thu, 29 Jun 2017 15:04:25 +0000 (16:04 +0100)
committerJani Nikula <jani.nikula@intel.com>
Mon, 3 Jul 2017 13:12:48 +0000 (16:12 +0300)
When computing a hash for looking up relocation target handles in an
execbuf, we start with a large size for the hashtable and proceed to
halve it until the allocation succeeds. The final attempt is with an
order of 0 (i.e. a single element). This means that we then pass bits=0
to hash_32() which then computes "hash >> (32 - 0)" to lookup the single
element. Right shifting a value by the width of the operand is
undefined, so limit the smallest hash table we use to order 1.

v2: Keep the retry allocation flag for the final pass

Fixes: 4ff4b44cbb70 ("drm/i915: Store a direct lookup from object handle to vma")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170629150425.27508-1-chris@chris-wilson.co.uk
(cherry picked from commit 4d470f7359c4bf22518baa30700ad45649371a22)
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
drivers/gpu/drm/i915/i915_gem_execbuffer.c

index 9337446..054b2e5 100644 (file)
@@ -288,20 +288,26 @@ static int eb_create(struct i915_execbuffer *eb)
                 * direct lookup.
                 */
                do {
+                       unsigned int flags;
+
+                       /* While we can still reduce the allocation size, don't
+                        * raise a warning and allow the allocation to fail.
+                        * On the last pass though, we want to try as hard
+                        * as possible to perform the allocation and warn
+                        * if it fails.
+                        */
+                       flags = GFP_TEMPORARY;
+                       if (size > 1)
+                               flags |= __GFP_NORETRY | __GFP_NOWARN;
+
                        eb->buckets = kzalloc(sizeof(struct hlist_head) << size,
-                                             GFP_TEMPORARY |
-                                             __GFP_NORETRY |
-                                             __GFP_NOWARN);
+                                             flags);
                        if (eb->buckets)
                                break;
                } while (--size);
 
-               if (unlikely(!eb->buckets)) {
-                       eb->buckets = kzalloc(sizeof(struct hlist_head),
-                                             GFP_TEMPORARY);
-                       if (unlikely(!eb->buckets))
-                               return -ENOMEM;
-               }
+               if (unlikely(!size))
+                       return -ENOMEM;
 
                eb->lut_size = size;
        } else {
@@ -452,7 +458,7 @@ eb_add_vma(struct i915_execbuffer *eb,
                        return err;
        }
 
-       if (eb->lut_size >= 0) {
+       if (eb->lut_size > 0) {
                vma->exec_handle = entry->handle;
                hlist_add_head(&vma->exec_node,
                               &eb->buckets[hash_32(entry->handle,
@@ -894,7 +900,7 @@ static void eb_release_vmas(const struct i915_execbuffer *eb)
 static void eb_reset_vmas(const struct i915_execbuffer *eb)
 {
        eb_release_vmas(eb);
-       if (eb->lut_size >= 0)
+       if (eb->lut_size > 0)
                memset(eb->buckets, 0,
                       sizeof(struct hlist_head) << eb->lut_size);
 }
@@ -903,7 +909,7 @@ static void eb_destroy(const struct i915_execbuffer *eb)
 {
        GEM_BUG_ON(eb->reloc_cache.rq);
 
-       if (eb->lut_size >= 0)
+       if (eb->lut_size > 0)
                kfree(eb->buckets);
 }
 
@@ -2180,8 +2186,11 @@ i915_gem_do_execbuffer(struct drm_device *dev,
                }
        }
 
-       if (eb_create(&eb))
-               return -ENOMEM;
+       err = eb_create(&eb);
+       if (err)
+               goto err_out_fence;
+
+       GEM_BUG_ON(!eb.lut_size);
 
        /*
         * Take a local wakeref for preparing to dispatch the execbuf as
@@ -2340,6 +2349,7 @@ err_unlock:
 err_rpm:
        intel_runtime_pm_put(eb.i915);
        eb_destroy(&eb);
+err_out_fence:
        if (out_fence_fd != -1)
                put_unused_fd(out_fence_fd);
 err_in_fence: