Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: host1x: Implement job tracking using DMA fences

In anticipation of removal of the intr API, implement job tracking
using DMA fences instead. The main two things about this are
making cdma_update schedule the work since fence completion can
now be called from interrupt context, and some complication in
ensuring the callback is not running when we free the fence.

Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Mikko Perttunen and committed by
Thierry Reding
c24973ed f0fb260a

+53 -29
+11 -3
drivers/gpu/host1x/cdma.c
··· 490 490 host1x_hw_cdma_resume(host1x, cdma, restart_addr); 491 491 } 492 492 493 + static void cdma_update_work(struct work_struct *work) 494 + { 495 + struct host1x_cdma *cdma = container_of(work, struct host1x_cdma, update_work); 496 + 497 + mutex_lock(&cdma->lock); 498 + update_cdma_locked(cdma); 499 + mutex_unlock(&cdma->lock); 500 + } 501 + 493 502 /* 494 503 * Create a cdma 495 504 */ ··· 508 499 509 500 mutex_init(&cdma->lock); 510 501 init_completion(&cdma->complete); 502 + INIT_WORK(&cdma->update_work, cdma_update_work); 511 503 512 504 INIT_LIST_HEAD(&cdma->sync_queue); 513 505 ··· 689 679 */ 690 680 void host1x_cdma_update(struct host1x_cdma *cdma) 691 681 { 692 - mutex_lock(&cdma->lock); 693 - update_cdma_locked(cdma); 694 - mutex_unlock(&cdma->lock); 682 + schedule_work(&cdma->update_work); 695 683 }
+2
drivers/gpu/host1x/cdma.h
··· 11 11 #include <linux/sched.h> 12 12 #include <linux/completion.h> 13 13 #include <linux/list.h> 14 + #include <linux/workqueue.h> 14 15 15 16 struct host1x_syncpt; 16 17 struct host1x_userctx_timeout; ··· 70 69 struct buffer_timeout timeout; /* channel's timeout state/wq */ 71 70 bool running; 72 71 bool torndown; 72 + struct work_struct update_work; 73 73 }; 74 74 75 75 #define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma)
+27 -21
drivers/gpu/host1x/hw/channel_hw.c
··· 278 278 #endif 279 279 } 280 280 281 + static void job_complete_callback(struct dma_fence *fence, struct dma_fence_cb *cb) 282 + { 283 + struct host1x_job *job = container_of(cb, struct host1x_job, fence_cb); 284 + 285 + /* Schedules CDMA update. */ 286 + host1x_cdma_update(&job->channel->cdma); 287 + } 288 + 281 289 static int channel_submit(struct host1x_job *job) 282 290 { 283 291 struct host1x_channel *ch = job->channel; ··· 293 285 u32 prev_max = 0; 294 286 u32 syncval; 295 287 int err; 296 - struct host1x_waitlist *completed_waiter = NULL; 297 288 struct host1x *host = dev_get_drvdata(ch->dev->parent); 298 289 299 290 trace_host1x_channel_submit(dev_name(ch->dev), ··· 305 298 /* get submit lock */ 306 299 err = mutex_lock_interruptible(&ch->submitlock); 307 300 if (err) 308 - goto error; 309 - 310 - completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL); 311 - if (!completed_waiter) { 312 - mutex_unlock(&ch->submitlock); 313 - err = -ENOMEM; 314 - goto error; 315 - } 301 + return err; 316 302 317 303 host1x_channel_set_streamid(ch); 318 304 host1x_enable_gather_filter(ch); ··· 315 315 err = host1x_cdma_begin(&ch->cdma, job); 316 316 if (err) { 317 317 mutex_unlock(&ch->submitlock); 318 - goto error; 318 + return err; 319 319 } 320 320 321 321 channel_program_cdma(job); 322 322 syncval = host1x_syncpt_read_max(sp); 323 + 324 + /* 325 + * Create fence before submitting job to HW to avoid job completing 326 + * before the fence is set up. 327 + */ 328 + job->fence = host1x_fence_create(sp, syncval); 329 + if (WARN(IS_ERR(job->fence), "Failed to create submit complete fence")) { 330 + job->fence = NULL; 331 + } else { 332 + err = dma_fence_add_callback(job->fence, &job->fence_cb, 333 + job_complete_callback); 334 + } 323 335 324 336 /* end CDMA submit & stash pinned hMems into sync queue */ 325 337 host1x_cdma_end(&ch->cdma, job); 326 338 327 339 trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval); 328 340 329 - /* schedule a submit complete interrupt */ 330 - err = host1x_intr_add_action(host, sp, syncval, 331 - HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch, 332 - completed_waiter, &job->waiter); 333 - completed_waiter = NULL; 334 - WARN(err, "Failed to set submit complete interrupt"); 335 - 336 341 mutex_unlock(&ch->submitlock); 337 342 338 - return 0; 343 + if (err == -ENOENT) 344 + host1x_cdma_update(&ch->cdma); 345 + else 346 + WARN(err, "Failed to set submit complete interrupt"); 339 347 340 - error: 341 - kfree(completed_waiter); 342 - return err; 348 + return 0; 343 349 } 344 350 345 351 static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
+9 -3
drivers/gpu/host1x/job.c
··· 88 88 if (job->release) 89 89 job->release(job); 90 90 91 - if (job->waiter) 92 - host1x_intr_put_ref(job->syncpt->host, job->syncpt->id, 93 - job->waiter, false); 91 + if (job->fence) { 92 + /* 93 + * remove_callback is atomic w.r.t. fence signaling, so 94 + * after the call returns, we know that the callback is not 95 + * in execution, and the fence can be safely freed. 96 + */ 97 + dma_fence_remove_callback(job->fence, &job->fence_cb); 98 + dma_fence_put(job->fence); 99 + } 94 100 95 101 if (job->syncpt) 96 102 host1x_syncpt_put(job->syncpt);
+4 -2
include/linux/host1x.h
··· 8 8 9 9 #include <linux/device.h> 10 10 #include <linux/dma-direction.h> 11 + #include <linux/dma-fence.h> 11 12 #include <linux/spinlock.h> 12 13 #include <linux/types.h> 13 14 ··· 289 288 u32 syncpt_incrs; 290 289 u32 syncpt_end; 291 290 292 - /* Completion waiter ref */ 293 - void *waiter; 291 + /* Completion fence for job tracking */ 292 + struct dma_fence *fence; 293 + struct dma_fence_cb fence_cb; 294 294 295 295 /* Maximum time to wait for this job */ 296 296 unsigned int timeout;