summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérôme Glisse <jglisse@redhat.com>2017-08-10 16:25:21 -0400
committerJérôme Glisse <jglisse@redhat.com>2017-09-05 14:52:37 -0400
commitf1fcc4ed7b234391e1aec6b7f3e1001e9729bdfe (patch)
treef8f8aff1b1f5debccf0b6887035e80304bca3e37
parent2d95783f7265e2ffae0aed70d196a495c951e809 (diff)
drm/nouveau/compote: add GPU page fault handler
Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/compote/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-driver.c7
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-pfault.c172
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote.h5
4 files changed, 185 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/compote/Kbuild b/drivers/gpu/drm/nouveau/compote/Kbuild
index 76de5f87299b..c210eb8f60ea 100644
--- a/drivers/gpu/drm/nouveau/compote/Kbuild
+++ b/drivers/gpu/drm/nouveau/compote/Kbuild
@@ -3,4 +3,5 @@ compote-y := compote/compote-driver.o
compote-y += compote/compote-memory.o
compote-y += compote/compote-channel.o
compote-y += compote/compote-hmm.o
+compote-y += compote/compote-pfault.o
endif
diff --git a/drivers/gpu/drm/nouveau/compote/compote-driver.c b/drivers/gpu/drm/nouveau/compote/compote-driver.c
index 9ffd966546b9..8ed833683772 100644
--- a/drivers/gpu/drm/nouveau/compote/compote-driver.c
+++ b/drivers/gpu/drm/nouveau/compote/compote-driver.c
@@ -132,6 +132,10 @@ static int compote_open(struct inode *inode, struct file *file)
if (ret)
goto error_vm;
+ ret = compote_file_pfault_init(cfile);
+ if (ret)
+ goto error_pfault;
+
/* share address_space across all char-devs of a single device */
file->f_mapping = cdevice->nvdrm->dev->anon_inode->i_mapping;
@@ -141,6 +145,8 @@ static int compote_open(struct inode *inode, struct file *file)
file->private_data = cfile;
return 0;
+error_pfault:
+ compote_file_hmm_fini(cfile);
error_vm:
compote_client_fini(&cfile->nvclient);
error:
@@ -167,6 +173,7 @@ static int compote_close(struct inode *inode, struct file *file)
compote_channel_unref(channel);
}
+ compote_file_pfault_fini(cfile);
compote_file_hmm_fini(cfile);
compote_client_fini(&cfile->nvclient);
diff --git a/drivers/gpu/drm/nouveau/compote/compote-pfault.c b/drivers/gpu/drm/nouveau/compote/compote-pfault.c
new file mode 100644
index 000000000000..f46c13fad44d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote-pfault.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors: Jérôme Glisse <jglisse@redhat.com>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <uapi/drm/compote-uapi.h>
+#include "compote.h"
+
+#include <nvif/driver.h>
+#include <nvif/class.h>
+#include <nvif/clb069.h>
+
+#include "nouveau_ttm.h"
+#include "nouveau_usif.h"
+#include "nouveau_abi16.h"
+
+struct compote_pfault {
+ uint64_t inst;
+ uint64_t addr;
+ uint64_t time;
+ uint32_t rsvd;
+ uint32_t info;
+};
+
+static inline uint32_t compote_pfault_valid(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x80000000) >> 31;
+}
+
+static inline uint32_t compote_pfault_gpc(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x1f000000) >> 24;
+}
+
+static inline uint32_t compote_pfault_isgpc(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x00100000) >> 20;
+}
+
+static inline uint32_t compote_pfault_access(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x00070000) >> 16;
+}
+
+static inline uint32_t compote_pfault_client(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x00007f00) >> 8;
+}
+
+static inline uint32_t compote_pfault_fault(const struct compote_pfault *pfault)
+{
+ return (pfault->info & 0x0000001f) >> 0;
+}
+
+static inline void compote_file_pfault_read(struct compote_file *cfile,
+ struct compote_pfault *pfault,
+ uint32_t index)
+{
+ index *= 32;
+ pfault->inst = nvif_rd32(&cfile->nvpfault_buffer, index + 0x00);
+ pfault->inst |= ((uint64_t)nvif_rd32(&cfile->nvpfault_buffer, index + 0x04)) << 32;
+ pfault->addr = nvif_rd32(&cfile->nvpfault_buffer, index + 0x08);
+ pfault->addr |= ((uint64_t)nvif_rd32(&cfile->nvpfault_buffer, index + 0x0c)) << 32;
+ pfault->time = nvif_rd32(&cfile->nvpfault_buffer, index + 0x10);
+ pfault->time |= ((uint64_t)nvif_rd32(&cfile->nvpfault_buffer, index + 0x14)) << 32;
+ pfault->rsvd = nvif_rd32(&cfile->nvpfault_buffer, index + 0x18);
+ pfault->info = nvif_rd32(&cfile->nvpfault_buffer, index + 0x1c);
+}
+
+static int compote_pfault_handler(struct compote_file *cfile,
+ const struct compote_pfault *pfault)
+{
+ return -EINVAL;
+}
+
+static int compote_file_pfault_process(struct nvif_notify *notify)
+{
+ struct compote_file *cfile;
+ struct nvif_device *device;
+ uint32_t get, put;
+
+ cfile = container_of(notify, struct compote_file, nvpfault_notifier);
+ device = &cfile->nvclient.device;
+
+ get = nvif_rd32(&device->object, 0x002a7c);
+ put = nvif_rd32(&device->object, 0x002a80);
+
+ for (; get != put; get++) {
+ struct compote_pfault pfault;
+ int ret;
+
+ compote_file_pfault_read(cfile, &pfault, get);
+
+ nvif_wr32(&device->object, 0x002a7c, get + 1);
+ if (!compote_pfault_valid(&pfault))
+ continue;
+
+ nvif_mask(&cfile->nvpfault_buffer, 0x1c, 0x80000000, 0x00000000);
+
+ ret = compote_pfault_handler(cfile, &pfault);
+ if (ret) {
+ /* Kill threads */
+ nvif_wr32(&device->object, 0x100cbc, 0x80000000 | (4 << 3) |
+ (compote_pfault_client(&pfault) << 9) |
+ (compote_pfault_gpc(&pfault) << 15) |
+ (compote_pfault_isgpc(&pfault) << 20));
+ } else {
+ /* Resume threads */
+ nvif_wr32(&device->object, 0x100cbc, 0x80000000 | (1 << 3) |
+ (compote_pfault_client(&pfault) << 9) |
+ (compote_pfault_gpc(&pfault) << 15) |
+ (compote_pfault_isgpc(&pfault) << 20));
+ }
+ }
+
+ return NVIF_NOTIFY_KEEP;
+}
+
+int compote_file_pfault_init(struct compote_file *cfile)
+{
+ struct nvif_device *device = &cfile->nvclient.device;
+ int ret;
+
+ /* Allocate replayable fault buffer. */
+ ret = nvif_object_init(&device->object, 0, MAXWELL_FAULT_BUFFER_A,
+ NULL, 0, &cfile->nvpfault_buffer);
+ if (ret)
+ return ret;
+ nvif_object_map(&cfile->nvpfault_buffer);
+
+ /* Request notification of pending replayable faults. */
+ ret = nvif_notify_init(&cfile->nvpfault_buffer,
+ compote_file_pfault_process,
+ true, NVB069_VN_NTFY_FAULT,
+ NULL, 0, 0,
+ &cfile->nvpfault_notifier);
+ if (ret)
+ goto error;
+ ret = nvif_notify_get(&cfile->nvpfault_notifier);
+ if (ret)
+ goto error_get;
+
+ return 0;
+
+error_get:
+ nvif_notify_fini(&cfile->nvpfault_notifier);
+error:
+ nvif_object_fini(&cfile->nvpfault_buffer);
+ return ret;
+}
+
+void compote_file_pfault_fini(struct compote_file *cfile)
+{
+ nvif_notify_put(&cfile->nvpfault_notifier);
+ nvif_notify_fini(&cfile->nvpfault_notifier);
+ nvif_object_fini(&cfile->nvpfault_buffer);
+}
diff --git a/drivers/gpu/drm/nouveau/compote/compote.h b/drivers/gpu/drm/nouveau/compote/compote.h
index 23900689409a..b0fff7fa7cb9 100644
--- a/drivers/gpu/drm/nouveau/compote/compote.h
+++ b/drivers/gpu/drm/nouveau/compote/compote.h
@@ -38,6 +38,8 @@ struct compote_file {
struct rw_semaphore rwsem;
struct mm_struct *mm;
struct hmm_mirror mirror;
+ struct nvif_notify nvpfault_notifier;
+ struct nvif_object nvpfault_buffer;
};
struct compote_mo {
@@ -84,4 +86,7 @@ int compote_mo_mmap(struct compote_file *cfile,
int compote_file_hmm_init(struct compote_file *cfile);
void compote_file_hmm_fini(struct compote_file *cfile);
+int compote_file_pfault_init(struct compote_file *cfile);
+void compote_file_pfault_fini(struct compote_file *cfile);
+
#endif /* COMPOTE_H */