summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérôme Glisse <jglisse@redhat.com>2017-07-25 12:52:49 -0400
committerJérôme Glisse <jglisse@redhat.com>2017-08-29 10:35:00 -0400
commit5e6a91b62e3926d3ed26949ddd48597cd9e47f7d (patch)
tree58bd1b3bd3c93c63ce9eed0801ece7dd25225093
parent89d00fdb42239f470b1f312a6c6ac2693ab75a77 (diff)
drm/nouveau/compote: GPU compute on top of nouveau
Starting with Kepler GPU we can do unified memory for compute. With Pascal we can even transparently share the same virtual address space on the GPU as on the CPU. Compote is an attempt to prototype a new set of API for userspace to leverage those features. Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/Kbuild4
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig11
-rw-r--r--drivers/gpu/drm/nouveau/compote/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-driver.c299
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-interface.h48
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote.h37
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c13
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h3
8 files changed, 418 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/Kbuild b/drivers/gpu/drm/nouveau/Kbuild
index 2e9ce53ae3a8..a5fc736b367d 100644
--- a/drivers/gpu/drm/nouveau/Kbuild
+++ b/drivers/gpu/drm/nouveau/Kbuild
@@ -13,6 +13,10 @@ nouveau-y := $(nvif-y)
include $(src)/nvkm/Kbuild
nouveau-y += $(nvkm-y)
+# COMPOTE - Compute experimental API
+include $(src)/compote/Kbuild
+nouveau-y += $(compote-y)
+
# DRM - general
ifdef CONFIG_X86
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index c02a13406a81..f06567d91854 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -63,3 +63,14 @@ config DRM_NOUVEAU_BACKLIGHT
help
Say Y here if you want to control the backlight of your display
(e.g. a laptop panel).
+
+config DRM_COMPOTE
+ bool "Experimental GPU compute with nouveau"
+ depends on DRM_NOUVEAU
+ default n
+ help
+ EXPERIMENTAL DO NOT USE OUTSIDE HELL !
+
+ Compote provide experimental API for GPU compute outside of nouveau
+ reguler userspace API. It is experimental and it should not be use
+ for any production developmenet.
diff --git a/drivers/gpu/drm/nouveau/compote/Kbuild b/drivers/gpu/drm/nouveau/compote/Kbuild
new file mode 100644
index 000000000000..89af6841ab15
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/Kbuild
@@ -0,0 +1,3 @@
+ifdef CONFIG_DRM_COMPOTE
+compote-y := compote/compote-driver.o
+endif
diff --git a/drivers/gpu/drm/nouveau/compote/compote-driver.c b/drivers/gpu/drm/nouveau/compote/compote-driver.c
new file mode 100644
index 000000000000..409e4c803c26
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote-driver.c
@@ -0,0 +1,299 @@
+/*
+ * 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 "compote.h"
+
+#include <nvif/driver.h>
+#include <nvif/class.h>
+
+#include "nouveau_ttm.h"
+#include "nouveau_usif.h"
+#include "nouveau_abi16.h"
+
+static DEFINE_SPINLOCK(compote_minor_lock);
+static DECLARE_BITMAP(compote_minor, 256);
+static struct class *compote_class = NULL;
+static dev_t _compote_devt;
+
+static u64 compote_name(struct nouveau_drm *nvdrm)
+{
+ struct pci_dev *pdev = nvdrm->dev->pdev;
+
+ u64 name = (u64)pci_domain_nr(pdev->bus) << 32;
+ name |= pdev->bus->number << 16;
+ name |= PCI_SLOT(pdev->devfn) << 8;
+ return name | PCI_FUNC(pdev->devfn);
+}
+
+static void compote_client_fini(struct nouveau_cli *nvclient)
+{
+ if (nvclient->vm)
+ nvkm_vm_ref(NULL, &nvclient->vm, NULL);
+ usif_client_fini(nvclient);
+ nvif_device_fini(&nvclient->device);
+ nvif_client_fini(&nvclient->base);
+}
+
+static int compote_client_init(struct nouveau_cli *nvclient,
+ struct nouveau_drm *nvdrm,
+ const char *sname)
+{
+ u64 device = compote_name(nvdrm);
+ int ret;
+
+ snprintf(nvclient->name, sizeof(nvclient->name), "%s", sname);
+ nvclient->dev = nvdrm->dev;
+ mutex_init(&nvclient->mutex);
+ usif_client_init(nvclient);
+
+ ret = nvif_client_init(&nvdrm->client.base, nvclient->name,
+ device, &nvclient->base);
+ if (ret) {
+ printk(KERN_ERR "nvif_client_init(): %d\n", ret);
+ goto error;
+ }
+
+ ret = nvif_device_init(&nvclient->base.object, 0, NV_DEVICE,
+ &(struct nv_device_v0) {
+ .device = ~0,
+ }, sizeof(struct nv_device_v0),
+ &nvclient->device);
+ if (ret) {
+ printk(KERN_ERR "nvif_device_init(): %d\n", ret);
+ goto error;
+ }
+ return 0;
+
+error:
+ compote_client_fini(nvclient);
+ return ret;
+}
+
+static int compote_open(struct inode *inode, struct file *file)
+{
+ struct compote_file *cfile = kzalloc(sizeof(*cfile), GFP_KERNEL);
+ char name[TASK_COMM_LEN + 8], tmpname[TASK_COMM_LEN];
+ struct compote_device *cdevice;
+ int ret;
+
+ if(cfile == NULL)
+ return -ENOMEM;
+
+ /* need to bring up power immediately if opening device */
+ cdevice = container_of(inode->i_cdev, struct compote_device, cdev);
+ ret = pm_runtime_get_sync(cdevice->nvdrm->dev->dev);
+ if (ret < 0 && ret != -EACCES) {
+ kfree(cfile);
+ return ret;
+ }
+
+ get_task_comm(tmpname, current);
+ snprintf(name, sizeof(name), "%s[%d]", tmpname,
+ pid_nr(get_pid(task_pid(current))));
+
+ cfile->cdevice = cdevice;
+ cfile->file = file;
+
+ ret = compote_client_init(&cfile->nvclient, cdevice->nvdrm, name);
+ if (ret)
+ goto error;
+
+ /*
+ * Too many place in nouveau that rely on 40bits so stick with that for
+ * now.
+ */
+ ret = nvkm_vm_new(nvxx_device(&cdevice->nvdrm->client.device), 0,
+ (1ULL << 40), 0x1000, NULL, &cfile->nvclient.vm);
+ if (ret)
+ goto error_vm;
+ nvxx_client(&cfile->nvclient.base)->vm = cfile->nvclient.vm;
+
+ /* share address_space across all char-devs of a single device */
+ file->f_mapping = cdevice->nvdrm->dev->anon_inode->i_mapping;
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+ cfile->nvclient.base.super = false;
+ file->private_data = cfile;
+ return 0;
+
+error_vm:
+ compote_client_fini(&cfile->nvclient);
+error:
+ kfree(cfile);
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+
+ return ret;
+}
+
+static int compote_close(struct inode *inode, struct file *file)
+{
+ struct compote_file *cfile = file->private_data;
+ struct compote_device *cdevice;
+
+ if(cfile == NULL)
+ return 0;
+
+ pm_runtime_get_sync(cfile->cdevice->nvdrm->dev->dev);
+
+ compote_client_fini(&cfile->nvclient);
+ cdevice = cfile->cdevice;
+ kfree(cfile);
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+
+ return 0;
+}
+
+static long compote_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return -EINVAL;
+}
+
+static const struct file_operations compote_fops = {
+ .open = compote_open,
+ .release = compote_close,
+ .unlocked_ioctl = compote_ioctl,
+ .owner = THIS_MODULE,
+};
+
+static bool compote_minor_get(int *minor)
+{
+ spin_lock(&compote_minor_lock);
+ *minor = bitmap_find_next_zero_area(compote_minor, 256, 0, 1, 0);
+ if ((*minor) < 256) {
+ bitmap_set(compote_minor, *minor, 1);
+ spin_unlock(&compote_minor_lock);
+ return true;
+ }
+ spin_unlock(&compote_minor_lock);
+ return false;
+}
+
+static void compote_minor_put(int minor)
+{
+ spin_lock(&compote_minor_lock);
+ bitmap_clear(compote_minor, minor, 1);
+ spin_unlock(&compote_minor_lock);
+}
+
+void compote_device_fini(void *compote_device)
+{
+ struct compote_device *cdevice = compote_device;
+
+ if (cdevice == NULL)
+ return;
+
+ if (cdevice->device) {
+ dev_t dev = MKDEV(MAJOR(_compote_devt), cdevice->minor);
+
+ device_destroy(compote_class, dev);
+ cdev_del(&cdevice->cdev);
+ }
+
+ compote_minor_put(cdevice->minor);
+ kfree(cdevice);
+}
+
+void *compote_device_init(struct nouveau_drm *nvdrm)
+{
+ const struct nv_device_info_v0 *devinfo = &nvdrm->client.device.info;
+ struct compote_device *cdevice;
+ dev_t dev;
+ int ret;
+
+ /* Only support maxwell and pascal */
+ switch (devinfo->family) {
+ case NV_DEVICE_INFO_V0_MAXWELL:
+ case NV_DEVICE_INFO_V0_PASCAL:
+ break;
+ default:
+ return NULL;
+ }
+
+ cdevice = kzalloc(sizeof(*cdevice), GFP_KERNEL);
+ if (cdevice == NULL)
+ return NULL;
+
+ cdevice->chipset = devinfo->chipset;
+ cdevice->nvdrm = nvdrm;
+
+ if (!compote_minor_get(&cdevice->minor)) {
+ kfree(cdevice);
+ return NULL;
+ }
+
+ dev = MKDEV(MAJOR(_compote_devt), cdevice->minor);
+ cdev_init(&cdevice->cdev, &compote_fops);
+ cdevice->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&cdevice->cdev, dev, 1);
+ if (ret) {
+ kfree(cdevice);
+ return NULL;
+ }
+
+ cdevice->device = device_create(compote_class, nvdrm->dev->dev, dev,
+ NULL, "%s%d", COMPOTE_DEVICE_NAME,
+ cdevice->minor);
+ if (cdevice->device == NULL) {
+ cdev_del(&cdevice->cdev);
+ compote_device_fini(cdevice);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s%d for 0x%02x chipset\n", COMPOTE_DEVICE_NAME,
+ cdevice->minor, cdevice->chipset);
+ return cdevice;
+}
+
+void compote_exit(void)
+{
+ if (!compote_class)
+ return;
+
+ class_destroy(compote_class);
+ unregister_chrdev_region(_compote_devt, 256);
+}
+
+int compote_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&_compote_devt, 0, 256, COMPOTE_DEVICE_NAME);
+ if (ret)
+ return ret;
+
+ compote_class = class_create(THIS_MODULE, COMPOTE_DEVICE_NAME);
+ if (IS_ERR(compote_class)) {
+ int ret = PTR_ERR(compote_class);
+
+ unregister_chrdev_region(_compote_devt, 256);
+ compote_class = NULL;
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/compote/compote-interface.h b/drivers/gpu/drm/nouveau/compote/compote-interface.h
new file mode 100644
index 000000000000..f29f9c72432f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote-interface.h
@@ -0,0 +1,48 @@
+/*
+ * 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>
+ */
+#ifndef COMPOTE_INTERFACE_H
+#define COMPOTE_INTERFACE_H
+
+#include <linux/kconfig.h>
+
+#if IS_ENABLED(CONFIG_DRM_COMPOTE)
+int compote_init(void);
+void compote_exit(void);
+void compote_device_fini(void *compote_device);
+void *compote_device_init(struct nouveau_drm *nvdrm);
+
+#else /* IS_ENABLED(CONFIG_DRM_COMPOTE) */
+
+static inline int compote_init(void)
+{
+ return 0;
+}
+
+static inline void compote_exit(void)
+{
+}
+
+static inline void *compote_device_init(struct nouveau_drm *nvdrm)
+{
+ return NULL;
+}
+
+static inline void compote_device_fini(void *compote_device)
+{
+}
+#endif /* IS_ENABLED(CONFIG_DRM_COMPOTE) */
+
+#endif /* COMPOTE_INTERFACE_H */
diff --git a/drivers/gpu/drm/nouveau/compote/compote.h b/drivers/gpu/drm/nouveau/compote/compote.h
new file mode 100644
index 000000000000..40f437703259
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote.h
@@ -0,0 +1,37 @@
+/*
+ * 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>
+ */
+#ifndef COMPOTE_H
+#define COMPOTE_H
+
+#include "nouveau_drv.h"
+
+#define COMPOTE_DEVICE_NAME "compote"
+
+struct compote_device {
+ struct device *device;
+ struct nouveau_drm *nvdrm;
+ struct cdev cdev;
+ int minor;
+ uint8_t chipset;
+};
+
+struct compote_file {
+ struct compote_device *cdevice;
+ struct file *file;
+ struct nouveau_cli nvclient;
+};
+
+#endif /* COMPOTE_H */
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 90757af9bc73..0b7fe600239b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -61,6 +61,7 @@
#include "nouveau_usif.h"
#include "nouveau_connector.h"
#include "nouveau_platform.h"
+#include "compote/compote-interface.h"
MODULE_PARM_DESC(config, "option string to pass to driver core");
static char *nouveau_config;
@@ -495,6 +496,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
nouveau_fbcon_init(dev);
nouveau_led_init(dev);
+ drm->compote = compote_device_init(drm);
+
if (nouveau_pmops_runtime()) {
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_autosuspend_delay(dev->dev, 5000);
@@ -532,6 +535,8 @@ nouveau_drm_unload(struct drm_device *dev)
pm_runtime_forbid(dev->dev);
}
+ compote_device_fini(drm->compote);
+
nouveau_led_fini(dev);
nouveau_fbcon_fini(dev);
nouveau_accel_fini(drm);
@@ -1097,6 +1102,12 @@ err_free:
static int __init
nouveau_drm_init(void)
{
+ int ret;
+
+ ret = compote_init();
+ if (ret)
+ return ret;
+
driver_pci = driver_stub;
driver_pci.set_busid = drm_pci_set_busid;
driver_platform = driver_stub;
@@ -1133,6 +1144,8 @@ nouveau_drm_exit(void)
#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER
platform_driver_unregister(&nouveau_platform_driver);
#endif
+
+ compote_exit();
}
module_init(nouveau_drm_init);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 822fe1d4d35e..84a111777256 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -112,6 +112,9 @@ struct nouveau_drm {
struct nouveau_cli client;
struct drm_device *dev;
+ /* Experimental GPU compote */
+ void *compote;
+
struct list_head clients;
struct {