gpu: nvgpu: support binding multiple channels to a debug session

We currently bind only one channel to a debug session
But some use cases might need multiple channels bound
to same debug session

Add this support by adding a list of channels to debug session.
List structure is implemented as struct dbg_session_channel_data

List node dbg_s_list_node is currently defined in struct
dbg_session_gk20a. But this is inefficient when we need to
add debug session to multiple channels

Hence add new reference structure dbg_session_data to
store dbg_session pointer and list entry

For each NVGPU_DBG_GPU_IOCTL_BIND_CHANNEL call, create
two reference structure dbg_session_channel_data for channel
and dbg_session_data for debug session and bind them together

Define API nvgpu_dbg_gpu_get_session_channel() which will
get first channel in the list of debug session
Use this API wherever we refer to channel bound to debug
session

Remove dbg_sessions define in struct gk20a since it is
not being used anywhere

Add new API NVGPU_DBG_GPU_IOCTL_UNBIND_CHANNEL to support
unbinding of channel from debug sesssion

Bug 200156699

Change-Id: I3bfa6f9cd5b90e7254a75c7e64ac893739776b7f
Signed-off-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-on: http://git-master/r/1120331
GVS: Gerrit_Virtual_Submit
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
This commit is contained in:
Deepak Nibade
2016-03-29 15:01:25 +05:30
committed by Terje Bergstrom
parent c651adbeaa
commit dfac8ce704
7 changed files with 257 additions and 67 deletions

View File

