Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8963ea2d authored by Shubhraprakash Das's avatar Shubhraprakash Das
Browse files

msm: kgsl: Add event to complete context detachment



Add an event with a callback to complete the detachment of context.
This allows the context destroy ioctl to return early without
waiting for the GPU to complete the last command submitted by
the context.

Change-Id: I2cce962f3260877906cbdf95a1b894bd2617d988
Signed-off-by: default avatarShubhraprakash Das <sadas@codeaurora.org>
parent f2548d8b
Loading
Loading
Loading
Loading
+51 −65
Original line number Diff line number Diff line
@@ -118,42 +118,6 @@ done:
	return ret;
}

/**
 * adreno_drawctxt_wait_rb() - Wait for the last RB timestamp at which this
 * context submitted a command to the corresponding RB
 * @adreno_dev: The device on which the timestamp is active
 * @context: The context which subbmitted command to RB
 * @timestamp: The RB timestamp of last command submitted to RB by context
 * @timeout: Timeout value for the wait
 */
static int adreno_drawctxt_wait_rb(struct adreno_device *adreno_dev,
		struct kgsl_context *context,
		uint32_t timestamp, unsigned int timeout)
{
	struct kgsl_device *device = &adreno_dev->dev;
	struct adreno_context *drawctxt = ADRENO_CONTEXT(context);
	int ret = 0;

	/* Needs to hold the device mutex */
	BUG_ON(!mutex_is_locked(&device->mutex));

	/*
	 * If the context is invalid then return immediately - we may end up
	 * waiting for a timestamp that will never come
	 */
	if (kgsl_context_invalid(context))
		goto done;

	trace_adreno_drawctxt_wait_start(drawctxt->rb->id, context->id,
					timestamp);

	ret = adreno_ringbuffer_waittimestamp(drawctxt->rb, timestamp, timeout);
done:
	trace_adreno_drawctxt_wait_done(drawctxt->rb->id, context->id,
					timestamp, ret);
	return ret;
}

/**
 * adreno_drawctxt_invalidate() - Invalidate an adreno draw context
 * @device: Pointer to the KGSL device structure for the GPU
@@ -319,6 +283,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
			KGSL_MEMSTORE_OFFSET(drawctxt->base.id, eoptimestamp),
			0);

	init_completion(&(drawctxt->base.detach_gate));

	adreno_context_debugfs_init(ADRENO_DEVICE(device), drawctxt);

	/* copy back whatever flags we dediced were valid */
@@ -341,6 +307,47 @@ void adreno_drawctxt_sched(struct kgsl_device *device,
	adreno_dispatcher_queue_context(device, ADRENO_CONTEXT(context));
}

/**
 * adreno_drawctxt_detach_callback() - Callback function called when
 * the last timestamp on which a context submitted command expires
 * @device: Device pointer
 * @ctx: The event groups context. should be NULL since the event group is RB
 * @priv: The context this callback was registered for
 * @result: Indicates the result of wait
 *
 * Signal waiters that were waiting on the last command of this context
 */
static void adreno_drawctxt_detach_callback(struct kgsl_device *device,
			struct kgsl_context *ctx,
			void *priv, int result)
{
	struct adreno_context *drawctxt = priv;
	struct kgsl_context *context = &drawctxt->base;
	struct adreno_device *adreno_dev = ADRENO_DEVICE(drawctxt->rb->device);

	kgsl_sharedmem_writel(device, &device->memstore,
		KGSL_MEMSTORE_OFFSET(context->id, soptimestamp),
		drawctxt->timestamp);

	kgsl_sharedmem_writel(device, &device->memstore,
		KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp),
		drawctxt->timestamp);

	/*
	 * Cancel all pending events after the device-specific context is
	 * detached, to avoid possibly freeing memory while it is still
	 * in use by the GPU.
	 */
	kgsl_cancel_events(device, &context->events);

	/* wake threads waiting to submit commands from this context */
	wake_up_all(&drawctxt->waiting);
	wake_up_all(&drawctxt->wq);
	complete_all(&drawctxt->base.detach_gate);
	kgsl_context_put(context);
	adreno_profile_process_results(adreno_dev);
}

