summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/dma-buf/fence.c98
-rw-r--r--include/linux/fence.h3
2 files changed, 100 insertions, 1 deletions
diff --git a/drivers/dma-buf/fence.c b/drivers/dma-buf/fence.c
index 50ef8bd8708b..7b05dbe9b296 100644
--- a/drivers/dma-buf/fence.c
+++ b/drivers/dma-buf/fence.c
@@ -397,6 +397,104 @@ out:
397} 397}
398EXPORT_SYMBOL(fence_default_wait); 398EXPORT_SYMBOL(fence_default_wait);
399 399
400static bool
401fence_test_signaled_any(struct fence **fences, uint32_t count)
402{
403 int i;
404
405 for (i = 0; i < count; ++i) {
406 struct fence *fence = fences[i];
407 if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags))
408 return true;
409 }
410 return false;
411}
412
413/**
414 * fence_wait_any_timeout - sleep until any fence gets signaled
415 * or until timeout elapses
416 * @fences: [in] array of fences to wait on
417 * @count: [in] number of fences to wait on
418 * @intr: [in] if true, do an interruptible wait
419 * @timeout: [in] timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT
420 *
421 * Returns -EINVAL on custom fence wait implementation, -ERESTARTSYS if
422 * interrupted, 0 if the wait timed out, or the remaining timeout in jiffies
423 * on success.
424 *
425 * Synchronous waits for the first fence in the array to be signaled. The
426 * caller needs to hold a reference to all fences in the array, otherwise a
427 * fence might be freed before return, resulting in undefined behavior.
428 */
429signed long
430fence_wait_any_timeout(struct fence **fences, uint32_t count,
431 bool intr, signed long timeout)
432{
433 struct default_wait_cb *cb;
434 signed long ret = timeout;
435 unsigned i;
436
437 if (WARN_ON(!fences || !count || timeout < 0))
438 return -EINVAL;
439
440 if (timeout == 0) {
441 for (i = 0; i < count; ++i)
442 if (fence_is_signaled(fences[i]))
443 return 1;
444
445 return 0;
446 }
447
448 cb = kcalloc(count, sizeof(struct default_wait_cb), GFP_KERNEL);
449 if (cb == NULL) {
450 ret = -ENOMEM;
451 goto err_free_cb;
452 }
453
454 for (i = 0; i < count; ++i) {
455 struct fence *fence = fences[i];
456
457 if (fence->ops->wait != fence_default_wait) {
458 ret = -EINVAL;
459 goto fence_rm_cb;
460 }
461
462 cb[i].task = current;
463 if (fence_add_callback(fence, &cb[i].base,
464 fence_default_wait_cb)) {
465 /* This fence is already signaled */
466 goto fence_rm_cb;
467 }
468 }
469
470 while (ret > 0) {
471 if (intr)
472 set_current_state(TASK_INTERRUPTIBLE);
473 else
474 set_current_state(TASK_UNINTERRUPTIBLE);
475
476 if (fence_test_signaled_any(fences, count))
477 break;
478
479 ret = schedule_timeout(ret);
480
481 if (ret > 0 && intr && signal_pending(current))
482 ret = -ERESTARTSYS;
483 }
484
485 __set_current_state(TASK_RUNNING);
486
487fence_rm_cb:
488 while (i-- > 0)
489 fence_remove_callback(fences[i], &cb[i].base);
490
491err_free_cb:
492 kfree(cb);
493
494 return ret;
495}
496EXPORT_SYMBOL(fence_wait_any_timeout);
497
400/** 498/**
401 * fence_init - Initialize a custom fence. 499 * fence_init - Initialize a custom fence.
402 * @fence: [in] the fence to initialize 500 * @fence: [in] the fence to initialize
diff --git a/include/linux/fence.h b/include/linux/fence.h
index 39efee130d2b..a4084d6bb851 100644
--- a/include/linux/fence.h
+++ b/include/linux/fence.h
@@ -305,7 +305,8 @@ static inline struct fence *fence_later(struct fence *f1, struct fence *f2)
305} 305}
306 306
307signed long fence_wait_timeout(struct fence *, bool intr, signed long timeout); 307signed long fence_wait_timeout(struct fence *, bool intr, signed long timeout);
308 308signed long fence_wait_any_timeout(struct fence **fences, uint32_t count,
309 bool intr, signed long timeout);
309 310
310/** 311/**
311 * fence_wait - sleep until the fence gets signaled 312 * fence_wait - sleep until the fence gets signaled