drm/radeon: add userptr flag to directly validate the BO to GTT
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / radeon / radeon_gem.c
index 1ec62444e32e0c90b2a014969f7e9af78269a754..450656027aba91db2a6574c5ddc20c718266ee23 100644 (file)
@@ -40,9 +40,9 @@ void radeon_gem_object_free(struct drm_gem_object *gobj)
        }
 }
 
-int radeon_gem_object_create(struct radeon_device *rdev, int size,
+int radeon_gem_object_create(struct radeon_device *rdev, unsigned long size,
                                int alignment, int initial_domain,
-                               u32 flags, bool discardable, bool kernel,
+                               u32 flags, bool kernel,
                                struct drm_gem_object **obj)
 {
        struct radeon_bo *robj;
@@ -55,10 +55,12 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
                alignment = PAGE_SIZE;
        }
 
-       /* maximun bo size is the minimun btw visible vram and gtt size */
-       max_size = min(rdev->mc.visible_vram_size, rdev->mc.gtt_size);
+       /* Maximum bo size is the unpinned gtt size since we use the gtt to
+        * handle vram to system pool migrations.
+        */
+       max_size = rdev->mc.gtt_size - rdev->gart_pin_size;
        if (size > max_size) {
-               DRM_DEBUG("Allocation size %dMb bigger than %ldMb limit\n",
+               DRM_DEBUG("Allocation size %ldMb bigger than %ldMb limit\n",
                          size >> 20, max_size >> 20);
                return -ENOMEM;
        }
@@ -72,7 +74,7 @@ retry:
                                initial_domain |= RADEON_GEM_DOMAIN_GTT;
                                goto retry;
                        }
-                       DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n",
+                       DRM_ERROR("Failed to allocate GEM object (%ld, %d, %u, %d)\n",
                                  size, initial_domain, alignment, r);
                }
                return r;
@@ -251,7 +253,7 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
        args->size = roundup(args->size, PAGE_SIZE);
        r = radeon_gem_object_create(rdev, args->size, args->alignment,
                                     args->initial_domain, args->flags,
-                                    false, false, &gobj);
+                                    false, &gobj);
        if (r) {
                up_read(&rdev->exclusive_lock);
                r = radeon_gem_handle_lockup(rdev, r);
@@ -270,6 +272,82 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
+                            struct drm_file *filp)
+{
+       struct radeon_device *rdev = dev->dev_private;
+       struct drm_radeon_gem_userptr *args = data;
+       struct drm_gem_object *gobj;
+       struct radeon_bo *bo;
+       uint32_t handle;
+       int r;
+
+       if (offset_in_page(args->addr | args->size))
+               return -EINVAL;
+
+       /* we only support read only mappings for now */
+       if (!(args->flags & RADEON_GEM_USERPTR_READONLY))
+               return -EACCES;
+
+       /* reject unknown flag values */
+       if (args->flags & ~(RADEON_GEM_USERPTR_READONLY |
+           RADEON_GEM_USERPTR_ANONONLY | RADEON_GEM_USERPTR_VALIDATE))
+               return -EINVAL;
+
+       /* readonly pages not tested on older hardware */
+       if (rdev->family < CHIP_R600)
+               return -EINVAL;
+
+       down_read(&rdev->exclusive_lock);
+
+       /* create a gem object to contain this object in */
+       r = radeon_gem_object_create(rdev, args->size, 0,
+                                    RADEON_GEM_DOMAIN_CPU, 0,
+                                    false, &gobj);
+       if (r)
+               goto handle_lockup;
+
+       bo = gem_to_radeon_bo(gobj);
+       r = radeon_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
+       if (r)
+               goto release_object;
+
+       if (args->flags & RADEON_GEM_USERPTR_VALIDATE) {
+               down_read(&current->mm->mmap_sem);
+               r = radeon_bo_reserve(bo, true);
+               if (r) {
+                       up_read(&current->mm->mmap_sem);
+                       goto release_object;
+               }
+
+               radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_GTT);
+               r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+               radeon_bo_unreserve(bo);
+               up_read(&current->mm->mmap_sem);
+               if (r)
+                       goto release_object;
+       }
+
+       r = drm_gem_handle_create(filp, gobj, &handle);
+       /* drop reference from allocate - handle holds it now */
+       drm_gem_object_unreference_unlocked(gobj);
+       if (r)
+               goto handle_lockup;
+
+       args->handle = handle;
+       up_read(&rdev->exclusive_lock);
+       return 0;
+
+release_object:
+       drm_gem_object_unreference_unlocked(gobj);
+
+handle_lockup:
+       up_read(&rdev->exclusive_lock);
+       r = radeon_gem_handle_lockup(rdev, r);
+
+       return r;
+}
+
 int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
                                struct drm_file *filp)
 {
@@ -313,6 +391,10 @@ int radeon_mode_dumb_mmap(struct drm_file *filp,
                return -ENOENT;
        }
        robj = gem_to_radeon_bo(gobj);
+       if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) {
+               drm_gem_object_unreference_unlocked(gobj);
+               return -EPERM;
+       }
        *offset_p = radeon_bo_mmap_offset(robj);
        drm_gem_object_unreference_unlocked(gobj);
        return 0;
@@ -356,16 +438,18 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *gobj;
        struct radeon_bo *robj;
        int r;
+       uint32_t cur_placement = 0;
 
        gobj = drm_gem_object_lookup(dev, filp, args->handle);
        if (gobj == NULL) {
                return -ENOENT;
        }
        robj = gem_to_radeon_bo(gobj);
-       r = radeon_bo_wait(robj, NULL, false);
-       /* callback hw specific functions if any */
-       if (rdev->asic->ioctl_wait_idle)
-               robj->rdev->asic->ioctl_wait_idle(rdev, robj);
+       r = radeon_bo_wait(robj, &cur_placement, false);
+       /* Flush HDP cache via MMIO if necessary */
+       if (rdev->asic->mmio_hdp_flush &&
+           radeon_mem_type_to_domain(cur_placement) == RADEON_GEM_DOMAIN_VRAM)
+               robj->rdev->asic->mmio_hdp_flush(rdev);
        drm_gem_object_unreference_unlocked(gobj);
        r = radeon_gem_handle_lockup(rdev, r);
        return r;
@@ -492,9 +576,9 @@ int radeon_gem_va_ioctl(struct drm_device *dev, void *data,
 
        switch (args->operation) {
        case RADEON_VA_MAP:
-               if (bo_va->soffset) {
+               if (bo_va->it.start) {
                        args->operation = RADEON_VA_RESULT_VA_EXIST;
-                       args->offset = bo_va->soffset;
+                       args->offset = bo_va->it.start * RADEON_GPU_PAGE_SIZE;
                        goto out;
                }
                r = radeon_vm_bo_set_addr(rdev, bo_va, args->offset, args->flags);
@@ -528,6 +612,11 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
                return -ENOENT;
        }
        robj = gem_to_radeon_bo(gobj);
+
+       r = -EPERM;
+       if (radeon_ttm_tt_has_userptr(robj->tbo.ttm))
+               goto out;
+
        r = radeon_bo_reserve(robj, false);
        if (unlikely(r))
                goto out;
@@ -566,8 +655,7 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
 
        r = radeon_gem_object_create(rdev, args->size, 0,
                                     RADEON_GEM_DOMAIN_VRAM, 0,
-                                    false, ttm_bo_type_device,
-                                    &gobj);
+                                    false, &gobj);
        if (r)
                return -ENOMEM;