// ioctl命令:IOCTL_KGSL_DRAWCTXT_CREATE// ioctl函数:kgsl_ioctl_drawctxt_createKGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_CREATE, kgsl_ioctl_drawctxt_create);longkgsl_ioctl_drawctxt_create(structkgsl_device_private*dev_priv,unsignedint cmd,void*data){int result =0;// ioctl命令参数:kgsl_drawctxt_create[见第1节]structkgsl_drawctxt_create*param = data;structkgsl_context*context =NULL;structkgsl_device*device = dev_priv->device;// 创建kgsl_context:调用在adreno_functable中定义的adreno_drawctxt_create[见第2节]
context = device->ftbl->drawctxt_create(dev_priv,¶m->flags);if(IS_ERR(context)){
result =PTR_ERR(context);goto done;}trace_kgsl_context_create(dev_priv->device, context, param->flags);/* Commit the pointer to the context in context_idr */write_lock(&device->context_lock);idr_replace(&device->context_idr, context, context->id);// 通过kgsl_drawctxt_create返回创建的kgsl_context的id
param->drawctxt_id = context->id;write_unlock(&device->context_lock);
done:return result;}
1. kgsl_drawctxt_create
/* create a draw context, which is used to preserve GPU state.
* The flags field may contain a mask KGSL_CONTEXT_* values
*/structkgsl_drawctxt_create{// 入参:标志位unsignedint flags;// 返回值:kgsl_context的idunsignedint drawctxt_id;/*output param */};// IOCTL_KGSL_DRAWCTXT_CREATE命令:参数为kgsl_drawctxt_create#defineIOCTL_KGSL_DRAWCTXT_CREATE\_IOWR(KGSL_IOC_TYPE,0x13,structkgsl_drawctxt_create)
2. adreno_drawctxt_create
/**
* adreno_drawctxt_create - create a new adreno draw context
* @dev_priv: the owner of the context
* @flags: flags for the context (passed from user space)
*
* Create and return a new draw context for the 3D core.
*/structkgsl_context*adreno_drawctxt_create(structkgsl_device_private*dev_priv,uint32_t*flags){structadreno_context*drawctxt;structkgsl_device*device = dev_priv->device;structadreno_device*adreno_dev =ADRENO_DEVICE(device);conststructadreno_gpudev*gpudev =ADRENO_GPU_DEVICE(adreno_dev);int ret;unsignedint local;
local =*flags &(KGSL_CONTEXT_PREAMBLE |
KGSL_CONTEXT_NO_GMEM_ALLOC |
KGSL_CONTEXT_PER_CONTEXT_TS |
KGSL_CONTEXT_USER_GENERATED_TS |
KGSL_CONTEXT_NO_FAULT_TOLERANCE |
KGSL_CONTEXT_INVALIDATE_ON_FAULT |
KGSL_CONTEXT_CTX_SWITCH |
KGSL_CONTEXT_PRIORITY_MASK |
KGSL_CONTEXT_TYPE_MASK |
KGSL_CONTEXT_PWR_CONSTRAINT |
KGSL_CONTEXT_IFH_NOP |
KGSL_CONTEXT_SECURE |
KGSL_CONTEXT_PREEMPT_STYLE_MASK |
KGSL_CONTEXT_NO_SNAPSHOT);/* Check for errors before trying to initialize *//* If preemption is not supported, ignore preemption request */// 判断是否支持抢占功能: 如果不支持则移除抢占相关的标志位if(!test_bit(ADRENO_DEVICE_PREEMPTION,&adreno_dev->priv))
local &=~KGSL_CONTEXT_PREEMPT_STYLE_MASK;/* We no longer support legacy context switching */if((local & KGSL_CONTEXT_PREAMBLE)==0||(local & KGSL_CONTEXT_NO_GMEM_ALLOC)==0){dev_err_once(device->dev,"legacy context switch not supported\n");returnERR_PTR(-EINVAL);}/* Make sure that our target can support secure contexts if requested */if(!kgsl_mmu_is_secured(&dev_priv->device->mmu)&&(local & KGSL_CONTEXT_SECURE)){dev_err_once(device->dev,"Secure context not supported\n");returnERR_PTR(-EOPNOTSUPP);}// 创建adreno_context对象[见2.1节]
drawctxt =kzalloc(sizeof(structadreno_context), GFP_KERNEL);if(drawctxt ==NULL)returnERR_PTR(-ENOMEM);
drawctxt->timestamp =0;// 将标志位传递给kgsl_context
drawctxt->base.flags = local;/* Always enable per-context timestamps */
drawctxt->base.flags |= KGSL_CONTEXT_PER_CONTEXT_TS;// kgsl_context类型:GL, CL, C2D, RS, VK
drawctxt->type =(drawctxt->base.flags & KGSL_CONTEXT_TYPE_MASK)>> KGSL_CONTEXT_TYPE_SHIFT;spin_lock_init(&drawctxt->lock);// 初始化工作队列init_waitqueue_head(&drawctxt->wq);init_waitqueue_head(&drawctxt->waiting);init_waitqueue_head(&drawctxt->timeout);/* If the priority is not set by user, set it for them */// 如果未定义优先级, 则默认设置为KGSL_CONTEXT_PRIORITY_MEDif((drawctxt->base.flags & KGSL_CONTEXT_PRIORITY_MASK)==
KGSL_CONTEXT_PRIORITY_UNDEF)
drawctxt->base.flags |=(KGSL_CONTEXT_PRIORITY_MED <<
KGSL_CONTEXT_PRIORITY_SHIFT);/* Store the context priority */
drawctxt->base.priority =(drawctxt->base.flags & KGSL_CONTEXT_PRIORITY_MASK)>>
KGSL_CONTEXT_PRIORITY_SHIFT;/*
* Now initialize the common part of the context. This allocates the
* context id, and then possibly another thread could look it up.
* So we want all of our initializtion that doesn't require the context
* id to be done before this call.
*/// 初始化kgsl_context[见2.2节], 初始化成功返回0
ret =kgsl_context_init(dev_priv,&drawctxt->base);if(ret !=0){kfree(drawctxt);returnERR_PTR(ret);}kgsl_sharedmem_writel(device->memstore,KGSL_MEMSTORE_OFFSET(drawctxt->base.id, soptimestamp),0);kgsl_sharedmem_writel(device->memstore,KGSL_MEMSTORE_OFFSET(drawctxt->base.id, eoptimestamp),0);// 在debugfs文件系统下为adreno_context创建节点[见2.3节]adreno_context_debugfs_init(ADRENO_DEVICE(device), drawctxt);INIT_LIST_HEAD(&drawctxt->active_node);// adreno_dispatcher_init中定义的setup_context方法:adreno_dispatcher_setup_context[见2.4节]if(adreno_dev->dispatch_ops && adreno_dev->dispatch_ops->setup_context)
adreno_dev->dispatch_ops->setup_context(adreno_dev, drawctxt);if(gpudev->preemption_context_init){
ret = gpudev->preemption_context_init(&drawctxt->base);if(ret !=0){kgsl_context_detach(&drawctxt->base);returnERR_PTR(ret);}}/* copy back whatever flags we dediced were valid */// 更新传入的标志位*flags = drawctxt->base.flags;// 返回kgsl_contextreturn&drawctxt->base;}
2.1 adreno_context
#defineADRENO_CONTEXT_DRAWQUEUE_SIZE128#defineSUBMIT_RETIRE_TICKS_SIZE7/**
* struct adreno_context - Adreno GPU draw context
*/structadreno_context{// kgsl_context[见2.1.1节]structkgsl_context base;// Last issued context-specific timestampunsignedint timestamp;// Global timestamp of the last issued commandunsignedint internal_timestamp;// Context type (GL, CL, RS)unsignedint type;spinlock_t lock;/* Dispatcher */// Queue of drawobjs waiting to be dispatched for this contextstructkgsl_drawobj*drawqueue[ADRENO_CONTEXT_DRAWQUEUE_SIZE];// Head of the drawqueue queueunsignedint drawqueue_head;// Tail of the drawqueue queueunsignedint drawqueue_tail;wait_queue_head_t wq;wait_queue_head_t waiting;wait_queue_head_t timeout;// Number of commands queued in the drawqueueint queued;unsignedint fault_policy;structdentry*debug_root;// The last timestamp that was queued on this contextunsignedint queued_timestamp;// The ringbuffer in which this context submits commands.structadreno_ringbuffer*rb;// The last timestamp that was submitted for this contextunsignedint submitted_timestamp;// Array to hold command obj execution times from submit to retireuint64_t submit_retire_ticks[SUBMIT_RETIRE_TICKS_SIZE];// The index into submit_retire_ticks[] where the new delta will be written. int ticks_index;// Linkage for nodes in active_liststructlist_head active_node;// Time when this context last seenunsignedlong active_time;};
2.1.1 kgsl_context
/**
* struct kgsl_context - The context fields that are valid for a user defined context
*/structkgsl_context{// kref object for reference counting the contextstructkref refcount;// integer identifier for the contextuint32_t id;// The context's priority to submit commands to GPUuint32_t priority;// task that created this context.pid_t tid;// pointer to the owning device instancestructkgsl_device_private*dev_priv;// pointer to process private, the process that allocated the context[见2.1.2节]structkgsl_process_private*proc_priv;unsignedlong priv;structkgsl_device*device;unsignedint reset_status;structkgsl_sync_timeline*ktimeline;// A kgsl_event_group for this context - contains the list of GPU events[见2.1.3节]structkgsl_event_group events;unsignedint flags;structkgsl_pwr_constraint pwr_constraint;structkgsl_pwr_constraint l3_pwr_constraint;unsignedint fault_count;ktime_t fault_time;// memory descriptor used by CP to save/restore VPC data across preemptionstructkgsl_mem_entry*user_ctxt_record;unsignedint total_fault_count;unsignedint last_faulted_cmd_ts;
bool gmu_registered;/**
* @gmu_dispatch_queue: dispatch queue id to which this context will be
* submitted
*/
u32 gmu_dispatch_queue;};
2.1.2 kgsl_process_private
/**
* struct kgsl_process_private - Private structure for a KGSL process (across
* all devices)
*/structkgsl_process_private{unsignedlong priv;structpid*pid;char comm[TASK_COMM_LEN];spinlock_t mem_lock;structkref refcount;structidr mem_idr;// 页表structkgsl_pagetable*pagetable;structlist_head list;structlist_head reclaim_list;structkobject kobj;structdentry*debug_root;struct{atomic64_t cur;uint64_t max;} stats[KGSL_MEM_ENTRY_MAX];atomic64_t gpumem_mapped;structidr syncsource_idr;spinlock_t syncsource_lock;int fd_count;// 进程的kgsl_context数量atomic_t ctxt_count;spinlock_t ctxt_count_lock;atomic64_t frame_count;/**
* @state: state consisting KGSL_PROC_STATE and KGSL_PROC_PINNED_STATE
*/unsignedlong state;/**
* @unpinned_page_count: The number of pages unpinned for reclaim
*/atomic_t unpinned_page_count;/**
* @fg_work: Work struct to schedule foreground work
*/structwork_struct fg_work;/**
* @reclaim_lock: Mutex lock to protect KGSL_PROC_PINNED_STATE
*/structmutex reclaim_lock;/**
* @cmd_count: The number of cmds that are active for the process
*/atomic_t cmd_count;/**
* @kobj_memtype: Pointer to a kobj for memtype sysfs directory for this
* process
*/structkobject kobj_memtype;};
2.1.3 kgsl_event_group
/**
* struct event_group - A list of GPU events
*/structkgsl_event_group{// Pointer to the active context for the eventsstructkgsl_context*context;spinlock_t lock;// List of active GPU eventsstructlist_head events;// Node for the master group liststructlist_head group;// Last processed timestampunsignedint processed;// String name for the group (for the debugfs file)char name[64];// Function pointer to read a timestamp
readtimestamp_func readtimestamp;// Priv member to pass to the readtimestamp functionvoid*priv;};
2.2 kgsl_context_init
#defineKGSL_MAX_CONTEXTS_PER_PROC200/**
* kgsl_context_init() - helper to initialize kgsl_context members
* @dev_priv: the owner of the context
* @context: the newly created context struct, should be allocated by
* the device specific drawctxt_create function.
*/intkgsl_context_init(structkgsl_device_private*dev_priv,structkgsl_context*context){structkgsl_device*device = dev_priv->device;int ret =0, id;// 创建kgsl_context的进程structkgsl_process_private*proc_priv = dev_priv->process_priv;/*
* Read and increment the context count under lock to make sure
* no process goes beyond the specified context limit.
*/spin_lock(&proc_priv->ctxt_count_lock);// 每个进程持有的kgsl_context数量不能超过200if(atomic_read(&proc_priv->ctxt_count)> KGSL_MAX_CONTEXTS_PER_PROC){dev_err(device->dev,"Per process context limit reached for pid %u\n",pid_nr(dev_priv->process_priv->pid));spin_unlock(&proc_priv->ctxt_count_lock);kgsl_context_debug_info(device);return-ENOSPC;}// 进程的kgsl_context数量加1atomic_inc(&proc_priv->ctxt_count);spin_unlock(&proc_priv->ctxt_count_lock);// 分配新的context id
id =_kgsl_get_context_id(device);if(id ==-ENOSPC){/*
* Before declaring that there are no contexts left try
* flushing the event workqueue just in case there are
* detached contexts waiting to finish
*/flush_workqueue(device->events_wq);
id =_kgsl_get_context_id(device);}if(id <0){if(id ==-ENOSPC){dev_warn(device->dev,"cannot have more than %zu contexts due to memstore limitation\n",
KGSL_MEMSTORE_MAX);kgsl_context_debug_info(device);}atomic_dec(&proc_priv->ctxt_count);return id;}// 设置kgsl_context的id
context->id = id;// kgsl_context的引用计数加1kref_init(&context->refcount);/*
* Get a refernce to the process private so its not destroyed, until
* the context is destroyed. This will also prevent the pagetable
* from being destroyed
*/if(!kgsl_process_private_get(dev_priv->process_priv)){
ret =-EBADF;goto out;}// 设置kgsl_context的kgsl_device
context->device = dev_priv->device;// 设置kgsl_context的kgsl_device_private
context->dev_priv = dev_priv;// 设置kgsl_context的kgsl_process_private
context->proc_priv = dev_priv->process_priv;// 创建kgsl_context的进程pid
context->tid =task_pid_nr(current);
ret =kgsl_sync_timeline_create(context);if(ret){kgsl_process_private_put(dev_priv->process_priv);goto out;}// 初始化kgsl_context的kgsl_event_group[见2.2.1节]kgsl_add_event_group(device,&context->events, context,
kgsl_readtimestamp, context,"context-%d", id);
out:if(ret){atomic_dec(&proc_priv->ctxt_count);write_lock(&device->context_lock);idr_remove(&dev_priv->device->context_idr, id);write_unlock(&device->context_lock);}return ret;}
voidadreno_context_debugfs_init(structadreno_device*adreno_dev,structadreno_context*ctx){unsignedchar name[16];/*
* Get the context here to make sure it still exists for the life of the
* file
*/_kgsl_context_get(&ctx->base);// 节点名称为kgsl_context的idsnprintf(name,sizeof(name),"%d", ctx->base.id);// 在debugfs节点下创建"context-id":文件操作函数为ctx_fops
ctx->debug_root =debugfs_create_file(name,0444,
adreno_dev->ctx_d_debugfs, ctx,&ctx_fops);}
/* Return the ringbuffer that matches the draw context priority */staticstructadreno_ringbuffer*dispatch_get_rb(structadreno_device*adreno_dev,structadreno_context*drawctxt){int level;/* If preemption is disabled everybody goes on the same ringbuffer */// 不支持抢占, 则直接返回adreno_device的ringbuffers数组的第一个元素if(!adreno_is_preemption_enabled(adreno_dev))return&adreno_dev->ringbuffers[0];/*
* Math to convert the priority field in context structure to an RB ID.
* Divide up the context priority based on number of ringbuffer levels.
*/// 否则根据kgsl_context的优先级来选择对应的adreno_ringbuffer
level =min_t(int, drawctxt->base.priority / adreno_dev->num_ringbuffers,
adreno_dev->num_ringbuffers -1);return&adreno_dev->ringbuffers[level];}