123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- /*
- * KGSL drawobj management
- * A drawobj is a single submission from userland. The drawobj
- * encapsulates everything about the submission : command buffers, flags and
- * sync points.
- *
- * Sync points are events that need to expire before the
- * drawobj can be queued to the hardware. All synpoints are contained in an
- * array of kgsl_drawobj_sync_event structs in the drawobj. There can be
- * multiple types of events both internal ones (GPU events) and external
- * triggers. As the events expire bits are cleared in a pending bitmap stored
- * in the drawobj. The GPU will submit the command as soon as the bitmap
- * goes to zero indicating no more pending events.
- */
- #include <linux/slab.h>
- #include <linux/dma-fence-array.h>
- #include "adreno_drawctxt.h"
- #include "kgsl_compat.h"
- #include "kgsl_device.h"
- #include "kgsl_drawobj.h"
- #include "kgsl_eventlog.h"
- #include "kgsl_sync.h"
- #include "kgsl_timeline.h"
- #include "kgsl_trace.h"
- /*
- * Define an kmem cache for the memobj structures since we
- * allocate and free them so frequently
- */
- static struct kmem_cache *memobjs_cache;
- static void syncobj_destroy_object(struct kgsl_drawobj *drawobj)
- {
- struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);
- int i;
- for (i = 0; i < syncobj->numsyncs; i++) {
- struct kgsl_drawobj_sync_event *event = &syncobj->synclist[i];
- if (event->type == KGSL_CMD_SYNCPOINT_TYPE_FENCE) {
- struct event_fence_info *priv = event->priv;
- if (priv) {
- kfree(priv->fences);
- kfree(priv);
- }
- if (event->handle) {
- struct kgsl_sync_fence_cb *kcb = event->handle;
- dma_fence_put(kcb->fence);
- kfree(kcb);
- }
- } else if (event->type == KGSL_CMD_SYNCPOINT_TYPE_TIMELINE) {
- kfree(event->priv);
- }
- }
- kfree(syncobj->synclist);
- kfree(syncobj);
- }
- static void cmdobj_destroy_object(struct kgsl_drawobj *drawobj)
- {
- kfree(CMDOBJ(drawobj));
- }
- static void bindobj_destroy_object(struct kgsl_drawobj *drawobj)
- {
- kfree(BINDOBJ(drawobj));
- }
- static void timelineobj_destroy_object(struct kgsl_drawobj *drawobj)
- {
- kfree(TIMELINEOBJ(drawobj));
- }
- void kgsl_drawobj_destroy_object(struct kref *kref)
- {
- struct kgsl_drawobj *drawobj = container_of(kref,
- struct kgsl_drawobj, refcount);
- kgsl_context_put(drawobj->context);
- drawobj->destroy_object(drawobj);
- }
- void kgsl_dump_syncpoints(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj)
- {
- struct kgsl_drawobj_sync_event *event;
- unsigned int i;
- for (i = 0; i < syncobj->numsyncs; i++) {
- event = &syncobj->synclist[i];
- if (!kgsl_drawobj_event_pending(syncobj, i))
- continue;
- switch (event->type) {
- case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP: {
- unsigned int retired;
- kgsl_readtimestamp(event->device,
- event->context, KGSL_TIMESTAMP_RETIRED,
- &retired);
- dev_err(device->dev,
- " [timestamp] context %u timestamp %u (retired %u)\n",
- event->context->id, event->timestamp,
- retired);
- break;
- }
- case KGSL_CMD_SYNCPOINT_TYPE_FENCE: {
- int j;
- struct event_fence_info *info = event->priv;
- for (j = 0; info && j < info->num_fences; j++)
- dev_err(device->dev, "[%d] fence: %s\n",
- i, info->fences[j].name);
- break;
- }
- case KGSL_CMD_SYNCPOINT_TYPE_TIMELINE: {
- int j;
- struct event_timeline_info *info = event->priv;
- for (j = 0; info && info[j].timeline; j++)
- dev_err(device->dev, "[%d] timeline: %d seqno %lld\n",
- i, info[j].timeline, info[j].seqno);
- break;
- }
- }
- }
- }
- static void syncobj_timer(struct timer_list *t)
- {
- struct kgsl_device *device;
- struct kgsl_drawobj_sync *syncobj = from_timer(syncobj, t, timer);
- struct kgsl_drawobj *drawobj;
- struct kgsl_drawobj_sync_event *event;
- unsigned int i;
- if (syncobj == NULL)
- return;
- drawobj = DRAWOBJ(syncobj);
- if (!kref_get_unless_zero(&drawobj->refcount))
- return;
- if (drawobj->context == NULL) {
- kgsl_drawobj_put(drawobj);
- return;
- }
- device = drawobj->context->device;
- dev_err(device->dev,
- "kgsl: possible gpu syncpoint deadlock for context %u timestamp %u\n",
- drawobj->context->id, drawobj->timestamp);
- set_bit(ADRENO_CONTEXT_FENCE_LOG, &drawobj->context->priv);
- kgsl_context_dump(drawobj->context);
- clear_bit(ADRENO_CONTEXT_FENCE_LOG, &drawobj->context->priv);
- dev_err(device->dev, " pending events:\n");
- for (i = 0; i < syncobj->numsyncs; i++) {
- event = &syncobj->synclist[i];
- if (!kgsl_drawobj_event_pending(syncobj, i))
- continue;
- switch (event->type) {
- case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP:
- dev_err(device->dev, " [%u] TIMESTAMP %u:%u\n",
- i, event->context->id, event->timestamp);
- break;
- case KGSL_CMD_SYNCPOINT_TYPE_FENCE: {
- int j;
- struct event_fence_info *info = event->priv;
- for (j = 0; info && j < info->num_fences; j++)
- dev_err(device->dev, " [%u] FENCE %s\n",
- i, info->fences[j].name);
- break;
- }
- case KGSL_CMD_SYNCPOINT_TYPE_TIMELINE: {
- int j;
- struct event_timeline_info *info = event->priv;
- struct dma_fence *fence = event->fence;
- bool retired = false;
- bool signaled = test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
- &fence->flags);
- const char *str = NULL;
- if (fence->ops->signaled && fence->ops->signaled(fence))
- retired = true;
- if (!retired)
- str = "not retired";
- else if (retired && signaled)
- str = "signaled";
- else if (retired && !signaled)
- str = "retired but not signaled";
- dev_err(device->dev, " [%u] FENCE %s\n",
- i, str);
- for (j = 0; info && info[j].timeline; j++)
- dev_err(device->dev, " TIMELINE %d SEQNO %lld\n",
- info[j].timeline, info[j].seqno);
- break;
- }
- }
- }
- kgsl_drawobj_put(drawobj);
- dev_err(device->dev, "--gpu syncpoint deadlock print end--\n");
- }
- /*
- * a generic function to retire a pending sync event and (possibly) kick the
- * dispatcher.
- * Returns false if the event was already marked for cancellation in another
- * thread. This function should return true if this thread is responsible for
- * freeing up the memory, and the event will not be cancelled.
- */
- static bool drawobj_sync_expire(struct kgsl_device *device,
- struct kgsl_drawobj_sync_event *event)
- {
- struct kgsl_drawobj_sync *syncobj = event->syncobj;
- /*
- * Clear the event from the pending mask - if it is already clear, then
- * leave without doing anything useful
- */
- if (!test_and_clear_bit(event->id, &syncobj->pending))
- return false;
- /*
- * If no more pending events, delete the timer and schedule the command
- * for dispatch
- */
- if (!kgsl_drawobj_events_pending(event->syncobj)) {
- del_timer(&syncobj->timer);
- if (device->ftbl->drawctxt_sched)
- device->ftbl->drawctxt_sched(device,
- event->syncobj->base.context);
- }
- return true;
- }
- /*
- * This function is called by the GPU event when the sync event timestamp
- * expires
- */
- static void drawobj_sync_func(struct kgsl_device *device,
- struct kgsl_event_group *group, void *priv, int result)
- {
- struct kgsl_drawobj_sync_event *event = priv;
- trace_syncpoint_timestamp_expire(event->syncobj,
- event->context, event->timestamp);
- /*
- * Put down the context ref count only if
- * this thread successfully clears the pending bit mask.
- */
- if (drawobj_sync_expire(device, event))
- kgsl_context_put(event->context);
- kgsl_drawobj_put(&event->syncobj->base);
- }
- static void drawobj_sync_timeline_fence_work(struct work_struct *work)
- {
- struct kgsl_drawobj_sync_event *event = container_of(work,
- struct kgsl_drawobj_sync_event, work);
- dma_fence_put(event->fence);
- kgsl_drawobj_put(&event->syncobj->base);
- }
- static void trace_syncpoint_timeline_fence(struct kgsl_drawobj_sync *syncobj,
- struct dma_fence *f, bool expire)
- {
- struct dma_fence_array *array = to_dma_fence_array(f);
- struct dma_fence **fences = &f;
- u32 num_fences = 1;
- int i;
- if (array) {
- num_fences = array->num_fences;
- fences = array->fences;
- }
- for (i = 0; i < num_fences; i++) {
- char fence_name[KGSL_FENCE_NAME_LEN];
- snprintf(fence_name, sizeof(fence_name), "%s:%llu",
- fences[i]->ops->get_timeline_name(fences[i]),
- fences[i]->seqno);
- if (expire) {
- trace_syncpoint_fence_expire(syncobj, fence_name);
- log_kgsl_syncpoint_fence_expire_event(
- syncobj->base.context->id, fence_name);
- } else {
- trace_syncpoint_fence(syncobj, fence_name);
- log_kgsl_syncpoint_fence_event(
- syncobj->base.context->id, fence_name);
- }
- }
- }
- static void drawobj_sync_timeline_fence_callback(struct dma_fence *f,
- struct dma_fence_cb *cb)
- {
- struct kgsl_drawobj_sync_event *event = container_of(cb,
- struct kgsl_drawobj_sync_event, cb);
- trace_syncpoint_timeline_fence(event->syncobj, f, true);
- /*
- * Mark the event as synced and then fire off a worker to handle
- * removing the fence
- */
- if (drawobj_sync_expire(event->device, event))
- queue_work(kgsl_driver.lockless_workqueue, &event->work);
- }
- static void syncobj_destroy(struct kgsl_drawobj *drawobj)
- {
- struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);
- unsigned int i;
- /* Zap the canary timer */
- del_timer_sync(&syncobj->timer);
- /*
- * Clear all pending events - this will render any subsequent async
- * callbacks harmless
- */
- for (i = 0; i < syncobj->numsyncs; i++) {
- struct kgsl_drawobj_sync_event *event = &syncobj->synclist[i];
- /*
- * Don't do anything if the event has already expired.
- * If this thread clears the pending bit mask then it is
- * responsible for doing context put.
- */
- if (!test_and_clear_bit(i, &syncobj->pending))
- continue;
- switch (event->type) {
- case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP:
- kgsl_cancel_event(drawobj->device,
- &event->context->events, event->timestamp,
- drawobj_sync_func, event);
- /*
- * Do context put here to make sure the context is alive
- * till this thread cancels kgsl event.
- */
- kgsl_context_put(event->context);
- break;
- case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
- kgsl_sync_fence_async_cancel(event->handle);
- kgsl_drawobj_put(drawobj);
- break;
- case KGSL_CMD_SYNCPOINT_TYPE_TIMELINE:
- dma_fence_remove_callback(event->fence, &event->cb);
- dma_fence_put(event->fence);
- kgsl_drawobj_put(drawobj);
- break;
- }
- }
- /*
- * If we cancelled an event, there's a good chance that the context is
- * on a dispatcher queue, so schedule to get it removed.
- */
- if (!bitmap_empty(&syncobj->pending, KGSL_MAX_SYNCPOINTS) &&
- drawobj->device->ftbl->drawctxt_sched)
- drawobj->device->ftbl->drawctxt_sched(drawobj->device,
- drawobj->context);
- }
- static void _drawobj_timelineobj_retire(struct kref *kref)
- {
- int i;
- struct kgsl_drawobj_timeline *timelineobj = container_of(kref,
- struct kgsl_drawobj_timeline, sig_refcount);
- for (i = 0; i < timelineobj->count; i++) {
- kgsl_timeline_signal(timelineobj->timelines[i].timeline,
- timelineobj->timelines[i].seqno);
- kgsl_timeline_put(timelineobj->timelines[i].timeline);
- kgsl_context_put(timelineobj->timelines[i].context);
- }
- kvfree(timelineobj->timelines);
- timelineobj->timelines = NULL;
- timelineobj->count = 0;
- }
- static void kgsl_timelineobj_signal(struct kgsl_drawobj_timeline *timelineobj)
- {
- kref_put(&timelineobj->sig_refcount, _drawobj_timelineobj_retire);
- }
- static void timelineobj_destroy(struct kgsl_drawobj *drawobj)
- {
- struct kgsl_drawobj_timeline *timelineobj = TIMELINEOBJ(drawobj);
- int i;
- /*
- * At this point any syncobjs blocking this timelinobj have been
- * signaled. The timelineobj now only needs all preceding timestamps to
- * retire before signaling the timelines. Notify timelines to keep them
- * in sync with the timestamps as they retire.
- */
- for (i = 0; i < timelineobj->count; i++)
- kgsl_timeline_add_signal(&timelineobj->timelines[i]);
- /*
- * The scheduler is done with the timelineobj. Put the initial
- * sig_refcount to continue with the signaling process.
- */
- kgsl_timelineobj_signal(timelineobj);
- }
- static void bindobj_destroy(struct kgsl_drawobj *drawobj)
- {
- struct kgsl_drawobj_bind *bindobj = BINDOBJ(drawobj);
- kgsl_sharedmem_put_bind_op(bindobj->bind);
- }
- static void cmdobj_destroy(struct kgsl_drawobj *drawobj)
- {
- struct kgsl_drawobj_cmd *cmdobj = CMDOBJ(drawobj);
- struct kgsl_memobj_node *mem, *tmpmem;
- /*
- * Release the refcount on the mem entry associated with the
- * ib profiling buffer
- */
- if (cmdobj->base.flags & KGSL_DRAWOBJ_PROFILING)
- kgsl_mem_entry_put(cmdobj->profiling_buf_entry);
- /* Destroy the command list */
- list_for_each_entry_safe(mem, tmpmem, &cmdobj->cmdlist, node) {
- list_del_init(&mem->node);
- kmem_cache_free(memobjs_cache, mem);
- }
- /* Destroy the memory list */
- list_for_each_entry_safe(mem, tmpmem, &cmdobj->memlist, node) {
- list_del_init(&mem->node);
- kmem_cache_free(memobjs_cache, mem);
- }
- if (drawobj->type & CMDOBJ_TYPE) {
- atomic_dec(&drawobj->context->proc_priv->cmd_count);
- atomic_dec(&drawobj->context->proc_priv->period->active_cmds);
- }
- }
- /**
- * kgsl_drawobj_destroy() - Destroy a kgsl object structure
- * @obj: Pointer to the kgsl object to destroy
- *
- * Start the process of destroying a command batch. Cancel any pending events
- * and decrement the refcount. Asynchronous events can still signal after
- * kgsl_drawobj_destroy has returned.
- */
- void kgsl_drawobj_destroy(struct kgsl_drawobj *drawobj)
- {
- if (IS_ERR_OR_NULL(drawobj))
- return;
- drawobj->destroy(drawobj);
- kgsl_drawobj_put(drawobj);
- }
- static bool drawobj_sync_fence_func(void *priv)
- {
- struct kgsl_drawobj_sync_event *event = priv;
- struct event_fence_info *info = event->priv;
- int i;
- for (i = 0; info && i < info->num_fences; i++) {
- trace_syncpoint_fence_expire(event->syncobj,
- info->fences[i].name);
- log_kgsl_syncpoint_fence_expire_event(
- event->syncobj->base.context->id, info->fences[i].name);
- }
- /*
- * Only call kgsl_drawobj_put() if it's not marked for cancellation
- * in another thread.
- */
- if (drawobj_sync_expire(event->device, event)) {
- kgsl_drawobj_put(&event->syncobj->base);
- return true;
- }
- return false;
- }
- static struct event_timeline_info *
- drawobj_get_sync_timeline_priv(void __user *uptr, u64 usize, u32 count)
- {
- int i;
- struct event_timeline_info *priv;
- /* Make sure we don't accidently overflow count */
- if (count == UINT_MAX)
- return NULL;
- priv = kcalloc(count + 1, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return NULL;
- for (i = 0; i < count; i++, uptr += usize) {
- struct kgsl_timeline_val val;
- if (copy_struct_from_user(&val, sizeof(val), uptr, usize))
- continue;
- priv[i].timeline = val.timeline;
- priv[i].seqno = val.seqno;
- }
- priv[i].timeline = 0;
- return priv;
- }
- static int drawobj_add_sync_timeline(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *uptr,
- u64 usize)
- {
- struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
- struct kgsl_cmd_syncpoint_timeline sync;
- struct kgsl_drawobj_sync_event *event;
- struct dma_fence *fence;
- unsigned int id;
- int ret;
- if (copy_struct_from_user(&sync, sizeof(sync), uptr, usize))
- return -EFAULT;
- fence = kgsl_timelines_to_fence_array(device, sync.timelines,
- sync.count, sync.timelines_size, false);
- if (IS_ERR(fence))
- return PTR_ERR(fence);
- kref_get(&drawobj->refcount);
- id = syncobj->numsyncs++;
- event = &syncobj->synclist[id];
- event->id = id;
- event->type = KGSL_CMD_SYNCPOINT_TYPE_TIMELINE;
- event->syncobj = syncobj;
- event->device = device;
- event->context = NULL;
- event->fence = fence;
- INIT_WORK(&event->work, drawobj_sync_timeline_fence_work);
- INIT_LIST_HEAD(&event->cb.node);
- event->priv =
- drawobj_get_sync_timeline_priv(u64_to_user_ptr(sync.timelines),
- sync.timelines_size, sync.count);
- /* Set pending flag before adding callback to avoid race */
- set_bit(event->id, &syncobj->pending);
- /* Get a dma_fence refcount to hand over to the callback */
- dma_fence_get(event->fence);
- ret = dma_fence_add_callback(event->fence,
- &event->cb, drawobj_sync_timeline_fence_callback);
- if (ret) {
- clear_bit(event->id, &syncobj->pending);
- if (dma_fence_is_signaled(event->fence)) {
- trace_syncpoint_fence_expire(syncobj, "signaled");
- log_kgsl_syncpoint_fence_expire_event(
- syncobj->base.context->id, "signaled");
- dma_fence_put(event->fence);
- ret = 0;
- }
- /* Put the refcount from fence creation */
- dma_fence_put(event->fence);
- kgsl_drawobj_put(drawobj);
- return ret;
- }
- trace_syncpoint_timeline_fence(event->syncobj, event->fence, false);
- /* Put the refcount from fence creation */
- dma_fence_put(event->fence);
- return 0;
- }
- static int drawobj_add_sync_fence(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *data,
- u64 datasize)
- {
- struct kgsl_cmd_syncpoint_fence sync;
- struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
- struct kgsl_drawobj_sync_event *event;
- struct event_fence_info *priv;
- unsigned int id, i;
- if (copy_struct_from_user(&sync, sizeof(sync), data, datasize))
- return -EFAULT;
- kref_get(&drawobj->refcount);
- id = syncobj->numsyncs++;
- event = &syncobj->synclist[id];
- event->id = id;
- event->type = KGSL_CMD_SYNCPOINT_TYPE_FENCE;
- event->syncobj = syncobj;
- event->device = device;
- event->context = NULL;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- set_bit(event->id, &syncobj->pending);
- event->handle = kgsl_sync_fence_async_wait(sync.fd, drawobj_sync_fence_func, event);
- event->priv = priv;
- if (IS_ERR_OR_NULL(event->handle)) {
- int ret = PTR_ERR(event->handle);
- clear_bit(event->id, &syncobj->pending);
- event->handle = NULL;
- kgsl_drawobj_put(drawobj);
- /*
- * If ret == 0 the fence was already signaled - print a trace
- * message so we can track that
- */
- if (ret == 0) {
- trace_syncpoint_fence_expire(syncobj, "signaled");
- log_kgsl_syncpoint_fence_expire_event(
- syncobj->base.context->id, "signaled");
- }
- return ret;
- }
- kgsl_get_fence_info(event);
- for (i = 0; priv && i < priv->num_fences; i++) {
- trace_syncpoint_fence(syncobj, priv->fences[i].name);
- log_kgsl_syncpoint_fence_event(syncobj->base.context->id,
- priv->fences[i].name);
- }
- return 0;
- }
- /* drawobj_add_sync_timestamp() - Add a new sync point for a sync obj
- * @device: KGSL device
- * @syncobj: KGSL sync obj to add the sync point to
- * @priv: Private structure passed by the user
- *
- * Add a new sync point timestamp event to the sync obj.
- */
- static int drawobj_add_sync_timestamp(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj,
- struct kgsl_cmd_syncpoint_timestamp *timestamp)
- {
- struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
- struct kgsl_context *context = kgsl_context_get(device,
- timestamp->context_id);
- struct kgsl_drawobj_sync_event *event;
- int ret = -EINVAL;
- unsigned int id;
- if (context == NULL)
- return -EINVAL;
- /*
- * We allow somebody to create a sync point on their own context.
- * This has the effect of delaying a command from submitting until the
- * dependent command has cleared. That said we obviously can't let them
- * create a sync point on a future timestamp.
- */
- if (context == drawobj->context) {
- unsigned int queued;
- kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED,
- &queued);
- if (timestamp_cmp(timestamp->timestamp, queued) > 0) {
- dev_err(device->dev,
- "Cannot create syncpoint for future timestamp %d (current %d)\n",
- timestamp->timestamp, queued);
- goto done;
- }
- }
- kref_get(&drawobj->refcount);
- id = syncobj->numsyncs++;
- event = &syncobj->synclist[id];
- event->id = id;
- event->type = KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP;
- event->syncobj = syncobj;
- event->context = context;
- event->timestamp = timestamp->timestamp;
- event->device = device;
- set_bit(event->id, &syncobj->pending);
- ret = kgsl_add_event(device, &context->events, timestamp->timestamp,
- drawobj_sync_func, event);
- if (ret) {
- clear_bit(event->id, &syncobj->pending);
- kgsl_drawobj_put(drawobj);
- } else {
- trace_syncpoint_timestamp(syncobj, context,
- timestamp->timestamp);
- }
- done:
- if (ret)
- kgsl_context_put(context);
- return ret;
- }
- static int drawobj_add_sync_timestamp_from_user(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *data,
- u64 datasize)
- {
- struct kgsl_cmd_syncpoint_timestamp timestamp;
- if (copy_struct_from_user(×tamp, sizeof(timestamp),
- data, datasize))
- return -EFAULT;
- return drawobj_add_sync_timestamp(device, syncobj, ×tamp);
- }
- /**
- * kgsl_drawobj_sync_add_sync() - Add a sync point to a command
- * batch
- * @device: Pointer to the KGSL device struct for the GPU
- * @syncobj: Pointer to the sync obj
- * @sync: Pointer to the user-specified struct defining the syncpoint
- *
- * Create a new sync point in the sync obj based on the
- * user specified parameters
- */
- int kgsl_drawobj_sync_add_sync(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj,
- struct kgsl_cmd_syncpoint *sync)
- {
- struct kgsl_drawobj *drawobj = DRAWOBJ(syncobj);
- if (sync->type != KGSL_CMD_SYNCPOINT_TYPE_FENCE)
- syncobj->flags |= KGSL_SYNCOBJ_SW;
- if (sync->type == KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP)
- return drawobj_add_sync_timestamp_from_user(device,
- syncobj, sync->priv, sync->size);
- else if (sync->type == KGSL_CMD_SYNCPOINT_TYPE_FENCE)
- return drawobj_add_sync_fence(device,
- syncobj, sync->priv, sync->size);
- else if (sync->type == KGSL_CMD_SYNCPOINT_TYPE_TIMELINE)
- return drawobj_add_sync_timeline(device,
- syncobj, sync->priv, sync->size);
- dev_err(device->dev, "bad syncpoint type %d for ctxt %u\n",
- sync->type, drawobj->context->id);
- return -EINVAL;
- }
- static void add_profiling_buffer(struct kgsl_device *device,
- struct kgsl_drawobj_cmd *cmdobj,
- uint64_t gpuaddr, uint64_t size,
- unsigned int id, uint64_t offset)
- {
- struct kgsl_mem_entry *entry;
- struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
- u64 start;
- if (!(drawobj->flags & KGSL_DRAWOBJ_PROFILING))
- return;
- /* Only the first buffer entry counts - ignore the rest */
- if (cmdobj->profiling_buf_entry != NULL)
- return;
- if (id != 0)
- entry = kgsl_sharedmem_find_id(drawobj->context->proc_priv,
- id);
- else
- entry = kgsl_sharedmem_find(drawobj->context->proc_priv,
- gpuaddr);
- if (entry != NULL) {
- start = id ? (entry->memdesc.gpuaddr + offset) : gpuaddr;
- /*
- * Make sure there is enough room in the object to store the
- * entire profiling buffer object
- */
- if (!kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size) ||
- !kgsl_gpuaddr_in_memdesc(&entry->memdesc, start,
- sizeof(struct kgsl_drawobj_profiling_buffer))) {
- kgsl_mem_entry_put(entry);
- entry = NULL;
- }
- }
- if (entry == NULL) {
- dev_err(device->dev,
- "ignore bad profile buffer ctxt %u id %d offset %lld gpuaddr %llx size %lld\n",
- drawobj->context->id, id, offset, gpuaddr, size);
- return;
- }
- cmdobj->profiling_buffer_gpuaddr = start;
- cmdobj->profiling_buf_entry = entry;
- }
- /**
- * kgsl_drawobj_cmd_add_ibdesc() - Add a legacy ibdesc to a command
- * batch
- * @cmdobj: Pointer to the ib
- * @ibdesc: Pointer to the user-specified struct defining the memory or IB
- *
- * Create a new memory entry in the ib based on the
- * user specified parameters
- */
- int kgsl_drawobj_cmd_add_ibdesc(struct kgsl_device *device,
- struct kgsl_drawobj_cmd *cmdobj, struct kgsl_ibdesc *ibdesc)
- {
- uint64_t gpuaddr = (uint64_t) ibdesc->gpuaddr;
- uint64_t size = (uint64_t) ibdesc->sizedwords << 2;
- struct kgsl_memobj_node *mem;
- struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
- /* sanitize the ibdesc ctrl flags */
- ibdesc->ctrl &= KGSL_IBDESC_MEMLIST | KGSL_IBDESC_PROFILING_BUFFER;
- if (drawobj->flags & KGSL_DRAWOBJ_MEMLIST &&
- ibdesc->ctrl & KGSL_IBDESC_MEMLIST) {
- if (ibdesc->ctrl & KGSL_IBDESC_PROFILING_BUFFER) {
- add_profiling_buffer(device, cmdobj,
- gpuaddr, size, 0, 0);
- return 0;
- }
- }
- /* Ignore if SYNC or MARKER is specified */
- if (drawobj->type & (SYNCOBJ_TYPE | MARKEROBJ_TYPE))
- return 0;
- mem = kmem_cache_alloc(memobjs_cache, GFP_KERNEL);
- if (mem == NULL)
- return -ENOMEM;
- mem->gpuaddr = gpuaddr;
- mem->size = size;
- mem->priv = 0;
- mem->id = 0;
- mem->offset = 0;
- mem->flags = 0;
- if (drawobj->flags & KGSL_DRAWOBJ_MEMLIST &&
- ibdesc->ctrl & KGSL_IBDESC_MEMLIST)
- /* add to the memlist */
- list_add_tail(&mem->node, &cmdobj->memlist);
- else {
- /* set the preamble flag if directed to */
- if (drawobj->context->flags & KGSL_CONTEXT_PREAMBLE &&
- list_empty(&cmdobj->cmdlist))
- mem->flags = KGSL_CMDLIST_CTXTSWITCH_PREAMBLE;
- /* add to the cmd list */
- list_add_tail(&mem->node, &cmdobj->cmdlist);
- }
- return 0;
- }
- static int drawobj_init(struct kgsl_device *device,
- struct kgsl_context *context, struct kgsl_drawobj *drawobj,
- int type)
- {
- /*
- * Increase the reference count on the context so it doesn't disappear
- * during the lifetime of this object
- */
- if (!_kgsl_context_get(context))
- return -ENOENT;
- kref_init(&drawobj->refcount);
- drawobj->device = device;
- drawobj->context = context;
- drawobj->type = type;
- return 0;
- }
- static int get_aux_command(void __user *ptr, u64 generic_size,
- int type, void *auxcmd, size_t auxcmd_size)
- {
- struct kgsl_gpu_aux_command_generic generic;
- u64 size;
- if (copy_struct_from_user(&generic, sizeof(generic), ptr, generic_size))
- return -EFAULT;
- if (generic.type != type)
- return -EINVAL;
- size = min_t(u64, auxcmd_size, generic.size);
- if (copy_from_user(auxcmd, u64_to_user_ptr(generic.priv), size))
- return -EFAULT;
- return 0;
- }
- struct kgsl_drawobj_timeline *
- kgsl_drawobj_timeline_create(struct kgsl_device *device,
- struct kgsl_context *context)
- {
- int ret;
- struct kgsl_drawobj_timeline *timelineobj =
- kzalloc(sizeof(*timelineobj), GFP_KERNEL);
- if (!timelineobj)
- return ERR_PTR(-ENOMEM);
- ret = drawobj_init(device, context, &timelineobj->base,
- TIMELINEOBJ_TYPE);
- if (ret) {
- kfree(timelineobj);
- return ERR_PTR(ret);
- }
- /*
- * Initialize the sig_refcount that triggers the timeline signal.
- * This refcount goes to 0 when:
- * 1) This timelineobj is popped off the context queue. This implies
- * any syncobj blocking this timelineobj was already signaled, or
- * the context queue is cleaned up at detach time.
- * 2) The cmdobjs queued on this context before this timeline object
- * are retired.
- */
- kref_init(&timelineobj->sig_refcount);
- timelineobj->base.destroy = timelineobj_destroy;
- timelineobj->base.destroy_object = timelineobj_destroy_object;
- return timelineobj;
- }
- static void _timeline_signaled(struct kgsl_device *device,
- struct kgsl_event_group *group, void *priv, int ret)
- {
- struct kgsl_drawobj_timeline *timelineobj = priv;
- struct kgsl_drawobj *drawobj = DRAWOBJ(timelineobj);
- /* Put the sig_refcount we took when registering this event */
- kgsl_timelineobj_signal(timelineobj);
- /* Put the drawobj refcount we took when registering this event */
- kgsl_drawobj_put(drawobj);
- }
- int kgsl_drawobj_add_timeline(struct kgsl_device_private *dev_priv,
- struct kgsl_drawobj_timeline *timelineobj,
- void __user *src, u64 cmdsize)
- {
- struct kgsl_device *device = dev_priv->device;
- struct kgsl_gpu_aux_command_timeline cmd;
- struct kgsl_drawobj *drawobj = DRAWOBJ(timelineobj);
- struct kgsl_context *context = drawobj->context;
- int i, ret;
- u32 queued;
- memset(&cmd, 0, sizeof(cmd));
- ret = get_aux_command(src, cmdsize,
- KGSL_GPU_AUX_COMMAND_TIMELINE, &cmd, sizeof(cmd));
- if (ret)
- return ret;
- if (!cmd.count)
- return -EINVAL;
- timelineobj->timelines = kvcalloc(cmd.count,
- sizeof(*timelineobj->timelines),
- GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
- if (!timelineobj->timelines)
- return -ENOMEM;
- src = u64_to_user_ptr(cmd.timelines);
- /* Get the last queued timestamp on the drawobj context */
- ret = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED, &queued);
- if (ret)
- return ret;
- for (i = 0; i < cmd.count; i++) {
- struct kgsl_timeline_val val;
- if (copy_struct_from_user(&val, sizeof(val), src,
- cmd.timelines_size)) {
- ret = -EFAULT;
- goto err;
- }
- if (val.padding) {
- ret = -EINVAL;
- goto err;
- }
- timelineobj->timelines[i].timeline =
- kgsl_timeline_by_id(dev_priv->device,
- val.timeline);
- if (!timelineobj->timelines[i].timeline) {
- ret = -ENODEV;
- goto err;
- }
- /* Get a context refcount so we can use the context pointer */
- if (!_kgsl_context_get(context)) {
- ret = -ENODEV;
- goto err;
- }
- trace_kgsl_drawobj_timeline(val.timeline, val.seqno);
- timelineobj->timelines[i].seqno = val.seqno;
- timelineobj->timelines[i].context = context;
- timelineobj->timelines[i].timestamp = queued;
- src += cmd.timelines_size;
- }
- timelineobj->count = cmd.count;
- /*
- * Register a kgsl_event to notify us when the last queued timestamp
- * retires. Take a refcount on the drawobj to keep it valid for the
- * callback, and take the sig_refcount to synchronize with the
- * timelineobj retire. Both these refcounts are put in the callback.
- */
- kref_get(&drawobj->refcount);
- kref_get(&timelineobj->sig_refcount);
- ret = kgsl_add_event(device, &context->events, queued,
- _timeline_signaled, timelineobj);
- if (ret)
- goto event_err;
- return 0;
- event_err:
- /*
- * If there was an error, put back sig_refcount and drawobj refcounts.
- * The caller still holds initial refcounts on both and puts them in
- * kgsl_drawobj_destroy(). Clean up the timelinelines array since we
- * do not want to signal anything now.
- */
- kgsl_timelineobj_signal(timelineobj);
- kgsl_drawobj_put(drawobj);
- err:
- for (i = 0; i < cmd.count; i++) {
- kgsl_timeline_put(timelineobj->timelines[i].timeline);
- kgsl_context_put(timelineobj->timelines[i].context);
- }
- kvfree(timelineobj->timelines);
- timelineobj->timelines = NULL;
- return ret;
- }
- static void kgsl_drawobj_bind_callback(struct kgsl_sharedmem_bind_op *op)
- {
- struct kgsl_drawobj_bind *bindobj = op->data;
- struct kgsl_drawobj *drawobj = DRAWOBJ(bindobj);
- struct kgsl_device *device = drawobj->device;
- set_bit(KGSL_BINDOBJ_STATE_DONE, &bindobj->state);
- /* Re-schedule the context */
- if (device->ftbl->drawctxt_sched)
- device->ftbl->drawctxt_sched(device,
- drawobj->context);
- /* Put back the reference we took when we started the operation */
- kgsl_context_put(drawobj->context);
- kgsl_drawobj_put(drawobj);
- }
- int kgsl_drawobj_add_bind(struct kgsl_device_private *dev_priv,
- struct kgsl_drawobj_bind *bindobj,
- void __user *src, u64 cmdsize)
- {
- struct kgsl_gpu_aux_command_bind cmd;
- struct kgsl_process_private *private = dev_priv->process_priv;
- struct kgsl_sharedmem_bind_op *op;
- int ret;
- ret = get_aux_command(src, cmdsize,
- KGSL_GPU_AUX_COMMAND_BIND, &cmd, sizeof(cmd));
- if (ret)
- return ret;
- op = kgsl_sharedmem_create_bind_op(private, cmd.target,
- u64_to_user_ptr(cmd.rangeslist), cmd.numranges,
- cmd.rangesize);
- if (IS_ERR(op))
- return PTR_ERR(op);
- op->callback = kgsl_drawobj_bind_callback;
- op->data = bindobj;
- bindobj->bind = op;
- return 0;
- }
- struct kgsl_drawobj_bind *kgsl_drawobj_bind_create(struct kgsl_device *device,
- struct kgsl_context *context)
- {
- int ret;
- struct kgsl_drawobj_bind *bindobj =
- kzalloc(sizeof(*bindobj), GFP_KERNEL);
- if (!bindobj)
- return ERR_PTR(-ENOMEM);
- ret = drawobj_init(device, context, &bindobj->base, BINDOBJ_TYPE);
- if (ret) {
- kfree(bindobj);
- return ERR_PTR(ret);
- }
- bindobj->base.destroy = bindobj_destroy;
- bindobj->base.destroy_object = bindobj_destroy_object;
- return bindobj;
- }
- /**
- * kgsl_drawobj_sync_create() - Create a new sync obj
- * structure
- * @device: Pointer to a KGSL device struct
- * @context: Pointer to a KGSL context struct
- *
- * Allocate an new kgsl_drawobj_sync structure
- */
- struct kgsl_drawobj_sync *kgsl_drawobj_sync_create(struct kgsl_device *device,
- struct kgsl_context *context)
- {
- struct kgsl_drawobj_sync *syncobj =
- kzalloc(sizeof(*syncobj), GFP_KERNEL);
- int ret;
- if (!syncobj)
- return ERR_PTR(-ENOMEM);
- ret = drawobj_init(device, context, &syncobj->base, SYNCOBJ_TYPE);
- if (ret) {
- kfree(syncobj);
- return ERR_PTR(ret);
- }
- syncobj->base.destroy = syncobj_destroy;
- syncobj->base.destroy_object = syncobj_destroy_object;
- timer_setup(&syncobj->timer, syncobj_timer, 0);
- return syncobj;
- }
- /**
- * kgsl_drawobj_cmd_create() - Create a new command obj
- * structure
- * @device: Pointer to a KGSL device struct
- * @context: Pointer to a KGSL context struct
- * @flags: Flags for the command obj
- * @type: type of cmdobj MARKER/CMD
- *
- * Allocate a new kgsl_drawobj_cmd structure
- */
- 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;
- /* sanitize our flags for drawobjs */
- 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
- | KGSL_DRAWOBJ_START_RECURRING
- | KGSL_DRAWOBJ_STOP_RECURRING);
- INIT_LIST_HEAD(&cmdobj->cmdlist);
- INIT_LIST_HEAD(&cmdobj->memlist);
- cmdobj->requeue_cnt = 0;
- if (!(type & CMDOBJ_TYPE))
- return cmdobj;
- atomic_inc(&context->proc_priv->cmd_count);
- atomic_inc(&context->proc_priv->period->active_cmds);
- spin_lock(&device->work_period_lock);
- if (!__test_and_set_bit(KGSL_WORK_PERIOD, &device->flags)) {
- mod_timer(&device->work_period_timer,
- jiffies + msecs_to_jiffies(KGSL_WORK_PERIOD_MS));
- device->gpu_period.begin = ktime_get_ns();
- }
- /* Take a refcount here and put it back in kgsl_work_period_timer() */
- if (!__test_and_set_bit(KGSL_WORK_PERIOD, &context->proc_priv->period->flags))
- kref_get(&context->proc_priv->period->refcount);
- spin_unlock(&device->work_period_lock);
- return cmdobj;
- }
- #ifdef CONFIG_COMPAT
- static int add_ibdesc_list_compat(struct kgsl_device *device,
- struct kgsl_drawobj_cmd *cmdobj, void __user *ptr, int count)
- {
- int i, ret = 0;
- struct kgsl_ibdesc_compat ibdesc32;
- struct kgsl_ibdesc ibdesc;
- for (i = 0; i < count; i++) {
- memset(&ibdesc32, 0, sizeof(ibdesc32));
- if (copy_from_user(&ibdesc32, ptr, sizeof(ibdesc32))) {
- ret = -EFAULT;
- break;
- }
- ibdesc.gpuaddr = (unsigned long) ibdesc32.gpuaddr;
- ibdesc.sizedwords = (size_t) ibdesc32.sizedwords;
- ibdesc.ctrl = (unsigned int) ibdesc32.ctrl;
- ret = kgsl_drawobj_cmd_add_ibdesc(device, cmdobj, &ibdesc);
- if (ret)
- break;
- ptr += sizeof(ibdesc32);
- }
- return ret;
- }
- static int add_syncpoints_compat(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *ptr, int count)
- {
- struct kgsl_cmd_syncpoint_compat sync32;
- struct kgsl_cmd_syncpoint sync;
- int i, ret = 0;
- for (i = 0; i < count; i++) {
- memset(&sync32, 0, sizeof(sync32));
- if (copy_from_user(&sync32, ptr, sizeof(sync32))) {
- ret = -EFAULT;
- break;
- }
- sync.type = sync32.type;
- sync.priv = compat_ptr(sync32.priv);
- sync.size = (size_t) sync32.size;
- ret = kgsl_drawobj_sync_add_sync(device, syncobj, &sync);
- if (ret)
- break;
- ptr += sizeof(sync32);
- }
- return ret;
- }
- #else
- static int add_ibdesc_list_compat(struct kgsl_device *device,
- struct kgsl_drawobj_cmd *cmdobj, void __user *ptr, int count)
- {
- return -EINVAL;
- }
- static int add_syncpoints_compat(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *ptr, int count)
- {
- return -EINVAL;
- }
- #endif
- /* Returns:
- * -EINVAL: Bad data
- * 0: All data fields are empty (nothing to do)
- * 1: All list information is valid
- */
- static int _verify_input_list(unsigned int count, void __user *ptr,
- unsigned int size)
- {
- /* Return early if nothing going on */
- if (count == 0 && ptr == NULL && size == 0)
- return 0;
- /* Sanity check inputs */
- if (count == 0 || ptr == NULL || size == 0)
- return -EINVAL;
- return 1;
- }
- int kgsl_drawobj_cmd_add_ibdesc_list(struct kgsl_device *device,
- struct kgsl_drawobj_cmd *cmdobj, void __user *ptr, int count)
- {
- struct kgsl_ibdesc ibdesc;
- struct kgsl_drawobj *baseobj = DRAWOBJ(cmdobj);
- int i, ret;
- /* Ignore everything if this is a MARKER */
- if (baseobj->type & MARKEROBJ_TYPE)
- return 0;
- ret = _verify_input_list(count, ptr, sizeof(ibdesc));
- if (ret <= 0)
- return -EINVAL;
- if (is_compat_task())
- return add_ibdesc_list_compat(device, cmdobj, ptr, count);
- for (i = 0; i < count; i++) {
- memset(&ibdesc, 0, sizeof(ibdesc));
- if (copy_from_user(&ibdesc, ptr, sizeof(ibdesc)))
- return -EFAULT;
- ret = kgsl_drawobj_cmd_add_ibdesc(device, cmdobj, &ibdesc);
- if (ret)
- return ret;
- ptr += sizeof(ibdesc);
- }
- return 0;
- }
- int kgsl_drawobj_sync_add_syncpoints(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *ptr, int count)
- {
- struct kgsl_cmd_syncpoint sync;
- int i, ret;
- if (count == 0)
- return 0;
- syncobj->synclist = kcalloc(count,
- sizeof(struct kgsl_drawobj_sync_event), GFP_KERNEL);
- if (syncobj->synclist == NULL)
- return -ENOMEM;
- if (is_compat_task())
- return add_syncpoints_compat(device, syncobj, ptr, count);
- for (i = 0; i < count; i++) {
- memset(&sync, 0, sizeof(sync));
- if (copy_from_user(&sync, ptr, sizeof(sync)))
- return -EFAULT;
- ret = kgsl_drawobj_sync_add_sync(device, syncobj, &sync);
- if (ret)
- return ret;
- ptr += sizeof(sync);
- }
- return 0;
- }
- static int kgsl_drawobj_add_memobject(struct list_head *head,
- struct kgsl_command_object *obj)
- {
- struct kgsl_memobj_node *mem;
- mem = kmem_cache_alloc(memobjs_cache, GFP_KERNEL);
- if (mem == NULL)
- return -ENOMEM;
- mem->gpuaddr = obj->gpuaddr;
- mem->size = obj->size;
- mem->id = obj->id;
- mem->offset = obj->offset;
- mem->flags = obj->flags;
- mem->priv = 0;
- list_add_tail(&mem->node, head);
- return 0;
- }
- #define CMDLIST_FLAGS \
- (KGSL_CMDLIST_IB | \
- KGSL_CMDLIST_CTXTSWITCH_PREAMBLE | \
- KGSL_CMDLIST_IB_PREAMBLE)
- /* This can only accept MARKEROBJ_TYPE and CMDOBJ_TYPE */
- 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;
- /* Ignore everything if this is a MARKER */
- 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++) {
- if (copy_struct_from_user(&obj, sizeof(obj), ptr, size))
- return -EFAULT;
- /* Sanity check the flags */
- if (!(obj.flags & CMDLIST_FLAGS)) {
- dev_err(device->dev,
- "invalid cmdobj ctxt %u 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;
- }
- 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;
- /* Ignore everything if this is a MARKER */
- 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++) {
- if (copy_struct_from_user(&obj, sizeof(obj), ptr, size))
- return -EFAULT;
- if (!(obj.flags & KGSL_OBJLIST_MEMOBJ)) {
- dev_err(device->dev,
- "invalid memobj ctxt %u 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;
- }
- struct kgsl_drawobj_sync *
- kgsl_drawobj_create_timestamp_syncobj(struct kgsl_device *device,
- struct kgsl_context *context, unsigned int timestamp)
- {
- struct kgsl_drawobj_sync *syncobj;
- struct kgsl_cmd_syncpoint_timestamp priv;
- int ret;
- syncobj = kgsl_drawobj_sync_create(device, context);
- if (IS_ERR(syncobj))
- return syncobj;
- syncobj->synclist = kzalloc(sizeof(*syncobj->synclist), GFP_KERNEL);
- if (!syncobj->synclist) {
- kgsl_drawobj_destroy(DRAWOBJ(syncobj));
- return ERR_PTR(-ENOMEM);
- }
- priv.timestamp = timestamp;
- priv.context_id = context->id;
- ret = drawobj_add_sync_timestamp(device, syncobj, &priv);
- if (ret) {
- kgsl_drawobj_destroy(DRAWOBJ(syncobj));
- return ERR_PTR(ret);
- }
- return syncobj;
- }
- int kgsl_drawobj_sync_add_synclist(struct kgsl_device *device,
- struct kgsl_drawobj_sync *syncobj, void __user *ptr,
- unsigned int size, unsigned int count)
- {
- struct kgsl_command_syncpoint syncpoint;
- struct kgsl_cmd_syncpoint sync;
- int i, ret;
- /* If creating a sync and the data is not there or wrong then error */
- ret = _verify_input_list(count, ptr, size);
- if (ret <= 0)
- return -EINVAL;
- syncobj->synclist = kcalloc(count,
- sizeof(struct kgsl_drawobj_sync_event), GFP_KERNEL);
- if (syncobj->synclist == NULL)
- return -ENOMEM;
- for (i = 0; i < count; i++) {
- if (copy_struct_from_user(&syncpoint, sizeof(syncpoint), ptr, size))
- return -EFAULT;
- sync.type = syncpoint.type;
- sync.priv = u64_to_user_ptr(syncpoint.priv);
- sync.size = syncpoint.size;
- ret = kgsl_drawobj_sync_add_sync(device, syncobj, &sync);
- if (ret)
- return ret;
- ptr += sizeof(syncpoint);
- }
- return 0;
- }
- void kgsl_drawobjs_cache_exit(void)
- {
- kmem_cache_destroy(memobjs_cache);
- }
- int kgsl_drawobjs_cache_init(void)
- {
- memobjs_cache = KMEM_CACHE(kgsl_memobj_node, 0);
- if (!memobjs_cache)
- return -ENOMEM;
- return 0;
- }
|