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

Commit f474354a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Add event to complete context detachment"

parents a6a59cec 8963ea2d
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;
};

/**