KGSL_IOCTL_FUNC(IOCTL_KGSL_GPU_COMMAND, kgsl_ioctl_gpu_command),
1. kgsl_ioctl_gpu_command
long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
// IOCTL_KGSL_GPU_COMMAND命令参数[见1.1节]
struct kgsl_gpu_command *param = data
struct kgsl_device *device = dev_priv->device
struct kgsl_context *context
// kgsl_drawobj[见第1.2节]数组:用于存放kgsl_drawobj_sync和kgsl_drawobj_cmd中的kgsl_drawobj
struct kgsl_drawobj *drawobj[2]
unsigned int type
long result
unsigned int i = 0
// 根据参数计算对象类型[见第2节]
type = _process_command_input(device, param->flags, param->numcmds,
param->numobjs, param->numsyncs)
if (!type)
return -EINVAL
// 根据context id查找对应的kgsl_context
context = kgsl_context_get_owner(dev_priv, param->context_id)
if (context == NULL)
return -EINVAL
// 1.如果指定KGSL_DRAWOBJ_SYNC标志位, 则命令类型为SYNCOBJ_TYPE
// 2.如果传入的kgsl_gpu_command的numsyncs大于0, 则命令类型包含SYNCOBJ_TYPE
if (type & SYNCOBJ_TYPE) {
struct kgsl_drawobj_sync *syncobj =
kgsl_drawobj_sync_create(device, context)
if (IS_ERR(syncobj)) {
result = PTR_ERR(syncobj)
goto done
}
drawobj[i++] = DRAWOBJ(syncobj)
result = kgsl_drawobj_sync_add_synclist(device, syncobj,
u64_to_user_ptr(param->synclist),
param->syncsize, param->numsyncs)
if (result)
goto done
}
// 1.如果指定KGSL_DRAWOBJ_MARKER标志位, 则命令类型为MARKEROBJ_TYPE
// 2.如果传入的kgsl_gpu_command的numcmds大于0, 则命令类型包含CMDOBJ_TYPE
// 3.如果传入的kgsl_gpu_command的numcmds等于0, 则命令类型为MARKEROBJ_TYPE
if (type & (CMDOBJ_TYPE | MARKEROBJ_TYPE)) {
// 调用kgsl_drawobj_cmd_create[见第3节]创建kgsl_drawobj_cmd
struct kgsl_drawobj_cmd *cmdobj =
kgsl_drawobj_cmd_create(device,
context, param->flags, type)
if (IS_ERR(cmdobj)) {
result = PTR_ERR(cmdobj)
goto done
}
// drawobj数组元素加1
drawobj[i++] = DRAWOBJ(cmdobj)
// 将cmdlist插入kgsl_drawobj_cmd的cmdlist链表[第4节]
result = kgsl_drawobj_cmd_add_cmdlist(device, cmdobj,
u64_to_user_ptr(param->cmdlist),
param->cmdsize, param->numcmds)
if (result)
goto done
// 将objlist插入kgsl_drawobj_cmd的memlist链表[第5节]
result = kgsl_drawobj_cmd_add_memlist(device, cmdobj,
u64_to_user_ptr(param->objlist),
param->objsize, param->numobjs)
if (result)
goto done
/* If no profiling buffer was specified, clear the flag */
if (cmdobj->profiling_buf_entry == NULL)
DRAWOBJ(cmdobj)->flags &=
~(unsigned long)KGSL_DRAWOBJ_PROFILING
}
// 调用adreno_functable中定义的adreno_queue_cmds[第6节]将kgsl_drawobj提交给ringbuffer
result = device->ftbl->queue_cmds(dev_priv, context, drawobj,
i, ¶m->timestamp)
done:
/*
* -EPROTO is a "success" error - it just tells the user that the
* context had previously faulted
*/
if (result && result != -EPROTO)
while (i--)
kgsl_drawobj_destroy(drawobj[i])
kgsl_context_put(context)
return result
}
1.1 kgsl_gpu_command
struct kgsl_gpu_command {
__u64 flags;
__u64 __user cmdlist;
unsigned int cmdsize;
unsigned int numcmds;
__u64 __user objlist;
unsigned int objsize;
unsigned int numobjs;
__u64 __user synclist;
unsigned int syncsize;
unsigned int numsyncs;
unsigned int context_id;
unsigned int timestamp;
};
1.2 kgsl_drawobj
struct kgsl_drawobj {
struct kgsl_device *device;
struct kgsl_context *context;
uint32_t type;
uint32_t timestamp;
unsigned long flags;
struct kref refcount;
void (*destroy)(struct kgsl_drawobj *drawobj);
void (*destroy_object)(struct kgsl_drawobj *drawobj);
};
1.3 kgsl_drawobj_cmd
struct kgsl_drawobj_cmd {
struct kgsl_drawobj base;
unsigned long priv;
unsigned int global_ts;
unsigned long fault_policy;
unsigned long fault_recovery;
struct list_head cmdlist;
struct list_head memlist;
unsigned int marker_timestamp;
struct kgsl_mem_entry *profiling_buf_entry;
uint64_t profiling_buffer_gpuaddr;
unsigned int profile_index;
uint64_t submit_ticks;
};
2. _process_command_input
#define KGSL_MAX_NUMIBS 100000
#define KGSL_MAX_SYNCPOINTS 32
#define CMDOBJ_TYPE BIT(0)
#define MARKEROBJ_TYPE BIT(1)
#define SYNCOBJ_TYPE BIT(2)
#define TIMELINEOBJ_TYPE BIT(4)
static unsigned int _process_command_input(struct kgsl_device *device,
unsigned int flags, unsigned int numcmds,
unsigned int numobjs, unsigned int numsyncs)
{
if (numcmds > KGSL_MAX_NUMIBS ||
numobjs > KGSL_MAX_NUMIBS ||
numsyncs > KGSL_MAX_SYNCPOINTS)
return 0;
if (flags & KGSL_DRAWOBJ_MARKER)
return MARKEROBJ_TYPE;
else if (flags & KGSL_DRAWOBJ_SYNC)
return SYNCOBJ_TYPE;
if (numsyncs && numcmds)
return SYNCOBJ_TYPE | CMDOBJ_TYPE;
else if (numsyncs)
return SYNCOBJ_TYPE;
else if (numcmds)
return CMDOBJ_TYPE;
else if (numcmds == 0)
return MARKEROBJ_TYPE;
return 0;
}
3. kgsl_drawobj_cmd_create
struct kgsl_drawobj_cmd *kgsl_drawobj_cmd_create(struct kgsl_device *device,
struct kgsl_context *context, unsigned int flags,
unsigned int type)
{
struct kgsl_drawobj_cmd *cmdobj = kzalloc(sizeof(*cmdobj), GFP_KERNEL);
int ret;
if (!cmdobj)
return ERR_PTR(-ENOMEM);
ret = drawobj_init(device, context, &cmdobj->base,
(type & (CMDOBJ_TYPE | MARKEROBJ_TYPE)));
if (ret) {
kfree(cmdobj);
return ERR_PTR(ret);
}
cmdobj->base.destroy = cmdobj_destroy;
cmdobj->base.destroy_object = cmdobj_destroy_object;
cmdobj->base.flags = flags & (KGSL_DRAWOBJ_CTX_SWITCH
| KGSL_DRAWOBJ_MARKER
| KGSL_DRAWOBJ_END_OF_FRAME
| KGSL_DRAWOBJ_PWR_CONSTRAINT
| KGSL_DRAWOBJ_MEMLIST
| KGSL_DRAWOBJ_PROFILING
| KGSL_DRAWOBJ_PROFILING_KTIME);
INIT_LIST_HEAD(&cmdobj->cmdlist);
INIT_LIST_HEAD(&cmdobj->memlist);
return cmdobj;
}
3.1 drawobj_init
static int drawobj_init(struct kgsl_device *device,
struct kgsl_context *context, struct kgsl_drawobj *drawobj,
int type)
{
if (!_kgsl_context_get(context))
return -ENOENT;
kref_init(&drawobj->refcount);
drawobj->device = device;
drawobj->context = context;
drawobj->type = type;
return 0;
}
4. kgsl_drawobj_cmd_add_cmdlist
int kgsl_drawobj_cmd_add_cmdlist(struct kgsl_device *device,
struct kgsl_drawobj_cmd *cmdobj, void __user *ptr,
unsigned int size, unsigned int count)
{
struct kgsl_command_object obj;
struct kgsl_drawobj *baseobj = DRAWOBJ(cmdobj);
int i, ret;
if (baseobj->type & MARKEROBJ_TYPE)
return 0;
ret = _verify_input_list(count, ptr, size);
if (ret <= 0)
return ret;
for (i = 0; i < count; i++) {
memset(&obj, 0, sizeof(obj));
ret = kgsl_copy_from_user(&obj, ptr, sizeof(obj), size);
if (ret)
return ret;
if (!(obj.flags & CMDLIST_FLAGS)) {
dev_err(device->dev,
"invalid cmdobj ctxt %d flags %d id %d offset %llu addr %llx size %llu\n",
baseobj->context->id, obj.flags, obj.id,
obj.offset, obj.gpuaddr, obj.size);
return -EINVAL;
}
ret = kgsl_drawobj_add_memobject(&cmdobj->cmdlist, &obj);
if (ret)
return ret;
ptr += sizeof(obj);
}
return 0;
}
4.1 kgsl_command_object
struct kgsl_command_object {
__u64 offset;
__u64 gpuaddr;
__u64 size;
unsigned int flags;
unsigned int id;
};
4.2 kgsl_drawobj_add_memobject
static int kgsl_drawobj_add_memobject(struct list_head *head,
struct kgsl_command_object *obj)
{
struct kgsl_memobj_node *mem
// 创建kgsl_memobj_node[见4.2.1节]
mem = kmem_cache_alloc(memobjs_cache, GFP_KERNEL)
if (mem == NULL)
return -ENOMEM
// 将kgsl_command_object的成员赋值给kgsl_memobj_node
mem->gpuaddr = obj->gpuaddr
mem->size = obj->size
mem->id = obj->id
mem->offset = obj->offset
mem->flags = obj->flags
// 初始化priv成员
mem->priv = 0
// 将kgsl_memobj_node插入到kgsl_drawobj_cmd链表cmdlist的表尾
list_add_tail(&mem->node, head)
return 0
}
4.2.1 kgsl_memobj_node
struct kgsl_memobj_node {
struct list_head node;
unsigned int id;
uint64_t offset;
uint64_t gpuaddr;
uint64_t size;
unsigned long flags;
unsigned long priv;
};
5. kgsl_drawobj_cmd_add_memlist
int kgsl_drawobj_cmd_add_memlist(struct kgsl_device *device,
struct kgsl_drawobj_cmd *cmdobj, void __user *ptr,
unsigned int size, unsigned int count)
{
struct kgsl_command_object obj;
struct kgsl_drawobj *baseobj = DRAWOBJ(cmdobj);
int i, ret;
if (baseobj->type & MARKEROBJ_TYPE)
return 0;
ret = _verify_input_list(count, ptr, size);
if (ret <= 0)
return ret;
for (i = 0; i < count; i++) {
memset(&obj, 0, sizeof(obj));
ret = kgsl_copy_from_user(&obj, ptr, sizeof(obj), size);
if (ret)
return ret;
if (!(obj.flags & KGSL_OBJLIST_MEMOBJ)) {
dev_err(device->dev,
"invalid memobj ctxt %d flags %d id %d offset %lld addr %lld size %lld\n",
DRAWOBJ(cmdobj)->context->id, obj.flags,
obj.id, obj.offset, obj.gpuaddr,
obj.size);
return -EINVAL;
}
if (obj.flags & KGSL_OBJLIST_PROFILE)
add_profiling_buffer(device, cmdobj, obj.gpuaddr,
obj.size, obj.id, obj.offset);
else {
ret = kgsl_drawobj_add_memobject(&cmdobj->memlist,
&obj);
if (ret)
return ret;
}
ptr += sizeof(obj);
}
return 0;
}
6. adreno_queue_cmds
static int adreno_queue_cmds(struct kgsl_device_private *dev_priv,
struct kgsl_context *context, struct kgsl_drawobj *drawobj[],
u32 count, u32 *timestamp)
{
struct kgsl_device *device = dev_priv->device;
if (test_bit(GMU_DISPATCH, &device->gmu_core.flags))
return adreno_hwsched_queue_cmds(dev_priv, context, drawobj,
count, timestamp);
return adreno_dispatcher_queue_cmds(dev_priv, context, drawobj, count,
timestamp);
}
7. adreno_dispatcher_queue_cmds
static unsigned int _context_drawqueue_size = 50;
int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
struct kgsl_context *context, struct kgsl_drawobj *drawobj[],
uint32_t count, uint32_t *timestamp)
{
struct kgsl_device *device = dev_priv->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_context *drawctxt = ADRENO_CONTEXT(context);
struct adreno_dispatcher_drawqueue *dispatch_q;
struct adreno_dispatch_job *job;
int ret;
unsigned int i, user_ts;
if (!count || count > _context_drawqueue_size - 1)
return -EINVAL;
ret = _check_context_state(&drawctxt->base);
if (ret)
return ret;
ret = _verify_cmdobj(dev_priv, context, drawobj, count);
if (ret)
return ret;
wait_for_completion(&device->halt_gate);
job = kmem_cache_alloc(jobs_cache, GFP_KERNEL);
if (!job)
return -ENOMEM;
job->drawctxt = drawctxt;
spin_lock(&drawctxt->lock);
ret = _check_context_state_to_queue_cmds(drawctxt, count);
if (ret) {
spin_unlock(&drawctxt->lock);
kmem_cache_free(jobs_cache, job);
return ret;
}
user_ts = *timestamp;
if (!(count == 1 && drawobj[0]->type == SYNCOBJ_TYPE) &&
(drawctxt->base.flags & KGSL_CONTEXT_USER_GENERATED_TS)) {
if (timestamp_cmp(drawctxt->timestamp, user_ts) >= 0) {
spin_unlock(&drawctxt->lock);
kmem_cache_free(jobs_cache, job);
return -ERANGE;
}
}
for (i = 0; i < count; i++) {
switch (drawobj[i]->type) {
case MARKEROBJ_TYPE:
ret = drawctxt_queue_markerobj(adreno_dev, drawctxt,
drawobj[i], timestamp, user_ts);
if (ret) {
spin_unlock(&drawctxt->lock);
kmem_cache_free(jobs_cache, job);
}
if (ret == 1)
goto done;
else if (ret)
return ret;
break;
case CMDOBJ_TYPE:
// drawctxt_queue_cmdobj[见7.1节]
ret = drawctxt_queue_cmdobj(adreno_dev, drawctxt,
drawobj[i], timestamp, user_ts);
if (ret) {
spin_unlock(&drawctxt->lock);
kmem_cache_free(jobs_cache, job);
return ret;
}
break;
case SYNCOBJ_TYPE:
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);
kmem_cache_free(jobs_cache, job);
return ret;
}
break;
default:
spin_unlock(&drawctxt->lock);
kmem_cache_free(jobs_cache, job);
return -EINVAL;
}
}
dispatch_q = ADRENO_DRAWOBJ_DISPATCH_DRAWQUEUE(drawobj[0]);
_track_context(adreno_dev, dispatch_q, drawctxt);
spin_unlock(&drawctxt->lock);
if (_kgsl_context_get(&drawctxt->base)) {
trace_dispatch_queue_context(drawctxt);
llist_add(&job->node,
&adreno_dev->dispatcher.jobs[drawctxt->base.priority]);
} else {
kmem_cache_free(jobs_cache, job);
goto done;
}
if (dispatch_q->inflight < _context_drawobj_burst)
adreno_dispatcher_issuecmds(adreno_dev);
done:
if (test_and_clear_bit(ADRENO_CONTEXT_FAULT, &context->priv))
return -EPROTO;
return 0;
}
7.1 drawctxt_queue_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_cmd *cmdobj = CMDOBJ(drawobj)
unsigned int j
int ret
ret = get_timestamp(drawctxt, drawobj, timestamp, user_ts)
if (ret)
return ret
/*
* If this is a real command then we need to force any markers
* queued before it to dispatch to keep time linear - set the
* skip bit so the commands get NOPed.
*/
j = drawctxt->drawqueue_head
while (j != drawctxt->drawqueue_tail) {
if (drawctxt->drawqueue[j]->type == MARKEROBJ_TYPE) {
struct kgsl_drawobj_cmd *markerobj =
CMDOBJ(drawctxt->drawqueue[j])
set_bit(CMDOBJ_SKIP, &markerobj->priv)
}
j = DRAWQUEUE_NEXT(j, ADRENO_CONTEXT_DRAWQUEUE_SIZE)
}
drawctxt->queued_timestamp = *timestamp
_set_ft_policy(adreno_dev, drawctxt, cmdobj)
_cmdobj_set_flags(drawctxt, cmdobj)
_queue_drawobj(drawctxt, drawobj)
return 0
}