@@ -857,6 +857,8 @@ static void gk20a_free_channel(struct channel_gk20a *ch)
struct vm_gk20a *ch_vm = ch->vm; struct vm_gk20a *ch_vm = ch->vm;
unsigned long timeout = gk20a_get_gr_idle_timeout(g); unsigned long timeout = gk20a_get_gr_idle_timeout(g);
struct dbg_session_gk20a *dbg_s; struct dbg_session_gk20a *dbg_s;
struct dbg_session_data *session_data, *tmp_s;
struct dbg_session_channel_data *ch_data, *tmp;
bool was_reset; bool was_reset;
gk20a_dbg_fn(""); gk20a_dbg_fn("");
@@ -992,14 +994,21 @@ unbind:
WARN_ON(ch->sync); WARN_ON(ch->sync);
/* unlink all debug sessions */ /* unlink all debug sessions */
mutex_lock(&ch->dbg_s_lock); mutex_lock(&g->dbg_sessions_lock);
list_for_each_entry(dbg_s, &ch->dbg_s_list, dbg_s_list_node) { list_for_each_entry_safe(session_data, tmp_s,
dbg_s->ch = NULL; &ch->dbg_s_list, dbg_s_entry) {
list_del_init(&dbg_s->dbg_s_list_node); dbg_s = session_data->dbg_s;
mutex_lock(&dbg_s->ch_list_lock);
list_for_each_entry_safe(ch_data, tmp,
&dbg_s->ch_list, ch_entry) {
if (ch_data->chid == ch->hw_chid)
dbg_unbind_single_channel_gk20a(dbg_s, ch_data);
}
mutex_unlock(&dbg_s->ch_list_lock);
} }
mutex_unlock(&ch->dbg_s_lock); mutex_unlock(&g->dbg_sessions_lock);
release: release:
/* make sure we catch accesses of unopened channels in case /* make sure we catch accesses of unopened channels in case

View File

@@ -35,6 +35,33 @@ struct dbg_gpu_session_ops dbg_gpu_session_ops_gk20a = {
.exec_reg_ops = exec_regops_gk20a, .exec_reg_ops = exec_regops_gk20a,
}; };
/*
* API to get first channel from the list of all channels
* bound to the debug session
*/
struct channel_gk20a *
nvgpu_dbg_gpu_get_session_channel(struct dbg_session_gk20a *dbg_s)
{
struct dbg_session_channel_data *ch_data;
struct channel_gk20a *ch;
struct gk20a *g = dbg_s->g;
mutex_lock(&dbg_s->ch_list_lock);
if (list_empty(&dbg_s->ch_list)) {
mutex_unlock(&dbg_s->ch_list_lock);
return NULL;
}
ch_data = list_first_entry(&dbg_s->ch_list,
struct dbg_session_channel_data,
ch_entry);
ch = g->fifo.channel + ch_data->chid;
mutex_unlock(&dbg_s->ch_list_lock);
return ch;
}
/* silly allocator - just increment session id */ /* silly allocator - just increment session id */
static atomic_t session_id = ATOMIC_INIT(0); static atomic_t session_id = ATOMIC_INIT(0);
static int generate_session_id(void) static int generate_session_id(void)
@@ -95,8 +122,9 @@ static int gk20a_dbg_gpu_do_dev_open(struct inode *inode,
if (gk20a_gpu_is_virtual(dev)) if (gk20a_gpu_is_virtual(dev))
dbg_session->is_pg_disabled = true; dbg_session->is_pg_disabled = true;
INIT_LIST_HEAD(&dbg_session->dbg_s_list_node);
init_waitqueue_head(&dbg_session->dbg_events.wait_queue); init_waitqueue_head(&dbg_session->dbg_events.wait_queue);
INIT_LIST_HEAD(&dbg_session->ch_list);
mutex_init(&dbg_session->ch_list_lock);
dbg_session->dbg_events.events_enabled = false; dbg_session->dbg_events.events_enabled = false;
dbg_session->dbg_events.num_pending_events = 0; dbg_session->dbg_events.num_pending_events = 0;
@@ -108,18 +136,22 @@ static int gk20a_dbg_gpu_do_dev_open(struct inode *inode,
* since it might not have an associated channel. */ * since it might not have an associated channel. */
static void gk20a_dbg_session_mutex_lock(struct dbg_session_gk20a *dbg_s) static void gk20a_dbg_session_mutex_lock(struct dbg_session_gk20a *dbg_s)
{ {
if (dbg_s->is_profiler) struct channel_gk20a *ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (dbg_s->is_profiler || !ch)
mutex_lock(&dbg_s->g->dbg_sessions_lock); mutex_lock(&dbg_s->g->dbg_sessions_lock);
else else
mutex_lock(&dbg_s->ch->dbg_s_lock); mutex_lock(&ch->dbg_s_lock);
} }
static void gk20a_dbg_session_mutex_unlock(struct dbg_session_gk20a *dbg_s) static void gk20a_dbg_session_mutex_unlock(struct dbg_session_gk20a *dbg_s)
{ {
if (dbg_s->is_profiler) struct channel_gk20a *ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (dbg_s->is_profiler || !ch)
mutex_unlock(&dbg_s->g->dbg_sessions_lock); mutex_unlock(&dbg_s->g->dbg_sessions_lock);
else else
mutex_unlock(&dbg_s->ch->dbg_s_lock); mutex_unlock(&ch->dbg_s_lock);
} }
static void gk20a_dbg_gpu_events_enable(struct dbg_session_gk20a *dbg_s) static void gk20a_dbg_gpu_events_enable(struct dbg_session_gk20a *dbg_s)
@@ -163,10 +195,12 @@ static int gk20a_dbg_gpu_events_ctrl(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_events_ctrl_args *args) struct nvgpu_dbg_gpu_events_ctrl_args *args)
{ {
int ret = 0; int ret = 0;
struct channel_gk20a *ch;
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "dbg events ctrl cmd %d", args->cmd); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "dbg events ctrl cmd %d", args->cmd);
if (!dbg_s->ch) { ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch) {
gk20a_err(dev_from_gk20a(dbg_s->g), gk20a_err(dev_from_gk20a(dbg_s->g),
"no channel bound to dbg session\n"); "no channel bound to dbg session\n");
return -EINVAL; return -EINVAL;
@@ -235,6 +269,7 @@ int gk20a_prof_gpu_dev_open(struct inode *inode, struct file *filp)
void gk20a_dbg_gpu_post_events(struct channel_gk20a *ch) void gk20a_dbg_gpu_post_events(struct channel_gk20a *ch)
{ {
struct dbg_session_data *session_data;
struct dbg_session_gk20a *dbg_s; struct dbg_session_gk20a *dbg_s;
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, ""); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "");
@@ -242,7 +277,8 @@ void gk20a_dbg_gpu_post_events(struct channel_gk20a *ch)
/* guard against the session list being modified */ /* guard against the session list being modified */
mutex_lock(&ch->dbg_s_lock); mutex_lock(&ch->dbg_s_lock);
list_for_each_entry(dbg_s, &ch->dbg_s_list, dbg_s_list_node) { list_for_each_entry(session_data, &ch->dbg_s_list, dbg_s_entry) {
dbg_s = session_data->dbg_s;
if (dbg_s->dbg_events.events_enabled) { if (dbg_s->dbg_events.events_enabled) {
gk20a_dbg(gpu_dbg_gpu_dbg, "posting event on session id %d", gk20a_dbg(gpu_dbg_gpu_dbg, "posting event on session id %d",
dbg_s->id); dbg_s->id);
@@ -260,6 +296,7 @@ void gk20a_dbg_gpu_post_events(struct channel_gk20a *ch)
bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch) bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch)
{ {
struct dbg_session_data *session_data;
struct dbg_session_gk20a *dbg_s; struct dbg_session_gk20a *dbg_s;
bool broadcast = false; bool broadcast = false;
@@ -268,7 +305,8 @@ bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch)
/* guard against the session list being modified */ /* guard against the session list being modified */
mutex_lock(&ch->dbg_s_lock); mutex_lock(&ch->dbg_s_lock);
list_for_each_entry(dbg_s, &ch->dbg_s_list, dbg_s_list_node) { list_for_each_entry(session_data, &ch->dbg_s_list, dbg_s_entry) {
dbg_s = session_data->dbg_s;
if (dbg_s->broadcast_stop_trigger) { if (dbg_s->broadcast_stop_trigger) {
gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn | gpu_dbg_intr, gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn | gpu_dbg_intr,
"stop trigger broadcast enabled"); "stop trigger broadcast enabled");
@@ -284,6 +322,7 @@ bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch)
int gk20a_dbg_gpu_clear_broadcast_stop_trigger(struct channel_gk20a *ch) int gk20a_dbg_gpu_clear_broadcast_stop_trigger(struct channel_gk20a *ch)
{ {
struct dbg_session_data *session_data;
struct dbg_session_gk20a *dbg_s; struct dbg_session_gk20a *dbg_s;
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg | gpu_dbg_intr, ""); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg | gpu_dbg_intr, "");
@@ -291,7 +330,8 @@ int gk20a_dbg_gpu_clear_broadcast_stop_trigger(struct channel_gk20a *ch)
/* guard against the session list being modified */ /* guard against the session list being modified */
mutex_lock(&ch->dbg_s_lock); mutex_lock(&ch->dbg_s_lock);
list_for_each_entry(dbg_s, &ch->dbg_s_list, dbg_s_list_node) { list_for_each_entry(session_data, &ch->dbg_s_list, dbg_s_entry) {
dbg_s = session_data->dbg_s;
if (dbg_s->broadcast_stop_trigger) { if (dbg_s->broadcast_stop_trigger) {
gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn | gpu_dbg_intr, gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn | gpu_dbg_intr,
"stop trigger broadcast disabled"); "stop trigger broadcast disabled");
@@ -347,36 +387,87 @@ static int nvgpu_dbg_timeout_enable(struct dbg_session_gk20a *dbg_s,
return err; return err;
} }
static int dbg_unbind_channel_gk20a(struct dbg_session_gk20a *dbg_s) int dbg_unbind_single_channel_gk20a(struct dbg_session_gk20a *dbg_s,
struct dbg_session_channel_data *ch_data)
{ {
struct channel_gk20a *ch_gk20a = dbg_s->ch;
struct gk20a *g = dbg_s->g; struct gk20a *g = dbg_s->g;
int chid;
struct channel_gk20a *ch;
struct dbg_session_data *session_data;
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, ""); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "");
/* wasn't bound to start with ? */ chid = ch_data->chid;
if (!ch_gk20a) { ch = g->fifo.channel + chid;
gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn, "not bound already?");
return -ENODEV; list_del_init(&ch_data->ch_entry);
}
session_data = ch_data->session_data;
list_del_init(&session_data->dbg_s_entry);
kfree(session_data);
fput(ch_data->ch_f);
kfree(ch_data);
return 0;
}
static int dbg_unbind_all_channels_gk20a(struct dbg_session_gk20a *dbg_s)
{
struct dbg_session_channel_data *ch_data, *tmp;
struct gk20a *g = dbg_s->g;
mutex_lock(&g->dbg_sessions_lock); mutex_lock(&g->dbg_sessions_lock);
mutex_lock(&ch_gk20a->dbg_s_lock); mutex_lock(&dbg_s->ch_list_lock);
list_for_each_entry_safe(ch_data, tmp, &dbg_s->ch_list, ch_entry)
--g->dbg_sessions; dbg_unbind_single_channel_gk20a(dbg_s, ch_data);
mutex_unlock(&dbg_s->ch_list_lock);
dbg_s->ch = NULL;
fput(dbg_s->ch_f);
dbg_s->ch_f = NULL;
list_del_init(&dbg_s->dbg_s_list_node);
mutex_unlock(&ch_gk20a->dbg_s_lock);
mutex_unlock(&g->dbg_sessions_lock); mutex_unlock(&g->dbg_sessions_lock);
return 0; return 0;
} }
static int dbg_unbind_channel_gk20a(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_unbind_channel_args *args)
{
struct dbg_session_channel_data *ch_data;
struct gk20a *g = dbg_s->g;
bool channel_found = false;
struct channel_gk20a *ch;
int err;
gk20a_dbg(gpu_dbg_fn|gpu_dbg_gpu_dbg, "%s fd=%d",
dev_name(dbg_s->dev), args->channel_fd);
ch = gk20a_get_channel_from_file(args->channel_fd);
if (!ch) {
gk20a_dbg_fn("no channel found for fd");
return -EINVAL;
}
mutex_lock(&dbg_s->ch_list_lock);
list_for_each_entry(ch_data, &dbg_s->ch_list, ch_entry) {
if (ch->hw_chid == ch_data->chid) {
channel_found = true;
break;
}
}
mutex_unlock(&dbg_s->ch_list_lock);
if (!channel_found) {
gk20a_dbg_fn("channel not bounded, fd=%d\n", args->channel_fd);
return -EINVAL;
}
mutex_lock(&g->dbg_sessions_lock);
mutex_lock(&dbg_s->ch_list_lock);
err = dbg_unbind_single_channel_gk20a(dbg_s, ch_data);
mutex_unlock(&dbg_s->ch_list_lock);
mutex_unlock(&g->dbg_sessions_lock);
return err;
}
int gk20a_dbg_gpu_dev_release(struct inode *inode, struct file *filp) int gk20a_dbg_gpu_dev_release(struct inode *inode, struct file *filp)
{ {
struct dbg_session_gk20a *dbg_s = filp->private_data; struct dbg_session_gk20a *dbg_s = filp->private_data;
@@ -384,9 +475,8 @@ int gk20a_dbg_gpu_dev_release(struct inode *inode, struct file *filp)
gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn, "%s", dev_name(dbg_s->dev)); gk20a_dbg(gpu_dbg_gpu_dbg | gpu_dbg_fn, "%s", dev_name(dbg_s->dev));
/* unbind if it was bound */ /* unbind channels */
if (dbg_s->ch) dbg_unbind_all_channels_gk20a(dbg_s);
dbg_unbind_channel_gk20a(dbg_s);
/* Powergate/Timeout enable is called here as possibility of dbg_session /* Powergate/Timeout enable is called here as possibility of dbg_session
* which called powergate/timeout disable ioctl, to be killed without * which called powergate/timeout disable ioctl, to be killed without
@@ -405,14 +495,28 @@ static int dbg_bind_channel_gk20a(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_bind_channel_args *args) struct nvgpu_dbg_gpu_bind_channel_args *args)
{ {
struct file *f; struct file *f;
struct gk20a *g; struct gk20a *g = dbg_s->g;
struct channel_gk20a *ch; struct channel_gk20a *ch;
struct dbg_session_channel_data *ch_data, *tmp;
struct dbg_session_data *session_data;
gk20a_dbg(gpu_dbg_fn|gpu_dbg_gpu_dbg, "%s fd=%d", gk20a_dbg(gpu_dbg_fn|gpu_dbg_gpu_dbg, "%s fd=%d",
dev_name(dbg_s->dev), args->channel_fd); dev_name(dbg_s->dev), args->channel_fd);
if (args->channel_fd == ~0) if (args->channel_fd == ~0) {
return dbg_unbind_channel_gk20a(dbg_s); ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch)
return -EINVAL;
mutex_lock(&dbg_s->ch_list_lock);
list_for_each_entry_safe(ch_data, tmp,
&dbg_s->ch_list, ch_entry) {
if (ch_data->chid == ch->hw_chid)
dbg_unbind_single_channel_gk20a(dbg_s, ch_data);
}
mutex_unlock(&dbg_s->ch_list_lock);
return 0;
}
/* even though get_file_channel is doing this it releases it as well */ /* even though get_file_channel is doing this it releases it as well */
/* by holding it here we'll keep it from disappearing while the /* by holding it here we'll keep it from disappearing while the
@@ -428,20 +532,40 @@ static int dbg_bind_channel_gk20a(struct dbg_session_gk20a *dbg_s,
return -EINVAL; return -EINVAL;
} }
g = dbg_s->g;
gk20a_dbg_fn("%s hwchid=%d", dev_name(dbg_s->dev), ch->hw_chid); gk20a_dbg_fn("%s hwchid=%d", dev_name(dbg_s->dev), ch->hw_chid);
mutex_lock(&g->dbg_sessions_lock); mutex_lock(&g->dbg_sessions_lock);
mutex_lock(&ch->dbg_s_lock); mutex_lock(&ch->dbg_s_lock);
dbg_s->ch_f = f; ch_data = kzalloc(sizeof(*ch_data), GFP_KERNEL);
dbg_s->ch = ch; if (!ch_data) {
list_add(&dbg_s->dbg_s_list_node, &dbg_s->ch->dbg_s_list); fput(f);
return -ENOMEM;
}
ch_data->ch_f = f;
ch_data->channel_fd = args->channel_fd;
ch_data->chid = ch->hw_chid;
INIT_LIST_HEAD(&ch_data->ch_entry);
g->dbg_sessions++; session_data = kzalloc(sizeof(*session_data), GFP_KERNEL);
if (!session_data) {
kfree(ch_data);
fput(f);
return -ENOMEM;
}
session_data->dbg_s = dbg_s;
INIT_LIST_HEAD(&session_data->dbg_s_entry);
ch_data->session_data = session_data;
list_add(&session_data->dbg_s_entry, &ch->dbg_s_list);
mutex_lock(&dbg_s->ch_list_lock);
list_add_tail(&ch_data->ch_entry, &dbg_s->ch_list);
mutex_unlock(&dbg_s->ch_list_lock);
mutex_unlock(&ch->dbg_s_lock); mutex_unlock(&ch->dbg_s_lock);
mutex_unlock(&g->dbg_sessions_lock); mutex_unlock(&g->dbg_sessions_lock);
return 0; return 0;
} }
@@ -470,8 +594,12 @@ static int gk20a_perfbuf_unmap(struct dbg_session_gk20a *dbg_s,
static int gk20a_dbg_pc_sampling(struct dbg_session_gk20a *dbg_s, static int gk20a_dbg_pc_sampling(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_pc_sampling_args *args) struct nvgpu_dbg_gpu_pc_sampling_args *args)
{ {
struct channel_gk20a *ch = dbg_s->ch; struct channel_gk20a *ch;
struct gk20a *g = ch->g; struct gk20a *g = dbg_s->g;
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch)
return -EINVAL;
gk20a_dbg_fn(""); gk20a_dbg_fn("");
@@ -571,9 +699,13 @@ static int nvgpu_dbg_gpu_ioctl_clear_single_sm_error_state(
struct gk20a *g = get_gk20a(dbg_s->dev); struct gk20a *g = get_gk20a(dbg_s->dev);
struct gr_gk20a *gr = &g->gr; struct gr_gk20a *gr = &g->gr;
u32 sm_id; u32 sm_id;
struct channel_gk20a *ch = dbg_s->ch; struct channel_gk20a *ch;
int err = 0; int err = 0;
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch)
return -EINVAL;
sm_id = args->sm_id; sm_id = args->sm_id;
if (sm_id >= gr->no_of_sm) if (sm_id >= gr->no_of_sm)
@@ -598,10 +730,14 @@ static int nvgpu_dbg_gpu_ioctl_write_single_sm_error_state(
struct gk20a *g = get_gk20a(dbg_s->dev); struct gk20a *g = get_gk20a(dbg_s->dev);
struct gr_gk20a *gr = &g->gr; struct gr_gk20a *gr = &g->gr;
u32 sm_id; u32 sm_id;
struct channel_gk20a *ch = dbg_s->ch; struct channel_gk20a *ch;
struct nvgpu_dbg_gpu_sm_error_state_record *sm_error_state; struct nvgpu_dbg_gpu_sm_error_state_record *sm_error_state;
int err = 0; int err = 0;
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch)
return -EINVAL;
sm_id = args->sm_id; sm_id = args->sm_id;
if (sm_id >= gr->no_of_sm) if (sm_id >= gr->no_of_sm)
return -EINVAL; return -EINVAL;
@@ -756,6 +892,11 @@ long gk20a_dbg_gpu_dev_ioctl(struct file *filp, unsigned int cmd,
(struct nvgpu_dbg_gpu_write_single_sm_error_state_args *)buf); (struct nvgpu_dbg_gpu_write_single_sm_error_state_args *)buf);
break; break;
case NVGPU_DBG_GPU_IOCTL_UNBIND_CHANNEL:
err = dbg_unbind_channel_gk20a(dbg_s,
(struct nvgpu_dbg_gpu_unbind_channel_args *)buf);
break;
default: default:
gk20a_err(dev_from_gk20a(g), gk20a_err(dev_from_gk20a(g),
"unrecognized dbg gpu ioctl cmd: 0x%x", "unrecognized dbg gpu ioctl cmd: 0x%x",
@@ -805,6 +946,7 @@ static int nvgpu_ioctl_channel_reg_ops(struct dbg_session_gk20a *dbg_s,
struct device *dev = dbg_s->dev; struct device *dev = dbg_s->dev;
struct gk20a *g = get_gk20a(dbg_s->dev); struct gk20a *g = get_gk20a(dbg_s->dev);
struct nvgpu_dbg_gpu_reg_op *ops; struct nvgpu_dbg_gpu_reg_op *ops;
struct channel_gk20a *ch;
u64 ops_size = sizeof(ops[0]) * args->num_ops; u64 ops_size = sizeof(ops[0]) * args->num_ops;
gk20a_dbg_fn("%d ops, total size %llu", args->num_ops, ops_size); gk20a_dbg_fn("%d ops, total size %llu", args->num_ops, ops_size);
@@ -814,7 +956,8 @@ static int nvgpu_ioctl_channel_reg_ops(struct dbg_session_gk20a *dbg_s,
return -EINVAL; return -EINVAL;
} }
if (!dbg_s->is_profiler && !dbg_s->ch) { ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!dbg_s->is_profiler && !ch) {
gk20a_err(dev, "bind a channel before regops for a debugging session"); gk20a_err(dev, "bind a channel before regops for a debugging session");
return -EINVAL; return -EINVAL;
} }
@@ -1016,8 +1159,7 @@ static int nvgpu_dbg_gpu_ioctl_smpc_ctxsw_mode(struct dbg_session_gk20a *dbg_s,
/* Take the global lock, since we'll be doing global regops */ /* Take the global lock, since we'll be doing global regops */
mutex_lock(&g->dbg_sessions_lock); mutex_lock(&g->dbg_sessions_lock);
ch_gk20a = dbg_s->ch; ch_gk20a = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch_gk20a) { if (!ch_gk20a) {
gk20a_err(dev_from_gk20a(g), gk20a_err(dev_from_gk20a(g),
"no bound channel for smpc ctxsw mode update\n"); "no bound channel for smpc ctxsw mode update\n");
@@ -1052,8 +1194,7 @@ static int nvgpu_dbg_gpu_ioctl_hwpm_ctxsw_mode(struct dbg_session_gk20a *dbg_s,
/* Take the global lock, since we'll be doing global regops */ /* Take the global lock, since we'll be doing global regops */
mutex_lock(&g->dbg_sessions_lock); mutex_lock(&g->dbg_sessions_lock);
ch_gk20a = dbg_s->ch; ch_gk20a = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch_gk20a) { if (!ch_gk20a) {
gk20a_err(dev_from_gk20a(g), gk20a_err(dev_from_gk20a(g),
"no bound channel for pm ctxsw mode update\n"); "no bound channel for pm ctxsw mode update\n");
@@ -1080,12 +1221,16 @@ static int nvgpu_dbg_gpu_ioctl_suspend_resume_sm(
struct nvgpu_dbg_gpu_suspend_resume_all_sms_args *args) struct nvgpu_dbg_gpu_suspend_resume_all_sms_args *args)
{ {
struct gk20a *g = get_gk20a(dbg_s->dev); struct gk20a *g = get_gk20a(dbg_s->dev);
struct channel_gk20a *ch = dbg_s->ch; struct channel_gk20a *ch;
bool ch_is_curr_ctx; bool ch_is_curr_ctx;
int err = 0, action = args->mode; int err = 0, action = args->mode;
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "action: %d", args->mode); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "action: %d", args->mode);
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (!ch)
return -EINVAL;
mutex_lock(&g->dbg_sessions_lock); mutex_lock(&g->dbg_sessions_lock);
/* Suspend GPU context switching */ /* Suspend GPU context switching */

View File

@@ -31,6 +31,9 @@ int gk20a_prof_gpu_dev_open(struct inode *inode, struct file *filp);
/* used by the interrupt handler to post events */ /* used by the interrupt handler to post events */
void gk20a_dbg_gpu_post_events(struct channel_gk20a *fault_ch); void gk20a_dbg_gpu_post_events(struct channel_gk20a *fault_ch);
struct channel_gk20a *
nvgpu_dbg_gpu_get_session_channel(struct dbg_session_gk20a *dbg_s);
struct dbg_gpu_session_ops { struct dbg_gpu_session_ops {
int (*exec_reg_ops)(struct dbg_session_gk20a *dbg_s, int (*exec_reg_ops)(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_reg_op *ops, struct nvgpu_dbg_gpu_reg_op *ops,
@@ -68,22 +71,37 @@ struct dbg_session_gk20a {
struct device *dev; struct device *dev;
struct gk20a *g; struct gk20a *g;
/* bound channel, if any */ /* list of bound channels, if any */
struct file *ch_f; struct list_head ch_list;
struct channel_gk20a *ch; struct mutex ch_list_lock;
/* session operations */ /* session operations */
struct dbg_gpu_session_ops *ops; struct dbg_gpu_session_ops *ops;
/* event support */ /* event support */
struct dbg_gpu_session_events dbg_events; struct dbg_gpu_session_events dbg_events;
struct list_head dbg_s_list_node;
bool broadcast_stop_trigger; bool broadcast_stop_trigger;
}; };
struct dbg_session_data {
struct dbg_session_gk20a *dbg_s;
struct list_head dbg_s_entry;
};
struct dbg_session_channel_data {
struct file *ch_f;
int channel_fd;
int chid;
struct list_head ch_entry;
struct dbg_session_data *session_data;
};
extern struct dbg_gpu_session_ops dbg_gpu_session_ops_gk20a; extern struct dbg_gpu_session_ops dbg_gpu_session_ops_gk20a;
int dbg_unbind_single_channel_gk20a(struct dbg_session_gk20a *dbg_s,
struct dbg_session_channel_data *ch_data);
bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch); bool gk20a_dbg_gpu_broadcast_stop_trigger(struct channel_gk20a *ch);
int gk20a_dbg_gpu_clear_broadcast_stop_trigger(struct channel_gk20a *ch); int gk20a_dbg_gpu_clear_broadcast_stop_trigger(struct channel_gk20a *ch);

View File

@@ -647,7 +647,6 @@ struct gk20a {
/* held while manipulating # of debug/profiler sessions present */ /* held while manipulating # of debug/profiler sessions present */
/* also prevents debug sessions from attaching until released */ /* also prevents debug sessions from attaching until released */
struct mutex dbg_sessions_lock; struct mutex dbg_sessions_lock;
int dbg_sessions; /* number attached */
int dbg_powergating_disabled_refcount; /*refcount for pg disable */ int dbg_powergating_disabled_refcount; /*refcount for pg disable */
int dbg_timeout_disabled_refcount; /*refcount for timeout disable */ int dbg_timeout_disabled_refcount; /*refcount for timeout disable */

View File

@@ -390,7 +390,7 @@ int exec_regops_gk20a(struct dbg_session_gk20a *dbg_s,
gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, ""); gk20a_dbg(gpu_dbg_fn | gpu_dbg_gpu_dbg, "");
ch = dbg_s->ch; ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
/* For vgpu, the regops routines need to be handled in the /* For vgpu, the regops routines need to be handled in the
* context of the server and support for that does not exist. * context of the server and support for that does not exist.
@@ -559,6 +559,9 @@ static bool check_whitelists(struct dbg_session_gk20a *dbg_s,
{ {
struct gk20a *g = dbg_s->g; struct gk20a *g = dbg_s->g;
bool valid = false; bool valid = false;
struct channel_gk20a *ch;
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
if (op->type == REGOP(TYPE_GLOBAL)) { if (op->type == REGOP(TYPE_GLOBAL)) {
/* search global list */ /* search global list */
@@ -570,7 +573,7 @@ static bool check_whitelists(struct dbg_session_gk20a *dbg_s,
regop_bsearch_range_cmp); regop_bsearch_range_cmp);
/* if debug session and channel is bound search context list */ /* if debug session and channel is bound search context list */
if ((!valid) && (!dbg_s->is_profiler && dbg_s->ch)) { if ((!valid) && (!dbg_s->is_profiler && ch)) {
/* binary search context list */ /* binary search context list */
valid = g->ops.regops.get_context_whitelist_ranges && valid = g->ops.regops.get_context_whitelist_ranges &&
!!bsearch(&offset, !!bsearch(&offset,
@@ -581,7 +584,7 @@ static bool check_whitelists(struct dbg_session_gk20a *dbg_s,
} }
/* if debug session and channel is bound search runcontrol list */ /* if debug session and channel is bound search runcontrol list */
if ((!valid) && (!dbg_s->is_profiler && dbg_s->ch)) { if ((!valid) && (!dbg_s->is_profiler && ch)) {
valid = g->ops.regops.get_runcontrol_whitelist && valid = g->ops.regops.get_runcontrol_whitelist &&
linear_search(offset, linear_search(offset,
g->ops.regops.get_runcontrol_whitelist(), g->ops.regops.get_runcontrol_whitelist(),
@@ -589,7 +592,7 @@ static bool check_whitelists(struct dbg_session_gk20a *dbg_s,
} }
} else if (op->type == REGOP(TYPE_GR_CTX)) { } else if (op->type == REGOP(TYPE_GR_CTX)) {
/* it's a context-relative op */ /* it's a context-relative op */
if (!dbg_s->ch) { if (!ch) {
gk20a_err(dbg_s->dev, "can't perform ctx regop unless bound"); gk20a_err(dbg_s->dev, "can't perform ctx regop unless bound");
op->status = REGOP(STATUS_UNSUPPORTED_OP); op->status = REGOP(STATUS_UNSUPPORTED_OP);
return valid; return valid;
@@ -604,7 +607,7 @@ static bool check_whitelists(struct dbg_session_gk20a *dbg_s,
regop_bsearch_range_cmp); regop_bsearch_range_cmp);
/* if debug session and channel is bound search runcontrol list */ /* if debug session and channel is bound search runcontrol list */
if ((!valid) && (!dbg_s->is_profiler && dbg_s->ch)) { if ((!valid) && (!dbg_s->is_profiler && ch)) {
valid = g->ops.regops.get_runcontrol_whitelist && valid = g->ops.regops.get_runcontrol_whitelist &&
linear_search(offset, linear_search(offset,
g->ops.regops.get_runcontrol_whitelist(), g->ops.regops.get_runcontrol_whitelist(),

View File

@@ -26,7 +26,7 @@ static int vgpu_exec_regops(struct dbg_session_gk20a *dbg_s,
struct nvgpu_dbg_gpu_reg_op *ops, struct nvgpu_dbg_gpu_reg_op *ops,
u64 num_ops) u64 num_ops)
{ {
struct channel_gk20a *ch = dbg_s->ch; struct channel_gk20a *ch;
struct gk20a_platform *platform = gk20a_get_platform(dbg_s->g->dev); struct gk20a_platform *platform = gk20a_get_platform(dbg_s->g->dev);
struct tegra_vgpu_cmd_msg msg; struct tegra_vgpu_cmd_msg msg;
struct tegra_vgpu_reg_ops_params *p = &msg.params.reg_ops; struct tegra_vgpu_reg_ops_params *p = &msg.params.reg_ops;
@@ -55,6 +55,7 @@ static int vgpu_exec_regops(struct dbg_session_gk20a *dbg_s,
msg.cmd = TEGRA_VGPU_CMD_REG_OPS; msg.cmd = TEGRA_VGPU_CMD_REG_OPS;
msg.handle = platform->virt_handle; msg.handle = platform->virt_handle;
ch = nvgpu_dbg_gpu_get_session_channel(dbg_s);
p->handle = ch ? ch->virt_ctx : 0; p->handle = ch ? ch->virt_ctx : 0;
p->num_ops = num_ops; p->num_ops = num_ops;
p->is_profiler = dbg_s->is_profiler; p->is_profiler = dbg_s->is_profiler;

View File

@@ -496,8 +496,7 @@ struct nvgpu_gpu_get_gpu_time_args {
* Binding/attaching a debugger session to an nvgpu channel * Binding/attaching a debugger session to an nvgpu channel
* *
* The 'channel_fd' given here is the fd used to allocate the * The 'channel_fd' given here is the fd used to allocate the
* gpu channel context. To detach/unbind the debugger session * gpu channel context.
* use a channel_fd of -1.
* *
*/ */
struct nvgpu_dbg_gpu_bind_channel_args { struct nvgpu_dbg_gpu_bind_channel_args {
@@ -510,6 +509,8 @@ struct nvgpu_dbg_gpu_bind_channel_args {
/* /*
* Register operations * Register operations
* All operations are targeted towards first channel
* attached to debug session
*/ */
/* valid op values */ /* valid op values */
#define NVGPU_DBG_GPU_REG_OP_READ_32 (0x00000000) #define NVGPU_DBG_GPU_REG_OP_READ_32 (0x00000000)
@@ -722,9 +723,23 @@ struct nvgpu_dbg_gpu_write_single_sm_error_state_args {
#define NVGPU_DBG_GPU_IOCTL_WRITE_SINGLE_SM_ERROR_STATE \ #define NVGPU_DBG_GPU_IOCTL_WRITE_SINGLE_SM_ERROR_STATE \
_IOW(NVGPU_DBG_GPU_IOCTL_MAGIC, 16, struct nvgpu_dbg_gpu_write_single_sm_error_state_args) _IOW(NVGPU_DBG_GPU_IOCTL_MAGIC, 16, struct nvgpu_dbg_gpu_write_single_sm_error_state_args)
/*
* Unbinding/detaching a debugger session from a nvgpu channel
*
* The 'channel_fd' given here is the fd used to allocate the
* gpu channel context.
*/
struct nvgpu_dbg_gpu_unbind_channel_args {
__u32 channel_fd; /* in */
__u32 _pad0[1];
};
#define NVGPU_DBG_GPU_IOCTL_UNBIND_CHANNEL \
_IOW(NVGPU_DBG_GPU_IOCTL_MAGIC, 17, struct nvgpu_dbg_gpu_unbind_channel_args)
#define NVGPU_DBG_GPU_IOCTL_LAST \ #define NVGPU_DBG_GPU_IOCTL_LAST \
_IOC_NR(NVGPU_DBG_GPU_IOCTL_WRITE_SINGLE_SM_ERROR_STATE) _IOC_NR(NVGPU_DBG_GPU_IOCTL_UNBIND_CHANNEL)
#define NVGPU_DBG_GPU_IOCTL_MAX_ARG_SIZE \ #define NVGPU_DBG_GPU_IOCTL_MAX_ARG_SIZE \
sizeof(struct nvgpu_dbg_gpu_perfbuf_map_args) sizeof(struct nvgpu_dbg_gpu_perfbuf_map_args)