mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 17:36:20 +03:00
gk20a_perfbuf_map() allocates perfbuf VM, maps the user buffer into new
VM, and then triggers gops.perfbuf.perfbuf_enable(). This HAL then does
following :
- Allocate perfbuf instance block
- Initialize perfbuf instance block
- Reset stream buffer
- Program instance block address in PMA registers
- Program user buffer address into PMA registers
New profiler interface will have it's own API to setup PMA strem, and
it requires above setup to be done in two phases of perfbuf
initialization and then user buffer setup.
Split above functionalities into below functions
- nvgpu_perfbuf_init_vm()
- Allocate perfbuf VM
- Call gops.perfbuf.init_inst_block() to initialize perfbuf instance
block
- gops.perfbuf.init_inst_block()
- Allocate perfbuf instance block
- Initialize perfbuf instance block
- Program instance block address in PMA registers using
gops.perf.init_inst_block()
- In case of vGPU, trigger TEGRA_VGPU_CMD_PERFBUF_INST_BLOCK_MGT
command to gpu server
- gops.perf.init_inst_block()
- Reset stream buffer
- Program user buffer address into PMA registers
Also add corresponding cleanup functions as below :
gops.perf.deinit_inst_block()
gops.perfbuf.deinit_inst_block()
nvgpu_perfbuf_deinit_vm()
Bug 2510974
Jira NVGPU-5360
Change-Id: I486370f21012cbb7fea84fe46fb16db95bc16790
Signed-off-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2372984
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
607 lines
15 KiB
C
607 lines
15 KiB
C
/*
|
|
* Cycle stats snapshots support
|
|
*
|
|
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <nvgpu/bitops.h>
|
|
#include <nvgpu/kmem.h>
|
|
#include <nvgpu/lock.h>
|
|
#include <nvgpu/dma.h>
|
|
#include <nvgpu/mm.h>
|
|
#include <nvgpu/sizes.h>
|
|
#include <nvgpu/barrier.h>
|
|
#include <nvgpu/log.h>
|
|
#include <nvgpu/bug.h>
|
|
#include <nvgpu/io.h>
|
|
#include <nvgpu/utils.h>
|
|
#include <nvgpu/gk20a.h>
|
|
#include <nvgpu/channel.h>
|
|
#include <nvgpu/cyclestats_snapshot.h>
|
|
|
|
/* check client for pointed perfmon ownership */
|
|
#define CONTAINS_PERFMON(cl, pm) \
|
|
((cl)->perfmon_start <= (pm) && \
|
|
((pm) - (cl)->perfmon_start) < (cl)->perfmon_count)
|
|
|
|
/* address of fifo entry by offset */
|
|
#define CSS_FIFO_ENTRY(fifo, offs) \
|
|
((struct gk20a_cs_snapshot_fifo_entry *)(((char *)(fifo)) + (offs)))
|
|
|
|
/* calculate area capacity in number of fifo entries */
|
|
#define CSS_FIFO_ENTRY_CAPACITY(s) \
|
|
(((s) - sizeof(struct gk20a_cs_snapshot_fifo)) \
|
|
/ sizeof(struct gk20a_cs_snapshot_fifo_entry))
|
|
|
|
/* reserved to indicate failures with data */
|
|
#define CSS_FIRST_PERFMON_ID 32
|
|
/* should correlate with size of gk20a_cs_snapshot_fifo_entry::perfmon_id */
|
|
#define CSS_MAX_PERFMON_IDS 256
|
|
|
|
/* reports whether the hw queue overflowed */
|
|
bool nvgpu_css_get_overflow_status(struct gk20a *g)
|
|
{
|
|
return g->ops.perf.get_membuf_overflow_status(g);
|
|
}
|
|
|
|
/* returns how many pending snapshot entries are pending */
|
|
u32 nvgpu_css_get_pending_snapshots(struct gk20a *g)
|
|
{
|
|
return g->ops.perf.get_membuf_pending_bytes(g) /
|
|
U32(sizeof(struct gk20a_cs_snapshot_fifo_entry));
|
|
}
|
|
|
|
/* informs hw how many snapshots have been processed (frees up fifo space) */
|
|
void nvgpu_css_set_handled_snapshots(struct gk20a *g, u32 done)
|
|
{
|
|
if (done > 0) {
|
|
g->ops.perf.set_membuf_handled_bytes(g, done,
|
|
sizeof(struct gk20a_cs_snapshot_fifo_entry));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* WARNING: all css_gr_XXX functions are local and expected to be called
|
|
* from locked context (protected by cs_lock)
|
|
*/
|
|
|
|
static int css_gr_create_shared_data(struct gk20a *g)
|
|
{
|
|
struct gk20a_cs_snapshot *data;
|
|
|
|
if (g->cs_data) {
|
|
return 0;
|
|
}
|
|
|
|
data = nvgpu_kzalloc(g, sizeof(*data));
|
|
if (!data) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nvgpu_init_list_node(&data->clients);
|
|
g->cs_data = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nvgpu_css_enable_snapshot(struct nvgpu_channel *ch,
|
|
struct gk20a_cs_snapshot_client *cs_client)
|
|
{
|
|
struct gk20a *g = ch->g;
|
|
struct gk20a_cs_snapshot *data = g->cs_data;
|
|
u32 snapshot_size = cs_client->snapshot_size;
|
|
int ret;
|
|
|
|
if (data->hw_snapshot) {
|
|
return 0;
|
|
}
|
|
|
|
if (snapshot_size < CSS_MIN_HW_SNAPSHOT_SIZE) {
|
|
snapshot_size = CSS_MIN_HW_SNAPSHOT_SIZE;
|
|
}
|
|
|
|
ret = nvgpu_dma_alloc_map_sys(g->mm.pmu.vm, snapshot_size,
|
|
&data->hw_memdesc);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* perf output buffer may not cross a 4GB boundary - with a separate */
|
|
/* va smaller than that, it won't but check anyway */
|
|
if (!data->hw_memdesc.cpu_va ||
|
|
data->hw_memdesc.size < snapshot_size ||
|
|
data->hw_memdesc.gpu_va + u64_lo32(snapshot_size) > SZ_4G) {
|
|
ret = -EFAULT;
|
|
goto failed_allocation;
|
|
}
|
|
|
|
data->hw_snapshot =
|
|
(struct gk20a_cs_snapshot_fifo_entry *)data->hw_memdesc.cpu_va;
|
|
data->hw_end = data->hw_snapshot +
|
|
snapshot_size / sizeof(struct gk20a_cs_snapshot_fifo_entry);
|
|
data->hw_get = data->hw_snapshot;
|
|
(void) memset(data->hw_snapshot, 0xff, snapshot_size);
|
|
|
|
g->ops.perf.membuf_reset_streaming(g);
|
|
g->ops.perf.init_inst_block(g, &g->mm.hwpm.inst_block);
|
|
g->ops.perf.enable_membuf(g, snapshot_size, data->hw_memdesc.gpu_va);
|
|
|
|
nvgpu_log_info(g, "cyclestats: buffer for hardware snapshots enabled\n");
|
|
|
|
return 0;
|
|
|
|
failed_allocation:
|
|
if (data->hw_memdesc.size) {
|
|
nvgpu_dma_unmap_free(g->mm.pmu.vm, &data->hw_memdesc);
|
|
(void) memset(&data->hw_memdesc, 0, sizeof(data->hw_memdesc));
|
|
}
|
|
data->hw_snapshot = NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void nvgpu_css_disable_snapshot(struct gk20a *g)
|
|
{
|
|
struct gk20a_cs_snapshot *data = g->cs_data;
|
|
|
|
if (!data->hw_snapshot) {
|
|
return;
|
|
}
|
|
|
|
g->ops.perf.membuf_reset_streaming(g);
|
|
g->ops.perf.disable_membuf(g);
|
|
g->ops.perf.deinit_inst_block(g);
|
|
|
|
nvgpu_dma_unmap_free(g->mm.pmu.vm, &data->hw_memdesc);
|
|
(void) memset(&data->hw_memdesc, 0, sizeof(data->hw_memdesc));
|
|
data->hw_snapshot = NULL;
|
|
|
|
nvgpu_log_info(g, "cyclestats: buffer for hardware snapshots disabled\n");
|
|
}
|
|
|
|
static void css_gr_free_shared_data(struct gk20a *g)
|
|
{
|
|
if (g->cs_data) {
|
|
/* the clients list is expected to be empty */
|
|
g->ops.css.disable_snapshot(g);
|
|
|
|
/* release the objects */
|
|
nvgpu_kfree(g, g->cs_data);
|
|
g->cs_data = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
struct gk20a_cs_snapshot_client *
|
|
nvgpu_css_gr_search_client(struct nvgpu_list_node *clients, u32 perfmon)
|
|
{
|
|
struct gk20a_cs_snapshot_client *client;
|
|
|
|
nvgpu_list_for_each_entry(client, clients,
|
|
gk20a_cs_snapshot_client, list) {
|
|
if (CONTAINS_PERFMON(client, perfmon)) {
|
|
return client;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int css_gr_flush_snapshots(struct nvgpu_channel *ch)
|
|
{
|
|
struct gk20a *g = ch->g;
|
|
struct gk20a_cs_snapshot *css = g->cs_data;
|
|
struct gk20a_cs_snapshot_client *cur;
|
|
u32 pending, completed;
|
|
bool hw_overflow;
|
|
int err;
|
|
|
|
/* variables for iterating over HW entries */
|
|
u32 sid;
|
|
struct gk20a_cs_snapshot_fifo_entry *src;
|
|
|
|
/* due to data sharing with userspace we allowed update only */
|
|
/* overflows and put field in the fifo header */
|
|
struct gk20a_cs_snapshot_fifo *dst;
|
|
struct gk20a_cs_snapshot_fifo_entry *dst_get;
|
|
struct gk20a_cs_snapshot_fifo_entry *dst_put;
|
|
struct gk20a_cs_snapshot_fifo_entry *dst_nxt;
|
|
struct gk20a_cs_snapshot_fifo_entry *dst_head;
|
|
struct gk20a_cs_snapshot_fifo_entry *dst_tail;
|
|
|
|
if (!css) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvgpu_list_empty(&css->clients)) {
|
|
return -EBADF;
|
|
}
|
|
|
|
/* check data available */
|
|
err = g->ops.css.check_data_available(ch, &pending, &hw_overflow);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (!pending) {
|
|
return 0;
|
|
}
|
|
|
|
if (hw_overflow) {
|
|
nvgpu_list_for_each_entry(cur, &css->clients,
|
|
gk20a_cs_snapshot_client, list) {
|
|
cur->snapshot->hw_overflow_events_occured++;
|
|
}
|
|
|
|
nvgpu_warn(g, "cyclestats: hardware overflow detected");
|
|
}
|
|
|
|
/* process all items in HW buffer */
|
|
sid = 0;
|
|
completed = 0;
|
|
cur = NULL;
|
|
dst = NULL;
|
|
dst_put = NULL;
|
|
src = css->hw_get;
|
|
|
|
/* proceed all completed records */
|
|
while (sid < pending && 0 == src->zero0) {
|
|
/* we may have a new perfmon_id which required to */
|
|
/* switch to a new client -> let's forget current */
|
|
if (cur && !CONTAINS_PERFMON(cur, src->perfmon_id)) {
|
|
s64 tmp_ptr = (char *)dst_put - (char *)dst;
|
|
|
|
nvgpu_assert(tmp_ptr < (s64)U32_MAX);
|
|
dst->put = U32(tmp_ptr);
|
|
dst = NULL;
|
|
cur = NULL;
|
|
}
|
|
|
|
/* now we have to select a new current client */
|
|
/* the client selection rate depends from experiment */
|
|
/* activity but on Android usually happened 1-2 times */
|
|
if (!cur) {
|
|
cur = nvgpu_css_gr_search_client(&css->clients,
|
|
src->perfmon_id);
|
|
if (cur) {
|
|
/* found - setup all required data */
|
|
dst = cur->snapshot;
|
|
dst_get = CSS_FIFO_ENTRY(dst, dst->get);
|
|
dst_put = CSS_FIFO_ENTRY(dst, dst->put);
|
|
dst_head = CSS_FIFO_ENTRY(dst, dst->start);
|
|
dst_tail = CSS_FIFO_ENTRY(dst, dst->end);
|
|
|
|
dst_nxt = dst_put + 1;
|
|
if (dst_nxt == dst_tail) {
|
|
dst_nxt = dst_head;
|
|
}
|
|
} else {
|
|
/* client not found - skipping this entry */
|
|
nvgpu_warn(g, "cyclestats: orphaned perfmon %u",
|
|
src->perfmon_id);
|
|
goto next_hw_fifo_entry;
|
|
}
|
|
}
|
|
|
|
/* check for software overflows */
|
|
if (dst_nxt == dst_get) {
|
|
/* no data copy, no pointer updates */
|
|
dst->sw_overflow_events_occured++;
|
|
nvgpu_warn(g, "cyclestats: perfmon %u soft overflow",
|
|
src->perfmon_id);
|
|
} else {
|
|
*dst_put = *src;
|
|
completed++;
|
|
|
|
dst_put = dst_nxt++;
|
|
|
|
if (dst_nxt == dst_tail) {
|
|
dst_nxt = dst_head;
|
|
}
|
|
}
|
|
|
|
next_hw_fifo_entry:
|
|
sid++;
|
|
if (++src >= css->hw_end) {
|
|
src = css->hw_snapshot;
|
|
}
|
|
}
|
|
|
|
/* update client put pointer if necessary */
|
|
if (cur && dst) {
|
|
s64 tmp_ptr = (char *)dst_put - (char *)dst;
|
|
|
|
nvgpu_assert(tmp_ptr < (s64)U32_MAX);
|
|
dst->put = U32(tmp_ptr);
|
|
}
|
|
|
|
/* re-set HW buffer after processing taking wrapping into account */
|
|
if (css->hw_get < src) {
|
|
(void) memset(css->hw_get, 0xff,
|
|
(src - css->hw_get) * sizeof(*src));
|
|
} else {
|
|
(void) memset(css->hw_snapshot, 0xff,
|
|
(src - css->hw_snapshot) * sizeof(*src));
|
|
(void) memset(css->hw_get, 0xff,
|
|
(css->hw_end - css->hw_get) * sizeof(*src));
|
|
}
|
|
g->cs_data->hw_get = src;
|
|
|
|
if (g->ops.css.set_handled_snapshots) {
|
|
g->ops.css.set_handled_snapshots(g, sid);
|
|
}
|
|
|
|
if (completed != sid) {
|
|
/* not all entries proceed correctly. some of problems */
|
|
/* reported as overflows, some as orphaned perfmons, */
|
|
/* but it will be better notify with summary about it */
|
|
nvgpu_warn(g, "cyclestats: completed %u from %u entries",
|
|
completed, pending);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 nvgpu_css_allocate_perfmon_ids(struct gk20a_cs_snapshot *data,
|
|
u32 count)
|
|
{
|
|
unsigned long *pids = data->perfmon_ids;
|
|
unsigned int f;
|
|
|
|
f = U32(bitmap_find_next_zero_area(pids, CSS_MAX_PERFMON_IDS,
|
|
CSS_FIRST_PERFMON_ID, count, 0));
|
|
if (f > CSS_MAX_PERFMON_IDS) {
|
|
f = 0;
|
|
} else {
|
|
nvgpu_bitmap_set(pids, f, count);
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
u32 nvgpu_css_release_perfmon_ids(struct gk20a_cs_snapshot *data,
|
|
u32 start,
|
|
u32 count)
|
|
{
|
|
unsigned long *pids = data->perfmon_ids;
|
|
u32 end = start + count;
|
|
u32 cnt = 0;
|
|
|
|
if (start >= CSS_FIRST_PERFMON_ID && end <= CSS_MAX_PERFMON_IDS) {
|
|
nvgpu_bitmap_clear(pids, start, count);
|
|
cnt = count;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int css_gr_free_client_data(struct gk20a *g,
|
|
struct gk20a_cs_snapshot *data,
|
|
struct gk20a_cs_snapshot_client *client)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (client->list.next && client->list.prev) {
|
|
nvgpu_list_del(&client->list);
|
|
}
|
|
|
|
if (client->perfmon_start && client->perfmon_count
|
|
&& g->ops.css.release_perfmon_ids) {
|
|
if (client->perfmon_count != g->ops.css.release_perfmon_ids(data,
|
|
client->perfmon_start, client->perfmon_count)) {
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int css_gr_create_client_data(struct gk20a *g,
|
|
struct gk20a_cs_snapshot *data,
|
|
u32 perfmon_count,
|
|
struct gk20a_cs_snapshot_client *cur)
|
|
{
|
|
/*
|
|
* Special handling in-case of rm-server
|
|
*
|
|
* client snapshot buffer will not be mapped
|
|
* in-case of rm-server its only mapped in
|
|
* guest side
|
|
*/
|
|
if (cur->snapshot) {
|
|
(void) memset(cur->snapshot, 0, sizeof(*cur->snapshot));
|
|
cur->snapshot->start = U32(sizeof(*cur->snapshot));
|
|
/* we should be ensure that can fit all fifo entries here */
|
|
cur->snapshot->end =
|
|
U32(CSS_FIFO_ENTRY_CAPACITY(cur->snapshot_size)
|
|
* sizeof(struct gk20a_cs_snapshot_fifo_entry)
|
|
+ sizeof(struct gk20a_cs_snapshot_fifo));
|
|
cur->snapshot->get = cur->snapshot->start;
|
|
cur->snapshot->put = cur->snapshot->start;
|
|
}
|
|
|
|
cur->perfmon_count = perfmon_count;
|
|
|
|
/* In virtual case, perfmon ID allocation is handled by the server
|
|
* at the time of the attach (allocate_perfmon_ids is NULL in this case)
|
|
*/
|
|
if (cur->perfmon_count && g->ops.css.allocate_perfmon_ids) {
|
|
cur->perfmon_start = g->ops.css.allocate_perfmon_ids(data,
|
|
cur->perfmon_count);
|
|
if (!cur->perfmon_start) {
|
|
return -ENOENT;
|
|
}
|
|
}
|
|
|
|
nvgpu_list_add_tail(&cur->list, &data->clients);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int nvgpu_css_attach(struct nvgpu_channel *ch,
|
|
u32 perfmon_count,
|
|
u32 *perfmon_start,
|
|
struct gk20a_cs_snapshot_client *cs_client)
|
|
{
|
|
int ret = 0;
|
|
struct gk20a *g = ch->g;
|
|
|
|
/* we must have a placeholder to store pointer to client structure */
|
|
if (!cs_client) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!perfmon_count ||
|
|
perfmon_count > CSS_MAX_PERFMON_IDS - CSS_FIRST_PERFMON_ID) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvgpu_speculation_barrier();
|
|
|
|
nvgpu_mutex_acquire(&g->cs_lock);
|
|
|
|
ret = css_gr_create_shared_data(g);
|
|
if (ret != 0) {
|
|
goto failed;
|
|
}
|
|
|
|
ret = css_gr_create_client_data(g, g->cs_data,
|
|
perfmon_count,
|
|
cs_client);
|
|
if (ret != 0) {
|
|
goto failed;
|
|
}
|
|
|
|
ret = g->ops.css.enable_snapshot(ch, cs_client);
|
|
if (ret != 0) {
|
|
goto failed;
|
|
}
|
|
|
|
if (perfmon_start) {
|
|
*perfmon_start = cs_client->perfmon_start;
|
|
}
|
|
|
|
nvgpu_mutex_release(&g->cs_lock);
|
|
|
|
return 0;
|
|
|
|
failed:
|
|
if (g->cs_data) {
|
|
if (cs_client) {
|
|
css_gr_free_client_data(g, g->cs_data, cs_client);
|
|
cs_client = NULL;
|
|
}
|
|
|
|
if (nvgpu_list_empty(&g->cs_data->clients)) {
|
|
css_gr_free_shared_data(g);
|
|
}
|
|
}
|
|
nvgpu_mutex_release(&g->cs_lock);
|
|
|
|
if (perfmon_start) {
|
|
*perfmon_start = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int nvgpu_css_detach(struct nvgpu_channel *ch,
|
|
struct gk20a_cs_snapshot_client *cs_client)
|
|
{
|
|
int ret = 0;
|
|
struct gk20a *g = ch->g;
|
|
|
|
if (!cs_client) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvgpu_mutex_acquire(&g->cs_lock);
|
|
if (g->cs_data) {
|
|
struct gk20a_cs_snapshot *data = g->cs_data;
|
|
|
|
if (g->ops.css.detach_snapshot) {
|
|
g->ops.css.detach_snapshot(ch, cs_client);
|
|
}
|
|
|
|
ret = css_gr_free_client_data(g, data, cs_client);
|
|
if (nvgpu_list_empty(&data->clients)) {
|
|
css_gr_free_shared_data(g);
|
|
}
|
|
} else {
|
|
ret = -EBADF;
|
|
}
|
|
nvgpu_mutex_release(&g->cs_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int nvgpu_css_flush(struct nvgpu_channel *ch,
|
|
struct gk20a_cs_snapshot_client *cs_client)
|
|
{
|
|
int ret = 0;
|
|
struct gk20a *g = ch->g;
|
|
|
|
if (!cs_client) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvgpu_mutex_acquire(&g->cs_lock);
|
|
ret = css_gr_flush_snapshots(ch);
|
|
nvgpu_mutex_release(&g->cs_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* helper function with locking to cleanup snapshot code code in gr_gk20a.c */
|
|
void nvgpu_free_cyclestats_snapshot_data(struct gk20a *g)
|
|
{
|
|
nvgpu_mutex_acquire(&g->cs_lock);
|
|
css_gr_free_shared_data(g);
|
|
nvgpu_mutex_release(&g->cs_lock);
|
|
nvgpu_mutex_destroy(&g->cs_lock);
|
|
}
|
|
|
|
int nvgpu_css_check_data_available(struct nvgpu_channel *ch, u32 *pending,
|
|
bool *hw_overflow)
|
|
{
|
|
struct gk20a *g = ch->g;
|
|
struct gk20a_cs_snapshot *css = g->cs_data;
|
|
|
|
if (!css->hw_snapshot) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*pending = nvgpu_css_get_pending_snapshots(g);
|
|
if (!*pending) {
|
|
return 0;
|
|
}
|
|
|
|
*hw_overflow = nvgpu_css_get_overflow_status(g);
|
|
return 0;
|
|
}
|
|
|
|
u32 nvgpu_css_get_max_buffer_size(struct gk20a *g)
|
|
{
|
|
return 0xffffffffU;
|
|
}
|