Merge tag 'drm-for-v4.15' of git://people.freedesktop.org/~airlied/linux
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / i915_gem_userptr.c
index aa22361bd5a15a47129b42a7bcd90fcf51bbdce9..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
@@ -399,64 +409,47 @@ struct get_pages_work {
        struct task_struct *task;
 };
 
-#if IS_ENABLED(CONFIG_SWIOTLB)
-#define swiotlb_active() swiotlb_nr_tbl()
-#else
-#define swiotlb_active() 0
-#endif
-
-static int
-st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
-{
-       struct scatterlist *sg;
-       int ret, n;
-
-       *st = kmalloc(sizeof(**st), GFP_KERNEL);
-       if (*st == NULL)
-               return -ENOMEM;
-
-       if (swiotlb_active()) {
-               ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
-               if (ret)
-                       goto err;
-
-               for_each_sg((*st)->sgl, sg, num_pages, n)
-                       sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
-       } else {
-               ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
-                                               0, num_pages << PAGE_SHIFT,
-                                               GFP_KERNEL);
-               if (ret)
-                       goto err;
-       }
-
-       return 0;
-
-err:
-       kfree(*st);
-       *st = NULL;
-       return ret;
-}
-
 static struct sg_table *
-__i915_gem_userptr_set_pages(struct drm_i915_gem_object *obj,
-                            struct page **pvec, int num_pages)
+__i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
+                              struct page **pvec, int num_pages)
 {
-       struct sg_table *pages;
+       unsigned int max_segment = i915_sg_segment_size();
+       struct sg_table *st;
+       unsigned int sg_page_sizes;
        int ret;
 
-       ret = st_set_pages(&pages, pvec, num_pages);
-       if (ret)
+       st = kmalloc(sizeof(*st), GFP_KERNEL);
+       if (!st)
+               return ERR_PTR(-ENOMEM);
+
+alloc_table:
+       ret = __sg_alloc_table_from_pages(st, pvec, num_pages,
+                                         0, num_pages << PAGE_SHIFT,
+                                         max_segment,
+                                         GFP_KERNEL);
+       if (ret) {
+               kfree(st);
                return ERR_PTR(ret);
+       }
 
-       ret = i915_gem_gtt_prepare_pages(obj, pages);
+       ret = i915_gem_gtt_prepare_pages(obj, st);
        if (ret) {
-               sg_free_table(pages);
-               kfree(pages);
+               sg_free_table(st);
+
+               if (max_segment > PAGE_SIZE) {
+                       max_segment = PAGE_SIZE;
+                       goto alloc_table;
+               }
+
+               kfree(st);
                return ERR_PTR(ret);
        }
 
-       return pages;
+       sg_page_sizes = i915_sg_page_sizes(st->sgl);
+
+       __i915_gem_object_set_pages(obj, st, sg_page_sizes);
+
+       return st;
 }
 
 static int
@@ -540,9 +533,9 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
                struct sg_table *pages = ERR_PTR(ret);
 
                if (pinned == npages) {
-                       pages = __i915_gem_userptr_set_pages(obj, pvec, npages);
+                       pages = __i915_gem_userptr_alloc_pages(obj, pvec,
+                                                              npages);
                        if (!IS_ERR(pages)) {
-                               __i915_gem_object_set_pages(obj, pages);
                                pinned = 0;
                                pages = NULL;
                        }
@@ -603,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;
@@ -633,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;
@@ -661,7 +653,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
                pages = __i915_gem_userptr_get_pages_schedule(obj);
                active = pages == ERR_PTR(-EAGAIN);
        } else {
-               pages = __i915_gem_userptr_set_pages(obj, pvec, num_pages);
+               pages = __i915_gem_userptr_alloc_pages(obj, pvec, num_pages);
                active = !IS_ERR(pages);
        }
        if (active)
@@ -671,7 +663,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
                release_pages(pvec, pinned);
        kvfree(pvec);
 
-       return pages;
+       return PTR_ERR_OR_ZERO(pages);
 }
 
 static void
@@ -834,7 +826,9 @@ int i915_gem_init_userptr(struct drm_i915_private *dev_priv)
        hash_init(dev_priv->mm_structs);
 
        dev_priv->mm.userptr_wq =
-               alloc_workqueue("i915-userptr-acquire", WQ_HIGHPRI, 0);
+               alloc_workqueue("i915-userptr-acquire",
+                               WQ_HIGHPRI | WQ_MEM_RECLAIM,
+                               0);
        if (!dev_priv->mm.userptr_wq)
                return -ENOMEM;