summaryrefslogtreecommitdiff
path: root/mali_kbase/mali_kbase_kinstr_prfcnt.c
diff options
context:
space:
mode:
authorDebarshi Dutta <debarshid@google.com>2023-06-02 13:36:22 +0000
committerDebarshi Dutta <debarshid@google.com>2023-07-12 18:55:15 +0000
commit20fff721667a227b3d6decf9dbc3798476390302 (patch)
treefba7129be28198dc2af1fb34fe0ec3a9ec0ce572 /mali_kbase/mali_kbase_kinstr_prfcnt.c
parent9e12ba5986f91fa0192b1ab55fafcea5e9b37094 (diff)
downloadgpu-20fff721667a227b3d6decf9dbc3798476390302.tar.gz
Merge upstream DDK R43P0 KMD
Merge DDK version R43P0 from upstream branch Provenance: 48a9c7e25986318c8475bc245de51e7bec2606e8 (ipdelivery/EAC/v_r43p0) VX504X08X-BU-00000-r43p0-01eac0 - Valhall Android DDK VX504X08X-BU-60000-r43p0-01eac0 - Valhall Android Document Bundle VX504X08X-DC-11001-r43p0-01eac0 - Valhall Android DDK Software Errata VX504X08X-SW-99006-r43p0-01eac0 - Valhall Android Renderscript AOSP parts Bug 278174418 Commit-Topic: R43P0_KMD Signed-off-by: Debarshi Dutta <debarshid@google.com> Change-Id: I84fb19e7ce5f28e735d44a4993d51bd985aac80b
Diffstat (limited to 'mali_kbase/mali_kbase_kinstr_prfcnt.c')
-rw-r--r--mali_kbase/mali_kbase_kinstr_prfcnt.c540
1 files changed, 206 insertions, 334 deletions
diff --git a/mali_kbase/mali_kbase_kinstr_prfcnt.c b/mali_kbase/mali_kbase_kinstr_prfcnt.c
index b7c8a16..8d52689 100644
--- a/mali_kbase/mali_kbase_kinstr_prfcnt.c
+++ b/mali_kbase/mali_kbase_kinstr_prfcnt.c
@@ -21,8 +21,8 @@
#include "mali_kbase.h"
#include "mali_kbase_kinstr_prfcnt.h"
-#include "mali_kbase_hwcnt_virtualizer.h"
-#include "mali_kbase_hwcnt_gpu.h"
+#include "hwcnt/mali_kbase_hwcnt_virtualizer.h"
+#include "hwcnt/mali_kbase_hwcnt_gpu.h"
#include <uapi/gpu/arm/midgard/mali_kbase_ioctl.h>
#include "mali_malisw.h"
#include "mali_kbase_debug.h"
@@ -36,9 +36,15 @@
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/slab.h>
+#include <linux/overflow.h>
#include <linux/version_compat_defs.h>
#include <linux/workqueue.h>
+/* Explicitly include epoll header for old kernels. Not required from 4.16. */
+#if KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE
+#include <uapi/linux/eventpoll.h>
+#endif
+
/* The minimum allowed interval between dumps, in nanoseconds
* (equivalent to 10KHz)
*/
@@ -47,9 +53,6 @@
/* The maximum allowed buffers per client */
#define MAX_BUFFER_COUNT 32
-/* The module printing prefix */
-#define KINSTR_PRFCNT_PREFIX "mali_kbase_kinstr_prfcnt: "
-
/**
* struct kbase_kinstr_prfcnt_context - IOCTL interface for userspace hardware
* counters.
@@ -118,16 +121,31 @@ struct kbase_kinstr_prfcnt_client_config {
};
/**
- * struct kbase_kinstr_prfcnt_async - Asynchronous sampling operation to
- * carry out for a kinstr_prfcnt_client.
- * @dump_work: Worker for performing asynchronous counter dumps.
- * @user_data: User data for asynchronous dump in progress.
- * @ts_end_ns: End timestamp of most recent async dump.
+ * enum kbase_kinstr_prfcnt_client_init_state - A list of
+ * initialisation states that the
+ * kinstr_prfcnt client can be at
+ * during initialisation. Useful
+ * for terminating a partially
+ * initialised client.
+ *
+ * @KINSTR_PRFCNT_UNINITIALISED : Client is uninitialised
+ * @KINSTR_PRFCNT_PARSE_SETUP : Parse the setup session
+ * @KINSTR_PRFCNT_ENABLE_MAP : Allocate memory for enable map
+ * @KINSTR_PRFCNT_DUMP_BUFFER : Allocate memory for dump buffer
+ * @KINSTR_PRFCNT_SAMPLE_ARRAY : Allocate memory for and initialise sample array
+ * @KINSTR_PRFCNT_VIRTUALIZER_CLIENT : Create virtualizer client
+ * @KINSTR_PRFCNT_WAITQ_MUTEX : Create and initialise mutex and waitqueue
+ * @KINSTR_PRFCNT_INITIALISED : Client is fully initialised
*/
-struct kbase_kinstr_prfcnt_async {
- struct work_struct dump_work;
- u64 user_data;
- u64 ts_end_ns;
+enum kbase_kinstr_prfcnt_client_init_state {
+ KINSTR_PRFCNT_UNINITIALISED,
+ KINSTR_PRFCNT_PARSE_SETUP = KINSTR_PRFCNT_UNINITIALISED,
+ KINSTR_PRFCNT_ENABLE_MAP,
+ KINSTR_PRFCNT_DUMP_BUFFER,
+ KINSTR_PRFCNT_SAMPLE_ARRAY,
+ KINSTR_PRFCNT_VIRTUALIZER_CLIENT,
+ KINSTR_PRFCNT_WAITQ_MUTEX,
+ KINSTR_PRFCNT_INITIALISED
};
/**
@@ -137,9 +155,7 @@ struct kbase_kinstr_prfcnt_async {
* @hvcli: Hardware counter virtualizer client.
* @node: Node used to attach this client to list in
* kinstr_prfcnt context.
- * @cmd_sync_lock: Lock coordinating the reader interface for commands
- * that need interacting with the async sample dump
- * worker thread.
+ * @cmd_sync_lock: Lock coordinating the reader interface for commands.
* @next_dump_time_ns: Time in ns when this client's next periodic dump must
* occur. If 0, not a periodic client.
* @dump_interval_ns: Interval between periodic dumps. If 0, not a periodic
@@ -160,15 +176,10 @@ struct kbase_kinstr_prfcnt_async {
* @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.
- * @sync_sample_count: Number of available spaces for synchronous samples.
- * It can differ from sample_count if asynchronous
- * sample requests are reserving space in the buffer.
* @user_data: User data associated with the session.
* This is set when the session is started and stopped.
* This value is ignored for control commands that
* provide another value.
- * @async: Asynchronous sampling operations to carry out in this
- * client's session.
*/
struct kbase_kinstr_prfcnt_client {
struct kbase_kinstr_prfcnt_context *kinstr_ctx;
@@ -189,9 +200,7 @@ struct kbase_kinstr_prfcnt_client {
wait_queue_head_t waitq;
size_t sample_size;
size_t sample_count;
- atomic_t sync_sample_count;
u64 user_data;
- struct kbase_kinstr_prfcnt_async async;
};
static struct prfcnt_enum_item kinstr_prfcnt_supported_requests[] = {
@@ -224,8 +233,8 @@ static struct prfcnt_enum_item kinstr_prfcnt_supported_requests[] = {
* @filp: Non-NULL pointer to file structure.
* @wait: Non-NULL pointer to poll table.
*
- * Return: POLLIN if data can be read without blocking, 0 if data can not be
- * read without blocking, else error code.
+ * Return: EPOLLIN | EPOLLRDNORM if data can be read without blocking, 0 if
+ * data can not be read without blocking, else EPOLLHUP | EPOLLERR.
*/
static __poll_t
kbasep_kinstr_prfcnt_hwcnt_reader_poll(struct file *filp,
@@ -234,19 +243,19 @@ kbasep_kinstr_prfcnt_hwcnt_reader_poll(struct file *filp,
struct kbase_kinstr_prfcnt_client *cli;
if (!filp || !wait)
- return (__poll_t)-EINVAL;
+ return EPOLLHUP | EPOLLERR;
cli = filp->private_data;
if (!cli)
- return (__poll_t)-EINVAL;
+ return EPOLLHUP | EPOLLERR;
poll_wait(filp, &cli->waitq, wait);
if (atomic_read(&cli->write_idx) != atomic_read(&cli->fetch_idx))
- return POLLIN;
+ return EPOLLIN | EPOLLRDNORM;
- return 0;
+ return (__poll_t)0;
}
/**
@@ -429,6 +438,7 @@ int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_enable_map *ena
size_t grp, blk, blk_inst;
struct prfcnt_metadata **ptr_md = block_meta_base;
const struct kbase_hwcnt_metadata *metadata;
+ uint8_t block_idx = 0;
if (!dst || !*block_meta_base)
return -EINVAL;
@@ -437,6 +447,10 @@ int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_enable_map *ena
kbase_hwcnt_metadata_for_each_block(metadata, grp, blk, blk_inst) {
u8 *dst_blk;
+ /* Block indices must be reported with no gaps. */
+ if (blk_inst == 0)
+ block_idx = 0;
+
/* 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) ||
@@ -450,13 +464,14 @@ int kbasep_kinstr_prfcnt_set_block_meta_items(struct kbase_hwcnt_enable_map *ena
kbase_hwcnt_metadata_block_type_to_prfcnt_block_type(
kbase_hwcnt_metadata_block_type(metadata, grp,
blk));
- (*ptr_md)->u.block_md.block_idx = (u8)blk_inst;
+ (*ptr_md)->u.block_md.block_idx = block_idx;
(*ptr_md)->u.block_md.set = counter_set;
(*ptr_md)->u.block_md.block_state = BLOCK_STATE_UNKNOWN;
(*ptr_md)->u.block_md.values_offset = (u32)(dst_blk - base_addr);
/* update the buf meta data block pointer to next item */
(*ptr_md)++;
+ block_idx++;
}
return 0;
@@ -509,33 +524,6 @@ static void kbasep_kinstr_prfcnt_set_sample_metadata(
}
/**
- * kbasep_kinstr_prfcnt_client_output_empty_sample() - Assemble an empty sample
- * for output.
- * @cli: Non-NULL pointer to a kinstr_prfcnt client.
- * @buf_idx: The index to the sample array for saving the sample.
- */
-static void kbasep_kinstr_prfcnt_client_output_empty_sample(
- struct kbase_kinstr_prfcnt_client *cli, unsigned int buf_idx)
-{
- struct kbase_hwcnt_dump_buffer *dump_buf;
- struct prfcnt_metadata *ptr_md;
-
- if (WARN_ON(buf_idx >= cli->sample_arr.sample_count))
- return;
-
- dump_buf = &cli->sample_arr.samples[buf_idx].dump_buf;
- ptr_md = cli->sample_arr.samples[buf_idx].sample_meta;
-
- kbase_hwcnt_dump_buffer_zero(dump_buf, &cli->enable_map);
-
- /* Use end timestamp from most recent async dump */
- ptr_md->u.sample_md.timestamp_start = cli->async.ts_end_ns;
- ptr_md->u.sample_md.timestamp_end = cli->async.ts_end_ns;
-
- kbasep_kinstr_prfcnt_set_sample_metadata(cli, dump_buf, ptr_md);
-}
-
-/**
* kbasep_kinstr_prfcnt_client_output_sample() - Assemble a sample for output.
* @cli: Non-NULL pointer to a kinstr_prfcnt client.
* @buf_idx: The index to the sample array for saving the sample.
@@ -584,16 +572,11 @@ static void kbasep_kinstr_prfcnt_client_output_sample(
* @cli: Non-NULL pointer to a kinstr_prfcnt client.
* @event_id: Event type that triggered the dump.
* @user_data: User data to return to the user.
- * @async_dump: Whether this is an asynchronous dump or not.
- * @empty_sample: Sample block data will be 0 if this is true.
*
* Return: 0 on success, else error code.
*/
-static int
-kbasep_kinstr_prfcnt_client_dump(struct kbase_kinstr_prfcnt_client *cli,
- enum base_hwcnt_reader_event event_id,
- u64 user_data, bool async_dump,
- bool empty_sample)
+static int kbasep_kinstr_prfcnt_client_dump(struct kbase_kinstr_prfcnt_client *cli,
+ enum base_hwcnt_reader_event event_id, u64 user_data)
{
int ret;
u64 ts_start_ns = 0;
@@ -611,17 +594,11 @@ kbasep_kinstr_prfcnt_client_dump(struct kbase_kinstr_prfcnt_client *cli,
/* Check if there is a place to copy HWC block into. Calculate the
* number of available samples count, by taking into account the type
* of dump.
- * Asynchronous dumps have the ability to reserve space in the samples
- * array for future dumps, unlike synchronous dumps. Because of that,
- * the samples count for synchronous dumps is managed by a variable
- * called sync_sample_count, that originally is defined as equal to the
- * size of the whole array but later decreases every time an
- * asynchronous dump request is pending and then re-increased every
- * time an asynchronous dump request is completed.
*/
- available_samples_count = async_dump ?
- cli->sample_arr.sample_count :
- atomic_read(&cli->sync_sample_count);
+ available_samples_count = cli->sample_arr.sample_count;
+ WARN_ON(available_samples_count < 1);
+ /* Reserve one slot to store the implicit sample taken on CMD_STOP */
+ available_samples_count -= 1;
if (write_idx - read_idx == available_samples_count) {
/* For periodic sampling, the current active dump
* will be accumulated in the next sample, when
@@ -637,38 +614,19 @@ kbasep_kinstr_prfcnt_client_dump(struct kbase_kinstr_prfcnt_client *cli,
*/
write_idx %= cli->sample_arr.sample_count;
- if (!empty_sample) {
- ret = kbase_hwcnt_virtualizer_client_dump(
- cli->hvcli, &ts_start_ns, &ts_end_ns, &cli->tmp_buf);
- /* HWC dump error, set the sample with error flag */
- if (ret)
- cli->sample_flags |= SAMPLE_FLAG_ERROR;
-
- /* Make the sample ready and copy it to the userspace mapped buffer */
- kbasep_kinstr_prfcnt_client_output_sample(
- cli, write_idx, user_data, ts_start_ns, ts_end_ns);
- } else {
- if (!async_dump) {
- struct prfcnt_metadata *ptr_md;
- /* User data will not be updated for empty samples. */
- ptr_md = cli->sample_arr.samples[write_idx].sample_meta;
- ptr_md->u.sample_md.user_data = user_data;
- }
+ ret = kbase_hwcnt_virtualizer_client_dump(cli->hvcli, &ts_start_ns, &ts_end_ns,
+ &cli->tmp_buf);
+ /* HWC dump error, set the sample with error flag */
+ if (ret)
+ cli->sample_flags |= SAMPLE_FLAG_ERROR;
- /* Make the sample ready and copy it to the userspace mapped buffer */
- kbasep_kinstr_prfcnt_client_output_empty_sample(cli, write_idx);
- }
+ /* Make the sample ready and copy it to the userspace mapped buffer */
+ kbasep_kinstr_prfcnt_client_output_sample(cli, write_idx, user_data, ts_start_ns,
+ ts_end_ns);
/* Notify client. Make sure all changes to memory are visible. */
wmb();
atomic_inc(&cli->write_idx);
- if (async_dump) {
- /* Remember the end timestamp of async dump for empty samples */
- if (!empty_sample)
- cli->async.ts_end_ns = ts_end_ns;
-
- atomic_inc(&cli->sync_sample_count);
- }
wake_up_interruptible(&cli->waitq);
/* Reset the flags for the next sample dump */
cli->sample_flags = 0;
@@ -682,6 +640,9 @@ kbasep_kinstr_prfcnt_client_start(struct kbase_kinstr_prfcnt_client *cli,
{
int ret;
u64 tm_start, tm_end;
+ unsigned int write_idx;
+ unsigned int read_idx;
+ size_t available_samples_count;
WARN_ON(!cli);
lockdep_assert_held(&cli->cmd_sync_lock);
@@ -690,6 +651,16 @@ kbasep_kinstr_prfcnt_client_start(struct kbase_kinstr_prfcnt_client *cli,
if (cli->active)
return 0;
+ write_idx = atomic_read(&cli->write_idx);
+ read_idx = atomic_read(&cli->read_idx);
+
+ /* Check whether there is space to store atleast an implicit sample
+ * corresponding to CMD_STOP.
+ */
+ available_samples_count = cli->sample_count - (write_idx - read_idx);
+ if (!available_samples_count)
+ return -EBUSY;
+
kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map,
&cli->config.phys_em);
@@ -702,7 +673,6 @@ kbasep_kinstr_prfcnt_client_start(struct kbase_kinstr_prfcnt_client *cli,
cli->hvcli, &cli->enable_map, &tm_start, &tm_end, NULL);
if (!ret) {
- atomic_set(&cli->sync_sample_count, cli->sample_count);
cli->active = true;
cli->user_data = user_data;
cli->sample_flags = 0;
@@ -716,16 +686,6 @@ kbasep_kinstr_prfcnt_client_start(struct kbase_kinstr_prfcnt_client *cli,
return ret;
}
-static int kbasep_kinstr_prfcnt_client_wait_async_done(
- struct kbase_kinstr_prfcnt_client *cli)
-{
- lockdep_assert_held(&cli->cmd_sync_lock);
-
- return wait_event_interruptible(cli->waitq,
- atomic_read(&cli->sync_sample_count) ==
- cli->sample_count);
-}
-
static int
kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli,
u64 user_data)
@@ -734,7 +694,7 @@ kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli,
u64 tm_start = 0;
u64 tm_end = 0;
struct kbase_hwcnt_physical_enable_map phys_em;
- struct kbase_hwcnt_dump_buffer *tmp_buf = NULL;
+ size_t available_samples_count;
unsigned int write_idx;
unsigned int read_idx;
@@ -745,12 +705,11 @@ kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli,
if (!cli->active)
return -EINVAL;
- /* Wait until pending async sample operation done */
- ret = kbasep_kinstr_prfcnt_client_wait_async_done(cli);
-
- if (ret < 0)
- return -ERESTARTSYS;
+ mutex_lock(&cli->kinstr_ctx->lock);
+ /* Disable counters under the lock, so we do not race with the
+ * sampling thread.
+ */
phys_em.fe_bm = 0;
phys_em.tiler_bm = 0;
phys_em.mmu_l2_bm = 0;
@@ -758,15 +717,11 @@ kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli,
kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, &phys_em);
- mutex_lock(&cli->kinstr_ctx->lock);
-
/* Check whether one has the buffer to hold the last sample */
write_idx = atomic_read(&cli->write_idx);
read_idx = atomic_read(&cli->read_idx);
- /* Check if there is a place to save the last stop produced sample */
- if (write_idx - read_idx < cli->sample_arr.sample_count)
- tmp_buf = &cli->tmp_buf;
+ available_samples_count = cli->sample_count - (write_idx - read_idx);
ret = kbase_hwcnt_virtualizer_client_set_counters(cli->hvcli,
&cli->enable_map,
@@ -776,7 +731,8 @@ kbasep_kinstr_prfcnt_client_stop(struct kbase_kinstr_prfcnt_client *cli,
if (ret)
cli->sample_flags |= SAMPLE_FLAG_ERROR;
- if (tmp_buf) {
+ /* There must be a place to save the last stop produced sample */
+ if (!WARN_ON(!available_samples_count)) {
write_idx %= cli->sample_arr.sample_count;
/* Handle the last stop sample */
kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map,
@@ -806,7 +762,6 @@ kbasep_kinstr_prfcnt_client_sync_dump(struct kbase_kinstr_prfcnt_client *cli,
u64 user_data)
{
int ret;
- bool empty_sample = false;
lockdep_assert_held(&cli->cmd_sync_lock);
@@ -814,90 +769,9 @@ kbasep_kinstr_prfcnt_client_sync_dump(struct kbase_kinstr_prfcnt_client *cli,
if (!cli->active || cli->dump_interval_ns)
return -EINVAL;
- /* Wait until pending async sample operation done, this is required to
- * satisfy the stated sample sequence following their issuing order,
- * reflected by the sample start timestamp.
- */
- if (atomic_read(&cli->sync_sample_count) != cli->sample_count) {
- /* Return empty sample instead of performing real dump.
- * As there is an async dump currently in-flight which will
- * have the desired information.
- */
- empty_sample = true;
- ret = kbasep_kinstr_prfcnt_client_wait_async_done(cli);
-
- if (ret < 0)
- return -ERESTARTSYS;
- }
-
mutex_lock(&cli->kinstr_ctx->lock);
- ret = kbasep_kinstr_prfcnt_client_dump(cli,
- BASE_HWCNT_READER_EVENT_MANUAL,
- user_data, false, empty_sample);
-
- mutex_unlock(&cli->kinstr_ctx->lock);
-
- return ret;
-}
-
-static int
-kbasep_kinstr_prfcnt_client_async_dump(struct kbase_kinstr_prfcnt_client *cli,
- u64 user_data)
-{
- unsigned int write_idx;
- unsigned int read_idx;
- unsigned int active_async_dumps;
- unsigned int new_async_buf_idx;
- int ret;
-
- lockdep_assert_held(&cli->cmd_sync_lock);
-
- /* If the client is not started, or not manual, the command invalid */
- if (!cli->active || cli->dump_interval_ns)
- return -EINVAL;
-
- mutex_lock(&cli->kinstr_ctx->lock);
-
- write_idx = atomic_read(&cli->write_idx);
- read_idx = atomic_read(&cli->read_idx);
- active_async_dumps =
- cli->sample_count - atomic_read(&cli->sync_sample_count);
- new_async_buf_idx = write_idx + active_async_dumps;
-
- /* Check if there is a place to copy HWC block into.
- * If successful, reserve space in the buffer for the asynchronous
- * operation to make sure that it can actually take place.
- * Because we reserve space for asynchronous dumps we need to take that
- * in consideration here.
- */
- ret = (new_async_buf_idx - read_idx == cli->sample_arr.sample_count) ?
- -EBUSY :
- 0;
-
- if (ret == -EBUSY) {
- mutex_unlock(&cli->kinstr_ctx->lock);
- return ret;
- }
-
- if (active_async_dumps > 0) {
- struct prfcnt_metadata *ptr_md;
- unsigned int buf_idx =
- new_async_buf_idx % cli->sample_arr.sample_count;
- /* Instead of storing user_data, write it directly to future
- * empty sample.
- */
- ptr_md = cli->sample_arr.samples[buf_idx].sample_meta;
- ptr_md->u.sample_md.user_data = user_data;
-
- atomic_dec(&cli->sync_sample_count);
- } else {
- cli->async.user_data = user_data;
- atomic_dec(&cli->sync_sample_count);
-
- kbase_hwcnt_virtualizer_queue_work(cli->kinstr_ctx->hvirt,
- &cli->async.dump_work);
- }
+ ret = kbasep_kinstr_prfcnt_client_dump(cli, BASE_HWCNT_READER_EVENT_MANUAL, user_data);
mutex_unlock(&cli->kinstr_ctx->lock);
@@ -957,10 +831,6 @@ int kbasep_kinstr_prfcnt_cmd(struct kbase_kinstr_prfcnt_client *cli,
ret = kbasep_kinstr_prfcnt_client_sync_dump(
cli, control_cmd->user_data);
break;
- case PRFCNT_CONTROL_CMD_SAMPLE_ASYNC:
- ret = kbasep_kinstr_prfcnt_client_async_dump(
- cli, control_cmd->user_data);
- break;
case PRFCNT_CONTROL_CMD_DISCARD:
ret = kbasep_kinstr_prfcnt_client_discard(cli);
break;
@@ -1015,17 +885,6 @@ kbasep_kinstr_prfcnt_get_sample(struct kbase_kinstr_prfcnt_client *cli,
sample_meta = cli->sample_arr.samples[read_idx].sample_meta;
sample_offset_bytes = (u8 *)sample_meta - cli->sample_arr.user_buf;
- /* Verify that a valid sample has been dumped in the read_idx.
- * There are situations where this may not be the case,
- * 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 ||
- 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;
@@ -1172,22 +1031,49 @@ static void kbasep_kinstr_prfcnt_sample_array_free(
memset(sample_arr, 0, sizeof(*sample_arr));
}
-#if !MALI_KERNEL_TEST_API
-static
-#endif
-void kbasep_kinstr_prfcnt_client_destroy(struct kbase_kinstr_prfcnt_client *cli)
+static void
+kbasep_kinstr_prfcnt_client_destroy_partial(struct kbase_kinstr_prfcnt_client *cli,
+ enum kbase_kinstr_prfcnt_client_init_state init_state)
{
if (!cli)
return;
- kbase_hwcnt_virtualizer_client_destroy(cli->hvcli);
- kbasep_kinstr_prfcnt_sample_array_free(&cli->sample_arr);
- kbase_hwcnt_dump_buffer_free(&cli->tmp_buf);
- kbase_hwcnt_enable_map_free(&cli->enable_map);
- mutex_destroy(&cli->cmd_sync_lock);
+ while (init_state-- > KINSTR_PRFCNT_UNINITIALISED) {
+ switch (init_state) {
+ case KINSTR_PRFCNT_INITIALISED:
+ /* This shouldn't be reached */
+ break;
+ case KINSTR_PRFCNT_WAITQ_MUTEX:
+ mutex_destroy(&cli->cmd_sync_lock);
+ break;
+ case KINSTR_PRFCNT_VIRTUALIZER_CLIENT:
+ kbase_hwcnt_virtualizer_client_destroy(cli->hvcli);
+ break;
+ case KINSTR_PRFCNT_SAMPLE_ARRAY:
+ kbasep_kinstr_prfcnt_sample_array_free(&cli->sample_arr);
+ break;
+ case KINSTR_PRFCNT_DUMP_BUFFER:
+ kbase_hwcnt_dump_buffer_free(&cli->tmp_buf);
+ break;
+ case KINSTR_PRFCNT_ENABLE_MAP:
+ kbase_hwcnt_enable_map_free(&cli->enable_map);
+ break;
+ case KINSTR_PRFCNT_PARSE_SETUP:
+ /* Nothing to do here */
+ break;
+ }
+ }
kfree(cli);
}
+#if !MALI_KERNEL_TEST_API
+static
+#endif
+void kbasep_kinstr_prfcnt_client_destroy(struct kbase_kinstr_prfcnt_client *cli)
+{
+ kbasep_kinstr_prfcnt_client_destroy_partial(cli, KINSTR_PRFCNT_INITIALISED);
+}
+
/**
* kbasep_kinstr_prfcnt_hwcnt_reader_release() - hwcnt reader's release.
* @inode: Non-NULL pointer to inode structure.
@@ -1294,9 +1180,8 @@ static void kbasep_kinstr_prfcnt_dump_worker(struct work_struct *work)
list_for_each_entry(pos, &kinstr_ctx->clients, node) {
if (pos->active && (pos->next_dump_time_ns != 0) &&
(pos->next_dump_time_ns < cur_time_ns))
- kbasep_kinstr_prfcnt_client_dump(
- pos, BASE_HWCNT_READER_EVENT_PERIODIC,
- pos->user_data, false, false);
+ kbasep_kinstr_prfcnt_client_dump(pos, BASE_HWCNT_READER_EVENT_PERIODIC,
+ pos->user_data);
}
kbasep_kinstr_prfcnt_reschedule_worker(kinstr_ctx);
@@ -1305,48 +1190,6 @@ static void kbasep_kinstr_prfcnt_dump_worker(struct work_struct *work)
}
/**
- * kbasep_kinstr_prfcnt_async_dump_worker()- Dump worker for a manual client
- * to take a single asynchronous
- * sample.
- * @work: Work structure.
- */
-static void kbasep_kinstr_prfcnt_async_dump_worker(struct work_struct *work)
-{
- struct kbase_kinstr_prfcnt_async *cli_async =
- container_of(work, struct kbase_kinstr_prfcnt_async, dump_work);
- struct kbase_kinstr_prfcnt_client *cli = container_of(
- cli_async, struct kbase_kinstr_prfcnt_client, async);
-
- mutex_lock(&cli->kinstr_ctx->lock);
- /* While the async operation is in flight, a sync stop might have been
- * executed, for which the dump should be skipped. Further as we are
- * doing an async dump, we expect that there is reserved buffer for
- * this to happen. This is to avoid the rare corner case where the
- * user side has issued a stop/start pair before the async work item
- * get the chance to execute.
- */
- if (cli->active &&
- (atomic_read(&cli->sync_sample_count) < cli->sample_count))
- kbasep_kinstr_prfcnt_client_dump(cli,
- BASE_HWCNT_READER_EVENT_MANUAL,
- cli->async.user_data, true,
- false);
-
- /* While the async operation is in flight, more async dump requests
- * may have been submitted. In this case, no more async dumps work
- * will be queued. Instead space will be reserved for that dump and
- * an empty sample will be return after handling the current async
- * dump.
- */
- while (cli->active &&
- (atomic_read(&cli->sync_sample_count) < cli->sample_count)) {
- kbasep_kinstr_prfcnt_client_dump(
- cli, BASE_HWCNT_READER_EVENT_MANUAL, 0, true, true);
- }
- mutex_unlock(&cli->kinstr_ctx->lock);
-}
-
-/**
* kbasep_kinstr_prfcnt_dump_timer() - Dump timer that schedules the dump worker for
* execution as soon as possible.
* @timer: Timer structure.
@@ -1808,83 +1651,100 @@ int kbasep_kinstr_prfcnt_client_create(struct kbase_kinstr_prfcnt_context *kinst
{
int err;
struct kbase_kinstr_prfcnt_client *cli;
+ enum kbase_kinstr_prfcnt_client_init_state init_state;
- WARN_ON(!kinstr_ctx);
- WARN_ON(!setup);
- WARN_ON(!req_arr);
+ if (WARN_ON(!kinstr_ctx))
+ return -EINVAL;
+
+ if (WARN_ON(!setup))
+ return -EINVAL;
+
+ if (WARN_ON(!req_arr))
+ return -EINVAL;
cli = kzalloc(sizeof(*cli), GFP_KERNEL);
if (!cli)
return -ENOMEM;
- cli->kinstr_ctx = kinstr_ctx;
- err = kbasep_kinstr_prfcnt_parse_setup(kinstr_ctx, setup, &cli->config, req_arr);
+ for (init_state = KINSTR_PRFCNT_UNINITIALISED; init_state < KINSTR_PRFCNT_INITIALISED;
+ init_state++) {
+ err = 0;
+ switch (init_state) {
+ case KINSTR_PRFCNT_PARSE_SETUP:
+ cli->kinstr_ctx = kinstr_ctx;
+ err = kbasep_kinstr_prfcnt_parse_setup(kinstr_ctx, setup, &cli->config,
+ req_arr);
- if (err < 0)
- goto error;
-
- cli->config.buffer_count = MAX_BUFFER_COUNT;
- cli->dump_interval_ns = cli->config.period_ns;
- cli->next_dump_time_ns = 0;
- cli->active = false;
- atomic_set(&cli->write_idx, 0);
- atomic_set(&cli->read_idx, 0);
- atomic_set(&cli->fetch_idx, 0);
+ break;
- err = kbase_hwcnt_enable_map_alloc(kinstr_ctx->metadata,
- &cli->enable_map);
+ case KINSTR_PRFCNT_ENABLE_MAP:
+ cli->config.buffer_count = MAX_BUFFER_COUNT;
+ cli->dump_interval_ns = cli->config.period_ns;
+ cli->next_dump_time_ns = 0;
+ cli->active = false;
+ atomic_set(&cli->write_idx, 0);
+ atomic_set(&cli->read_idx, 0);
+ atomic_set(&cli->fetch_idx, 0);
- if (err < 0)
- goto error;
+ err = kbase_hwcnt_enable_map_alloc(kinstr_ctx->metadata, &cli->enable_map);
+ break;
- kbase_hwcnt_gpu_enable_map_from_physical(&cli->enable_map, &cli->config.phys_em);
+ case KINSTR_PRFCNT_DUMP_BUFFER:
+ 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(cli, kinstr_ctx->metadata);
+ cli->sample_count = cli->config.buffer_count;
+ 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.
- */
- err = kbase_hwcnt_dump_buffer_alloc(kinstr_ctx->metadata,
- &cli->tmp_buf);
+ /* Use virtualizer's metadata to alloc tmp buffer which interacts with
+ * the HWC virtualizer.
+ */
+ err = kbase_hwcnt_dump_buffer_alloc(kinstr_ctx->metadata, &cli->tmp_buf);
+ break;
- if (err < 0)
- goto error;
+ case KINSTR_PRFCNT_SAMPLE_ARRAY:
+ /* Disable clock map in setup, and enable clock map when start */
+ cli->enable_map.clk_enable_map = 0;
- /* 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(cli, kinstr_ctx->metadata);
- /* Use metadata from virtualizer to allocate dump buffers if
- * kinstr_prfcnt doesn't have the truncated metadata.
- */
- err = kbasep_kinstr_prfcnt_sample_array_alloc(cli, kinstr_ctx->metadata);
+ break;
- if (err < 0)
- goto error;
+ case KINSTR_PRFCNT_VIRTUALIZER_CLIENT:
+ /* 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 });
- /* 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);
+ break;
- err = kbase_hwcnt_virtualizer_client_create(
- kinstr_ctx->hvirt, &cli->enable_map, &cli->hvcli);
+ case KINSTR_PRFCNT_WAITQ_MUTEX:
+ init_waitqueue_head(&cli->waitq);
+ mutex_init(&cli->cmd_sync_lock);
+ break;
- if (err < 0)
- goto error;
+ case KINSTR_PRFCNT_INITIALISED:
+ /* This shouldn't be reached */
+ break;
+ }
- init_waitqueue_head(&cli->waitq);
- INIT_WORK(&cli->async.dump_work,
- kbasep_kinstr_prfcnt_async_dump_worker);
- mutex_init(&cli->cmd_sync_lock);
+ if (err < 0) {
+ kbasep_kinstr_prfcnt_client_destroy_partial(cli, init_state);
+ return err;
+ }
+ }
*out_vcli = cli;
return 0;
-error:
- kbasep_kinstr_prfcnt_client_destroy(cli);
- return err;
}
static size_t kbasep_kinstr_prfcnt_get_block_info_count(
@@ -2106,17 +1966,18 @@ 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;
+ size_t item_count;
+ size_t bytes;
+ struct prfcnt_request_item *req_arr = NULL;
struct kbase_kinstr_prfcnt_client *cli = NULL;
+ const size_t max_bytes = 32 * sizeof(*req_arr);
if (!kinstr_ctx || !setup)
return -EINVAL;
item_count = setup->in.request_item_count;
- /* Limiting the request items to 2x of the expected: acommodating
+ /* Limiting the request items to 2x of the expected: accommodating
* moderate duplications but rejecting excessive abuses.
*/
if (!setup->in.requests_ptr || (item_count < 2) || (setup->in.request_item_size == 0) ||
@@ -2124,7 +1985,18 @@ int kbase_kinstr_prfcnt_setup(struct kbase_kinstr_prfcnt_context *kinstr_ctx,
return -EINVAL;
}
- bytes = item_count * sizeof(*req_arr);
+ if (check_mul_overflow(item_count, sizeof(*req_arr), &bytes))
+ return -EINVAL;
+
+ /* Further limiting the max bytes to copy from userspace by setting it in the following
+ * fashion: a maximum of 1 mode item, 4 types of 3 sets for a total of 12 enable items,
+ * each currently at the size of prfcnt_request_item.
+ *
+ * Note: if more request types get added, this max limit needs to be updated.
+ */
+ if (bytes > max_bytes)
+ return -EINVAL;
+
req_arr = memdup_user(u64_to_user_ptr(setup->in.requests_ptr), bytes);
if (IS_ERR(req_arr))