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

Commit 36b196ce authored by Jordan Crouse's avatar Jordan Crouse
Browse files

msm: kgsl: Add software timelines



Some graphics APIs want the ability to create and use timelines
with 64 bit sequence numbers to synchronize between threads.

Add support for timelines that can be created, signaled and managed from
user space. Timelines can also be signaled from a draw context command
stream via an auxiliary command and a wait can be added as a sync object
to any GPU command.

Change-Id: Ic0dedbad0ffc99cc309951369eaf85b56727802c
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent f68e1ac9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ msm_kgsl-y = \
	kgsl_pwrscale.o \
	kgsl_sharedmem.o \
	kgsl_snapshot.o \
	kgsl_timeline.o \
	kgsl_trace.o \
	kgsl_util.o

+12 −2
Original line number Diff line number Diff line
@@ -139,10 +139,20 @@ static void sync_event_print(struct seq_file *s,
	}
	case KGSL_CMD_SYNCPOINT_TYPE_FENCE: {
		int i;
		struct event_fence_info *info = sync_event->priv;

		for (i = 0; i < sync_event->info.num_fences; i++)
		for (i = 0; info && i < info->num_fences; i++)
			seq_printf(s, "sync: %s",
				sync_event->info.fences[i].name);
				info->fences[i].name);
		break;
	}
	case KGSL_CMD_SYNCPOINT_TYPE_TIMELINE: {
		int j;
		struct event_timeline_info *info = sync_event->priv;

		for (j = 0; info && info[j].timeline; j++)
			seq_printf(s, "timeline: %d seqno: %d",
				info[j].timeline, info[j].seqno);
		break;
	}
	default:
+87 −40
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include "adreno.h"
#include "adreno_trace.h"
#include "kgsl_gmu_core.h"
#include "kgsl_timeline.h"

#define DRAWQUEUE_NEXT(_i, _s) (((_i) + 1) % (_s))

@@ -286,6 +287,8 @@ static void _retire_timestamp(struct kgsl_drawobj *drawobj)
		KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp),
		drawobj->timestamp);

	drawctxt->submitted_timestamp = drawobj->timestamp;

	/* Retire pending GPU events for the object */
	kgsl_process_event_group(device, &context->events);

@@ -357,12 +360,14 @@ static inline void _pop_drawobj(struct adreno_context *drawctxt)
	drawctxt->queued--;
}

static int _retire_markerobj(struct kgsl_drawobj_cmd *cmdobj,
static int dispatch_retire_markerobj(struct kgsl_drawobj *drawobj,
				struct adreno_context *drawctxt)
{
	struct kgsl_drawobj_cmd *cmdobj = CMDOBJ(drawobj);

	if (_marker_expired(cmdobj)) {
		_pop_drawobj(drawctxt);
		_retire_timestamp(DRAWOBJ(cmdobj));
		_retire_timestamp(drawobj);
		return 0;
	}

@@ -378,12 +383,14 @@ static int _retire_markerobj(struct kgsl_drawobj_cmd *cmdobj,
	return test_bit(CMDOBJ_SKIP, &cmdobj->priv) ? 1 : -EAGAIN;
}

static int _retire_syncobj(struct kgsl_drawobj_sync *syncobj,
static int dispatch_retire_syncobj(struct kgsl_drawobj *drawobj,
				struct adreno_context *drawctxt)
{
	struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);

	if (!kgsl_drawobj_events_pending(syncobj)) {
		_pop_drawobj(drawctxt);
		kgsl_drawobj_destroy(DRAWOBJ(syncobj));
		kgsl_drawobj_destroy(drawobj);
		return 0;
	}

@@ -399,6 +406,22 @@ static int _retire_syncobj(struct kgsl_drawobj_sync *syncobj,
	return -EAGAIN;
}

static int drawqueue_retire_timelineobj(struct kgsl_drawobj *drawobj,
		struct adreno_context *drawctxt)
{
	struct kgsl_drawobj_timeline *timelineobj = TIMELINEOBJ(drawobj);
	int i;

	for (i = 0; i < timelineobj->count; i++)
		kgsl_timeline_signal(timelineobj->timelines[i].timeline,
			timelineobj->timelines[i].seqno);

	_pop_drawobj(drawctxt);
	_retire_timestamp(drawobj);

	return 0;
}

/*
 * Retires all expired marker and sync objs from the context
 * queue and returns one of the below
@@ -412,35 +435,40 @@ static struct kgsl_drawobj *_process_drawqueue_get_next_drawobj(
{
	struct kgsl_drawobj *drawobj;
	unsigned int i = drawctxt->drawqueue_head;
	int ret = 0;

	if (drawctxt->drawqueue_head == drawctxt->drawqueue_tail)
		return NULL;

	for (i = drawctxt->drawqueue_head; i != drawctxt->drawqueue_tail;
			i = DRAWQUEUE_NEXT(i, ADRENO_CONTEXT_DRAWQUEUE_SIZE)) {
		int ret = 0;

		drawobj = drawctxt->drawqueue[i];

		if (drawobj == NULL)
		if (!drawobj)
			return NULL;

		if (drawobj->type == CMDOBJ_TYPE)
		switch (drawobj->type) {
		case CMDOBJ_TYPE:
			return drawobj;
		else if (drawobj->type == MARKEROBJ_TYPE) {
			ret = _retire_markerobj(CMDOBJ(drawobj), drawctxt);
		case MARKEROBJ_TYPE:
			ret = dispatch_retire_markerobj(drawobj, drawctxt);
			/* Special case where marker needs to be sent to GPU */
			if (ret == 1)
				return drawobj;
		} else if (drawobj->type == SYNCOBJ_TYPE)
			ret = _retire_syncobj(SYNCOBJ(drawobj), drawctxt);
		else
			return ERR_PTR(-EINVAL);

		if (ret == -EAGAIN)
			return ERR_PTR(-EAGAIN);
			break;
		case SYNCOBJ_TYPE:
			ret = dispatch_retire_syncobj(drawobj, drawctxt);
			break;
		case TIMELINEOBJ_TYPE:
			ret = drawqueue_retire_timelineobj(drawobj, drawctxt);
			break;
		default:
			ret = -EINVAL;
			break;
		}

		continue;
		if (ret)
			return ERR_PTR(ret);
	}

	return NULL;
