summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/os/linux/linux-channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/os/linux/linux-channel.c')
-rw-r--r--drivers/gpu/nvgpu/os/linux/linux-channel.c508
1 files changed, 508 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/os/linux/linux-channel.c b/drivers/gpu/nvgpu/os/linux/linux-channel.c
new file mode 100644
index 00000000..fef44f2b
--- /dev/null
+++ b/drivers/gpu/nvgpu/os/linux/linux-channel.c
@@ -0,0 +1,508 @@
1/*
2 * Copyright (c) 2017-2018, NVIDIA Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <nvgpu/enabled.h>
18#include <nvgpu/debug.h>
19#include <nvgpu/error_notifier.h>
20#include <nvgpu/os_sched.h>
21
22/*
23 * This is required for nvgpu_vm_find_buf() which is used in the tracing
24 * code. Once we can get and access userspace buffers without requiring
25 * direct dma_buf usage this can be removed.
26 */
27#include <nvgpu/linux/vm.h>
28
29#include "gk20a/gk20a.h"
30
31#include "channel.h"
32#include "ioctl_channel.h"
33#include "os_linux.h"
34
35#include <nvgpu/hw/gk20a/hw_pbdma_gk20a.h>
36
37#include <linux/uaccess.h>
38#include <linux/dma-buf.h>
39#include <trace/events/gk20a.h>
40#include <uapi/linux/nvgpu.h>
41
42#include "sync_sema_android.h"
43
44u32 nvgpu_submit_gpfifo_user_flags_to_common_flags(u32 user_flags)
45{
46 u32 flags = 0;
47
48 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_FENCE_WAIT)
49 flags |= NVGPU_SUBMIT_FLAGS_FENCE_WAIT;
50
51 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_FENCE_GET)
52 flags |= NVGPU_SUBMIT_FLAGS_FENCE_GET;
53
54 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_HW_FORMAT)
55 flags |= NVGPU_SUBMIT_FLAGS_HW_FORMAT;
56
57 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SYNC_FENCE)
58 flags |= NVGPU_SUBMIT_FLAGS_SYNC_FENCE;
59
60 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SUPPRESS_WFI)
61 flags |= NVGPU_SUBMIT_FLAGS_SUPPRESS_WFI;
62
63 if (user_flags & NVGPU_SUBMIT_GPFIFO_FLAGS_SKIP_BUFFER_REFCOUNTING)
64 flags |= NVGPU_SUBMIT_FLAGS_SKIP_BUFFER_REFCOUNTING;
65
66 return flags;
67}
68
69/*
70 * API to convert error_notifiers in common code and of the form
71 * NVGPU_ERR_NOTIFIER_* into Linux specific error_notifiers exposed to user
72 * space and of the form NVGPU_CHANNEL_*
73 */
74static u32 nvgpu_error_notifier_to_channel_notifier(u32 error_notifier)
75{
76 switch (error_notifier) {
77 case NVGPU_ERR_NOTIFIER_FIFO_ERROR_IDLE_TIMEOUT:
78 return NVGPU_CHANNEL_FIFO_ERROR_IDLE_TIMEOUT;
79 case NVGPU_ERR_NOTIFIER_GR_ERROR_SW_METHOD:
80 return NVGPU_CHANNEL_GR_ERROR_SW_METHOD;
81 case NVGPU_ERR_NOTIFIER_GR_ERROR_SW_NOTIFY:
82 return NVGPU_CHANNEL_GR_ERROR_SW_NOTIFY;
83 case NVGPU_ERR_NOTIFIER_GR_EXCEPTION:
84 return NVGPU_CHANNEL_GR_EXCEPTION;
85 case NVGPU_ERR_NOTIFIER_GR_SEMAPHORE_TIMEOUT:
86 return NVGPU_CHANNEL_GR_SEMAPHORE_TIMEOUT;
87 case NVGPU_ERR_NOTIFIER_GR_ILLEGAL_NOTIFY:
88 return NVGPU_CHANNEL_GR_ILLEGAL_NOTIFY;
89 case NVGPU_ERR_NOTIFIER_FIFO_ERROR_MMU_ERR_FLT:
90 return NVGPU_CHANNEL_FIFO_ERROR_MMU_ERR_FLT;
91 case NVGPU_ERR_NOTIFIER_PBDMA_ERROR:
92 return NVGPU_CHANNEL_PBDMA_ERROR;
93 case NVGPU_ERR_NOTIFIER_FECS_ERR_UNIMP_FIRMWARE_METHOD:
94 return NVGPU_CHANNEL_FECS_ERR_UNIMP_FIRMWARE_METHOD;
95 case NVGPU_ERR_NOTIFIER_RESETCHANNEL_VERIF_ERROR:
96 return NVGPU_CHANNEL_RESETCHANNEL_VERIF_ERROR;
97 case NVGPU_ERR_NOTIFIER_PBDMA_PUSHBUFFER_CRC_MISMATCH:
98 return NVGPU_CHANNEL_PBDMA_PUSHBUFFER_CRC_MISMATCH;
99 }
100
101 pr_warn("%s: invalid error_notifier requested %u\n", __func__, error_notifier);
102
103 return error_notifier;
104}
105
106/**
107 * nvgpu_set_error_notifier_locked()
108 * Should be called with ch->error_notifier_mutex held
109 *
110 * error should be of the form NVGPU_ERR_NOTIFIER_*
111 */
112void nvgpu_set_error_notifier_locked(struct channel_gk20a *ch, u32 error)
113{
114 struct nvgpu_channel_linux *priv = ch->os_priv;
115
116 error = nvgpu_error_notifier_to_channel_notifier(error);
117
118 if (priv->error_notifier.dmabuf) {
119 struct nvgpu_notification *notification =
120 priv->error_notifier.notification;
121 struct timespec time_data;
122 u64 nsec;
123
124 getnstimeofday(&time_data);
125 nsec = ((u64)time_data.tv_sec) * 1000000000u +
126 (u64)time_data.tv_nsec;
127 notification->time_stamp.nanoseconds[0] =
128 (u32)nsec;
129 notification->time_stamp.nanoseconds[1] =
130 (u32)(nsec >> 32);
131 notification->info32 = error;
132 notification->status = 0xffff;
133
134 nvgpu_err(ch->g,
135 "error notifier set to %d for ch %d", error, ch->chid);
136 }
137}
138
139/* error should be of the form NVGPU_ERR_NOTIFIER_* */
140void nvgpu_set_error_notifier(struct channel_gk20a *ch, u32 error)
141{
142 struct nvgpu_channel_linux *priv = ch->os_priv;
143
144 nvgpu_mutex_acquire(&priv->error_notifier.mutex);
145 nvgpu_set_error_notifier_locked(ch, error);
146 nvgpu_mutex_release(&priv->error_notifier.mutex);
147}
148
149void nvgpu_set_error_notifier_if_empty(struct channel_gk20a *ch, u32 error)
150{
151 struct nvgpu_channel_linux *priv = ch->os_priv;
152
153 nvgpu_mutex_acquire(&priv->error_notifier.mutex);
154 if (priv->error_notifier.dmabuf) {
155 struct nvgpu_notification *notification =
156 priv->error_notifier.notification;
157
158 /* Don't overwrite error flag if it is already set */
159 if (notification->status != 0xffff)
160 nvgpu_set_error_notifier_locked(ch, error);
161 }
162 nvgpu_mutex_release(&priv->error_notifier.mutex);
163}
164
165/* error_notifier should be of the form NVGPU_ERR_NOTIFIER_* */
166bool nvgpu_is_error_notifier_set(struct channel_gk20a *ch, u32 error_notifier)
167{
168 struct nvgpu_channel_linux *priv = ch->os_priv;
169 bool notifier_set = false;
170
171 error_notifier = nvgpu_error_notifier_to_channel_notifier(error_notifier);
172
173 nvgpu_mutex_acquire(&priv->error_notifier.mutex);
174 if (priv->error_notifier.dmabuf) {
175 struct nvgpu_notification *notification =
176 priv->error_notifier.notification;
177 u32 err = notification->info32;
178
179 if (err == error_notifier)
180 notifier_set = true;
181 }
182 nvgpu_mutex_release(&priv->error_notifier.mutex);
183
184 return notifier_set;
185}
186
187static void gk20a_channel_update_runcb_fn(struct work_struct *work)
188{
189 struct nvgpu_channel_completion_cb *completion_cb =
190 container_of(work, struct nvgpu_channel_completion_cb, work);
191 struct nvgpu_channel_linux *priv =
192 container_of(completion_cb,
193 struct nvgpu_channel_linux, completion_cb);
194 struct channel_gk20a *ch = priv->ch;
195 void (*fn)(struct channel_gk20a *, void *);
196 void *user_data;
197
198 nvgpu_spinlock_acquire(&completion_cb->lock);
199 fn = completion_cb->fn;
200 user_data = completion_cb->user_data;
201 nvgpu_spinlock_release(&completion_cb->lock);
202
203 if (fn)
204 fn(ch, user_data);
205}
206
207static void nvgpu_channel_work_completion_init(struct channel_gk20a *ch)
208{
209 struct nvgpu_channel_linux *priv = ch->os_priv;
210
211 priv->completion_cb.fn = NULL;
212 priv->completion_cb.user_data = NULL;
213 nvgpu_spinlock_init(&priv->completion_cb.lock);
214 INIT_WORK(&priv->completion_cb.work, gk20a_channel_update_runcb_fn);
215}
216
217static void nvgpu_channel_work_completion_clear(struct channel_gk20a *ch)
218{
219 struct nvgpu_channel_linux *priv = ch->os_priv;
220
221 nvgpu_spinlock_acquire(&priv->completion_cb.lock);
222 priv->completion_cb.fn = NULL;
223 priv->completion_cb.user_data = NULL;
224 nvgpu_spinlock_release(&priv->completion_cb.lock);
225 cancel_work_sync(&priv->completion_cb.work);
226}
227
228static void nvgpu_channel_work_completion_signal(struct channel_gk20a *ch)
229{
230 struct nvgpu_channel_linux *priv = ch->os_priv;
231
232 if (priv->completion_cb.fn)
233 schedule_work(&priv->completion_cb.work);
234}
235
236static void nvgpu_channel_work_completion_cancel_sync(struct channel_gk20a *ch)
237{
238 struct nvgpu_channel_linux *priv = ch->os_priv;
239
240 if (priv->completion_cb.fn)
241 cancel_work_sync(&priv->completion_cb.work);
242}
243
244struct channel_gk20a *gk20a_open_new_channel_with_cb(struct gk20a *g,
245 void (*update_fn)(struct channel_gk20a *, void *),
246 void *update_fn_data,
247 int runlist_id,
248 bool is_privileged_channel)
249{
250 struct channel_gk20a *ch;
251 struct nvgpu_channel_linux *priv;
252
253 ch = gk20a_open_new_channel(g, runlist_id, is_privileged_channel,
254 nvgpu_current_pid(g), nvgpu_current_tid(g));
255
256 if (ch) {
257 priv = ch->os_priv;
258 nvgpu_spinlock_acquire(&priv->completion_cb.lock);
259 priv->completion_cb.fn = update_fn;
260 priv->completion_cb.user_data = update_fn_data;
261 nvgpu_spinlock_release(&priv->completion_cb.lock);
262 }
263
264 return ch;
265}
266
267static void nvgpu_channel_open_linux(struct channel_gk20a *ch)
268{
269}
270
271static void nvgpu_channel_close_linux(struct channel_gk20a *ch)
272{
273 nvgpu_channel_work_completion_clear(ch);
274
275#if defined(CONFIG_GK20A_CYCLE_STATS)
276 gk20a_channel_free_cycle_stats_buffer(ch);
277 gk20a_channel_free_cycle_stats_snapshot(ch);
278#endif
279}
280
281static int nvgpu_channel_alloc_linux(struct gk20a *g, struct channel_gk20a *ch)
282{
283 struct nvgpu_channel_linux *priv;
284 int err;
285
286 priv = nvgpu_kzalloc(g, sizeof(*priv));
287 if (!priv)
288 return -ENOMEM;
289
290 ch->os_priv = priv;
291 priv->ch = ch;
292
293#ifdef CONFIG_SYNC
294 ch->has_os_fence_framework_support = true;
295#endif
296
297 err = nvgpu_mutex_init(&priv->error_notifier.mutex);
298 if (err) {
299 nvgpu_kfree(g, priv);
300 return err;
301 }
302
303 nvgpu_channel_work_completion_init(ch);
304
305 return 0;
306}
307
308static void nvgpu_channel_free_linux(struct gk20a *g, struct channel_gk20a *ch)
309{
310 struct nvgpu_channel_linux *priv = ch->os_priv;
311
312 nvgpu_mutex_destroy(&priv->error_notifier.mutex);
313 nvgpu_kfree(g, priv);
314
315 ch->os_priv = NULL;
316
317#ifdef CONFIG_SYNC
318 ch->has_os_fence_framework_support = false;
319#endif
320}
321
322static int nvgpu_channel_init_os_fence_framework(struct channel_gk20a *ch,
323 const char *fmt, ...)
324{
325 struct nvgpu_channel_linux *priv = ch->os_priv;
326 struct nvgpu_os_fence_framework *fence_framework;
327 char name[30];
328 va_list args;
329
330 fence_framework = &priv->fence_framework;
331
332 va_start(args, fmt);
333 vsnprintf(name, sizeof(name), fmt, args);
334 va_end(args);
335
336 fence_framework->timeline = gk20a_sync_timeline_create(name);
337
338 if (!fence_framework->timeline)
339 return -EINVAL;
340
341 return 0;
342}
343static void nvgpu_channel_signal_os_fence_framework(struct channel_gk20a *ch)
344{
345 struct nvgpu_channel_linux *priv = ch->os_priv;
346 struct nvgpu_os_fence_framework *fence_framework;
347
348 fence_framework = &priv->fence_framework;
349
350 gk20a_sync_timeline_signal(fence_framework->timeline);
351}
352
353static void nvgpu_channel_destroy_os_fence_framework(struct channel_gk20a *ch)
354{
355 struct nvgpu_channel_linux *priv = ch->os_priv;
356 struct nvgpu_os_fence_framework *fence_framework;
357
358 fence_framework = &priv->fence_framework;
359
360 gk20a_sync_timeline_destroy(fence_framework->timeline);
361 fence_framework->timeline = NULL;
362}
363
364static bool nvgpu_channel_fence_framework_exists(struct channel_gk20a *ch)
365{
366 struct nvgpu_channel_linux *priv = ch->os_priv;
367 struct nvgpu_os_fence_framework *fence_framework;
368
369 fence_framework = &priv->fence_framework;
370
371 return (fence_framework->timeline != NULL);
372}
373
374static int nvgpu_channel_copy_user_gpfifo(struct nvgpu_gpfifo_entry *dest,
375 struct nvgpu_gpfifo_userdata userdata, u32 start, u32 length)
376{
377 struct nvgpu_gpfifo_entry __user *user_gpfifo = userdata.entries;
378 unsigned long n;
379
380 n = copy_from_user(dest, user_gpfifo + start,
381 length * sizeof(struct nvgpu_gpfifo_entry));
382
383 return n == 0 ? 0 : -EFAULT;
384}
385
386int nvgpu_init_channel_support_linux(struct nvgpu_os_linux *l)
387{
388 struct gk20a *g = &l->g;
389 struct fifo_gk20a *f = &g->fifo;
390 int chid;
391 int err;
392
393 for (chid = 0; chid < (int)f->num_channels; chid++) {
394 struct channel_gk20a *ch = &f->channel[chid];
395
396 err = nvgpu_channel_alloc_linux(g, ch);
397 if (err)
398 goto err_clean;
399 }
400
401 g->os_channel.open = nvgpu_channel_open_linux;
402 g->os_channel.close = nvgpu_channel_close_linux;
403 g->os_channel.work_completion_signal =
404 nvgpu_channel_work_completion_signal;
405 g->os_channel.work_completion_cancel_sync =
406 nvgpu_channel_work_completion_cancel_sync;
407
408 g->os_channel.os_fence_framework_inst_exists =
409 nvgpu_channel_fence_framework_exists;
410 g->os_channel.init_os_fence_framework =
411 nvgpu_channel_init_os_fence_framework;
412 g->os_channel.signal_os_fence_framework =
413 nvgpu_channel_signal_os_fence_framework;
414 g->os_channel.destroy_os_fence_framework =
415 nvgpu_channel_destroy_os_fence_framework;
416
417 g->os_channel.copy_user_gpfifo =
418 nvgpu_channel_copy_user_gpfifo;
419
420 return 0;
421
422err_clean:
423 for (; chid >= 0; chid--) {
424 struct channel_gk20a *ch = &f->channel[chid];
425
426 nvgpu_channel_free_linux(g, ch);
427 }
428 return err;
429}
430
431void nvgpu_remove_channel_support_linux(struct nvgpu_os_linux *l)
432{
433 struct gk20a *g = &l->g;
434 struct fifo_gk20a *f = &g->fifo;
435 unsigned int chid;
436
437 for (chid = 0; chid < f->num_channels; chid++) {
438 struct channel_gk20a *ch = &f->channel[chid];
439
440 nvgpu_channel_free_linux(g, ch);
441 }
442
443 g->os_channel.os_fence_framework_inst_exists = NULL;
444 g->os_channel.init_os_fence_framework = NULL;
445 g->os_channel.signal_os_fence_framework = NULL;
446 g->os_channel.destroy_os_fence_framework = NULL;
447}
448
449u32 nvgpu_get_gpfifo_entry_size(void)
450{
451 return sizeof(struct nvgpu_gpfifo_entry);
452}
453
454#ifdef CONFIG_DEBUG_FS
455static void trace_write_pushbuffer(struct channel_gk20a *c,
456 struct nvgpu_gpfifo_entry *g)
457{
458 void *mem = NULL;
459 unsigned int words;
460 u64 offset;
461 struct dma_buf *dmabuf = NULL;
462
463 if (gk20a_debug_trace_cmdbuf) {
464 u64 gpu_va = (u64)g->entry0 |
465 (u64)((u64)pbdma_gp_entry1_get_hi_v(g->entry1) << 32);
466 int err;
467
468 words = pbdma_gp_entry1_length_v(g->entry1);
469 err = nvgpu_vm_find_buf(c->vm, gpu_va, &dmabuf, &offset);
470 if (!err)
471 mem = dma_buf_vmap(dmabuf);
472 }
473
474 if (mem) {
475 u32 i;
476 /*
477 * Write in batches of 128 as there seems to be a limit
478 * of how much you can output to ftrace at once.
479 */
480 for (i = 0; i < words; i += 128U) {
481 trace_gk20a_push_cmdbuf(
482 c->g->name,
483 0,
484 min(words - i, 128U),
485 offset + i * sizeof(u32),
486 mem);
487 }
488 dma_buf_vunmap(dmabuf, mem);
489 }
490}
491
492void trace_write_pushbuffers(struct channel_gk20a *c, u32 count)
493{
494 struct nvgpu_gpfifo_entry *gp = c->gpfifo.mem.cpu_va;
495 u32 n = c->gpfifo.entry_num;
496 u32 start = c->gpfifo.put;
497 u32 i;
498
499 if (!gk20a_debug_trace_cmdbuf)
500 return;
501
502 if (!gp)
503 return;
504
505 for (i = 0; i < count; i++)
506 trace_write_pushbuffer(c, &gp[(start + i) % n]);
507}
508#endif