| 1 | |
| 2 | |
| 3 | * Syncpoint dma_fence implementation
|
| 4 | *
|
| 5 | * Copyright (c) 2020, NVIDIA Corporation.
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/dma-fence.h>
|
| 9 | #include <linux/file.h>
|
| 10 | #include <linux/fs.h>
|
| 11 | #include <linux/slab.h>
|
| 12 | #include <linux/sync_file.h>
|
| 13 |
|
| 14 | #include "fence.h"
|
| 15 | #include "intr.h"
|
| 16 | #include "syncpt.h"
|
| 17 |
|
| 18 | static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
|
| 19 | {
|
| 20 | return "host1x";
|
| 21 | }
|
| 22 |
|
| 23 | static const char *host1x_syncpt_fence_get_timeline_name(struct dma_fence *f)
|
| 24 | {
|
| 25 | return "syncpoint";
|
| 26 | }
|
| 27 |
|
| 28 | static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
|
| 29 | {
|
| 30 | return container_of(f, struct host1x_syncpt_fence, base);
|
| 31 | }
|
| 32 |
|
| 33 | static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
|
| 34 | {
|
| 35 | struct host1x_syncpt_fence *sf = to_host1x_fence(f);
|
| 36 |
|
| 37 | if (host1x_syncpt_is_expired(sf->sp, sf->threshold))
|
| 38 | return false;
|
| 39 |
|
| 40 |
|
| 41 | dma_fence_get(f);
|
| 42 |
|
| 43 | |
| 44 | * The dma_fence framework requires the fence driver to keep a
|
| 45 | * reference to any fences for which 'enable_signaling' has been
|
| 46 | * called (and that have not been signalled).
|
| 47 | *
|
| 48 | * We cannot currently always guarantee that all fences get signalled
|
| 49 | * or cancelled. As such, for such situations, set up a timeout, so
|
| 50 | * that long-lasting fences will get reaped eventually.
|
| 51 |
|
| 52 | if (sf->timeout) {
|
| 53 |
|
| 54 | dma_fence_get(f);
|
| 55 | schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000));
|
| 56 | }
|
| 57 |
|
| 58 | host1x_intr_add_fence_locked(sf->sp->host, sf);
|
| 59 |
|
| 60 | |
| 61 | * The fence may get signalled at any time after the above call,
|
| 62 | * so we need to initialize all state used by signalling
|
| 63 | * before it.
|
| 64 |
|
| 65 |
|
| 66 | return true;
|
| 67 | }
|
| 68 |
|
| 69 | static const struct dma_fence_ops host1x_syncpt_fence_ops = {
|
| 70 | .get_driver_name = host1x_syncpt_fence_get_driver_name,
|
| 71 | .get_timeline_name = host1x_syncpt_fence_get_timeline_name,
|
| 72 | .enable_signaling = host1x_syncpt_fence_enable_signaling,
|
| 73 | };
|
| 74 |
|
| 75 | void host1x_fence_signal(struct host1x_syncpt_fence *f)
|
| 76 | {
|
| 77 | if (atomic_xchg(&f->signaling, 1)) {
|
| 78 | |
| 79 | * Already on timeout path, but we removed the fence before
|
| 80 | * timeout path could, so drop interrupt path reference.
|
| 81 |
|
| 82 | dma_fence_put(&f->base);
|
| 83 | return;
|
| 84 | }
|
| 85 |
|
| 86 | if (f->timeout && cancel_delayed_work(&f->timeout_work)) {
|
| 87 | |
| 88 | * We know that the timeout path will not be entered.
|
| 89 | * Safe to drop the timeout path's reference now.
|
| 90 |
|
| 91 | dma_fence_put(&f->base);
|
| 92 | }
|
| 93 |
|
| 94 | dma_fence_signal_locked(&f->base);
|
| 95 | dma_fence_put(&f->base);
|
| 96 | }
|
| 97 |
|
| 98 | static void do_fence_timeout(struct work_struct *work)
|
| 99 | {
|
| 100 | struct delayed_work *dwork = (struct delayed_work *)work;
|
| 101 | struct host1x_syncpt_fence *f =
|
| 102 | container_of(dwork, struct host1x_syncpt_fence, timeout_work);
|
| 103 |
|
| 104 | if (atomic_xchg(&f->signaling, 1)) {
|
| 105 |
|
| 106 | if (f->timeout)
|
| 107 | dma_fence_put(&f->base);
|
| 108 | return;
|
| 109 | }
|
| 110 |
|
| 111 | if (host1x_intr_remove_fence(f->sp->host, f)) {
|
| 112 | |
| 113 | * Managed to remove fence from queue, so it's safe to drop
|
| 114 | * the interrupt path's reference.
|
| 115 |
|
| 116 | dma_fence_put(&f->base);
|
| 117 | }
|
| 118 |
|
| 119 | dma_fence_set_error(&f->base, -ETIMEDOUT);
|
| 120 | dma_fence_signal(&f->base);
|
| 121 | if (f->timeout)
|
| 122 | dma_fence_put(&f->base);
|
| 123 | }
|
| 124 |
|
| 125 | struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold,
|
| 126 | bool timeout)
|
| 127 | {
|
| 128 | struct host1x_syncpt_fence *fence;
|
| 129 |
|
| 130 | fence = kzalloc_obj(*fence);
|
| 131 | if (!fence)
|
| 132 | return ERR_PTR(-ENOMEM);
|
| 133 |
|
| 134 | fence->sp = sp;
|
| 135 | fence->threshold = threshold;
|
| 136 | fence->timeout = timeout;
|
| 137 |
|
| 138 | dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock,
|
| 139 | dma_fence_context_alloc(1), 0);
|
| 140 |
|
| 141 | INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
|
| 142 |
|
| 143 | return &fence->base;
|
| 144 | }
|
| 145 | EXPORT_SYMBOL(host1x_fence_create);
|
| 146 |
|
| 147 | void host1x_fence_cancel(struct dma_fence *f)
|
| 148 | {
|
| 149 | struct host1x_syncpt_fence *sf = to_host1x_fence(f);
|
| 150 |
|
| 151 | schedule_delayed_work(&sf->timeout_work, 0);
|
| 152 | flush_delayed_work(&sf->timeout_work);
|
| 153 | }
|
| 154 | EXPORT_SYMBOL(host1x_fence_cancel);
|