@@ -1184,11 +1212,27 @@ static void _queue_drawobj(struct adreno_context *drawctxt,
	trace_adreno_cmdbatch_queued(drawobj, drawctxt->queued);
}

static int _queue_markerobj(struct adreno_device *adreno_dev,
	struct adreno_context *drawctxt, struct kgsl_drawobj_cmd *markerobj,
static int drawctxt_queue_auxobj(struct adreno_device *adreno_dev,
		struct adreno_context *drawctxt, struct kgsl_drawobj *drawobj,
		u32 *timestamp, u32 user_ts)
{
	int ret;

	ret = get_timestamp(drawctxt, drawobj, timestamp, user_ts);
	if (ret)
		return ret;

	drawctxt->queued_timestamp = *timestamp;
	_queue_drawobj(drawctxt, drawobj);

	return 0;
}

static int drawctxt_queue_markerobj(struct adreno_device *adreno_dev,
	struct adreno_context *drawctxt, struct kgsl_drawobj *drawobj,
	uint32_t *timestamp, unsigned int user_ts)
{
	struct kgsl_drawobj *drawobj = DRAWOBJ(markerobj);
	struct kgsl_drawobj_cmd *markerobj = CMDOBJ(drawobj);
	int ret;

	ret = get_timestamp(drawctxt, drawobj, timestamp, user_ts);
@@ -1221,11 +1265,11 @@ static int _queue_markerobj(struct adreno_device *adreno_dev,
	return 0;
}

static int _queue_cmdobj(struct adreno_device *adreno_dev,
	struct adreno_context *drawctxt, struct kgsl_drawobj_cmd *cmdobj,
static int drawctxt_queue_cmdobj(struct adreno_device *adreno_dev,
	struct adreno_context *drawctxt, struct kgsl_drawobj *drawobj,
	uint32_t *timestamp, unsigned int user_ts)
{
	struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
	struct kgsl_drawobj_cmd *cmdobj = CMDOBJ(drawobj);
	unsigned int j;
	int ret;

@@ -1259,11 +1303,9 @@ static int _queue_cmdobj(struct adreno_device *adreno_dev,
	return 0;
}

static void _queue_syncobj(struct adreno_context *drawctxt,
	struct kgsl_drawobj_sync *syncobj, uint32_t *timestamp)
static void drawctxt_queue_syncobj(struct adreno_context *drawctxt,
	struct kgsl_drawobj *drawobj, uint32_t *timestamp)
{
	struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);

	*timestamp = 0;
	drawobj->timestamp = 0;

@@ -1337,29 +1379,34 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,

		switch (drawobj[i]->type) {
		case MARKEROBJ_TYPE:
			ret = _queue_markerobj(adreno_dev, drawctxt,
					CMDOBJ(drawobj[i]),
					timestamp, user_ts);
			if (ret == 1) {
			ret = drawctxt_queue_markerobj(adreno_dev, drawctxt,
				drawobj[i], timestamp, user_ts);
			if (ret)
				spin_unlock(&drawctxt->lock);

			if (ret == 1)
				goto done;
			} else if (ret) {
				spin_unlock(&drawctxt->lock);
			else if (ret)
				return ret;
			}
			break;
		case CMDOBJ_TYPE:
			ret = _queue_cmdobj(adreno_dev, drawctxt,
						CMDOBJ(drawobj[i]),
						timestamp, user_ts);
			ret = drawctxt_queue_cmdobj(adreno_dev, drawctxt,
				drawobj[i], timestamp, user_ts);
			if (ret) {
				spin_unlock(&drawctxt->lock);
				return ret;
			}
			break;
		case SYNCOBJ_TYPE:
			_queue_syncobj(drawctxt, SYNCOBJ(drawobj[i]),
						timestamp);
			drawctxt_queue_syncobj(drawctxt, drawobj[i], timestamp);
			break;
		case TIMELINEOBJ_TYPE:
			ret = drawctxt_queue_auxobj(adreno_dev,
				drawctxt, drawobj[i], timestamp, user_ts);
			if (ret) {
				spin_unlock(&drawctxt->lock);
				return ret;
			}
			break;
		default:
			spin_unlock(&drawctxt->lock);
+135 −0
Original line number Diff line number Diff line
@@ -1858,6 +1858,138 @@ long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv,
	return result;
}

long kgsl_ioctl_gpu_aux_command(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data)
{
	struct kgsl_gpu_aux_command *param = data;
	struct kgsl_device *device = dev_priv->device;
	struct kgsl_context *context;
	struct kgsl_drawobj **drawobjs;
	struct kgsl_drawobj_sync *tsobj;
	void __user *cmdlist;
	u32 queued, count;
	int i, index = 0;
	long ret;

	/* Aux commands don't make sense without commands */
	if (!param->numcmds)
		return -EINVAL;

	if (!(param->flags &
		(KGSL_GPU_AUX_COMMAND_TIMELINE)))
		return -EINVAL;

	/* Make sure we don't overflow count */
	if (param->numcmds == UINT_MAX)
		return -EINVAL;

	context = kgsl_context_get_owner(dev_priv, param->context_id);
	if (!context)
		return -EINVAL;

	/*
	 * We have one drawobj for the timestamp sync plus one for all of the
	 * commands
	 */
	count = param->numcmds + 1;

	if (param->flags & KGSL_GPU_AUX_COMMAND_SYNC)
		count++;

	drawobjs = kvcalloc(count, sizeof(*drawobjs),
		GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);

	if (!drawobjs) {
		kgsl_context_put(context);
		return -ENOMEM;
	}

	if (param->flags & KGSL_GPU_AUX_COMMAND_SYNC) {
		struct kgsl_drawobj_sync *syncobj =
			kgsl_drawobj_sync_create(device, context);

		if (IS_ERR(syncobj)) {
			ret = PTR_ERR(syncobj);
			goto err;
		}

		drawobjs[index++] = DRAWOBJ(syncobj);

		ret = kgsl_drawobj_sync_add_synclist(device, syncobj,
				u64_to_user_ptr(param->synclist),
				param->syncsize, param->numsyncs);
		if (ret)
			goto err;
	}

	kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED, &queued);

	/*
	 * Make an implicit sync object for the last queued timestamp on this
	 * context
	 */
	tsobj = kgsl_drawobj_create_timestamp_syncobj(device,
		context, queued);

	if (IS_ERR(tsobj)) {
		ret = PTR_ERR(tsobj);
		goto err;
	}

	drawobjs[index++] = DRAWOBJ(tsobj);

	cmdlist = u64_to_user_ptr(param->cmdlist);

	/* Create a draw object for each command */
	for (i = 0; i < param->numcmds; i++) {
		struct kgsl_gpu_aux_command_generic generic;

		if (copy_struct_from_user(&generic, sizeof(generic),
			cmdlist, param->cmdsize)) {
			ret = -EFAULT;
			goto err;
		}

		if (generic.type == KGSL_GPU_AUX_COMMAND_TIMELINE) {
			struct kgsl_drawobj_timeline *timelineobj;

			timelineobj = kgsl_drawobj_timeline_create(device,
				context);

			if (IS_ERR(timelineobj)) {
				ret = PTR_ERR(timelineobj);
				goto err;
			}

			ret = kgsl_drawobj_add_timeline(dev_priv, timelineobj,
				cmdlist, param->cmdsize);
			if (ret)
				goto err;

			drawobjs[index++] = DRAWOBJ(timelineobj);
		} else {
			ret = -EINVAL;
			goto err;
		}

		cmdlist += param->cmdsize;
	}

	ret = device->ftbl->queue_cmds(dev_priv, context,
		drawobjs, index, &param->timestamp);

err:
	kgsl_context_put(context);

	if (ret && ret != -EPROTO) {
		for (i = 0; i < count; i++)
			kgsl_drawobj_destroy(drawobjs[i]);
	}

	kvfree(drawobjs);
	return ret;
}

long kgsl_ioctl_cmdstream_readtimestamp_ctxtid(struct kgsl_device_private
						*dev_priv, unsigned int cmd,
						void *data)
@@ -4124,6 +4256,9 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
	rwlock_init(&device->context_lock);
	spin_lock_init(&device->submit_lock);

	/* Use XA_FLAGS_ALLOC1 to disallow 0 as an option */
	xa_init_flags(&device->timelines, XA_FLAGS_ALLOC1);

	kgsl_device_debugfs_init(device);

	dma_set_coherent_mask(&pdev->dev, KGSL_DMA_BIT_MASK);
+14 −0
Original line number Diff line number Diff line
@@ -425,6 +425,20 @@ long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv,
				unsigned int cmd, void *data);
long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv,
				unsigned int cmd, void *data);
long kgsl_ioctl_gpu_aux_command(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_create(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_wait(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_query(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_fence_get(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_signal(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);
long kgsl_ioctl_timeline_destroy(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data);

void kgsl_mem_entry_destroy(struct kref *kref);

Loading