Files
linux-nvgpu/drivers/gpu/nvgpu/gk20a/tsg_gk20a.c
Alex Waterman d37e8f7dcf gpu: nvgpu: Split VM interface out
This patch begins the major rework of the GPU's virtual memory manager
(VMM). The VMM is the piece of code that handles the userspace interface
to buffers and their mappings into the GMMU. The core data structure is
the VM - for now still known as 'struct vm_gk20a'. Each one of these
structs represents one addres space to which channels or TSGs may bind
themselves to.

The VMM splits the interface up into two broad categories. First there's
the common, OS independent interfaces; and second there's the OS specific
interfaces.

OS independent
--------------

  This is the code that manages the lifetime of VMs, the buffers inside
  VMs (search, batch mapping) creation, destruction, etc.

OS Specific
-----------

  This handles mapping of buffers represented as they are represented by
  the OS (dma_buf's for example on Linux).

This patch is by no means complete. There's still Linux specific functions
scattered in ostensibly OS independent code. This is the first step. A
patch that rewrites everything in one go would simply be too big to
effectively review.

Instead the goal of this change is to simply separate out the basic
OS specific and OS agnostic interfaces into their own header files. The
next series of patches will start to pull the relevant implementations
into OS specific C files and common C files.

JIRA NVGPU-12
JIRA NVGPU-30

Change-Id: I242c7206047b6c769296226d855b7e44d5c4bfa8
Signed-off-by: Alex Waterman <alexw@nvidia.com>
Reviewed-on: http://git-master/r/1464939
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2017-05-19 15:34:01 -07:00

321 lines
7.3 KiB
C

/*
* Copyright (c) 2014-2017, 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 <nvgpu/kmem.h>
#include <nvgpu/log.h>
#include "gk20a.h"
#include "tsg_gk20a.h"
bool gk20a_is_channel_marked_as_tsg(struct channel_gk20a *ch)
{
return !(ch->tsgid == NVGPU_INVALID_TSG_ID);
}
int gk20a_enable_tsg(struct tsg_gk20a *tsg)
{
struct gk20a *g = tsg->g;
struct channel_gk20a *ch;
down_read(&tsg->ch_list_lock);
nvgpu_list_for_each_entry(ch, &tsg->ch_list, channel_gk20a, ch_entry) {
g->ops.fifo.enable_channel(ch);
}
up_read(&tsg->ch_list_lock);
return 0;
}
int gk20a_disable_tsg(struct tsg_gk20a *tsg)
{
struct gk20a *g = tsg->g;
struct channel_gk20a *ch;
down_read(&tsg->ch_list_lock);
nvgpu_list_for_each_entry(ch, &tsg->ch_list, channel_gk20a, ch_entry) {
g->ops.fifo.disable_channel(ch);
}
up_read(&tsg->ch_list_lock);
return 0;
}
static bool gk20a_is_channel_active(struct gk20a *g, struct channel_gk20a *ch)
{
struct fifo_gk20a *f = &g->fifo;
struct fifo_runlist_info_gk20a *runlist;
unsigned int i;
for (i = 0; i < f->max_runlists; ++i) {
runlist = &f->runlist_info[i];
if (test_bit(ch->hw_chid, runlist->active_channels))
return true;
}
return false;
}
/*
* API to mark channel as part of TSG
*
* Note that channel is not runnable when we bind it to TSG
*/
int gk20a_tsg_bind_channel(struct tsg_gk20a *tsg,
struct channel_gk20a *ch)
{
gk20a_dbg_fn("");
/* check if channel is already bound to some TSG */
if (gk20a_is_channel_marked_as_tsg(ch)) {
return -EINVAL;
}
/* channel cannot be bound to TSG if it is already active */
if (gk20a_is_channel_active(tsg->g, ch)) {
return -EINVAL;
}
ch->tsgid = tsg->tsgid;
/* all the channel part of TSG should need to be same runlist_id */
if (tsg->runlist_id == FIFO_INVAL_TSG_ID)
tsg->runlist_id = ch->runlist_id;
else if (tsg->runlist_id != ch->runlist_id) {
nvgpu_err(tsg->g,
"Error: TSG channel should be share same runlist ch[%d] tsg[%d]\n",
ch->runlist_id, tsg->runlist_id);
return -EINVAL;
}
down_write(&tsg->ch_list_lock);
nvgpu_list_add_tail(&ch->ch_entry, &tsg->ch_list);
up_write(&tsg->ch_list_lock);
kref_get(&tsg->refcount);
gk20a_dbg(gpu_dbg_fn, "BIND tsg:%d channel:%d\n",
tsg->tsgid, ch->hw_chid);
gk20a_dbg_fn("done");
return 0;
}
int gk20a_tsg_unbind_channel(struct channel_gk20a *ch)
{
struct fifo_gk20a *f = &ch->g->fifo;
struct tsg_gk20a *tsg = &f->tsg[ch->tsgid];
down_write(&tsg->ch_list_lock);
nvgpu_list_del(&ch->ch_entry);
up_write(&tsg->ch_list_lock);
kref_put(&tsg->refcount, gk20a_tsg_release);
ch->tsgid = NVGPU_INVALID_TSG_ID;
return 0;
}
int gk20a_init_tsg_support(struct gk20a *g, u32 tsgid)
{
struct tsg_gk20a *tsg = NULL;
int err;
if (tsgid >= g->fifo.num_channels)
return -EINVAL;
tsg = &g->fifo.tsg[tsgid];
tsg->in_use = false;
tsg->tsgid = tsgid;
nvgpu_init_list_node(&tsg->ch_list);
init_rwsem(&tsg->ch_list_lock);
nvgpu_init_list_node(&tsg->event_id_list);
err = nvgpu_mutex_init(&tsg->event_id_list_lock);
if (err) {
tsg->in_use = true; /* make this TSG unusable */
return err;
}
return 0;
}
int gk20a_tsg_set_priority(struct gk20a *g, struct tsg_gk20a *tsg,
u32 priority)
{
u32 timeslice_us;
switch (priority) {
case NVGPU_PRIORITY_LOW:
timeslice_us = g->timeslice_low_priority_us;
break;
case NVGPU_PRIORITY_MEDIUM:
timeslice_us = g->timeslice_medium_priority_us;
break;
case NVGPU_PRIORITY_HIGH:
timeslice_us = g->timeslice_high_priority_us;
break;
default:
pr_err("Unsupported priority");
return -EINVAL;
}
return gk20a_tsg_set_timeslice(tsg, timeslice_us);
}
int gk20a_tsg_set_runlist_interleave(struct tsg_gk20a *tsg, u32 level)
{
struct gk20a *g = tsg->g;
int ret;
gk20a_dbg(gpu_dbg_sched, "tsgid=%u interleave=%u", tsg->tsgid, level);
switch (level) {
case NVGPU_RUNLIST_INTERLEAVE_LEVEL_LOW:
case NVGPU_RUNLIST_INTERLEAVE_LEVEL_MEDIUM:
case NVGPU_RUNLIST_INTERLEAVE_LEVEL_HIGH:
ret = g->ops.fifo.set_runlist_interleave(g, tsg->tsgid,
true, 0, level);
if (!ret)
tsg->interleave_level = level;
break;
default:
ret = -EINVAL;
break;
}
return ret ? ret : g->ops.fifo.update_runlist(g, tsg->runlist_id, ~0, true, true);
}
int gk20a_tsg_set_timeslice(struct tsg_gk20a *tsg, u32 timeslice)
{
struct gk20a *g = tsg->g;
gk20a_dbg(gpu_dbg_sched, "tsgid=%u timeslice=%u us", tsg->tsgid, timeslice);
return g->ops.fifo.tsg_set_timeslice(tsg, timeslice);
}
static void release_used_tsg(struct fifo_gk20a *f, struct tsg_gk20a *tsg)
{
nvgpu_mutex_acquire(&f->tsg_inuse_mutex);
f->tsg[tsg->tsgid].in_use = false;
nvgpu_mutex_release(&f->tsg_inuse_mutex);
}
static struct tsg_gk20a *gk20a_tsg_acquire_unused_tsg(struct fifo_gk20a *f)
{
struct tsg_gk20a *tsg = NULL;
unsigned int tsgid;
nvgpu_mutex_acquire(&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;
}
}
nvgpu_mutex_release(&f->tsg_inuse_mutex);
return tsg;
}
struct tsg_gk20a *gk20a_tsg_open(struct gk20a *g)
{
struct tsg_gk20a *tsg;
int err;
tsg = gk20a_tsg_acquire_unused_tsg(&g->fifo);
if (!tsg)
return NULL;
tsg->g = g;
tsg->num_active_channels = 0;
kref_init(&tsg->refcount);
tsg->tsg_gr_ctx = NULL;
tsg->vm = NULL;
tsg->interleave_level = NVGPU_RUNLIST_INTERLEAVE_LEVEL_LOW;
tsg->timeslice_us = 0;
tsg->timeslice_timeout = 0;
tsg->timeslice_scale = 0;
tsg->runlist_id = ~0;
tsg->tgid = current->tgid;
if (g->ops.fifo.tsg_open) {
err = g->ops.fifo.tsg_open(tsg);
if (err) {
nvgpu_err(g, "tsg %d fifo open failed %d",
tsg->tsgid, err);
goto clean_up;
}
}
gk20a_dbg(gpu_dbg_fn, "tsg opened %d\n", tsg->tsgid);
gk20a_sched_ctrl_tsg_added(g, tsg);
return tsg;
clean_up:
kref_put(&tsg->refcount, gk20a_tsg_release);
return NULL;
}
void gk20a_tsg_release(struct kref *ref)
{
struct tsg_gk20a *tsg = container_of(ref, struct tsg_gk20a, refcount);
struct gk20a *g = tsg->g;
struct gk20a_event_id_data *event_id_data, *event_id_data_temp;
if (tsg->tsg_gr_ctx) {
gr_gk20a_free_tsg_gr_ctx(tsg);
tsg->tsg_gr_ctx = NULL;
}
if (tsg->vm) {
nvgpu_vm_put(tsg->vm);
tsg->vm = NULL;
}
gk20a_sched_ctrl_tsg_removed(g, tsg);
/* unhook all events created on this TSG */
nvgpu_mutex_acquire(&tsg->event_id_list_lock);
nvgpu_list_for_each_entry_safe(event_id_data, event_id_data_temp,
&tsg->event_id_list,
gk20a_event_id_data,
event_id_node) {
nvgpu_list_del(&event_id_data->event_id_node);
}
nvgpu_mutex_release(&tsg->event_id_list_lock);
release_used_tsg(&g->fifo, tsg);
tsg->runlist_id = ~0;
gk20a_dbg(gpu_dbg_fn, "tsg released %d\n", tsg->tsgid);
gk20a_put(g);
}
void gk20a_init_tsg_ops(struct gpu_ops *gops)
{
gops->fifo.tsg_bind_channel = gk20a_tsg_bind_channel;
gops->fifo.tsg_unbind_channel = gk20a_tsg_unbind_channel;
}