Commit a4e7ccda authored by Chris Wilson's avatar Chris Wilson
Browse files

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: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: default avatarTvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-15-chris@chris-wilson.co.uk
parent 2935ed53
Loading
Loading
Loading
Loading
+88 −89
Original line number Diff line number Diff line
@@ -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)
{
	i915_gem_context_set_closed(ctx);
	struct i915_address_space *vm;

	if (ctx->vm)
		i915_vm_close(ctx->vm);
	i915_gem_context_set_closed(ctx);

	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) {
		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;

+25 −2
Original line number Diff line number Diff line
@@ -11,7 +11,9 @@

#include "gt/intel_context.h"

#include "i915_drv.h"
#include "i915_gem.h"
#include "i915_gem_gtt.h"
#include "i915_scheduler.h"
#include "intel_device_info.h"

@@ -118,8 +120,8 @@ static inline bool i915_gem_context_is_kernel(struct i915_gem_context *ctx)
}

/* i915_gem_context.c */
int __must_check i915_gem_contexts_init(struct drm_i915_private *dev_priv);
void i915_gem_contexts_fini(struct drm_i915_private *dev_priv);
int __must_check i915_gem_init_contexts(struct drm_i915_private *i915);
void i915_gem_driver_release__contexts(struct drm_i915_private *i915);

int i915_gem_context_open(struct drm_i915_private *i915,
			  struct drm_file *file);
@@ -158,6 +160,27 @@ static inline void i915_gem_context_put(struct i915_gem_context *ctx)
	kref_put(&ctx->ref, i915_gem_context_release);
}

static inline struct i915_address_space *
i915_gem_context_vm(struct i915_gem_context *ctx)
{
	return rcu_dereference_protected(ctx->vm, lockdep_is_held(&ctx->mutex));
}

static inline struct i915_address_space *
i915_gem_context_get_vm_rcu(struct i915_gem_context *ctx)
{
	struct i915_address_space *vm;

	rcu_read_lock();
	vm = rcu_dereference(ctx->vm);
	if (!vm)
		vm = &ctx->i915->ggtt.vm;
	vm = i915_vm_get(vm);
	rcu_read_unlock();

	return vm;
}

static inline struct i915_gem_engines *
i915_gem_context_engines(struct i915_gem_context *ctx)
{
+1 −1
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ struct i915_gem_context {
	 * In other modes, this is a NULL pointer with the expectation that
	 * the caller uses the shared global GTT.
	 */
	struct i915_address_space *vm;
	struct i915_address_space __rcu *vm;

	/**
	 * @pid: process id of creator
+1 −1
Original line number Diff line number Diff line
@@ -728,7 +728,7 @@ static int eb_select_context(struct i915_execbuffer *eb)
		return -ENOENT;

	eb->gem_context = ctx;
	if (ctx->vm)
	if (rcu_access_pointer(ctx->vm))
		eb->invalid_flags |= EXEC_OBJECT_NEEDS_GTT;

	eb->context_flags = 0;
+2 −1
Original line number Diff line number Diff line
@@ -766,7 +766,8 @@ i915_gem_userptr_ioctl(struct drm_device *dev,
		 * On almost all of the older hw, we cannot tell the GPU that
		 * a page is readonly.
		 */
		vm = dev_priv->kernel_context->vm;
		vm = rcu_dereference_protected(dev_priv->kernel_context->vm,
					       true); /* static vm */
		if (!vm || !vm->has_read_only)
			return -ENODEV;
	}
Loading