/**
 * adreno_drawctxt_detach(): detach a context from the GPU
 * @context: Generic KGSL context container for the context
@@ -403,35 +410,14 @@ int adreno_drawctxt_detach(struct kgsl_context *context)
	 */
	BUG_ON(!mutex_is_locked(&device->mutex));

	/* Wait for the last global timestamp to pass before continuing */
	ret = adreno_drawctxt_wait_rb(adreno_dev, context,
		drawctxt->internal_timestamp, 10 * 1000);

	/*
	 * If the wait for global fails due to timeout then nothing after this
	 * point is likely to work very well - BUG_ON() so we can take advantage
	 * of the debug tools to figure out what the h - e - double hockey
	 * sticks happened. If EAGAIN error is returned then recovery will kick
	 * in and there will be no more commands in the RB pipe from this
	 * context which is waht we are waiting for, so ignore -EAGAIN error
	 */
	if (-EAGAIN == ret)
		ret = 0;
	BUG_ON(ret);

	kgsl_sharedmem_writel(device, &device->memstore,
			KGSL_MEMSTORE_OFFSET(context->id, soptimestamp),
			drawctxt->timestamp);

	kgsl_sharedmem_writel(device, &device->memstore,
			KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp),
			drawctxt->timestamp);

	adreno_profile_process_results(adreno_dev);

	/* wake threads waiting to submit commands from this context */
	wake_up_all(&drawctxt->waiting);
	wake_up_all(&drawctxt->wq);
	/* add event to complete context detachment */
	_kgsl_context_get(context);
	ret = kgsl_add_event(device, &drawctxt->rb->events,
			drawctxt->internal_timestamp,
			adreno_drawctxt_detach_callback,
			(void *) drawctxt);
	if (ret)
		kgsl_context_put(context);

	return ret;
}
+62 −27
Original line number Diff line number Diff line
@@ -64,6 +64,11 @@
#define KGSL_DMA_BIT_MASK	DMA_BIT_MASK(32)
#endif

/* 2s timeout wait for contexts to complete */
#define CTXT_DETACH_TIMEOUT	10000

static struct workqueue_struct *release_work_queue;

/*
 * Define an kmem cache for the memobj structures since we allocate and free
 * them so frequently
@@ -586,15 +591,9 @@ int kgsl_context_detach(struct kgsl_context *context)
	mutex_unlock(&device->mutex);

	/*
	 * Cancel all pending events after the device-specific context is
	 * detached, to avoid possibly freeing memory while it is still
	 * in use by the GPU.
	 * This put corresponds to the get when user space
	 * first created the context
	 */
	kgsl_cancel_events(device, &context->events);

	/* Remove the event group from the list */
	kgsl_del_event_group(&context->events);

	kgsl_context_put(context);

	return ret;
@@ -635,6 +634,7 @@ kgsl_context_destroy(struct kref *kref)
	write_unlock(&device->context_lock);
	kgsl_sync_timeline_destroy(context);
	kgsl_process_private_put(context->proc_priv);
	kgsl_del_event_group(&context->events);

	device->ftbl->drawctxt_destroy(context);
}
@@ -936,6 +936,7 @@ error:
static int kgsl_close_device(struct kgsl_device *device)
{
	int result = 0;
	int flush = 0;

	mutex_lock(&device->mutex);
	device->open_count--;
@@ -951,35 +952,30 @@ static int kgsl_close_device(struct kgsl_device *device)
		kgsl_pwrctrl_enable(device);
		result = device->ftbl->stop(device);
		kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
		flush = 1;
	}
	mutex_unlock(&device->mutex);

	if (flush)
		flush_workqueue(release_work_queue);
	return result;

}

static int kgsl_release(struct inode *inodep, struct file *filep)
/**
 * kgsl_release_deferred_proc_priv() - Free a process private structure
 * @work: The work structure used to schedule the release of the process private
 */
