summaryrefslogtreecommitdiff
path: root/mali_kbase/tl/mali_kbase_timeline_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'mali_kbase/tl/mali_kbase_timeline_io.c')
-rw-r--r--mali_kbase/tl/mali_kbase_timeline_io.c200
1 files changed, 143 insertions, 57 deletions
diff --git a/mali_kbase/tl/mali_kbase_timeline_io.c b/mali_kbase/tl/mali_kbase_timeline_io.c
index 3391e75..ae57006 100644
--- a/mali_kbase/tl/mali_kbase_timeline_io.c
+++ b/mali_kbase/tl/mali_kbase_timeline_io.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
*
- * (C) COPYRIGHT 2019-2022 ARM Limited. All rights reserved.
+ * (C) COPYRIGHT 2019-2023 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
@@ -24,26 +24,74 @@
#include "mali_kbase_tracepoints.h"
#include "mali_kbase_timeline.h"
-#include <linux/delay.h>
+#include <device/mali_kbase_device.h>
+
#include <linux/poll.h>
+#include <linux/version_compat_defs.h>
+#include <linux/anon_inodes.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
+
+static int kbase_unprivileged_global_profiling;
+
+/**
+ * kbase_unprivileged_global_profiling_set - set permissions for unprivileged processes
+ *
+ * @val: String containing value to set. Only strings representing positive
+ * integers are accepted as valid; any non-positive integer (including 0)
+ * is rejected.
+ * @kp: Module parameter associated with this method.
+ *
+ * This method can only be used to enable permissions for unprivileged processes,
+ * if they are disabled: for this reason, the only values which are accepted are
+ * strings representing positive integers. Since it's impossible to disable
+ * permissions once they're set, any integer which is non-positive is rejected,
+ * including 0.
+ *
+ * Return: 0 if success, otherwise error code.
+ */
+static int kbase_unprivileged_global_profiling_set(const char *val, const struct kernel_param *kp)
+{
+ int new_val;
+ int ret = kstrtoint(val, 0, &new_val);
+
+ if (ret == 0) {
+ if (new_val < 1)
+ return -EINVAL;
+
+ kbase_unprivileged_global_profiling = 1;
+ }
+
+ return ret;
+}
+
+static const struct kernel_param_ops kbase_global_unprivileged_profiling_ops = {
+ .get = param_get_int,
+ .set = kbase_unprivileged_global_profiling_set,
+};
+
+module_param_cb(kbase_unprivileged_global_profiling, &kbase_global_unprivileged_profiling_ops,
+ &kbase_unprivileged_global_profiling, 0600);
/* The timeline stream file operations functions. */
static ssize_t kbasep_timeline_io_read(struct file *filp, char __user *buffer,
size_t size, loff_t *f_pos);
-static unsigned int kbasep_timeline_io_poll(struct file *filp,
- poll_table *wait);
+static __poll_t kbasep_timeline_io_poll(struct file *filp, poll_table *wait);
static int kbasep_timeline_io_release(struct inode *inode, struct file *filp);
static int kbasep_timeline_io_fsync(struct file *filp, loff_t start, loff_t end,
int datasync);
-/* The timeline stream file operations structure. */
-const struct file_operations kbasep_tlstream_fops = {
- .owner = THIS_MODULE,
- .release = kbasep_timeline_io_release,
- .read = kbasep_timeline_io_read,
- .poll = kbasep_timeline_io_poll,
- .fsync = kbasep_timeline_io_fsync,
-};
+static bool timeline_is_permitted(void)
+{
+#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
+ return kbase_unprivileged_global_profiling || perfmon_capable();
+#else
+ return kbase_unprivileged_global_profiling || capable(CAP_SYS_ADMIN);
+#endif
+}
/**
* kbasep_timeline_io_packet_pending - check timeline streams for pending
@@ -290,9 +338,10 @@ static ssize_t kbasep_timeline_io_read(struct file *filp, char __user *buffer,
* @filp: Pointer to file structure
* @wait: Pointer to poll table
*
- * Return: POLLIN if data can be read without blocking, otherwise zero
+ * Return: EPOLLIN | EPOLLRDNORM if data can be read without blocking,
+ * otherwise zero, or EPOLLHUP | EPOLLERR on error.
*/
-static unsigned int kbasep_timeline_io_poll(struct file *filp, poll_table *wait)
+static __poll_t kbasep_timeline_io_poll(struct file *filp, poll_table *wait)
{
struct kbase_tlstream *stream;
unsigned int rb_idx;
@@ -302,20 +351,94 @@ static unsigned int kbasep_timeline_io_poll(struct file *filp, poll_table *wait)
KBASE_DEBUG_ASSERT(wait);
if (WARN_ON(!filp->private_data))
- return -EFAULT;
+ return EPOLLHUP | EPOLLERR;
timeline = (struct kbase_timeline *)filp->private_data;
/* If there are header bytes to copy, read will not block */
if (kbasep_timeline_has_header_data(timeline))
- return POLLIN;
+ return EPOLLIN | EPOLLRDNORM;
poll_wait(filp, &timeline->event_queue, wait);
if (kbasep_timeline_io_packet_pending(timeline, &stream, &rb_idx))
- return POLLIN;
- return 0;
+ return EPOLLIN | EPOLLRDNORM;
+
+ return (__poll_t)0;
+}
+
+int kbase_timeline_io_acquire(struct kbase_device *kbdev, u32 flags)
+{
+ /* The timeline stream file operations structure. */
+ static const struct file_operations kbasep_tlstream_fops = {
+ .owner = THIS_MODULE,
+ .release = kbasep_timeline_io_release,
+ .read = kbasep_timeline_io_read,
+ .poll = kbasep_timeline_io_poll,
+ .fsync = kbasep_timeline_io_fsync,
+ };
+ int err;
+
+ if (!timeline_is_permitted())
+ return -EPERM;
+
+ if (WARN_ON(!kbdev) || (flags & ~BASE_TLSTREAM_FLAGS_MASK))
+ return -EINVAL;
+
+ err = kbase_timeline_acquire(kbdev, flags);
+ if (err)
+ return err;
+
+ err = anon_inode_getfd("[mali_tlstream]", &kbasep_tlstream_fops, kbdev->timeline,
+ O_RDONLY | O_CLOEXEC);
+ if (err < 0)
+ kbase_timeline_release(kbdev->timeline);
+
+ return err;
}
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int kbasep_timeline_io_open(struct inode *in, struct file *file)
+{
+ struct kbase_device *const kbdev = in->i_private;
+
+ if (WARN_ON(!kbdev))
+ return -EFAULT;
+
+ file->private_data = kbdev->timeline;
+ return kbase_timeline_acquire(kbdev, BASE_TLSTREAM_FLAGS_MASK &
+ ~BASE_TLSTREAM_JOB_DUMPING_ENABLED);
+}
+
+void kbase_timeline_io_debugfs_init(struct kbase_device *const kbdev)
+{
+ static const struct file_operations kbasep_tlstream_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = kbasep_timeline_io_open,
+ .release = kbasep_timeline_io_release,
+ .read = kbasep_timeline_io_read,
+ .poll = kbasep_timeline_io_poll,
+ .fsync = kbasep_timeline_io_fsync,
+ };
+ struct dentry *file;
+
+ if (WARN_ON(!kbdev) || WARN_ON(IS_ERR_OR_NULL(kbdev->mali_debugfs_directory)))
+ return;
+
+ file = debugfs_create_file("tlstream", 0400, kbdev->mali_debugfs_directory, kbdev,
+ &kbasep_tlstream_debugfs_fops);
+
+ if (IS_ERR_OR_NULL(file))
+ dev_warn(kbdev->dev, "Unable to create timeline debugfs entry");
+}
+#else
+/*
+ * Stub function for when debugfs is disabled
+ */
+void kbase_timeline_io_debugfs_init(struct kbase_device *const kbdev)
+{
+}
+#endif
+
/**
* kbasep_timeline_io_release - release timeline stream descriptor
* @inode: Pointer to inode structure
@@ -325,55 +448,18 @@ static unsigned int kbasep_timeline_io_poll(struct file *filp, poll_table *wait)
*/
static int kbasep_timeline_io_release(struct inode *inode, struct file *filp)
{
- struct kbase_timeline *timeline;
- ktime_t elapsed_time;
- s64 elapsed_time_ms, time_to_sleep;
-
- KBASE_DEBUG_ASSERT(inode);
- KBASE_DEBUG_ASSERT(filp);
- KBASE_DEBUG_ASSERT(filp->private_data);
-
CSTD_UNUSED(inode);
- timeline = (struct kbase_timeline *)filp->private_data;
-
- /* Get the amount of time passed since the timeline was acquired and ensure
- * we sleep for long enough such that it has been at least
- * TIMELINE_HYSTERESIS_TIMEOUT_MS amount of time between acquire and release.
- * This prevents userspace from spamming acquire and release too quickly.
- */
- elapsed_time = ktime_sub(ktime_get(), timeline->last_acquire_time);
- elapsed_time_ms = ktime_to_ms(elapsed_time);
- time_to_sleep = MIN(TIMELINE_HYSTERESIS_TIMEOUT_MS,
- TIMELINE_HYSTERESIS_TIMEOUT_MS - elapsed_time_ms);
- if (time_to_sleep > 0)
- msleep(time_to_sleep);
-
-#if MALI_USE_CSF
- kbase_csf_tl_reader_stop(&timeline->csf_tl_reader);
-#endif
-
- /* Stop autoflush timer before releasing access to streams. */
- atomic_set(&timeline->autoflush_timer_active, 0);
- del_timer_sync(&timeline->autoflush_timer);
-
- atomic_set(timeline->timeline_flags, 0);
+ kbase_timeline_release(filp->private_data);
return 0;
}
static int kbasep_timeline_io_fsync(struct file *filp, loff_t start, loff_t end,
int datasync)
{
- struct kbase_timeline *timeline;
-
CSTD_UNUSED(start);
CSTD_UNUSED(end);
CSTD_UNUSED(datasync);
- if (WARN_ON(!filp->private_data))
- return -EFAULT;
-
- timeline = (struct kbase_timeline *)filp->private_data;
-
- return kbase_timeline_streams_flush(timeline);
+ return kbase_timeline_streams_flush(filp->private_data);
}