mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-24 02:22:34 +03:00
gpu: nvgpu: add kernel APIs for TSG support
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
This commit is contained in:
committed by
Dan Willemsen
parent
6f492c3834
commit
e6eb4b59f6
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -69,7 +69,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;
|
||||
@@ -144,6 +144,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);
|
||||
@@ -1061,6 +1071,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);
|
||||
|
||||
@@ -1120,6 +1135,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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
261
drivers/gpu/nvgpu/gk20a/tsg_gk20a.c
Normal file
261
drivers/gpu/nvgpu/gk20a/tsg_gk20a.c
Normal file
@@ -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;
|
||||
}
|
||||
44
drivers/gpu/nvgpu/gk20a/tsg_gk20a.h
Normal file
44
drivers/gpu/nvgpu/gk20a/tsg_gk20a.h
Normal file
@@ -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_ */
|
||||
Reference in New Issue
Block a user