summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i915/i915_gem_userptr.c134
-rw-r--r--include/uapi/drm/i915_drm.h7
2 files changed, 109 insertions, 32 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index 21ea92886a56..450b28ec05e8 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -220,10 +220,6 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
struct interval_tree_node *it;
int ret;
- ret = i915_mutex_lock_interruptible(mmu->dev);
- if (ret)
- return ret;
-
/* Make sure we drop the final active reference (and thereby
* remove the objects from the interval tree) before we do
* the check for overlapping objects.
@@ -253,7 +249,6 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
ret = 0;
}
spin_unlock(&mmu->lock);
- mutex_unlock(&mmu->dev->struct_mutex);
return ret;
}
@@ -283,19 +278,12 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
down_write(&obj->userptr.mm->mmap_sem);
- ret = i915_mutex_lock_interruptible(obj->base.dev);
- if (ret == 0) {
- mmu = i915_mmu_notifier_get(obj->base.dev, obj->userptr.mm);
- if (!IS_ERR(mmu))
- mmu->count++; /* preemptive add to act as a refcount */
- else
- ret = PTR_ERR(mmu);
- mutex_unlock(&obj->base.dev->struct_mutex);
- }
+ mmu = i915_mmu_notifier_get(obj->base.dev, obj->userptr.mm);
+ if (!IS_ERR(mmu))
+ mmu->count++; /* preemptive add to act as a refcount */
+ else
+ ret = PTR_ERR(mmu);
up_write(&obj->userptr.mm->mmap_sem);
- if (ret)
- return ret;
-
mn = kzalloc(sizeof(*mn), GFP_KERNEL);
if (mn == NULL) {
ret = -ENOMEM;
@@ -317,10 +305,8 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
free_mn:
kfree(mn);
destroy_mmu:
- mutex_lock(&obj->base.dev->struct_mutex);
if (--mmu->count == 0)
__i915_mmu_notifier_destroy(mmu);
- mutex_unlock(&obj->base.dev->struct_mutex);
return ret;
}
@@ -591,10 +577,66 @@ i915_gem_userptr_release(struct drm_i915_gem_object *obj)
static int
i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj)
{
+ int ret;
+
if (obj->userptr.mn)
return 0;
- return i915_gem_userptr_init__mmu_notifier(obj, 0);
+ ret = i915_mutex_lock_interruptible(obj->base.dev);
+ if (ret)
+ return ret;
+
+ ret = i915_gem_userptr_init__mmu_notifier(obj, 0);
+
+ mutex_unlock(&obj->base.dev->struct_mutex);
+
+ return ret;
+}
+
+/* Carve out the address space for later use */
+static int i915_gem_userptr_reserve_vma(struct drm_i915_gem_object *obj,
+ struct i915_address_space *vm,
+ uint64_t offset,
+ uint64_t size)
+{
+ struct i915_vma *vma;
+ int ret;
+
+ vma = i915_gem_obj_to_vma(obj, vm);
+ if (vma)
+ return -ENXIO;
+
+ vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
+ if (!vma)
+ return PTR_ERR(vma);
+
+ BUG_ON(!drm_mm_initialized(&vm->mm));
+
+ if (vma->uptr) {
+ DRM_INFO("Already had a userptr\n");
+ return 0;
+ }
+ if (vma->node.allocated) {
+ DRM_INFO("Node was previously allocated\n");
+ return -EBUSY;
+ }
+
+ vma->node.start = offset;
+ vma->node.size = size;
+ vma->node.color = 0;
+ ret = drm_mm_reserve_node(&vm->mm, &vma->node);
+ if (ret) {
+ /* There are two reasons this can fail.
+ * 1. The user is using a mix of relocs and userptr, and a reloc
+ * won.
+ * TODO: handle better.
+ */
+ return ret;
+ }
+
+ vma->uptr = 1;
+
+ return 0;
}
static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
@@ -640,37 +682,62 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
int
i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
struct drm_i915_gem_userptr *args = data;
struct drm_i915_gem_object *obj;
+ struct intel_context *ctx;
+ struct i915_address_space *vm;
int ret;
u32 handle;
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ return ret;
+
+#define goto_err(__err) do { \
+ ret = (__err); \
+ goto out; \
+} while (0)
+
+ ctx = i915_gem_context_get(file_priv, args->ctx_id);
+ if (IS_ERR(ctx))
+ goto_err(PTR_ERR(ctx));
+
+ /* i915_gem_context_reference(ctx); */
+
if (args->flags & ~(I915_USERPTR_READ_ONLY |
+ I915_USERPTR_GPU_MIRROR |
I915_USERPTR_UNSYNCHRONIZED))
- return -EINVAL;
+ goto_err(-EINVAL);
if (offset_in_page(args->user_ptr | args->user_size))
- return -EINVAL;
-
- if (args->user_size > dev_priv->gtt.base.total)
- return -E2BIG;
+ goto_err(-EINVAL);
if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE,
(char __user *)(unsigned long)args->user_ptr, args->user_size))
- return -EFAULT;
+ goto_err(-EFAULT);
if (args->flags & I915_USERPTR_READ_ONLY) {
/* On almost all of the current hw, we cannot tell the GPU that a
* page is readonly, so this is just a placeholder in the uAPI.
*/
- return -ENODEV;
+ goto_err(-ENODEV);
+ }
+
+ vm = ctx->vm;
+ if (args->user_size > vm->total)
+ goto_err(-E2BIG);
+
+ if (args->flags & I915_USERPTR_GPU_MIRROR) {
+ if (!HAS_48B_PPGTT(dev))
+ goto_err(-ENODEV);
}
/* Allocate the new object */
obj = i915_gem_object_alloc(dev);
if (obj == NULL)
- return -ENOMEM;
+ goto_err(-ENOMEM);
+#undef goto_err
drm_gem_private_object_init(dev, &obj->base, args->user_size);
i915_gem_object_init(obj, &i915_gem_userptr_ops);
@@ -690,9 +757,16 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
ret = i915_gem_userptr_init__mmu_notifier(obj, args->flags);
if (ret == 0)
ret = drm_gem_handle_create(file, &obj->base, &handle);
+ if (ret == 0 && args->flags & I915_USERPTR_GPU_MIRROR) {
+ ret = i915_gem_userptr_reserve_vma(obj, vm, args->user_ptr, args->user_size);
+ if (ret)
+ DRM_DEBUG_DRIVER("Failed to reserve GPU mirror %d\n", ret);
+ }
/* drop reference from allocate - handle holds it now */
- drm_gem_object_unreference_unlocked(&obj->base);
+ drm_gem_object_unreference(&obj->base);
+out:
+ mutex_unlock(&dev->struct_mutex);
if (ret)
return ret;
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 97dea09a48ba..7652f8301cda 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -1056,15 +1056,18 @@ struct drm_i915_reset_stats {
struct drm_i915_gem_userptr {
__u64 user_ptr;
__u64 user_size;
+ __u32 ctx_id;
__u32 flags;
-#define I915_USERPTR_READ_ONLY 0x1
-#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
+#define I915_USERPTR_READ_ONLY (1<<0)
+#define I915_USERPTR_GPU_MIRROR (1<<1)
+#define I915_USERPTR_UNSYNCHRONIZED (1<<31)
/**
* Returned handle for the object.
*
* Object handles are nonzero.
*/
__u32 handle;
+ __u32 pad;
};
#endif /* _UAPI_I915_DRM_H_ */