static void kgsl_release_deferred_proc_priv(struct work_struct *work)
{
	int result = 0;
	struct kgsl_device_private *dev_priv = filep->private_data;
	struct kgsl_device_private *dev_priv =
		container_of(work, struct kgsl_device_private, release_work);
	struct kgsl_process_private *private = dev_priv->process_priv;
	struct kgsl_device *device = dev_priv->device;
	struct kgsl_context *context;
	struct kgsl_syncsource *syncsource;
	struct kgsl_mem_entry *entry;
	int next = 0;

	filep->private_data = NULL;

	next = 0;
	while (1) {
		syncsource = idr_get_next(&private->syncsource_idr, &next);

		if (syncsource == NULL)
			break;
		kgsl_syncsource_put(syncsource);
		next = next + 1;
	}

	next = 0;
	while (1) {
		read_lock(&device->context_lock);
@@ -996,7 +992,22 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
			 */

			if (_kgsl_context_get(context)) {
				int ret;
				kgsl_context_detach(context);
				/* wait for the ctxt to complete detachment */
				ret = wait_for_completion_timeout(
					&context->detach_gate,
					msecs_to_jiffies(CTXT_DETACH_TIMEOUT));
				if (0 == ret) {
					KGSL_DRV_ERR(device,
					"Timed out while waiting for context %d to detach\n",
					context->id);
					wait_for_completion(
						&context->detach_gate);
					KGSL_DRV_ERR(device,
					"Wait for context %d detach complete\n",
					context->id);
				}
				kgsl_context_put(context);
			}
		}
@@ -1027,14 +1038,36 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
		next = next + 1;
	}

	result = kgsl_close_device(device);

	kfree(dev_priv);

	kgsl_process_private_put(private);
}

static int kgsl_release(struct inode *inodep, struct file *filep)
{
	struct kgsl_device_private *dev_priv = filep->private_data;
	struct kgsl_process_private *private = dev_priv->process_priv;
	struct kgsl_device *device = dev_priv->device;
	struct kgsl_syncsource *syncsource;
	int next = 0;

	filep->private_data = NULL;

	next = 0;
	while (1) {
		syncsource = idr_get_next(&private->syncsource_idr, &next);

		if (syncsource == NULL)
			break;
		kgsl_syncsource_put(syncsource);
		next = next + 1;
	}

	INIT_WORK(&dev_priv->release_work, kgsl_release_deferred_proc_priv);
	queue_work(release_work_queue, &dev_priv->release_work);

	pm_runtime_put(&device->pdev->dev);
	return result;
	return kgsl_close_device(device);
}

static int kgsl_open_device(struct kgsl_device *device)
@@ -4540,6 +4573,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device)


	device->events_wq = create_workqueue("kgsl-events");
	release_work_queue = create_workqueue("kgsl-release");

	/* Initalize the snapshot engine */
	kgsl_device_snapshot_init(device);
@@ -4568,6 +4602,7 @@ EXPORT_SYMBOL(kgsl_device_platform_probe);
void kgsl_device_platform_remove(struct kgsl_device *device)
{
	destroy_workqueue(device->events_wq);
	destroy_workqueue(release_work_queue);

	kgsl_device_snapshot_close(device);

+4 −1
Original line number Diff line number Diff line
@@ -371,7 +371,6 @@ struct kgsl_device {
	int reset_counter; /* Track how many GPU core resets have occured */
	int cff_dump_enable;
	struct workqueue_struct *events_wq;

	struct device *busmondev; /* pseudo dev for GPU BW voting governor */
};

@@ -436,6 +435,8 @@ struct kgsl_process_private;
 * @pwr_constraint: power constraint from userspace for this context
 * @fault_count: number of times gpu hanged in last _context_throttle_time ms
 * @fault_time: time of the first gpu hang in last _context_throttle_time ms
 * @detach_gate: Once signalled it means the context is completely detached and
 * there are no pending commands in the pipe from this context
 */
struct kgsl_context {
	struct kref refcount;
@@ -455,6 +456,7 @@ struct kgsl_context {
	struct kgsl_pwr_constraint pwr_constraint;
	unsigned int fault_count;
	unsigned long fault_time;
	struct completion detach_gate;
};

/**
@@ -511,6 +513,7 @@ enum kgsl_process_priv_flags {
struct kgsl_device_private {
	struct kgsl_device *device;
	struct kgsl_process_private *process_priv;
	struct work_struct release_work;
};

/**