1// SPDX-License-Identifier: GPL-2.0-only
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
18static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
19{
20 return "host1x";
21}
22
23static const char *host1x_syncpt_fence_get_timeline_name(struct dma_fence *f)
24{
25 return "syncpoint";
26}
27
28static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
29{
30 return container_of(f, struct host1x_syncpt_fence, base);
31}
32
33static 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(sp: sf->sp, thresh: sf->threshold))
38 return false;
39
40 /* Reference for interrupt path. */
41 dma_fence_get(fence: 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 /* Reference for timeout path. */
54 dma_fence_get(fence: f);
55 schedule_delayed_work(dwork: &sf->timeout_work, delay: msecs_to_jiffies(m: 30000));
56 }
57
58 host1x_intr_add_fence_locked(host: sf->sp->host, fence: 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
69static 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
75void host1x_fence_signal(struct host1x_syncpt_fence *f)
76{
77 if (atomic_xchg(v: &f->signaling, new: 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(fence: &f->base);
83 return;
84 }
85
86 if (f->timeout && cancel_delayed_work(dwork: &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(fence: &f->base);
92 }
93
94 dma_fence_signal_locked(fence: &f->base);
95 dma_fence_put(fence: &f->base);
96}
97
98static 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(v: &f->signaling, new: 1)) {
105 /* Already on interrupt path, drop timeout path reference if any. */
106 if (f->timeout)
107 dma_fence_put(fence: &f->base);
108 return;
109 }
110
111 if (host1x_intr_remove_fence(host: f->sp->host, fence: 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(fence: &f->base);
117 }
118
119 dma_fence_set_error(fence: &f->base, error: -ETIMEDOUT);
120 dma_fence_signal(fence: &f->base);
121 if (f->timeout)
122 dma_fence_put(fence: &f->base);
123}
124
125struct 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(size: sizeof(*fence), GFP_KERNEL);
131 if (!fence)
132 return ERR_PTR(error: -ENOMEM);
133
134 fence->sp = sp;
135 fence->threshold = threshold;
136 fence->timeout = timeout;
137
138 dma_fence_init(fence: &fence->base, ops: &host1x_syncpt_fence_ops, lock: &sp->fences.lock,
139 context: dma_fence_context_alloc(num: 1), seqno: 0);
140
141 INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
142
143 return &fence->base;
144}
145EXPORT_SYMBOL(host1x_fence_create);
146
147void host1x_fence_cancel(struct dma_fence *f)
148{
149 struct host1x_syncpt_fence *sf = to_host1x_fence(f);
150
151 schedule_delayed_work(dwork: &sf->timeout_work, delay: 0);
152 flush_delayed_work(dwork: &sf->timeout_work);
153}
154EXPORT_SYMBOL(host1x_fence_cancel);
155

source code of linux/drivers/gpu/host1x/fence.c