diff options
author | Siddharth Kapoor <ksiddharth@google.com> | 2022-03-02 14:51:29 +0800 |
---|---|---|
committer | Siddharth Kapoor <ksiddharth@google.com> | 2022-03-02 14:51:29 +0800 |
commit | 88d7d984fed1c2a4358ce2bbc334e82d71e3a391 (patch) | |
tree | 18f20402a0ed15ae9fe62b29a9957922ebcc2ada /mali_kbase/mali_kbase_kinstr_prfcnt.c | |
parent | 0207d6c3b7a2002f15c60d08617e956faf5ba90c (diff) | |
download | gpu-88d7d984fed1c2a4358ce2bbc334e82d71e3a391.tar.gz |
Mali Valhall Android DDK r36p0 KMD
Provenance: 9f72c118d9 (ipdelivery/EAC/v_r36p0)
VX504X08X-BU-00000-r36p0-01eac0 - Valhall Android DDK
VX504X08X-BU-60000-r36p0-01eac0 - Valhall Android Document Bundle
VX504X08X-DC-11001-r36p0-01eac0 - Valhall Android DDK Software Errata
VX504X08X-SW-99006-r36p0-01eac0 - Valhall Android Renderscript AOSP parts
Signed-off-by: Siddharth Kapoor <ksiddharth@google.com>
Change-Id: I4a63b707fedc68d7b7d046596c7098da47a139cb
Diffstat (limited to 'mali_kbase/mali_kbase_kinstr_prfcnt.c')
-rw-r--r-- | mali_kbase/mali_kbase_kinstr_prfcnt.c | 394 |
1 files changed, 235 insertions, 159 deletions
diff --git a/mali_kbase/mali_kbase_kinstr_prfcnt.c b/mali_kbase/mali_kbase_kinstr_prfcnt.c index 27ff3bb..e0c2c2c 100644 --- a/mali_kbase/mali_kbase_kinstr_prfcnt.c +++ b/mali_kbase/mali_kbase_kinstr_prfcnt.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note /* * - * (C) COPYRIGHT 2021 ARM Limited. All rights reserved. + * (C) COPYRIGHT 2021-2022 ARM Limited. All rights reserved. * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software @@ -22,7 +22,6 @@ #include "mali_kbase.h" #include "mali_kbase_kinstr_prfcnt.h" #include "mali_kbase_hwcnt_virtualizer.h" -#include "mali_kbase_hwcnt_types.h" #include "mali_kbase_hwcnt_gpu.h" #include <uapi/gpu/arm/midgard/mali_kbase_ioctl.h> #include "mali_malisw.h" @@ -157,6 +156,9 @@ struct kbase_kinstr_prfcnt_async { * @sample_arr: Array of dump buffers allocated by this client. * @read_idx: Index of buffer read by userspace. * @write_idx: Index of buffer being written by dump worker. + * @fetch_idx: Index of buffer being fetched by userspace, but + * pending a confirmation of being read (consumed) if it + * differs from the read_idx. * @waitq: Client's notification queue. * @sample_size: Size of the data required for one sample, in bytes. * @sample_count: Number of samples the client is able to capture. @@ -185,6 +187,7 @@ struct kbase_kinstr_prfcnt_client { struct kbase_kinstr_prfcnt_sample_array sample_arr; atomic_t read_idx; atomic_t write_idx; + atomic_t fetch_idx; wait_queue_head_t waitq; size_t sample_size; size_t sample_count; @@ -248,7 +251,7 @@ kbasep_kinstr_prfcnt_hwcnt_reader_poll(struct file *filp, poll_wait(filp, &cli->waitq, wait); - if (atomic_read(&cli->write_idx) != atomic_read(&cli->read_idx)) + if (atomic_read(&cli->write_idx) != atomic_read(&cli->fetch_idx)) return POLLIN; return 0; @@ -398,17 +401,30 @@ kbase_hwcnt_metadata_block_type_to_prfcnt_block_type(u64 type) return block_type; } +static bool kbase_kinstr_is_block_type_reserved(const struct kbase_hwcnt_metadata *metadata, + size_t grp, size_t blk) +{ + enum prfcnt_block_type block_type = kbase_hwcnt_metadata_block_type_to_prfcnt_block_type( + kbase_hwcnt_metadata_block_type(metadata, grp, blk)); + + return block_type == PRFCNT_BLOCK_TYPE_RESERVED; +} + /** * kbasep_kinstr_prfcnt_set_block_meta_items() - Populate a sample's block meta * item array. + * @enable_map: Non-NULL pointer to the map of enabled counters. * @dst: Non-NULL pointer to the sample's dump buffer object. * @block_meta_base: Non-NULL double pointer to the start of the block meta * data items. * @base_addr: Address of allocated pages for array of samples. Used * to calculate offset of block values. * @counter_set: The SET which blocks represent. + * + * Return: 0 on success, else error code. */ -int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_dump_buffer *dst, +int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_enable_map *enable_map, + struct kbase_hwcnt_dump_buffer *dst, struct prfcnt_metadata **block_meta_base, u64 base_addr, u8 counter_set) { @@ -423,8 +439,10 @@ int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_dump_buffer *ds kbase_hwcnt_metadata_for_each_block(metadata, grp, blk, blk_inst) { u64 *dst_blk; - /* Skip unused blocks */ - if (!kbase_hwcnt_metadata_block_instance_avail(metadata, grp, blk, blk_inst)) + /* Skip unavailable or non-enabled blocks */ + if (kbase_kinstr_is_block_type_reserved(metadata, grp, blk) || + !kbase_hwcnt_metadata_block_instance_avail(metadata, grp, blk, blk_inst) || + !kbase_hwcnt_enable_map_block_enabled(enable_map, grp, blk, blk_inst)) continue; dst_blk = kbase_hwcnt_dump_buffer_block_instance(dst, grp, blk, blk_inst); @@ -482,8 +500,9 @@ static void kbasep_kinstr_prfcnt_set_sample_metadata( /* Dealing with counter blocks */ ptr_md++; - if (WARN_ON(kbasep_kinstr_prfcnt_set_block_meta_items( - dump_buf, &ptr_md, cli->sample_arr.page_addr, cli->config.counter_set))) + if (WARN_ON(kbasep_kinstr_prfcnt_set_block_meta_items(&cli->enable_map, dump_buf, &ptr_md, + cli->sample_arr.page_addr, + cli->config.counter_set))) return; /* Handle the last sentinel item */ @@ -676,6 +695,9 @@ kbasep_kinstr_prfcnt_client_start(struct kbase_kinstr_prfcnt_client *cli, kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, &cli->config.phys_em); + /* Enable all the available clk_enable_map. */ + cli->enable_map.clk_enable_map = (1ull << cli->kinstr_ctx->metadata->clk_cnt) - 1; + mutex_lock(&cli->kinstr_ctx->lock); /* Enable HWC from the configuration of the client creation */ ret = kbase_hwcnt_virtualizer_client_set_counters( @@ -778,7 +800,7 @@ kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli, mutex_unlock(&cli->kinstr_ctx->lock); - return ret; + return 0; } static int @@ -887,28 +909,35 @@ kbasep_kinstr_prfcnt_client_async_dump(struct kbase_kinstr_prfcnt_client *cli, static int kbasep_kinstr_prfcnt_client_discard(struct kbase_kinstr_prfcnt_client *cli) { + unsigned int write_idx; + WARN_ON(!cli); lockdep_assert_held(&cli->cmd_sync_lock); mutex_lock(&cli->kinstr_ctx->lock); - /* Discard (Clear) all internally buffered samples */ - atomic_set(&cli->read_idx, atomic_read(&cli->write_idx)); + write_idx = atomic_read(&cli->write_idx); + + /* Discard (clear) all internally buffered samples. Note, if there + * is a fetched sample in flight, one should not touch the read index, + * leaving it alone for the put-sample operation to update it. The + * consistency between the read_idx and the fetch_idx is coordinated by + * holding the cli->cmd_sync_lock. + */ + if (atomic_read(&cli->fetch_idx) != atomic_read(&cli->read_idx)) { + atomic_set(&cli->fetch_idx, write_idx); + } else { + atomic_set(&cli->fetch_idx, write_idx); + atomic_set(&cli->read_idx, write_idx); + } mutex_unlock(&cli->kinstr_ctx->lock); return 0; } -/** - * kbasep_kinstr_prfcnt_cmd() - Execute command for a client session. - * @cli: Non-NULL pointer to kinstr_prfcnt client. - * @control_cmd: Control command to execute. - * - * Return: 0 on success, else error code. - */ -static int kbasep_kinstr_prfcnt_cmd(struct kbase_kinstr_prfcnt_client *cli, - struct prfcnt_control_cmd *control_cmd) +int kbasep_kinstr_prfcnt_cmd(struct kbase_kinstr_prfcnt_client *cli, + struct prfcnt_control_cmd *control_cmd) { int ret = 0; @@ -950,14 +979,36 @@ kbasep_kinstr_prfcnt_get_sample(struct kbase_kinstr_prfcnt_client *cli, { unsigned int write_idx; unsigned int read_idx; + unsigned int fetch_idx; u64 sample_offset_bytes; struct prfcnt_metadata *sample_meta; + int err = 0; + mutex_lock(&cli->cmd_sync_lock); write_idx = atomic_read(&cli->write_idx); read_idx = atomic_read(&cli->read_idx); - if (write_idx == read_idx) - return -EINVAL; + if (write_idx == read_idx) { + err = -EINVAL; + goto error_out; + } + + /* If the client interface has already had a sample been fetched, + * reflected by the fetch index not equal to read_idx, i.e., typically + * read_idx + 1 == fetch_idx, + * further fetch is not allowed until the previously fetched buffer + * is put back (which brings the read_idx == fetch_idx). As a design, + * the above add one equal condition (i.e. typical cases) may only be + * untrue if there had been an interface operation on sample discard, + * after the sample in question already been fetched, in which case, + * the fetch_idx could have a delta larger than 1 relative to the + * read_idx. + */ + fetch_idx = atomic_read(&cli->fetch_idx); + if (read_idx != fetch_idx) { + err = -EBUSY; + goto error_out; + } read_idx %= cli->sample_arr.sample_count; sample_offset_bytes = @@ -972,19 +1023,21 @@ kbasep_kinstr_prfcnt_get_sample(struct kbase_kinstr_prfcnt_client *cli, * for instance if the client is trying to get an asynchronous * sample which has not been dumped yet. */ - if (sample_meta->hdr.item_type != PRFCNT_SAMPLE_META_TYPE_SAMPLE) - return -EINVAL; - if (sample_meta->hdr.item_version != PRFCNT_READER_API_VERSION) - return -EINVAL; + if (sample_meta->hdr.item_type != PRFCNT_SAMPLE_META_TYPE_SAMPLE || + sample_meta->hdr.item_version != PRFCNT_READER_API_VERSION) { + err = -EINVAL; + goto error_out; + } sample_access->sequence = sample_meta->u.sample_md.seq; sample_access->sample_offset_bytes = sample_offset_bytes; - /* read_idx is not incremented here, because the interface allows - * only one sample to be "in flight" between kernel space and user space. - */ + /* Marking a sample has been fetched by advancing the fetch index */ + atomic_inc(&cli->fetch_idx); - return 0; +error_out: + mutex_unlock(&cli->cmd_sync_lock); + return err; } static int @@ -993,28 +1046,39 @@ kbasep_kinstr_prfcnt_put_sample(struct kbase_kinstr_prfcnt_client *cli, { unsigned int write_idx; unsigned int read_idx; + unsigned int fetch_idx; u64 sample_offset_bytes; + int err = 0; + mutex_lock(&cli->cmd_sync_lock); write_idx = atomic_read(&cli->write_idx); read_idx = atomic_read(&cli->read_idx); - if (write_idx == read_idx) - return -EINVAL; - - if (sample_access->sequence != read_idx) - return -EINVAL; + if (write_idx == read_idx || sample_access->sequence != read_idx) { + err = -EINVAL; + goto error_out; + } read_idx %= cli->sample_arr.sample_count; sample_offset_bytes = (u64)(uintptr_t)cli->sample_arr.samples[read_idx].sample_meta - (u64)(uintptr_t)cli->sample_arr.page_addr; - if (sample_access->sample_offset_bytes != sample_offset_bytes) - return -EINVAL; + if (sample_access->sample_offset_bytes != sample_offset_bytes) { + err = -EINVAL; + goto error_out; + } - atomic_inc(&cli->read_idx); + fetch_idx = atomic_read(&cli->fetch_idx); + WARN_ON(read_idx == fetch_idx); + /* Setting the read_idx matching the fetch_idx, signals no in-flight + * fetched sample. + */ + atomic_set(&cli->read_idx, fetch_idx); - return 0; +error_out: + mutex_unlock(&cli->cmd_sync_lock); + return err; } /** @@ -1137,12 +1201,7 @@ static void kbasep_kinstr_prfcnt_sample_array_free( memset(sample_arr, 0, sizeof(*sample_arr)); } -/** - * kbasep_kinstr_prfcnt_client_destroy() - Destroy a kinstr_prfcnt client. - * @cli: kinstr_prfcnt client. Must not be attached to a kinstr_prfcnt context. - */ -static void -kbasep_kinstr_prfcnt_client_destroy(struct kbase_kinstr_prfcnt_client *cli) +void kbasep_kinstr_prfcnt_client_destroy(struct kbase_kinstr_prfcnt_client *cli) { if (!cli) return; @@ -1191,7 +1250,8 @@ static const struct file_operations kinstr_prfcnt_client_fops = { .release = kbasep_kinstr_prfcnt_hwcnt_reader_release, }; -size_t kbasep_kinstr_prfcnt_get_sample_md_count(const struct kbase_hwcnt_metadata *metadata) +size_t kbasep_kinstr_prfcnt_get_sample_md_count(const struct kbase_hwcnt_metadata *metadata, + struct kbase_hwcnt_enable_map *enable_map) { size_t grp, blk, blk_inst; size_t md_count = 0; @@ -1200,8 +1260,10 @@ size_t kbasep_kinstr_prfcnt_get_sample_md_count(const struct kbase_hwcnt_metadat return 0; kbase_hwcnt_metadata_for_each_block(metadata, grp, blk, blk_inst) { - /* Skip unused blocks */ - if (!kbase_hwcnt_metadata_block_instance_avail(metadata, grp, blk, blk_inst)) + /* Skip unavailable, non-enabled or reserved blocks */ + if (kbase_kinstr_is_block_type_reserved(metadata, grp, blk) || + !kbase_hwcnt_metadata_block_instance_avail(metadata, grp, blk, blk_inst) || + !kbase_hwcnt_enable_map_block_enabled(enable_map, grp, blk, blk_inst)) continue; md_count++; @@ -1216,14 +1278,14 @@ size_t kbasep_kinstr_prfcnt_get_sample_md_count(const struct kbase_hwcnt_metadat return md_count; } -static size_t kbasep_kinstr_prfcnt_get_sample_size( - const struct kbase_hwcnt_metadata *metadata, - struct kbase_hwcnt_dump_buffer *dump_buf) +static size_t kbasep_kinstr_prfcnt_get_sample_size(struct kbase_kinstr_prfcnt_client *cli, + const struct kbase_hwcnt_metadata *metadata) { size_t dump_buf_bytes; size_t clk_cnt_buf_bytes; size_t sample_meta_bytes; - size_t md_count = kbasep_kinstr_prfcnt_get_sample_md_count(metadata); + struct kbase_hwcnt_dump_buffer *dump_buf = &cli->tmp_buf; + size_t md_count = kbasep_kinstr_prfcnt_get_sample_md_count(metadata, &cli->enable_map); if (!metadata) return 0; @@ -1311,6 +1373,8 @@ static void kbasep_kinstr_prfcnt_async_dump_worker(struct work_struct *work) * kbasep_kinstr_prfcnt_dump_timer() - Dump timer that schedules the dump worker for * execution as soon as possible. * @timer: Timer structure. + * + * Return: HRTIMER_NORESTART always. */ static enum hrtimer_restart kbasep_kinstr_prfcnt_dump_timer(struct hrtimer *timer) @@ -1373,7 +1437,7 @@ void kbase_kinstr_prfcnt_term(struct kbase_kinstr_prfcnt_context *kinstr_ctx) if (WARN_ON(kinstr_ctx->client_count > 0)) { struct kbase_kinstr_prfcnt_client *pos, *n; - list_for_each_entry_safe(pos, n, &kinstr_ctx->clients, node) { + list_for_each_entry_safe (pos, n, &kinstr_ctx->clients, node) { list_del(&pos->node); kinstr_ctx->client_count--; kbasep_kinstr_prfcnt_client_destroy(pos); @@ -1431,7 +1495,7 @@ void kbase_kinstr_prfcnt_resume(struct kbase_kinstr_prfcnt_context *kinstr_ctx) struct kbase_kinstr_prfcnt_client *pos; bool has_periodic_clients = false; - list_for_each_entry(pos, &kinstr_ctx->clients, node) { + list_for_each_entry (pos, &kinstr_ctx->clients, node) { if (pos->dump_interval_ns != 0) { has_periodic_clients = true; break; @@ -1448,10 +1512,10 @@ void kbase_kinstr_prfcnt_resume(struct kbase_kinstr_prfcnt_context *kinstr_ctx) mutex_unlock(&kinstr_ctx->lock); } -static int kbasep_kinstr_prfcnt_sample_array_alloc( - const struct kbase_hwcnt_metadata *metadata, size_t n, - struct kbase_kinstr_prfcnt_sample_array *sample_arr) +static int kbasep_kinstr_prfcnt_sample_array_alloc(struct kbase_kinstr_prfcnt_client *cli, + const struct kbase_hwcnt_metadata *metadata) { + struct kbase_kinstr_prfcnt_sample_array *sample_arr = &cli->sample_arr; struct kbase_kinstr_prfcnt_sample *samples; size_t sample_idx; u64 addr; @@ -1461,24 +1525,25 @@ static int kbasep_kinstr_prfcnt_sample_array_alloc( size_t sample_meta_bytes; size_t md_count; size_t sample_size; + size_t buffer_count = cli->config.buffer_count; if (!metadata || !sample_arr) return -EINVAL; - md_count = kbasep_kinstr_prfcnt_get_sample_md_count(metadata); + md_count = kbasep_kinstr_prfcnt_get_sample_md_count(metadata, &cli->enable_map); sample_meta_bytes = sizeof(struct prfcnt_metadata) * md_count; dump_buf_bytes = metadata->dump_buf_bytes; clk_cnt_buf_bytes = sizeof(*samples->dump_buf.clk_cnt_buf) * metadata->clk_cnt; sample_size = sample_meta_bytes + dump_buf_bytes + clk_cnt_buf_bytes; - samples = kmalloc_array(n, sizeof(*samples), GFP_KERNEL); + samples = kmalloc_array(buffer_count, sizeof(*samples), GFP_KERNEL); if (!samples) return -ENOMEM; - order = get_order(sample_size * n); - addr = (u64)(uintptr_t)kzalloc(sample_size * n, GFP_KERNEL); + order = get_order(sample_size * buffer_count); + addr = (u64)(uintptr_t)kzalloc(sample_size * buffer_count, GFP_KERNEL); if (!addr) { kfree((void *)samples); @@ -1487,10 +1552,10 @@ static int kbasep_kinstr_prfcnt_sample_array_alloc( sample_arr->page_addr = addr; sample_arr->page_order = order; - sample_arr->sample_count = n; + sample_arr->sample_count = buffer_count; sample_arr->samples = samples; - for (sample_idx = 0; sample_idx < n; sample_idx++) { + for (sample_idx = 0; sample_idx < buffer_count; sample_idx++) { const size_t sample_meta_offset = sample_size * sample_idx; const size_t dump_buf_offset = sample_meta_offset + sample_meta_bytes; @@ -1652,6 +1717,7 @@ static int kbasep_kinstr_prfcnt_parse_request_scope( * @kinstr_ctx: Pointer to the kinstr_prfcnt context. * @setup: Session setup information to parse. * @config: Client object the session configuration should be written to. + * @req_arr: Pointer to array of request items for client session. * * This function parses the list of "request" items sent by the user space * client, and writes the configuration for the new client to be created @@ -1659,41 +1725,18 @@ static int kbasep_kinstr_prfcnt_parse_request_scope( * * Return: 0 on success, else error code. */ -static int kbasep_kinstr_prfcnt_parse_setup( - struct kbase_kinstr_prfcnt_context *kinstr_ctx, - union kbase_ioctl_kinstr_prfcnt_setup *setup, - struct kbase_kinstr_prfcnt_client_config *config) +static int kbasep_kinstr_prfcnt_parse_setup(struct kbase_kinstr_prfcnt_context *kinstr_ctx, + union kbase_ioctl_kinstr_prfcnt_setup *setup, + struct kbase_kinstr_prfcnt_client_config *config, + struct prfcnt_request_item *req_arr) { uint32_t i; - struct prfcnt_request_item *req_arr; unsigned int item_count = setup->in.request_item_count; - unsigned long bytes; int err = 0; - /* Limiting the request items to 2x of the expected: acommodating - * moderate duplications but rejecting excessive abuses. - */ - if (!setup->in.requests_ptr || (item_count < 2) || - (setup->in.request_item_size == 0) || - item_count > 2 * kinstr_ctx->info_item_count) { - return -EINVAL; - } - - bytes = item_count * sizeof(*req_arr); - req_arr = kmalloc(bytes, GFP_KERNEL); - if (!req_arr) - return -ENOMEM; - - if (copy_from_user(req_arr, u64_to_user_ptr(setup->in.requests_ptr), - bytes)) { - err = -EFAULT; - goto free_buf; - } - if (req_arr[item_count - 1].hdr.item_type != FLEX_LIST_TYPE_NONE || req_arr[item_count - 1].hdr.item_version != 0) { - err = -EINVAL; - goto free_buf; + return -EINVAL; } /* The session configuration can only feature one value for some @@ -1770,9 +1813,6 @@ static int kbasep_kinstr_prfcnt_parse_setup( break; } -free_buf: - kfree(req_arr); - if (!err) { /* Verify that properties (like capture mode and block counter * set) have been defined by the user space client. @@ -1787,28 +1827,17 @@ free_buf: return err; } -/** - * kbasep_kinstr_prfcnt_client_create() - Create a kinstr_prfcnt client. - * Does not attach to the kinstr_prfcnt - * context. - * @kinstr_ctx: Non-NULL pointer to kinstr_prfcnt context. - * @setup: Non-NULL pointer to hardware counter ioctl setup structure. - * @out_vcli: Non-NULL pointer to where created client will be stored on - * success. - * - * Return: 0 on success, else error code. - */ -static int kbasep_kinstr_prfcnt_client_create( - struct kbase_kinstr_prfcnt_context *kinstr_ctx, - union kbase_ioctl_kinstr_prfcnt_setup *setup, - struct kbase_kinstr_prfcnt_client **out_vcli) +int kbasep_kinstr_prfcnt_client_create(struct kbase_kinstr_prfcnt_context *kinstr_ctx, + union kbase_ioctl_kinstr_prfcnt_setup *setup, + struct kbase_kinstr_prfcnt_client **out_vcli, + struct prfcnt_request_item *req_arr) { int err; struct kbase_kinstr_prfcnt_client *cli; - struct kbase_hwcnt_physical_enable_map phys_em; WARN_ON(!kinstr_ctx); WARN_ON(!setup); + WARN_ON(!req_arr); cli = kzalloc(sizeof(*cli), GFP_KERNEL); @@ -1816,7 +1845,7 @@ static int kbasep_kinstr_prfcnt_client_create( return -ENOMEM; cli->kinstr_ctx = kinstr_ctx; - err = kbasep_kinstr_prfcnt_parse_setup(kinstr_ctx, setup, &cli->config); + err = kbasep_kinstr_prfcnt_parse_setup(kinstr_ctx, setup, &cli->config, req_arr); if (err < 0) goto error; @@ -1827,6 +1856,7 @@ static int kbasep_kinstr_prfcnt_client_create( cli->active = false; atomic_set(&cli->write_idx, 0); atomic_set(&cli->read_idx, 0); + atomic_set(&cli->fetch_idx, 0); err = kbase_hwcnt_enable_map_alloc(kinstr_ctx->metadata, &cli->enable_map); @@ -1834,17 +1864,11 @@ static int kbasep_kinstr_prfcnt_client_create( if (err < 0) goto error; - phys_em.fe_bm = 0; - phys_em.shader_bm = 0; - phys_em.tiler_bm = 0; - phys_em.mmu_l2_bm = 0; - - kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, &phys_em); + kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, &cli->config.phys_em); cli->sample_count = cli->config.buffer_count; atomic_set(&cli->sync_sample_count, cli->sample_count); - cli->sample_size = kbasep_kinstr_prfcnt_get_sample_size( - kinstr_ctx->metadata, &cli->tmp_buf); + cli->sample_size = kbasep_kinstr_prfcnt_get_sample_size(cli, kinstr_ctx->metadata); /* Use virtualizer's metadata to alloc tmp buffer which interacts with * the HWC virtualizer. @@ -1855,20 +1879,21 @@ static int kbasep_kinstr_prfcnt_client_create( if (err < 0) goto error; - /* Enable all the available clk_enable_map. */ - cli->enable_map.clk_enable_map = - (1ull << kinstr_ctx->metadata->clk_cnt) - 1; + /* Disable clock map in setup, and enable clock map when start */ + cli->enable_map.clk_enable_map = 0; /* Use metadata from virtualizer to allocate dump buffers if * kinstr_prfcnt doesn't have the truncated metadata. */ - err = kbasep_kinstr_prfcnt_sample_array_alloc(kinstr_ctx->metadata, - cli->config.buffer_count, - &cli->sample_arr); + err = kbasep_kinstr_prfcnt_sample_array_alloc(cli, kinstr_ctx->metadata); if (err < 0) goto error; + /* Set enable map to be 0 to prevent virtualizer to init and kick the backend to count */ + kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, + &(struct kbase_hwcnt_physical_enable_map){ 0 }); + err = kbase_hwcnt_virtualizer_client_create( kinstr_ctx->hvirt, &cli->enable_map, &cli->hvcli); @@ -1891,22 +1916,23 @@ error: static size_t kbasep_kinstr_prfcnt_get_block_info_count( const struct kbase_hwcnt_metadata *metadata) { - size_t grp; + size_t grp, blk; size_t block_info_count = 0; if (!metadata) return 0; for (grp = 0; grp < kbase_hwcnt_metadata_group_count(metadata); grp++) { - block_info_count += - kbase_hwcnt_metadata_block_count(metadata, grp); + for (blk = 0; blk < kbase_hwcnt_metadata_block_count(metadata, grp); blk++) { + if (!kbase_kinstr_is_block_type_reserved(metadata, grp, blk)) + block_info_count++; + } } return block_info_count; } static void kbasep_kinstr_prfcnt_get_request_info_list( - struct kbase_kinstr_prfcnt_context *kinstr_ctx, struct prfcnt_enum_item *item_arr, size_t *arr_idx) { memcpy(&item_arr[*arr_idx], kinstr_prfcnt_supported_requests, @@ -1914,6 +1940,24 @@ static void kbasep_kinstr_prfcnt_get_request_info_list( *arr_idx += ARRAY_SIZE(kinstr_prfcnt_supported_requests); } +static void kbasep_kinstr_prfcnt_get_sample_info_item(const struct kbase_hwcnt_metadata *metadata, + struct prfcnt_enum_item *item_arr, + size_t *arr_idx) +{ + struct prfcnt_enum_item sample_info = { + .hdr = { + .item_type = PRFCNT_ENUM_TYPE_SAMPLE_INFO, + .item_version = PRFCNT_READER_API_VERSION, + }, + .u.sample_info = { + .num_clock_domains = metadata->clk_cnt, + }, + }; + + item_arr[*arr_idx] = sample_info; + *arr_idx += 1; +} + int kbasep_kinstr_prfcnt_get_block_info_list(const struct kbase_hwcnt_metadata *metadata, size_t block_set, struct prfcnt_enum_item *item_arr, size_t *arr_idx) @@ -1924,23 +1968,17 @@ int kbasep_kinstr_prfcnt_get_block_info_list(const struct kbase_hwcnt_metadata * return -EINVAL; for (grp = 0; grp < kbase_hwcnt_metadata_group_count(metadata); grp++) { - for (blk = 0; - blk < kbase_hwcnt_metadata_block_count(metadata, grp); - blk++, (*arr_idx)++) { + for (blk = 0; blk < kbase_hwcnt_metadata_block_count(metadata, grp); blk++) { size_t blk_inst; size_t unused_blk_inst_count = 0; size_t blk_inst_count = kbase_hwcnt_metadata_block_instance_count(metadata, grp, blk); - - item_arr[*arr_idx].hdr.item_type = - PRFCNT_ENUM_TYPE_BLOCK; - item_arr[*arr_idx].hdr.item_version = - PRFCNT_READER_API_VERSION; - item_arr[*arr_idx].u.block_counter.set = block_set; - item_arr[*arr_idx].u.block_counter.block_type = + enum prfcnt_block_type block_type = kbase_hwcnt_metadata_block_type_to_prfcnt_block_type( - kbase_hwcnt_metadata_block_type( - metadata, grp, blk)); + kbase_hwcnt_metadata_block_type(metadata, grp, blk)); + + if (block_type == PRFCNT_BLOCK_TYPE_RESERVED) + continue; /* Count number of unused blocks to updated number of instances */ for (blk_inst = 0; blk_inst < blk_inst_count; blk_inst++) { @@ -1949,20 +1987,24 @@ int kbasep_kinstr_prfcnt_get_block_info_list(const struct kbase_hwcnt_metadata * unused_blk_inst_count++; } - item_arr[*arr_idx].u.block_counter.num_instances = - blk_inst_count - unused_blk_inst_count; - item_arr[*arr_idx].u.block_counter.num_values = - kbase_hwcnt_metadata_block_values_count( - metadata, grp, blk); - - /* The bitmask of available counters should be dynamic. - * Temporarily, it is set to U64_MAX, waiting for the - * required functionality to be available in the future. - */ - item_arr[*arr_idx].u.block_counter.counter_mask[0] = - U64_MAX; - item_arr[*arr_idx].u.block_counter.counter_mask[1] = - U64_MAX; + item_arr[(*arr_idx)++] = (struct prfcnt_enum_item){ + .hdr = { + .item_type = PRFCNT_ENUM_TYPE_BLOCK, + .item_version = PRFCNT_READER_API_VERSION, + }, + .u.block_counter = { + .set = block_set, + .block_type = block_type, + .num_instances = blk_inst_count - unused_blk_inst_count, + .num_values = kbase_hwcnt_metadata_block_values_count( + metadata, grp, blk), + /* The bitmask of available counters should be dynamic. + * Temporarily, it is set to U64_MAX, waiting for the + * required functionality to be available in the future. + */ + .counter_mask = {U64_MAX, U64_MAX}, + }, + }; } } @@ -1980,6 +2022,11 @@ static int kbasep_kinstr_prfcnt_enum_info_count( count = ARRAY_SIZE(kinstr_prfcnt_supported_requests); metadata = kbase_hwcnt_virtualizer_metadata(kinstr_ctx->hvirt); + + /* Add the sample_info (clock domain) descriptive item */ + count++; + + /* Other blocks based on meta data */ block_info_count = kbasep_kinstr_prfcnt_get_block_info_count(metadata); count += block_info_count; @@ -2014,9 +2061,12 @@ static int kbasep_kinstr_prfcnt_enum_info_list( if (!prfcnt_item_arr) return -ENOMEM; - kbasep_kinstr_prfcnt_get_request_info_list(kinstr_ctx, prfcnt_item_arr, - &arr_idx); + kbasep_kinstr_prfcnt_get_request_info_list(prfcnt_item_arr, &arr_idx); + metadata = kbase_hwcnt_virtualizer_metadata(kinstr_ctx->hvirt); + /* Place the sample_info item */ + kbasep_kinstr_prfcnt_get_sample_info_item(metadata, prfcnt_item_arr, &arr_idx); + block_info_count = kbasep_kinstr_prfcnt_get_block_info_count(metadata); if (arr_idx + block_info_count >= enum_info->info_item_count) @@ -2080,12 +2130,36 @@ int kbase_kinstr_prfcnt_setup(struct kbase_kinstr_prfcnt_context *kinstr_ctx, union kbase_ioctl_kinstr_prfcnt_setup *setup) { int err; + unsigned int item_count; + unsigned long bytes; + struct prfcnt_request_item *req_arr; struct kbase_kinstr_prfcnt_client *cli = NULL; if (!kinstr_ctx || !setup) return -EINVAL; - err = kbasep_kinstr_prfcnt_client_create(kinstr_ctx, setup, &cli); + item_count = setup->in.request_item_count; + + /* Limiting the request items to 2x of the expected: acommodating + * moderate duplications but rejecting excessive abuses. + */ + if (!setup->in.requests_ptr || (item_count < 2) || (setup->in.request_item_size == 0) || + item_count > 2 * kinstr_ctx->info_item_count) { + return -EINVAL; + } + + bytes = item_count * sizeof(*req_arr); + req_arr = kmalloc(bytes, GFP_KERNEL); + + if (!req_arr) + return -ENOMEM; + + if (copy_from_user(req_arr, u64_to_user_ptr(setup->in.requests_ptr), bytes)) { + err = -EFAULT; + goto free_buf; + } + + err = kbasep_kinstr_prfcnt_client_create(kinstr_ctx, setup, &cli, req_arr); if (err < 0) goto error; @@ -2107,7 +2181,7 @@ int kbase_kinstr_prfcnt_setup(struct kbase_kinstr_prfcnt_context *kinstr_ctx, if (err < 0) goto client_installed_error; - return err; + goto free_buf; client_installed_error: mutex_lock(&kinstr_ctx->lock); @@ -2116,5 +2190,7 @@ client_installed_error: mutex_unlock(&kinstr_ctx->lock); error: kbasep_kinstr_prfcnt_client_destroy(cli); +free_buf: + kfree(req_arr); return err; } |