drm/i915: Move context management under GEM

Keep track of the GEM contexts underneath i915->gem.contexts and assign
them their own lock for the purposes of list management.

v2: Focus on lock tracking; ctx->vm is protected by ctx->mutex
v3: Correct split with removal of logical HW ID

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-15-chris@chris-wilson.co.uk
This commit is contained in:
Chris Wilson
2019-10-04 14:40:09 +01:00
parent 2935ed5339
commit a4e7ccdac3
28 changed files with 394 additions and 354 deletions

View File

@@ -218,9 +218,12 @@ static struct i915_gem_engines *default_engines(struct i915_gem_context *ctx)
static void i915_gem_context_free(struct i915_gem_context *ctx)
{
lockdep_assert_held(&ctx->i915->drm.struct_mutex);
GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
spin_lock(&ctx->i915->gem.contexts.lock);
list_del(&ctx->link);
spin_unlock(&ctx->i915->gem.contexts.lock);
free_engines(rcu_access_pointer(ctx->engines));
mutex_destroy(&ctx->engines_mutex);
@@ -230,67 +233,54 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
kfree(ctx->name);
put_pid(ctx->pid);
list_del(&ctx->link);
mutex_destroy(&ctx->mutex);
kfree_rcu(ctx, rcu);
}
static void contexts_free(struct drm_i915_private *i915)
static void contexts_free_all(struct llist_node *list)
{
struct llist_node *freed = llist_del_all(&i915->contexts.free_list);
struct i915_gem_context *ctx, *cn;
lockdep_assert_held(&i915->drm.struct_mutex);
llist_for_each_entry_safe(ctx, cn, freed, free_link)
llist_for_each_entry_safe(ctx, cn, list, free_link)
i915_gem_context_free(ctx);
}
static void contexts_free_first(struct drm_i915_private *i915)
static void contexts_flush_free(struct i915_gem_contexts *gc)
{
struct i915_gem_context *ctx;
struct llist_node *freed;
lockdep_assert_held(&i915->drm.struct_mutex);
freed = llist_del_first(&i915->contexts.free_list);
if (!freed)
return;
ctx = container_of(freed, typeof(*ctx), free_link);
i915_gem_context_free(ctx);
contexts_free_all(llist_del_all(&gc->free_list));
}
static void contexts_free_worker(struct work_struct *work)
{
struct drm_i915_private *i915 =
container_of(work, typeof(*i915), contexts.free_work);
struct i915_gem_contexts *gc =
container_of(work, typeof(*gc), free_work);
mutex_lock(&i915->drm.struct_mutex);
contexts_free(i915);
mutex_unlock(&i915->drm.struct_mutex);
contexts_flush_free(gc);
}
void i915_gem_context_release(struct kref *ref)
{
struct i915_gem_context *ctx = container_of(ref, typeof(*ctx), ref);
struct drm_i915_private *i915 = ctx->i915;
struct i915_gem_contexts *gc = &ctx->i915->gem.contexts;
trace_i915_context_free(ctx);
if (llist_add(&ctx->free_link, &i915->contexts.free_list))
queue_work(i915->wq, &i915->contexts.free_work);
if (llist_add(&ctx->free_link, &gc->free_list))
schedule_work(&gc->free_work);
}
static void context_close(struct i915_gem_context *ctx)
{
struct i915_address_space *vm;
i915_gem_context_set_closed(ctx);
if (ctx->vm)
i915_vm_close(ctx->vm);
mutex_lock(&ctx->mutex);
vm = i915_gem_context_vm(ctx);
if (vm)
i915_vm_close(vm);
ctx->file_priv = ERR_PTR(-EBADF);
/*
@@ -317,7 +307,6 @@ __create_context(struct drm_i915_private *i915)
return ERR_PTR(-ENOMEM);
kref_init(&ctx->ref);
list_add_tail(&ctx->link, &i915->contexts.list);
ctx->i915 = i915;
ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
mutex_init(&ctx->mutex);
@@ -343,6 +332,10 @@ __create_context(struct drm_i915_private *i915)
for (i = 0; i < ARRAY_SIZE(ctx->hang_timestamp); i++)
ctx->hang_timestamp[i] = jiffies - CONTEXT_FAST_HANG_JIFFIES;
spin_lock(&i915->gem.contexts.lock);
list_add_tail(&ctx->link, &i915->gem.contexts.list);
spin_unlock(&i915->gem.contexts.lock);
return ctx;
err_free:
@@ -372,11 +365,11 @@ static void __apply_ppgtt(struct intel_context *ce, void *vm)
static struct i915_address_space *
__set_ppgtt(struct i915_gem_context *ctx, struct i915_address_space *vm)
{
struct i915_address_space *old = ctx->vm;
struct i915_address_space *old = i915_gem_context_vm(ctx);
GEM_BUG_ON(old && i915_vm_is_4lvl(vm) != i915_vm_is_4lvl(old));
ctx->vm = i915_vm_open(vm);
rcu_assign_pointer(ctx->vm, i915_vm_open(vm));
context_apply_all(ctx, __apply_ppgtt, vm);
return old;
@@ -385,7 +378,7 @@ __set_ppgtt(struct i915_gem_context *ctx, struct i915_address_space *vm)
static void __assign_ppgtt(struct i915_gem_context *ctx,
struct i915_address_space *vm)
{
if (vm == ctx->vm)
if (vm == rcu_access_pointer(ctx->vm))
return;
vm = __set_ppgtt(ctx, vm);
@@ -417,27 +410,25 @@ static void __assign_timeline(struct i915_gem_context *ctx,
}
static struct i915_gem_context *
i915_gem_create_context(struct drm_i915_private *dev_priv, unsigned int flags)
i915_gem_create_context(struct drm_i915_private *i915, unsigned int flags)
{
struct i915_gem_context *ctx;
lockdep_assert_held(&dev_priv->drm.struct_mutex);
if (flags & I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE &&
!HAS_EXECLISTS(dev_priv))
!HAS_EXECLISTS(i915))
return ERR_PTR(-EINVAL);
/* Reap the most stale context */
contexts_free_first(dev_priv);
/* Reap the stale contexts */
contexts_flush_free(&i915->gem.contexts);
ctx = __create_context(dev_priv);
ctx = __create_context(i915);
if (IS_ERR(ctx))
return ctx;
if (HAS_FULL_PPGTT(dev_priv)) {
if (HAS_FULL_PPGTT(i915)) {
struct i915_ppgtt *ppgtt;
ppgtt = i915_ppgtt_create(dev_priv);
ppgtt = i915_ppgtt_create(i915);
if (IS_ERR(ppgtt)) {
DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n",
PTR_ERR(ppgtt));
@@ -445,14 +436,17 @@ i915_gem_create_context(struct drm_i915_private *dev_priv, unsigned int flags)
return ERR_CAST(ppgtt);
}
mutex_lock(&ctx->mutex);
__assign_ppgtt(ctx, &ppgtt->vm);
mutex_unlock(&ctx->mutex);
i915_vm_put(&ppgtt->vm);
}
if (flags & I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE) {
struct intel_timeline *timeline;
timeline = intel_timeline_create(&dev_priv->gt, NULL);
timeline = intel_timeline_create(&i915->gt, NULL);
if (IS_ERR(timeline)) {
context_close(ctx);
return ERR_CAST(timeline);
@@ -497,42 +491,40 @@ i915_gem_context_create_kernel(struct drm_i915_private *i915, int prio)
return ctx;
}
static void init_contexts(struct drm_i915_private *i915)
static void init_contexts(struct i915_gem_contexts *gc)
{
mutex_init(&i915->contexts.mutex);
INIT_LIST_HEAD(&i915->contexts.list);
spin_lock_init(&gc->lock);
INIT_LIST_HEAD(&gc->list);
INIT_WORK(&i915->contexts.free_work, contexts_free_worker);
init_llist_head(&i915->contexts.free_list);
INIT_WORK(&gc->free_work, contexts_free_worker);
init_llist_head(&gc->free_list);
}
int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
int i915_gem_init_contexts(struct drm_i915_private *i915)
{
struct i915_gem_context *ctx;
/* Reassure ourselves we are only called once */
GEM_BUG_ON(dev_priv->kernel_context);
GEM_BUG_ON(i915->kernel_context);
init_contexts(dev_priv);
init_contexts(&i915->gem.contexts);
/* lowest priority; idle task */
ctx = i915_gem_context_create_kernel(dev_priv, I915_PRIORITY_MIN);
ctx = i915_gem_context_create_kernel(i915, I915_PRIORITY_MIN);
if (IS_ERR(ctx)) {
DRM_ERROR("Failed to create default global context\n");
return PTR_ERR(ctx);
}
dev_priv->kernel_context = ctx;
i915->kernel_context = ctx;
DRM_DEBUG_DRIVER("%s context support initialized\n",
DRIVER_CAPS(dev_priv)->has_logical_contexts ?
DRIVER_CAPS(i915)->has_logical_contexts ?
"logical" : "fake");
return 0;
}
void i915_gem_contexts_fini(struct drm_i915_private *i915)
void i915_gem_driver_release__contexts(struct drm_i915_private *i915)
{
lockdep_assert_held(&i915->drm.struct_mutex);
destroy_kernel_context(&i915->kernel_context);
}
@@ -551,11 +543,16 @@ static int vm_idr_cleanup(int id, void *p, void *data)
static int gem_context_register(struct i915_gem_context *ctx,
struct drm_i915_file_private *fpriv)
{
struct i915_address_space *vm;
int ret;
ctx->file_priv = fpriv;
if (ctx->vm)
ctx->vm->file = fpriv;
mutex_lock(&ctx->mutex);
vm = i915_gem_context_vm(ctx);
if (vm)
WRITE_ONCE(vm->file, fpriv); /* XXX */
mutex_unlock(&ctx->mutex);
ctx->pid = get_task_pid(current, PIDTYPE_PID);
ctx->name = kasprintf(GFP_KERNEL, "%s[%d]",
@@ -592,9 +589,7 @@ int i915_gem_context_open(struct drm_i915_private *i915,
idr_init(&file_priv->context_idr);
idr_init_base(&file_priv->vm_idr, 1);
mutex_lock(&i915->drm.struct_mutex);
ctx = i915_gem_create_context(i915, 0);
mutex_unlock(&i915->drm.struct_mutex);
if (IS_ERR(ctx)) {
err = PTR_ERR(ctx);
goto err;
@@ -622,6 +617,7 @@ err:
void i915_gem_context_close(struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
struct drm_i915_private *i915 = file_priv->dev_priv;
idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
idr_destroy(&file_priv->context_idr);
@@ -630,6 +626,8 @@ void i915_gem_context_close(struct drm_file *file)
idr_for_each(&file_priv->vm_idr, vm_idr_cleanup, NULL);
idr_destroy(&file_priv->vm_idr);
mutex_destroy(&file_priv->vm_idr_lock);
contexts_flush_free(&i915->gem.contexts);
}
int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data,
@@ -808,16 +806,12 @@ static int get_ppgtt(struct drm_i915_file_private *file_priv,
struct i915_address_space *vm;
int ret;
if (!ctx->vm)
if (!rcu_access_pointer(ctx->vm))
return -ENODEV;
/* XXX rcu acquire? */
ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
if (ret)
return ret;
rcu_read_lock();
vm = i915_vm_get(ctx->vm);
mutex_unlock(&ctx->i915->drm.struct_mutex);
rcu_read_unlock();
ret = mutex_lock_interruptible(&file_priv->vm_idr_lock);
if (ret)
@@ -926,7 +920,7 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
if (args->size)
return -EINVAL;
if (!ctx->vm)
if (!rcu_access_pointer(ctx->vm))
return -ENODEV;
if (upper_32_bits(args->value))
@@ -940,17 +934,20 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
if (!vm)
return -ENOENT;
err = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex);
err = mutex_lock_interruptible(&ctx->mutex);
if (err)
goto out;
if (vm == ctx->vm)
if (i915_gem_context_is_closed(ctx)) {
err = -ENOENT;
goto out;
}
if (vm == rcu_access_pointer(ctx->vm))
goto unlock;
/* Teardown the existing obj:vma cache, it will have to be rebuilt. */
mutex_lock(&ctx->mutex);
lut_close(ctx);
mutex_unlock(&ctx->mutex);
old = __set_ppgtt(ctx, vm);
@@ -970,8 +967,7 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
}
unlock:
mutex_unlock(&ctx->i915->drm.struct_mutex);
mutex_unlock(&ctx->mutex);
out:
i915_vm_put(vm);
return err;
@@ -1827,10 +1823,11 @@ static int clone_vm(struct i915_gem_context *dst,
struct i915_gem_context *src)
{
struct i915_address_space *vm;
int err = 0;
rcu_read_lock();
do {
vm = READ_ONCE(src->vm);
vm = rcu_dereference(src->vm);
if (!vm)
break;
@@ -1852,7 +1849,7 @@ static int clone_vm(struct i915_gem_context *dst,
* it cannot be reallocated elsewhere.
*/
if (vm == READ_ONCE(src->vm))
if (vm == rcu_access_pointer(src->vm))
break;
i915_vm_put(vm);
@@ -1860,11 +1857,16 @@ static int clone_vm(struct i915_gem_context *dst,
rcu_read_unlock();
if (vm) {
__assign_ppgtt(dst, vm);
if (!mutex_lock_interruptible(&dst->mutex)) {
__assign_ppgtt(dst, vm);
mutex_unlock(&dst->mutex);
} else {
err = -EINTR;
}
i915_vm_put(vm);
}
return 0;
return err;
}
static int create_clone(struct i915_user_extension __user *ext, void *data)
@@ -1954,12 +1956,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
return -EIO;
}
ret = i915_mutex_lock_interruptible(dev);
if (ret)
return ret;
ext_data.ctx = i915_gem_create_context(i915, args->flags);
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(ext_data.ctx))
return PTR_ERR(ext_data.ctx);
@@ -2086,10 +2083,12 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
case I915_CONTEXT_PARAM_GTT_SIZE:
args->size = 0;
if (ctx->vm)
args->value = ctx->vm->total;
rcu_read_lock();
if (rcu_access_pointer(ctx->vm))
args->value = rcu_dereference(ctx->vm)->total;
else
args->value = to_i915(dev)->ggtt.vm.total;
rcu_read_unlock();
break;
case I915_CONTEXT_PARAM_NO_ERROR_CAPTURE:
@@ -2155,7 +2154,7 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
int i915_gem_context_reset_stats_ioctl(struct drm_device *dev,
void *data, struct drm_file *file)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_private *i915 = to_i915(dev);
struct drm_i915_reset_stats *args = data;
struct i915_gem_context *ctx;
int ret;
@@ -2177,7 +2176,7 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev,
*/
if (capable(CAP_SYS_ADMIN))
args->reset_count = i915_reset_count(&dev_priv->gpu_error);
args->reset_count = i915_reset_count(&i915->gpu_error);
else
args->reset_count = 0;