drm/gem: Warn on illegal use of the dumb buffer interface v2
[cascardo/linux.git] / drivers / gpu / drm / radeon / radeon_gem.c
index bfd7e1b..429213b 100644 (file)
@@ -67,7 +67,7 @@ int radeon_gem_object_create(struct radeon_device *rdev, unsigned long size,
 
 retry:
        r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain,
-                            flags, NULL, &robj);
+                            flags, NULL, NULL, &robj);
        if (r) {
                if (r != -ERESTARTSYS) {
                        if (initial_domain == RADEON_GEM_DOMAIN_VRAM) {
@@ -94,7 +94,7 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
 {
        struct radeon_bo *robj;
        uint32_t domain;
-       int r;
+       long r;
 
        /* FIXME: reeimplement */
        robj = gem_to_radeon_bo(gobj);
@@ -110,9 +110,12 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
        }
        if (domain == RADEON_GEM_DOMAIN_CPU) {
                /* Asking for cpu access wait for object idle */
-               r = radeon_bo_wait(robj, NULL, false);
-               if (r) {
-                       printk(KERN_ERR "Failed to wait for object !\n");
+               r = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ);
+               if (!r)
+                       r = -EBUSY;
+
+               if (r < 0 && r != -EINTR) {
+                       printk(KERN_ERR "Failed to wait for object: %li\n", r);
                        return r;
                }
        }
@@ -272,6 +275,94 @@ 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;
+
+       /* reject unknown flag values */
+       if (args->flags & ~(RADEON_GEM_USERPTR_READONLY |
+           RADEON_GEM_USERPTR_ANONONLY | RADEON_GEM_USERPTR_VALIDATE |
+           RADEON_GEM_USERPTR_REGISTER))
+               return -EINVAL;
+
+       if (args->flags & RADEON_GEM_USERPTR_READONLY) {
+               /* readonly pages not tested on older hardware */
+               if (rdev->family < CHIP_R600)
+                       return -EINVAL;
+
+       } else if (!(args->flags & RADEON_GEM_USERPTR_ANONONLY) ||
+                  !(args->flags & RADEON_GEM_USERPTR_REGISTER)) {
+
+               /* if we want to write to it we must require anonymous
+                  memory and install a MMU notifier */
+               return -EACCES;
+       }
+
+       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_REGISTER) {
+               r = radeon_mn_register(bo, args->addr);
+               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)
 {
@@ -303,9 +394,10 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        return r;
 }
 
-int radeon_mode_dumb_mmap(struct drm_file *filp,
-                         struct drm_device *dev,
-                         uint32_t handle, uint64_t *offset_p)
+static int radeon_mode_mmap(struct drm_file *filp,
+                           struct drm_device *dev,
+                           uint32_t handle, bool dumb,
+                           uint64_t *offset_p)
 {
        struct drm_gem_object *gobj;
        struct radeon_bo *robj;
@@ -314,18 +406,38 @@ int radeon_mode_dumb_mmap(struct drm_file *filp,
        if (gobj == NULL) {
                return -ENOENT;
        }
+
+       /*
+        * We don't allow dumb mmaps on objects created using another
+        * interface.
+        */
+       WARN_ONCE(dumb && !(gobj->dumb || gobj->import_attach),
+               "Illegal dumb map of GPU buffer.\n");
+
        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;
 }
 
+int radeon_mode_dumb_mmap(struct drm_file *filp,
+                         struct drm_device *dev,
+                         uint32_t handle, uint64_t *offset_p)
+{
+       return radeon_mode_mmap(filp, dev, handle, true, offset_p);
+}
+
 int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
                          struct drm_file *filp)
 {
        struct drm_radeon_gem_mmap *args = data;
 
-       return radeon_mode_dumb_mmap(filp, dev, args->handle, &args->addr_ptr);
+       return radeon_mode_mmap(filp, dev, args->handle, false,
+                               &args->addr_ptr);
 }
 
 int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
@@ -357,15 +469,22 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
        struct drm_radeon_gem_wait_idle *args = data;
        struct drm_gem_object *gobj;
        struct radeon_bo *robj;
-       int r;
+       int r = 0;
        uint32_t cur_placement = 0;
+       long ret;
 
        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, &cur_placement, false);
+
+       ret = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ);
+       if (ret == 0)
+               r = -EBUSY;
+       else if (ret < 0)
+               r = ret;
+
        /* Flush HDP cache via MMIO if necessary */
        if (rdev->asic->mmio_hdp_flush &&
            radeon_mem_type_to_domain(cur_placement) == RADEON_GEM_DOMAIN_VRAM)
@@ -532,6 +651,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;
@@ -575,6 +699,7 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
                return -ENOMEM;
 
        r = drm_gem_handle_create(file_priv, gobj, &handle);
+       gobj->dumb = true;
        /* drop reference from allocate - handle holds it now */
        drm_gem_object_unreference_unlocked(gobj);
        if (r) {