diff --git a/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c b/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c index d53442004..789e6af4e 100644 --- a/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/dbg_gpu_gk20a.c @@ -423,8 +423,8 @@ int dbg_unbind_single_channel_gk20a(struct dbg_session_gk20a *dbg_s, if ((prof_obj->session_id == dbg_s->id) && (prof_obj->ch->hw_chid == chid)) { if (prof_obj->has_reservation) { - g->profiler_reservation_count--; - dbg_s->has_profiler_reservation = false; + g->ops.dbg_session_ops. + release_profiler_reservation(dbg_s, prof_obj); } list_del(&prof_obj->prof_obj_entry); kfree(prof_obj); @@ -525,10 +525,9 @@ int gk20a_dbg_gpu_dev_release(struct inode *inode, struct file *filp) list_for_each_entry_safe(prof_obj, tmp_obj, &g->profiler_objects, prof_obj_entry) { if (prof_obj->session_id == dbg_s->id) { - if (prof_obj->has_reservation) { - g->global_profiler_reservation_held = false; - g->profiler_reservation_count--; - } + if (prof_obj->has_reservation) + g->ops.dbg_session_ops. + release_profiler_reservation(dbg_s, prof_obj); list_del(&prof_obj->prof_obj_entry); kfree(prof_obj); } @@ -1577,12 +1576,9 @@ static int nvgpu_ioctl_free_profiler_object( err = -EINVAL; break; } - if (prof_obj->has_reservation) { - if (prof_obj->ch == NULL) - g->global_profiler_reservation_held = false; - g->profiler_reservation_count--; - dbg_s->has_profiler_reservation = false; - } + if (prof_obj->has_reservation) + g->ops.dbg_session_ops. + release_profiler_reservation(dbg_s, prof_obj); list_del(&prof_obj->prof_obj_entry); kfree(prof_obj); obj_found = true; @@ -1620,6 +1616,51 @@ static struct dbg_profiler_object_data *find_matching_prof_obj( return NULL; } +static bool nvgpu_check_and_set_global_reservation( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + if (g->profiler_reservation_count == 0) { + g->global_profiler_reservation_held = true; + g->profiler_reservation_count = 1; + dbg_s->has_profiler_reservation = true; + prof_obj->has_reservation = true; + return true; + } + return false; +} + +static bool nvgpu_check_and_set_context_reservation( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + /* Assumes that we've already checked that no global reservation + * is in effect. + */ + g->profiler_reservation_count++; + dbg_s->has_profiler_reservation = true; + prof_obj->has_reservation = true; + return true; +} + +static void nvgpu_release_profiler_reservation(struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + g->profiler_reservation_count--; + if (g->profiler_reservation_count < 0) + gk20a_err(dev_from_gk20a(g), "Negative reservation count!"); + dbg_s->has_profiler_reservation = false; + prof_obj->has_reservation = false; + if (prof_obj->ch == NULL) + g->global_profiler_reservation_held = false; +} + static int nvgpu_profiler_reserve_acquire(struct dbg_session_gk20a *dbg_s, u32 profiler_handle) { @@ -1655,17 +1696,12 @@ static int nvgpu_profiler_reserve_acquire(struct dbg_session_gk20a *dbg_s, /* Global reservations are only allowed if there are no other * global or per-context reservations currently held */ - if (g->profiler_reservation_count > 0) { + if (!g->ops.dbg_session_ops.check_and_set_global_reservation( + dbg_s, my_prof_obj)) { gk20a_err(dev_from_gk20a(g), "global reserve: have existing reservation"); err = -EBUSY; - goto exit; } - - my_prof_obj->has_reservation = true; - g->global_profiler_reservation_held = true; - g->profiler_reservation_count = 1; - dbg_s->has_profiler_reservation = true; } else if (g->global_profiler_reservation_held) { /* If there's a global reservation, * we can't take a per-context one. @@ -1673,7 +1709,6 @@ static int nvgpu_profiler_reserve_acquire(struct dbg_session_gk20a *dbg_s, gk20a_err(dev_from_gk20a(g), "per-ctxt reserve: global reservation in effect"); err = -EBUSY; - goto exit; } else if (gk20a_is_channel_marked_as_tsg(my_prof_obj->ch)) { /* TSG: check that another channel in the TSG * doesn't already have the reservation @@ -1691,9 +1726,13 @@ static int nvgpu_profiler_reserve_acquire(struct dbg_session_gk20a *dbg_s, } } - my_prof_obj->has_reservation = true; - g->profiler_reservation_count++; - dbg_s->has_profiler_reservation = true; + if (!g->ops.dbg_session_ops.check_and_set_context_reservation( + dbg_s, my_prof_obj)) { + /* Another guest OS has the global reservation */ + gk20a_err(dev_from_gk20a(g), + "per-ctxt reserve: global reservation in effect"); + err = -EBUSY; + } } else { /* channel: check that some other profiler object doesn't * already have the reservation. @@ -1711,9 +1750,13 @@ static int nvgpu_profiler_reserve_acquire(struct dbg_session_gk20a *dbg_s, } } - my_prof_obj->has_reservation = true; - g->profiler_reservation_count++; - dbg_s->has_profiler_reservation = true; + if (!g->ops.dbg_session_ops.check_and_set_context_reservation( + dbg_s, my_prof_obj)) { + /* Another guest OS has the global reservation */ + gk20a_err(dev_from_gk20a(g), + "per-ctxt reserve: global reservation in effect"); + err = -EBUSY; + } } exit: nvgpu_mutex_release(&g->dbg_sessions_lock); @@ -1740,13 +1783,9 @@ static int nvgpu_profiler_reserve_release(struct dbg_session_gk20a *dbg_s, goto exit; } - if (prof_obj->has_reservation) { - prof_obj->has_reservation = false; - if (prof_obj->ch == NULL) - g->global_profiler_reservation_held = false; - g->profiler_reservation_count--; - dbg_s->has_profiler_reservation = false; - } else { + if (prof_obj->has_reservation) + g->ops.dbg_session_ops.release_profiler_reservation(dbg_s, prof_obj); + else { gk20a_err(dev_from_gk20a(g), "No reservation found"); err = -EINVAL; goto exit; @@ -1868,4 +1907,10 @@ void gk20a_init_dbg_session_ops(struct gpu_ops *gops) { gops->dbg_session_ops.exec_reg_ops = exec_regops_gk20a; gops->dbg_session_ops.dbg_set_powergate = dbg_set_powergate; + gops->dbg_session_ops.check_and_set_global_reservation = + nvgpu_check_and_set_global_reservation; + gops->dbg_session_ops.check_and_set_context_reservation = + nvgpu_check_and_set_context_reservation; + gops->dbg_session_ops.release_profiler_reservation = + nvgpu_release_profiler_reservation; }; diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.h b/drivers/gpu/nvgpu/gk20a/gk20a.h index 555660906..f11d31f45 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/gk20a.h @@ -28,6 +28,7 @@ struct gk20a_fecs_trace; struct gk20a_ctxsw_trace; struct acr_desc; struct nvgpu_mem_alloc_tracker; +struct dbg_profiler_object_data; #include #include @@ -749,6 +750,15 @@ struct gpu_ops { u64 num_ops); int (*dbg_set_powergate)(struct dbg_session_gk20a *dbg_s, u32 mode); + bool (*check_and_set_global_reservation)( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj); + bool (*check_and_set_context_reservation)( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj); + void (*release_profiler_reservation)( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj); } dbg_session_ops; struct { void (*get_program_numbers)(struct gk20a *g, diff --git a/drivers/gpu/nvgpu/vgpu/dbg_vgpu.c b/drivers/gpu/nvgpu/vgpu/dbg_vgpu.c index 609b497ab..a2e57ed5a 100644 --- a/drivers/gpu/nvgpu/vgpu/dbg_vgpu.c +++ b/drivers/gpu/nvgpu/vgpu/dbg_vgpu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2015-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, @@ -100,8 +100,90 @@ static int vgpu_dbg_set_powergate(struct dbg_session_gk20a *dbg_s, __u32 mode) return err; } +static int vgpu_sendrecv_prof_cmd(struct dbg_session_gk20a *dbg_s, u32 mode) +{ + struct tegra_vgpu_cmd_msg msg; + struct tegra_vgpu_prof_mgt_params *p = &msg.params.prof_management; + int err = 0; + + msg.cmd = TEGRA_VGPU_CMD_PROF_MGT; + msg.handle = vgpu_get_handle(dbg_s->g); + + p->mode = mode; + + err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg)); + err = err ? err : msg.ret; + return err; +} + +static bool vgpu_check_and_set_global_reservation( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + if (g->profiler_reservation_count > 0) + return false; + + /* Check that another guest OS doesn't already have a reservation */ + if (!vgpu_sendrecv_prof_cmd(dbg_s, TEGRA_VGPU_PROF_GET_GLOBAL)) { + g->global_profiler_reservation_held = true; + g->profiler_reservation_count = 1; + dbg_s->has_profiler_reservation = true; + prof_obj->has_reservation = true; + return true; + } + return false; +} + +static bool vgpu_check_and_set_context_reservation( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + /* Assumes that we've already checked that no global reservation + * is in effect for this guest. + * + * If our reservation count is non-zero, then no other guest has the + * global reservation; if it is zero, need to check with RM server. + * + */ + if ((g->profiler_reservation_count != 0) || + !vgpu_sendrecv_prof_cmd(dbg_s, TEGRA_VGPU_PROF_GET_CONTEXT)) { + g->profiler_reservation_count++; + dbg_s->has_profiler_reservation = true; + prof_obj->has_reservation = true; + return true; + } + return false; +} + +static void vgpu_release_profiler_reservation( + struct dbg_session_gk20a *dbg_s, + struct dbg_profiler_object_data *prof_obj) +{ + struct gk20a *g = dbg_s->g; + + dbg_s->has_profiler_reservation = false; + prof_obj->has_reservation = false; + if (prof_obj->ch == NULL) + g->global_profiler_reservation_held = false; + + /* If new reservation count is zero, notify server */ + g->profiler_reservation_count--; + if (g->profiler_reservation_count == 0) + vgpu_sendrecv_prof_cmd(dbg_s, TEGRA_VGPU_PROF_RELEASE); +} + void vgpu_init_dbg_session_ops(struct gpu_ops *gops) { gops->dbg_session_ops.exec_reg_ops = vgpu_exec_regops; gops->dbg_session_ops.dbg_set_powergate = vgpu_dbg_set_powergate; + gops->dbg_session_ops.check_and_set_global_reservation = + vgpu_check_and_set_global_reservation; + gops->dbg_session_ops.check_and_set_context_reservation = + vgpu_check_and_set_context_reservation; + gops->dbg_session_ops.release_profiler_reservation = + vgpu_release_profiler_reservation; } diff --git a/include/linux/tegra_vgpu.h b/include/linux/tegra_vgpu.h index 4884ccef1..dba83b925 100644 --- a/include/linux/tegra_vgpu.h +++ b/include/linux/tegra_vgpu.h @@ -105,6 +105,7 @@ enum { TEGRA_VGPU_CMD_GET_GPU_CLK_RATE = 69, TEGRA_VGPU_CMD_GET_GPU_FREQ_TABLE = 70, TEGRA_VGPU_CMD_CAP_GPU_CLK_RATE = 71, + TEGRA_VGPU_CMD_PROF_MGT = 72, }; struct tegra_vgpu_connect_params { @@ -479,6 +480,16 @@ struct tegra_vgpu_get_gpu_freq_table_params { u32 freqs[TEGRA_VGPU_GPU_FREQ_TABLE_SIZE]; /* in kHz */ }; +enum { + TEGRA_VGPU_PROF_GET_GLOBAL = 0, + TEGRA_VGPU_PROF_GET_CONTEXT, + TEGRA_VGPU_PROF_RELEASE +}; + +struct tegra_vgpu_prof_mgt_params { + u32 mode; +}; + struct tegra_vgpu_cmd_msg { u32 cmd; int ret; @@ -529,6 +540,7 @@ struct tegra_vgpu_cmd_msg { struct tegra_vgpu_suspend_resume_contexts resume_contexts; struct tegra_vgpu_clear_sm_error_state clear_sm_error_state; struct tegra_vgpu_get_gpu_freq_table_params get_gpu_freq_table; + struct tegra_vgpu_prof_mgt_params prof_management; char padding[192]; } params; };