gpu: nvgpu: add kernel APIs for TSG support
Deepak Nibade [Wed, 11 Jun 2014 11:15:54 +0000 (16:15 +0530)]
Add support to create/destroy TSGs using node "/dev/nvhost-tsg-gpu"

Provide below IOCTLs to bind/unbind channels to/from TSGs :

NVGPU_TSG_IOCTL_BIND_CHANNEL
NVGPU_TSG_IOCTL_UNBIND_CHANNEL

Bug 1470692

Change-Id: Iaf9f16a522379eb943906624548f8d28fc6d4486
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Signed-off-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-on: http://git-master/r/416610

drivers/gpu/nvgpu/gk20a/Makefile
drivers/gpu/nvgpu/gk20a/channel_gk20a.c
drivers/gpu/nvgpu/gk20a/channel_gk20a.h
drivers/gpu/nvgpu/gk20a/fifo_gk20a.c
drivers/gpu/nvgpu/gk20a/fifo_gk20a.h
drivers/gpu/nvgpu/gk20a/gk20a.c
drivers/gpu/nvgpu/gk20a/gk20a.h
drivers/gpu/nvgpu/gk20a/tsg_gk20a.c [new file with mode: 0644]
drivers/gpu/nvgpu/gk20a/tsg_gk20a.h [new file with mode: 0644]
include/linux/nvhost_gpu_ioctl.h

index a0fc9f6..d234db5 100644 (file)
@@ -33,7 +33,8 @@ nvgpu-y := \
        hal.o \
        hal_gk20a.o \
        gk20a_allocator.o \
-       platform_gk20a_generic.o
+       platform_gk20a_generic.o \
+       tsg_gk20a.o
 nvgpu-$(CONFIG_TEGRA_GK20A) += platform_gk20a_tegra.o
 nvgpu-$(CONFIG_SYNC) += sync_gk20a.o
 
index 486e815..ef7195a 100644 (file)
@@ -747,6 +747,9 @@ static struct channel_gk20a *gk20a_open_new_channel(struct gk20a *g)
        g->ops.fifo.bind_channel(ch);
        ch->pid = current->pid;
 
+       /* By default, channel is regular (non-TSG) channel */
+       ch->tsgid = NVGPU_INVALID_TSG_ID;
+
        /* reset timeout counter and update timestamp */
        ch->timeout_accumulated_ms = 0;
        ch->timeout_gpfifo_get = 0;
