mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-08 09:08:10 +02:00
freedreno/perfcntrs: Add helper to assign counters
Add a helper to allocate a counter for a requested countable, and (if supported by KMD) do the PERFCNTR_CONFIG ioctl to reserve the counter for UMD local (inline) usage. Signed-off-by: Rob Clark <rob.clark@oss.qualcomm.com>
This commit is contained in:
parent
cc743feb52
commit
74cfe319b7
3 changed files with 275 additions and 1 deletions
|
|
@ -7,6 +7,15 @@
|
|||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#include "util/hash_table.h"
|
||||
#include "util/ralloc.h"
|
||||
|
||||
#include "drm-uapi/msm_drm.h"
|
||||
#include "util/bitset.h"
|
||||
#include "util/simple_mtx.h"
|
||||
#include "freedreno_common.h"
|
||||
|
||||
#include "freedreno_perfcntr.h"
|
||||
|
||||
|
|
@ -47,6 +56,252 @@ fd_perfcntrs(const struct fd_dev_id *id, unsigned *count)
|
|||
}
|
||||
}
|
||||
|
||||
struct fd_perfcntr_counter_state {
|
||||
int group;
|
||||
int counter;
|
||||
int countable;
|
||||
unsigned nr_users;
|
||||
};
|
||||
|
||||
#define MAX_COUNTERS_PER_GROUP 32
|
||||
typedef BITSET_DECLARE(assigned_counters_t, MAX_COUNTERS_PER_GROUP);
|
||||
|
||||
/**
|
||||
* Helper to manage assigning counters, tracking if there are multiple users
|
||||
* for the same countable (to avoid assigning duplicate counters for the
|
||||
* same countable, etc)
|
||||
*/
|
||||
struct fd_perfcntr_state {
|
||||
simple_mtx_t lock;
|
||||
int fd;
|
||||
const struct fd_dev_id *id;
|
||||
|
||||
unsigned nr_groups;
|
||||
const struct fd_perfcntr_group *groups;
|
||||
|
||||
struct drm_msm_perfcntr_group *group_configs;
|
||||
struct drm_msm_perfcntr_config config;
|
||||
|
||||
/* bitmask of assigned counters per group: */
|
||||
assigned_counters_t *assigned_counters;
|
||||
|
||||
/* maps counter to fd_perfcntr_counter_state: */
|
||||
struct hash_table *counter_state;
|
||||
};
|
||||
|
||||
static int
|
||||
update_reserved_counters(struct fd_perfcntr_state *perfcntrs)
|
||||
{
|
||||
/* If no kernel support, just carry on and assume we can use all counters: */
|
||||
if (perfcntrs->fd < 0)
|
||||
return 0;
|
||||
|
||||
return drmIoctl(perfcntrs->fd, DRM_IOCTL_MSM_PERFCNTR_CONFIG, &perfcntrs->config);
|
||||
}
|
||||
|
||||
static int
|
||||
update_group_counters(struct fd_perfcntr_state *perfcntrs, int group_idx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Update reserved config with kernel if it changes. We might not
|
||||
* be assiging/releasing the last counter (and we cannot feasibly
|
||||
* re-map existing assigned counters to compact away gaps in the
|
||||
* used counters, as cmdstream might already
|
||||
* be built encoding the other assigned counters), but if we do
|
||||
* let the kernel know:
|
||||
*/
|
||||
unsigned nr = BITSET_LAST_BIT(perfcntrs->assigned_counters[group_idx]);
|
||||
if (nr != perfcntrs->group_configs[group_idx].nr_countables) {
|
||||
perfcntrs->group_configs[group_idx].nr_countables = nr;
|
||||
ret = update_reserved_counters(perfcntrs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct fd_perfcntr_state *
|
||||
fd_perfcntr_state_alloc(const struct fd_dev_id *id, int fd)
|
||||
{
|
||||
const struct fd_perfcntr_group *groups;
|
||||
unsigned nr_groups;
|
||||
|
||||
groups = fd_perfcntrs(id, &nr_groups);
|
||||
if (!groups)
|
||||
return NULL;
|
||||
|
||||
struct fd_perfcntr_state *perfcntrs = rzalloc(NULL, struct fd_perfcntr_state);
|
||||
|
||||
simple_mtx_init(&perfcntrs->lock, mtx_plain);
|
||||
perfcntrs->fd = fd;
|
||||
perfcntrs->id = id;
|
||||
perfcntrs->nr_groups = nr_groups;
|
||||
perfcntrs->groups = groups;
|
||||
perfcntrs->group_configs =
|
||||
rzalloc_array(perfcntrs, struct drm_msm_perfcntr_group, nr_groups);
|
||||
|
||||
for (unsigned i = 0; i < nr_groups; i++) {
|
||||
assert(strlen(groups[i].name) < sizeof(perfcntrs->group_configs[i].group_name));
|
||||
strcpy(perfcntrs->group_configs[i].group_name, groups[i].name);
|
||||
}
|
||||
|
||||
perfcntrs->config = (struct drm_msm_perfcntr_config) {
|
||||
.nr_groups = nr_groups,
|
||||
.groups = VOID2U64(perfcntrs->group_configs),
|
||||
.group_stride = sizeof(struct drm_msm_perfcntr_group),
|
||||
};
|
||||
|
||||
perfcntrs->assigned_counters = rzalloc_array(perfcntrs, assigned_counters_t, nr_groups);
|
||||
perfcntrs->counter_state = _mesa_pointer_hash_table_create(perfcntrs);
|
||||
|
||||
/* Probe for kernel PERFCNTR_CONFIG support with empty config: */
|
||||
if (update_reserved_counters(perfcntrs))
|
||||
perfcntrs->fd = -1;
|
||||
|
||||
return perfcntrs;
|
||||
}
|
||||
|
||||
void
|
||||
fd_perfcntr_state_free(struct fd_perfcntr_state *perfcntrs)
|
||||
{
|
||||
if (!perfcntrs)
|
||||
return;
|
||||
|
||||
perfcntrs->config.nr_groups = 0;
|
||||
update_reserved_counters(perfcntrs);
|
||||
ralloc_free(perfcntrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does KMD support perfcntr reservation (ie. PERFCNTR_CONFIG)
|
||||
*/
|
||||
bool
|
||||
fd_perfcntr_has_reservation(struct fd_perfcntr_state *perfcntrs)
|
||||
{
|
||||
return perfcntrs->fd >= 0;
|
||||
}
|
||||
|
||||
static int
|
||||
find_group_idx(struct fd_perfcntr_state *perfcntrs,
|
||||
const struct fd_perfcntr_group *group)
|
||||
{
|
||||
for (unsigned i = 0; i < perfcntrs->nr_groups; i++)
|
||||
if (&perfcntrs->groups[i] == group)
|
||||
return i;
|
||||
UNREACHABLE("invalid group");
|
||||
}
|
||||
|
||||
static int
|
||||
find_countable_idx(const struct fd_perfcntr_group *group,
|
||||
const struct fd_perfcntr_countable *countable)
|
||||
{
|
||||
for (unsigned i = 0; i < group->num_countables; i++)
|
||||
if (&group->countables[i] == countable)
|
||||
return i;
|
||||
UNREACHABLE("invalid countable");
|
||||
}
|
||||
|
||||
const struct fd_perfcntr_counter *
|
||||
fd_perfcntr_reserve(struct fd_perfcntr_state *perfcntrs,
|
||||
const struct fd_perfcntr_group *group,
|
||||
const struct fd_perfcntr_countable *countable)
|
||||
{
|
||||
struct fd_perfcntr_counter_state *state = NULL;
|
||||
int c, g = find_group_idx(perfcntrs, group);
|
||||
|
||||
simple_mtx_lock(&perfcntrs->lock);
|
||||
|
||||
/* Check if requested countable is already configured: */
|
||||
BITSET_FOREACH_SET (c, perfcntrs->assigned_counters[g], MAX_COUNTERS_PER_GROUP) {
|
||||
struct hash_entry *e =
|
||||
_mesa_hash_table_search(perfcntrs->counter_state, &group->counters[c]);
|
||||
|
||||
assert(e);
|
||||
struct fd_perfcntr_counter_state *s = e->data;
|
||||
|
||||
if (&group->countables[s->countable] == countable) {
|
||||
state = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't find a counter assigned to this countable, assign a new one: */
|
||||
if (!state) {
|
||||
assigned_counters_t *assigned_counters = &perfcntrs->assigned_counters[g];
|
||||
|
||||
/* Pick lowest #ed unassigned counter: */
|
||||
assigned_counters_t free_counters;
|
||||
memcpy(free_counters, *assigned_counters, sizeof(free_counters));
|
||||
BITSET_NOT(free_counters);
|
||||
|
||||
c = BITSET_FFS(free_counters) - 1;
|
||||
assert(c >= 0);
|
||||
|
||||
if (c < group->num_counters) {
|
||||
state = rzalloc(perfcntrs, struct fd_perfcntr_counter_state);
|
||||
state->group = g;
|
||||
state->counter = c;
|
||||
state->countable = find_countable_idx(group, countable);
|
||||
|
||||
assert(!BITSET_TEST(*assigned_counters, state->counter));
|
||||
|
||||
BITSET_SET(*assigned_counters, state->counter);
|
||||
|
||||
if (update_group_counters(perfcntrs, state->group)) {
|
||||
BITSET_CLEAR(*assigned_counters, state->counter);
|
||||
ralloc_free(state);
|
||||
state = NULL;
|
||||
} else {
|
||||
_mesa_hash_table_insert(perfcntrs->counter_state,
|
||||
&group->counters[state->counter],
|
||||
state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state)
|
||||
state->nr_users++;
|
||||
|
||||
simple_mtx_unlock(&perfcntrs->lock);
|
||||
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
return &group->counters[state->counter];
|
||||
}
|
||||
|
||||
void
|
||||
fd_perfcntr_release(struct fd_perfcntr_state *perfcntrs,
|
||||
const struct fd_perfcntr_counter *counter)
|
||||
{
|
||||
if (!counter)
|
||||
return;
|
||||
|
||||
simple_mtx_lock(&perfcntrs->lock);
|
||||
struct hash_entry *e = _mesa_hash_table_search(perfcntrs->counter_state, counter);
|
||||
if (e) {
|
||||
struct fd_perfcntr_counter_state *state = e->data;
|
||||
|
||||
assert(state->nr_users > 0);
|
||||
|
||||
if (--state->nr_users == 0) {
|
||||
/* dropping last user of the counter: */
|
||||
_mesa_hash_table_remove(perfcntrs->counter_state, e);
|
||||
|
||||
assigned_counters_t *assigned_counters =
|
||||
&perfcntrs->assigned_counters[state->group];
|
||||
|
||||
assert(BITSET_TEST(*assigned_counters, state->counter));
|
||||
|
||||
BITSET_CLEAR(*assigned_counters, state->counter);
|
||||
update_group_counters(perfcntrs, state->group);
|
||||
|
||||
ralloc_free(state);
|
||||
}
|
||||
}
|
||||
simple_mtx_unlock(&perfcntrs->lock);
|
||||
}
|
||||
|
||||
extern const struct fd_derived_counter *a7xx_derived_counters[];
|
||||
extern const unsigned a7xx_num_derived_counters;
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,21 @@ fd_perfcntrs_countable(const struct fd_perfcntr_group *group, const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct fd_perfcntr_state;
|
||||
|
||||
struct fd_perfcntr_state *
|
||||
fd_perfcntr_state_alloc(const struct fd_dev_id *id, int fd);
|
||||
void fd_perfcntr_state_free(struct fd_perfcntr_state *perfcntrs);
|
||||
|
||||
bool fd_perfcntr_has_reservation(struct fd_perfcntr_state *perfcntrs);
|
||||
|
||||
const struct fd_perfcntr_counter *
|
||||
fd_perfcntr_reserve(struct fd_perfcntr_state *perfcntrs,
|
||||
const struct fd_perfcntr_group *group,
|
||||
const struct fd_perfcntr_countable *countable);
|
||||
void fd_perfcntr_release(struct fd_perfcntr_state *perfcntrs,
|
||||
const struct fd_perfcntr_counter *counter);
|
||||
|
||||
#define FD_DERIVED_COUNTER_MAX_PERFCNTRS 8
|
||||
|
||||
struct fd_derivation_context {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ libfreedreno_perfcntrs = static_library(
|
|||
c_args : [no_override_init_args],
|
||||
gnu_symbol_visibility : 'hidden',
|
||||
link_with : [libfreedreno_common],
|
||||
dependencies : idep_nir_headers,
|
||||
dependencies : [
|
||||
dep_libdrm,
|
||||
idep_mesautil,
|
||||
idep_nir_headers,
|
||||
],
|
||||
build_by_default : false,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue