From e6eb4b59f6e8753c64133a4b86c6278ceef98e93 Mon Sep 17 00:00:00 2001 From: Deepak Nibade Date: Wed, 11 Jun 2014 16:45:54 +0530 Subject: 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 Signed-off-by: Deepak Nibade Reviewed-on: http://git-master/r/416610 --- drivers/gpu/nvgpu/gk20a/Makefile | 3 +- drivers/gpu/nvgpu/gk20a/channel_gk20a.c | 3 + drivers/gpu/nvgpu/gk20a/channel_gk20a.h | 3 + drivers/gpu/nvgpu/gk20a/fifo_gk20a.c | 4 + drivers/gpu/nvgpu/gk20a/fifo_gk20a.h | 4 + drivers/gpu/nvgpu/gk20a/gk20a.c | 23 ++- drivers/gpu/nvgpu/gk20a/gk20a.h | 6 + drivers/gpu/nvgpu/gk20a/tsg_gk20a.c | 261 ++++++++++++++++++++++++++++++++ drivers/gpu/nvgpu/gk20a/tsg_gk20a.h | 44 ++++++ 9 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/nvgpu/gk20a/tsg_gk20a.c create mode 100644 drivers/gpu/nvgpu/gk20a/tsg_gk20a.h (limited to 'drivers/gpu/nvgpu/gk20a') diff --git a/drivers/gpu/nvgpu/gk20a/Makefile b/drivers/gpu/nvgpu/gk20a/Makefile index a0fc9f60..d234db50 100644 --- a/drivers/gpu/nvgpu/gk20a/Makefile +++ b/drivers/gpu/nvgpu/gk20a/Makefile @@ -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 diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index 486e815c..ef7195a7 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c @@ -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; diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h index 36697e02..4d236a70 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h @@ -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; diff --git a/drivers/gpu/nvgpu/gk20a/fifo_gk20a.c b/drivers/gpu/nvgpu/gk20a/fifo_gk20a.c index 3c0611e6..e9febb77 100644 --- a/drivers/gpu/nvgpu/gk20a/fifo_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/fifo_gk20a.c @@ -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; diff --git a/drivers/gpu/nvgpu/gk20a/fifo_gk20a.h b/drivers/gpu/nvgpu/gk20a/fifo_gk20a.h index 8a4e0a8f..f94fce02 100644 --- a/drivers/gpu/nvgpu/gk20a/fifo_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/fifo_gk20a.h @@ -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 { diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.c b/drivers/gpu/nvgpu/gk20a/gk20a.c index c1150bac..4c6566a6 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/gk20a.c @@ -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); diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.h b/drivers/gpu/nvgpu/gk20a/gk20a.h index 9769ac99..60b9b80e 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/gk20a.h @@ -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 index 00000000..d9e10d30 --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/tsg_gk20a.c @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..2530a4bd --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/tsg_gk20a.h @@ -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 . + */ +#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_ */ -- cgit v1.2.2