diff --git a/src/freedreno/perfcntrs/freedreno_perfcntr.c b/src/freedreno/perfcntrs/freedreno_perfcntr.c index aa984903a6b..99239058295 100644 --- a/src/freedreno/perfcntrs/freedreno_perfcntr.c +++ b/src/freedreno/perfcntrs/freedreno_perfcntr.c @@ -7,6 +7,15 @@ */ #include +#include + +#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; diff --git a/src/freedreno/perfcntrs/freedreno_perfcntr.h b/src/freedreno/perfcntrs/freedreno_perfcntr.h index 5ab0630abf5..5b109b038e0 100644 --- a/src/freedreno/perfcntrs/freedreno_perfcntr.h +++ b/src/freedreno/perfcntrs/freedreno_perfcntr.h @@ -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 { diff --git a/src/freedreno/perfcntrs/meson.build b/src/freedreno/perfcntrs/meson.build index 5caa91fbf30..61d39e8afa4 100644 --- a/src/freedreno/perfcntrs/meson.build +++ b/src/freedreno/perfcntrs/meson.build @@ -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, )