xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c (revision 479d8f7d843cc1b22d497efdf1f27a50ee8418d4)
1 /*	$NetBSD: nouveau_fence.c,v 1.4 2016/04/13 07:57:15 riastradh Exp $	*/
2 
3 /*
4  * Copyright (C) 2007 Ben Skeggs.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial
17  * portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
23  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.4 2016/04/13 07:57:15 riastradh Exp $");
31 
32 #include <drm/drmP.h>
33 
34 #include <asm/param.h>
35 #include <linux/ktime.h>
36 #include <linux/hrtimer.h>
37 
38 #include "nouveau_drm.h"
39 #include "nouveau_dma.h"
40 #include "nouveau_fence.h"
41 
42 #include <engine/fifo.h>
43 
44 struct fence_work {
45 	struct work_struct base;
46 	struct list_head head;
47 	void (*func)(void *);
48 	void *data;
49 };
50 
51 static void
52 nouveau_fence_signal(struct nouveau_fence *fence)
53 {
54 	struct fence_work *work, *temp;
55 
56 	list_for_each_entry_safe(work, temp, &fence->work, head) {
57 		schedule_work(&work->base);
58 		list_del(&work->head);
59 	}
60 
61 	fence->channel = NULL;
62 	list_del(&fence->head);
63 }
64 
65 void
66 nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
67 {
68 	struct nouveau_fence *fence, *fnext;
69 	spin_lock(&fctx->lock);
70 	list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
71 		nouveau_fence_signal(fence);
72 	}
73 	spin_unlock(&fctx->lock);
74 	spin_lock_destroy(&fctx->lock);
75 }
76 
77 void
78 nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
79 {
80 	INIT_LIST_HEAD(&fctx->flip);
81 	INIT_LIST_HEAD(&fctx->pending);
82 	spin_lock_init(&fctx->lock);
83 }
84 
85 static void
86 nouveau_fence_work_handler(struct work_struct *kwork)
87 {
88 	struct fence_work *work = container_of(kwork, typeof(*work), base);
89 	work->func(work->data);
90 	kfree(work);
91 }
92 
93 void
94 nouveau_fence_work(struct nouveau_fence *fence,
95 		   void (*func)(void *), void *data)
96 {
97 	struct nouveau_channel *chan = fence->channel;
98 	struct nouveau_fence_chan *fctx;
99 	struct fence_work *work = NULL;
100 
101 	if (nouveau_fence_done(fence)) {
102 		func(data);
103 		return;
104 	}
105 
106 	fctx = chan->fence;
107 	work = kmalloc(sizeof(*work), GFP_KERNEL);
108 	if (!work) {
109 		WARN_ON(nouveau_fence_wait(fence, false, false));
110 		func(data);
111 		return;
112 	}
113 
114 	spin_lock(&fctx->lock);
115 	if (!fence->channel) {
116 		spin_unlock(&fctx->lock);
117 		kfree(work);
118 		func(data);
119 		return;
120 	}
121 
122 	INIT_WORK(&work->base, nouveau_fence_work_handler);
123 	work->func = func;
124 	work->data = data;
125 	list_add(&work->head, &fence->work);
126 	spin_unlock(&fctx->lock);
127 }
128 
129 static void
130 nouveau_fence_update(struct nouveau_channel *chan)
131 {
132 	struct nouveau_fence_chan *fctx = chan->fence;
133 	struct nouveau_fence *fence, *fnext;
134 
135 	spin_lock(&fctx->lock);
136 	list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
137 		if (fctx->read(chan) < fence->sequence)
138 			break;
139 
140 		nouveau_fence_signal(fence);
141 		nouveau_fence_unref(&fence);
142 	}
143 	spin_unlock(&fctx->lock);
144 }
145 
146 int
147 nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
148 {
149 	struct nouveau_fence_chan *fctx = chan->fence;
150 	int ret;
151 
152 	fence->channel  = chan;
153 	fence->timeout  = jiffies + (15 * HZ);
154 	fence->sequence = ++fctx->sequence;
155 
156 	ret = fctx->emit(fence);
157 	if (!ret) {
158 		kref_get(&fence->kref);
159 		spin_lock(&fctx->lock);
160 		list_add_tail(&fence->head, &fctx->pending);
161 		spin_unlock(&fctx->lock);
162 	}
163 
164 	return ret;
165 }
166 
167 bool
168 nouveau_fence_done(struct nouveau_fence *fence)
169 {
170 	if (fence->channel)
171 		nouveau_fence_update(fence->channel);
172 	return !fence->channel;
173 }
174 
175 static int
176 nouveau_fence_wait_uevent_handler(void *data, int index)
177 {
178 	struct nouveau_fence_priv *priv = data;
179 #ifdef __NetBSD__
180 	spin_lock(&priv->waitlock);
181 	/* XXX Set a flag...  */
182 	DRM_SPIN_WAKEUP_ALL(&priv->waitqueue, &priv->waitlock);
183 	spin_unlock(&priv->waitlock);
184 #else
185 	wake_up_all(&priv->waiting);
186 #endif
187 	return NVKM_EVENT_KEEP;
188 }
189 
190 static int
191 nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
192 
193 {
194 	struct nouveau_channel *chan = fence->channel;
195 	struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
196 	struct nouveau_fence_priv *priv = chan->drm->fence;
197 	struct nouveau_eventh *handler;
198 	int ret = 0;
199 
200 	ret = nouveau_event_new(pfifo->uevent, 0,
201 				nouveau_fence_wait_uevent_handler,
202 				priv, &handler);
203 	if (ret)
204 		return ret;
205 
206 	nouveau_event_get(handler);
207 
208 	if (fence->timeout) {
209 		unsigned long timeout = fence->timeout - jiffies;
210 
211 		if (time_before(jiffies, fence->timeout)) {
212 #ifdef __NetBSD__
213 			spin_lock(&priv->waitlock);
214 			if (intr) {
215 				DRM_SPIN_TIMED_WAIT_UNTIL(ret,
216 				    &priv->waitqueue, &priv->waitlock,
217 				    timeout,
218 				    nouveau_fence_done(fence));
219 			} else {
220 				DRM_SPIN_TIMED_WAIT_NOINTR_UNTIL(ret,
221 				    &priv->waitqueue, &priv->waitlock,
222 				    timeout,
223 				    nouveau_fence_done(fence));
224 			}
225 			spin_unlock(&priv->waitlock);
226 #else
227 			if (intr) {
228 				ret = wait_event_interruptible_timeout(
229 						priv->waiting,
230 						nouveau_fence_done(fence),
231 						timeout);
232 			} else {
233 				ret = wait_event_timeout(priv->waiting,
234 						nouveau_fence_done(fence),
235 						timeout);
236 			}
237 #endif
238 		}
239 
240 		if (ret >= 0) {
241 			fence->timeout = jiffies + ret;
242 			if (time_after_eq(jiffies, fence->timeout))
243 				ret = -EBUSY;
244 		}
245 	} else {
246 #ifdef __NetBSD__
247 		spin_lock(&priv->waitlock);
248 		if (intr) {
249 			DRM_SPIN_WAIT_UNTIL(ret, &priv->waitqueue,
250 			    &priv->waitlock,
251 			    nouveau_fence_done(fence));
252 		} else {
253 			DRM_SPIN_WAIT_NOINTR_UNTIL(ret, &priv->waitqueue,
254 			    &priv->waitlock,
255 			    nouveau_fence_done(fence));
256 		}
257 		spin_unlock(&priv->waitlock);
258 #else
259 		if (intr) {
260 			ret = wait_event_interruptible(priv->waiting,
261 					nouveau_fence_done(fence));
262 		} else {
263 			wait_event(priv->waiting, nouveau_fence_done(fence));
264 		}
265 #endif
266 	}
267 
268 	nouveau_event_ref(NULL, &handler);
269 	if (unlikely(ret < 0))
270 		return ret;
271 
272 	return 0;
273 }
274 
275 int
276 nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
277 {
278 	struct nouveau_channel *chan = fence->channel;
279 	struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL;
280 #ifndef __NetBSD__
281 	unsigned long sleep_time = NSEC_PER_MSEC / 1000;
282 	ktime_t t;
283 #endif
284 	int ret = 0;
285 
286 	while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
287 		ret = nouveau_fence_wait_uevent(fence, intr);
288 		if (ret < 0)
289 			return ret;
290 	}
291 
292 	while (!nouveau_fence_done(fence)) {
293 		if (fence->timeout && time_after_eq(jiffies, fence->timeout)) {
294 			ret = -EBUSY;
295 			break;
296 		}
297 
298 #ifdef __NetBSD__
299 		if (lazy)
300 			kpause("nvfencep", intr, 1, NULL);
301 		else
302 			DELAY(1);
303 #else
304 		__set_current_state(intr ? TASK_INTERRUPTIBLE :
305 					   TASK_UNINTERRUPTIBLE);
306 		if (lazy) {
307 			t = ktime_set(0, sleep_time);
308 			schedule_hrtimeout(&t, HRTIMER_MODE_REL);
309 			sleep_time *= 2;
310 			if (sleep_time > NSEC_PER_MSEC)
311 				sleep_time = NSEC_PER_MSEC;
312 		}
313 
314 		if (intr && signal_pending(current)) {
315 			ret = -ERESTARTSYS;
316 			break;
317 		}
318 #endif
319 	}
320 
321 #ifndef __NetBSD__
322 	__set_current_state(TASK_RUNNING);
323 #endif
324 	return ret;
325 }
326 
327 int
328 nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan)
329 {
330 	struct nouveau_fence_chan *fctx = chan->fence;
331 	struct nouveau_channel *prev;
332 	int ret = 0;
333 
334 	prev = fence ? fence->channel : NULL;
335 	if (prev) {
336 		if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
337 			ret = fctx->sync(fence, prev, chan);
338 			if (unlikely(ret))
339 				ret = nouveau_fence_wait(fence, true, false);
340 		}
341 	}
342 
343 	return ret;
344 }
345 
346 static void
347 nouveau_fence_del(struct kref *kref)
348 {
349 	struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref);
350 	kfree(fence);
351 }
352 
353 void
354 nouveau_fence_unref(struct nouveau_fence **pfence)
355 {
356 	if (*pfence)
357 		kref_put(&(*pfence)->kref, nouveau_fence_del);
358 	*pfence = NULL;
359 }
360 
361 struct nouveau_fence *
362 nouveau_fence_ref(struct nouveau_fence *fence)
363 {
364 	if (fence)
365 		kref_get(&fence->kref);
366 	return fence;
367 }
368 
369 int
370 nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
371 		  struct nouveau_fence **pfence)
372 {
373 	struct nouveau_fence *fence;
374 	int ret = 0;
375 
376 	if (unlikely(!chan->fence))
377 		return -ENODEV;
378 
379 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
380 	if (!fence)
381 		return -ENOMEM;
382 
383 	INIT_LIST_HEAD(&fence->work);
384 	fence->sysmem = sysmem;
385 	kref_init(&fence->kref);
386 
387 	ret = nouveau_fence_emit(fence, chan);
388 	if (ret)
389 		nouveau_fence_unref(&fence);
390 
391 	*pfence = fence;
392 	return ret;
393 }
394