index 36697e0..4d236a7 100644 (file)
@@ -83,6 +83,9 @@ struct channel_gk20a {
        bool vpr;
        pid_t pid;
 
+       int tsgid;
+       struct list_head ch_entry; /* channel's entry in TSG */
+
        struct list_head jobs;
        struct mutex jobs_lock;
        struct mutex submit_lock;
index 3c0611e..e9febb7 100644 (file)
@@ -538,6 +538,8 @@ static int gk20a_init_fifo_setup_sw(struct gk20a *g)
 
        f->channel = kzalloc(f->num_channels * sizeof(*f->channel),
                                GFP_KERNEL);
+       f->tsg = kzalloc(f->num_channels * sizeof(*f->tsg),
+                               GFP_KERNEL);
        f->pbdma_map = kzalloc(f->num_pbdma * sizeof(*f->pbdma_map),
                                GFP_KERNEL);
        f->engine_info = kzalloc(f->max_engines * sizeof(*f->engine_info),
@@ -566,8 +568,10 @@ static int gk20a_init_fifo_setup_sw(struct gk20a *g)
                        f->userd.gpu_va + chid * f->userd_entry_size;
 
                gk20a_init_channel_support(g, chid);
+               gk20a_init_tsg_support(g, chid);
        }
        mutex_init(&f->ch_inuse_mutex);
+       mutex_init(&f->tsg_inuse_mutex);
 
        f->remove_support = gk20a_remove_fifo_support;
 
index 8a4e0a8..f94fce0 100644 (file)
@@ -22,6 +22,7 @@
 #define __FIFO_GK20A_H__
 
 #include "channel_gk20a.h"
+#include "tsg_gk20a.h"
 
 #define MAX_RUNLIST_BUFFERS    2
 
@@ -111,6 +112,9 @@ struct fifo_gk20a {
        struct channel_gk20a *channel;
        struct mutex ch_inuse_mutex; /* protect unused chid look up */
 
+       struct tsg_gk20a *tsg;
+       struct mutex tsg_inuse_mutex;
+
        void (*remove_support)(struct fifo_gk20a *);
        bool sw_ready;
        struct {
index 4bd469d..791926a 100644 (file)
@@ -70,7 +70,7 @@
 /* TODO: Change to e.g. "nvidia-gpu%s" once we have symlinks in place. */
 #define INTERFACE_NAME "nvhost%s-gpu"
 
-#define GK20A_NUM_CDEVS 5
+#define GK20A_NUM_CDEVS 6
 
 #if defined(GK20A_DEBUG)
 u32 gk20a_dbg_mask = GK20A_DEFAULT_DBG_MASK;
@@ -145,6 +145,16 @@ static const struct file_operations gk20a_prof_ops = {
 #endif
 };
 
+static const struct file_operations gk20a_tsg_ops = {
+       .owner = THIS_MODULE,
+       .release = gk20a_tsg_dev_release,
+       .open = gk20a_tsg_dev_open,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = gk20a_tsg_dev_ioctl,
+#endif
+       .unlocked_ioctl = gk20a_tsg_dev_ioctl,
+};
+
 static inline void sim_writel(struct gk20a *g, u32 r, u32 v)
 {
        writel(v, g->sim.regs+r);
@@ -1062,6 +1072,11 @@ static void gk20a_user_deinit(struct platform_device *dev)
                cdev_del(&g->prof.cdev);
        }
 
+       if (g->tsg.node) {
+               device_destroy(g->class, g->tsg.cdev.dev);
+               cdev_del(&g->tsg.cdev);
+       }
+
        if (g->cdev_region)
                unregister_chrdev_region(g->cdev_region, GK20A_NUM_CDEVS);
 
@@ -1121,6 +1136,12 @@ static int gk20a_user_init(struct platform_device *dev)
        if (err)
                goto fail;
 
+       err = gk20a_create_device(dev, devno++, "-tsg",
+                                 &g->tsg.cdev, &g->tsg.node,
+                                 &gk20a_tsg_ops);
+       if (err)
+               goto fail;
+
        return 0;
 fail:
        gk20a_user_deinit(dev);
index 9769ac9..60b9b80 100644 (file)
@@ -40,6 +40,7 @@ struct acr_gm20b;
 #include "as_gk20a.h"
 #include "clk_gk20a.h"
 #include "fifo_gk20a.h"
+#include "tsg_gk20a.h"
 #include "gr_gk20a.h"
 #include "sim_gk20a.h"
 #include "pmu_gk20a.h"
@@ -308,6 +309,11 @@ struct gk20a {
                struct device *node;
        } prof;
 
+       struct {
+               struct cdev cdev;
+               struct device *node;
+       } tsg;
+
        struct mutex client_lock;
        int client_refcount; /* open channels and ctrl nodes */
 
diff --git a/drivers/gpu/nvgpu/gk20a/tsg_gk20a.c b/drivers/gpu/nvgpu/gk20a/tsg_gk20a.c
new file mode 100644 (file)
index 0000000..d9e10d3
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/nvhost.h>
+#include <linux/nvhost_gpu_ioctl.h>
+#include <linux/anon_inodes.h>
+
+#include "gk20a.h"
+
+bool gk20a_is_channel_marked_as_tsg(struct channel_gk20a *ch)
+{
+       return !(ch->tsgid == NVGPU_INVALID_TSG_ID);
+}
+
+/*
+ * API to add channel to runnable list of TSG.
+ *
+ * After this call, a channel will be scheduled as TSG channel
+ * in runlist
+ */
+int gk20a_bind_runnable_channel_to_tsg(struct channel_gk20a *ch, int tsgid)
+{
+       struct gk20a *g = ch->g;
+       struct tsg_gk20a *tsg = NULL;
+
+       if (ch->tsgid != tsgid)
+               return -EINVAL;
+
+       tsg = &g->fifo.tsg[tsgid];
+
+       mutex_lock(&tsg->ch_list_lock);
+       list_add_tail(&ch->ch_entry, &tsg->ch_runnable_list);
+       tsg->num_runnable_channels += 1;
+       mutex_unlock(&tsg->ch_list_lock);
+
+       return 0;
+}
+
+int gk20a_unbind_channel_from_tsg(struct channel_gk20a *ch, int tsgid)
+{
+       struct gk20a *g = ch->g;
+       struct tsg_gk20a *tsg = NULL;
+
+       if (ch->tsgid != tsgid)
+               return -EINVAL;
+
+       tsg = &g->fifo.tsg[tsgid];
+
+       mutex_lock(&tsg->ch_list_lock);
+       list_del_init(&ch->ch_entry);
+       tsg->num_runnable_channels -= 1;
+       mutex_unlock(&tsg->ch_list_lock);
+
+       return 0;
+}
+
+/*
+ * API to mark channel as part of TSG
+ *
+ * Note that channel is not runnable when we bind it to TSG
+ */
+static int nvgpu_tsg_bind_channel(struct tsg_gk20a *tsg, int ch_fd)
+{
+       struct file *f = fget(ch_fd);
+       struct channel_gk20a *ch = f->private_data;
+
+       /* check if channel is already bound to some TSG */
+       if (gk20a_is_channel_marked_as_tsg(ch))
+               return -EINVAL;
+
+       ch->tsgid = tsg->tsgid;
+
+       gk20a_dbg(gpu_dbg_fn, "BIND tsg:%d channel:%d\n",
+                                       tsg->tsgid, ch->hw_chid);
+
+       fput(f);
+
+       return 0;
+}
+
+static int nvgpu_tsg_unbind_channel(struct tsg_gk20a *tsg, int ch_fd)
+{
+       /* We do not support explicitly unbinding channel from TSG.
+        * Channel will be unbounded from TSG when it is closed.
+        */
+       return 0;
+}
+
+int gk20a_init_tsg_support(struct gk20a *g, u32 tsgid)
+{
+       struct tsg_gk20a *tsg = NULL;
+
+       if (tsgid < 0 || tsgid >= g->fifo.num_channels)
+               return -EINVAL;
+
+       tsg = &g->fifo.tsg[tsgid];
+
+       tsg->in_use = false;
+       tsg->tsgid = tsgid;
+
+       INIT_LIST_HEAD(&tsg->ch_runnable_list);
+       mutex_init(&tsg->ch_list_lock);
+
+       return 0;
+}
+
+static void release_used_tsg(struct fifo_gk20a *f, struct tsg_gk20a *tsg)
+{
+       mutex_lock(&f->tsg_inuse_mutex);
+       f->tsg[tsg->tsgid].in_use = false;
+       mutex_unlock(&f->tsg_inuse_mutex);
+}
+
+static struct tsg_gk20a *acquire_unused_tsg(struct fifo_gk20a *f)
+{
+       struct tsg_gk20a *tsg = NULL;
+       int tsgid;
+
+       mutex_lock(&f->tsg_inuse_mutex);
+       for (tsgid = 0; tsgid < f->num_channels; tsgid++) {
+               if (!f->tsg[tsgid].in_use) {
+                       f->tsg[tsgid].in_use = true;
+                       tsg = &f->tsg[tsgid];
+                       break;
+               }
+       }
+       mutex_unlock(&f->tsg_inuse_mutex);
+
+       return tsg;
+}
+
+int gk20a_tsg_dev_open(struct inode *inode, struct file *filp)
+{
+       struct tsg_gk20a *tsg;
+       struct gk20a *g;
+       struct device *dev;
+
+       g = container_of(inode->i_cdev,
+                        struct gk20a, tsg.cdev);
+       dev  = dev_from_gk20a(g);
+
+       gk20a_dbg(gpu_dbg_fn, "tsg: %s", dev_name(dev));
+
+       tsg = acquire_unused_tsg(&g->fifo);
+       if (!tsg)
+               return -ENOMEM;
+
+       tsg->g = g;
+       tsg->num_runnable_channels = 0;
+
+       filp->private_data = tsg;
+
+       gk20a_dbg(gpu_dbg_fn, "tsg opened %d\n", tsg->tsgid);
+
+       return 0;
+}
+
+int gk20a_tsg_dev_release(struct inode *inode, struct file *filp)
+{
+       struct tsg_gk20a *tsg = filp->private_data;
+       struct gk20a *g = container_of(inode->i_cdev,
+                        struct gk20a, tsg.cdev);
+
+       if (tsg->num_runnable_channels) {
+               gk20a_err(dev_from_gk20a(g),
+                       "Trying to free TSG %d with active channels %d\n",
+                       tsg->tsgid, tsg->num_runnable_channels);
+               return -EBUSY;
+       }
+
+       release_used_tsg(&g->fifo, tsg);
+
+       gk20a_dbg(gpu_dbg_fn, "tsg released %d\n", tsg->tsgid);
+
+       return 0;
+}
+
+long gk20a_tsg_dev_ioctl(struct file *filp, unsigned int cmd,
+                            unsigned long arg)
+{
+       struct tsg_gk20a *tsg = filp->private_data;
+       struct gk20a *g = tsg->g;
+       u8 __maybe_unused buf[NVGPU_TSG_IOCTL_MAX_ARG_SIZE];
+       int err = 0;
+
+       gk20a_dbg(gpu_dbg_fn, "");
+
+       if ((_IOC_TYPE(cmd) != NVGPU_TSG_IOCTL_MAGIC) ||
+           (_IOC_NR(cmd) == 0) ||
+           (_IOC_NR(cmd) > NVGPU_TSG_IOCTL_LAST))
+               return -EFAULT;
+
+       BUG_ON(_IOC_SIZE(cmd) > NVGPU_TSG_IOCTL_MAX_ARG_SIZE);
+
+       if (_IOC_DIR(cmd) & _IOC_WRITE) {
+               if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd)))
+                       return -EFAULT;
+       }
+
+       if (!g->gr.sw_ready) {
+               err = gk20a_busy(g->dev);
+               if (err)
+                       return err;
+
+               gk20a_idle(g->dev);
+       }
+
+       switch (cmd) {
+       case NVGPU_TSG_IOCTL_BIND_CHANNEL:
+               {
+               int ch_fd = *(int *)buf;
+               if (ch_fd < 0) {
+                       err = -EINVAL;
+                       break;
+               }
+               err = nvgpu_tsg_bind_channel(tsg, ch_fd);
+               break;
+               }
+
+       case NVGPU_TSG_IOCTL_UNBIND_CHANNEL:
+               {
+               int ch_fd = *(int *)buf;
+               if (ch_fd < 0) {
+                       err = -EINVAL;
+                       break;
+               }
+               err = nvgpu_tsg_unbind_channel(tsg, ch_fd);
+               break;
+               }
+
+       default:
+               gk20a_err(dev_from_gk20a(g),
+                          "unrecognized tsg gpu ioctl cmd: 0x%x",
+                          cmd);
+               err = -ENOTTY;
+               break;
+       }
+
+       if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ))
+               err = copy_to_user((void __user *)arg,
+                                  buf, _IOC_SIZE(cmd));
+
+       return err;
+}
diff --git a/drivers/gpu/nvgpu/gk20a/tsg_gk20a.h b/drivers/gpu/nvgpu/gk20a/tsg_gk20a.h
new file mode 100644 (file)
index 0000000..2530a4b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __TSG_GK20A_H_
+#define __TSG_GK20A_H_
+
+#define NVGPU_INVALID_TSG_ID (-1)
+
+bool gk20a_is_channel_marked_as_tsg(struct channel_gk20a *ch);
+
+int gk20a_tsg_dev_release(struct inode *inode, struct file *filp);
+int gk20a_tsg_dev_open(struct inode *inode, struct file *filp);
+long gk20a_tsg_dev_ioctl(struct file *filp,
+                        unsigned int cmd, unsigned long arg);
+
+int gk20a_init_tsg_support(struct gk20a *g, u32 tsgid);
+
+int gk20a_bind_runnable_channel_to_tsg(struct channel_gk20a *ch, int tsgid);
+int gk20a_unbind_channel_from_tsg(struct channel_gk20a *ch, int tsgid);
+
+struct tsg_gk20a {
+       struct gk20a *g;
+
+       bool in_use;
+       int tsgid;
+
+       struct list_head ch_runnable_list;
+       int num_runnable_channels;
+       struct mutex ch_list_lock;
+};
+
+#endif /* __TSG_GK20A_H_ */
index 65757cd..ba9dfbc 100644 (file)
@@ -165,4 +165,23 @@ struct nvhost_gpu_get_characteristics {
 #define NVHOST_GPU_IOCTL_MAX_ARG_SIZE  \
        sizeof(struct nvhost_gpu_zbc_query_table_args)
 
+
+/*
+ * /dev/nvhost-tsg-gpu devices
+ *
+ * Opening a '/dev/nvhost-tsg-gpu' device node creates a way to
+ * bind/unbind a channel to/from TSG group
+ */
+#define NVGPU_TSG_IOCTL_MAGIC 'T'
+
+#define NVGPU_TSG_IOCTL_BIND_CHANNEL \
+       _IOW(NVGPU_TSG_IOCTL_MAGIC, 1, int)
+#define NVGPU_TSG_IOCTL_UNBIND_CHANNEL \
+       _IOW(NVGPU_TSG_IOCTL_MAGIC, 2, int)
+
+#define NVGPU_TSG_IOCTL_MAX_ARG_SIZE   \
+       sizeof(int)
+#define NVGPU_TSG_IOCTL_LAST           \
+       _IOC_NR(NVGPU_TSG_IOCTL_UNBIND_CHANNEL)
+
 #endif