Merge tag 'drm-for-v4.15' of git://people.freedesktop.org/~airlied/linux
[linux-2.6-block.git] / drivers / gpu / drm / i915 / i915_gem_userptr.c
index 2d4996de733120b155a613f5f60137b3afcecbcb..135fc750a8375f172e130c6b45b85747535693d9 100644 (file)
@@ -82,11 +82,11 @@ static void cancel_userptr(struct work_struct *work)
        /* We are inside a kthread context and can't be interrupted */
        if (i915_gem_object_unbind(obj) == 0)
                __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
-       WARN_ONCE(obj->mm.pages,
-                 "Failed to release pages: bind_count=%d, pages_pin_count=%d, pin_display=%d\n",
+       WARN_ONCE(i915_gem_object_has_pages(obj),
+                 "Failed to release pages: bind_count=%d, pages_pin_count=%d, pin_global=%d\n",
                  obj->bind_count,
                  atomic_read(&obj->mm.pages_pin_count),
-                 obj->pin_display);
+                 obj->pin_global);
 
        mutex_unlock(&obj->base.dev->struct_mutex);
 
@@ -164,7 +164,6 @@ static struct i915_mmu_notifier *
 i915_mmu_notifier_create(struct mm_struct *mm)
 {
        struct i915_mmu_notifier *mn;
-       int ret;
 
        mn = kmalloc(sizeof(*mn), GFP_KERNEL);
        if (mn == NULL)
@@ -179,14 +178,6 @@ i915_mmu_notifier_create(struct mm_struct *mm)
                return ERR_PTR(-ENOMEM);
        }
 
-        /* Protected by mmap_sem (write-lock) */
-       ret = __mmu_notifier_register(&mn->mn, mm);
-       if (ret) {
-               destroy_workqueue(mn->wq);
-               kfree(mn);
-               return ERR_PTR(ret);
-       }
-
        return mn;
 }
 
@@ -210,23 +201,42 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
 static struct i915_mmu_notifier *
 i915_mmu_notifier_find(struct i915_mm_struct *mm)
 {
-       struct i915_mmu_notifier *mn = mm->mn;
+       struct i915_mmu_notifier *mn;
+       int err = 0;
 
        mn = mm->mn;
        if (mn)
                return mn;
 
+       mn = i915_mmu_notifier_create(mm->mm);
+       if (IS_ERR(mn))
+               err = PTR_ERR(mn);
+
        down_write(&mm->mm->mmap_sem);
        mutex_lock(&mm->i915->mm_lock);
-       if ((mn = mm->mn) == NULL) {
-               mn = i915_mmu_notifier_create(mm->mm);
-               if (!IS_ERR(mn))
-                       mm->mn = mn;
+       if (mm->mn == NULL && !err) {
+               /* Protected by mmap_sem (write-lock) */
+               err = __mmu_notifier_register(&mn->mn, mm->mm);
+               if (!err) {
+                       /* Protected by mm_lock */
+                       mm->mn = fetch_and_zero(&mn);
+               }
+       } else if (mm->mn) {
+               /*
+                * Someone else raced and successfully installed the mmu
+                * notifier, we can cancel our own errors.
+                */
+               err = 0;
        }
        mutex_unlock(&mm->i915->mm_lock);
        up_write(&mm->mm->mmap_sem);
 
-       return mn;
+       if (mn && !IS_ERR(mn)) {
+               destroy_workqueue(mn->wq);
+               kfree(mn);
+       }
+
+       return err ? ERR_PTR(err) : mm->mn;
 }
 
 static int
@@ -405,6 +415,7 @@ __i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
 {
        unsigned int max_segment = i915_sg_segment_size();
        struct sg_table *st;
+       unsigned int sg_page_sizes;
        int ret;
 
        st = kmalloc(sizeof(*st), GFP_KERNEL);
@@ -434,6 +445,10 @@ alloc_table:
                return ERR_PTR(ret);
        }
 
+       sg_page_sizes = i915_sg_page_sizes(st->sgl);
+
+       __i915_gem_object_set_pages(obj, st, sg_page_sizes);
+
        return st;
 }
 
@@ -521,7 +536,6 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
                        pages = __i915_gem_userptr_alloc_pages(obj, pvec,
                                                               npages);
                        if (!IS_ERR(pages)) {
-                               __i915_gem_object_set_pages(obj, pages);
                                pinned = 0;
                                pages = NULL;
                        }
@@ -533,7 +547,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
        }
        mutex_unlock(&obj->mm.lock);
 
-       release_pages(pvec, pinned, 0);
+       release_pages(pvec, pinned);
        kvfree(pvec);
 
        i915_gem_object_put(obj);
@@ -582,8 +596,7 @@ __i915_gem_userptr_get_pages_schedule(struct drm_i915_gem_object *obj)
        return ERR_PTR(-EAGAIN);
 }
 
-static struct sg_table *
-i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
+static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
 {
        const int num_pages = obj->base.size >> PAGE_SHIFT;
        struct mm_struct *mm = obj->userptr.mm->mm;
@@ -612,9 +625,9 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
        if (obj->userptr.work) {
                /* active flag should still be held for the pending work */
                if (IS_ERR(obj->userptr.work))
-                       return ERR_CAST(obj->userptr.work);
+                       return PTR_ERR(obj->userptr.work);
                else
-                       return ERR_PTR(-EAGAIN);
+                       return -EAGAIN;
        }
 
        pvec = NULL;
@@ -647,10 +660,10 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
                __i915_gem_userptr_set_active(obj, true);
 
        if (IS_ERR(pages))
-               release_pages(pvec, pinned, 0);
+               release_pages(pvec, pinned);
        kvfree(pvec);
 
-       return pages;
+       return PTR_ERR_OR_ZERO(pages);
 }
 
 static void