// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
/*
|
*
|
* (C) COPYRIGHT 2018, 2020-2021 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
|
* Foundation, and any use by you of this program is subject to the terms
|
* of such GNU license.
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*
|
* You should have received a copy of the GNU General Public License
|
* along with this program; if not, you can access it online at
|
* http://www.gnu.org/licenses/gpl-2.0.html.
|
*
|
*/
|
|
#include "mali_kbase_hwcnt_legacy.h"
|
#include "mali_kbase_hwcnt_virtualizer.h"
|
#include "mali_kbase_hwcnt_types.h"
|
#include "mali_kbase_hwcnt_gpu.h"
|
#include <uapi/gpu/arm/bifrost/mali_kbase_ioctl.h>
|
|
#include <linux/slab.h>
|
#include <linux/uaccess.h>
|
|
/**
|
* struct kbase_hwcnt_legacy_client - Legacy hardware counter client.
|
* @user_dump_buf: Pointer to a non-NULL user buffer, where dumps are returned.
|
* @enable_map: Counter enable map.
|
* @dump_buf: Dump buffer used to manipulate dumps before copied to user.
|
* @hvcli: Hardware counter virtualizer client.
|
*/
|
struct kbase_hwcnt_legacy_client {
|
void __user *user_dump_buf;
|
struct kbase_hwcnt_enable_map enable_map;
|
struct kbase_hwcnt_dump_buffer dump_buf;
|
struct kbase_hwcnt_virtualizer_client *hvcli;
|
};
|
|
int kbase_hwcnt_legacy_client_create(
|
struct kbase_hwcnt_virtualizer *hvirt,
|
struct kbase_ioctl_hwcnt_enable *enable,
|
struct kbase_hwcnt_legacy_client **out_hlcli)
|
{
|
int errcode;
|
struct kbase_hwcnt_legacy_client *hlcli;
|
const struct kbase_hwcnt_metadata *metadata;
|
struct kbase_hwcnt_physical_enable_map phys_em;
|
|
if (!hvirt || !enable || !enable->dump_buffer || !out_hlcli)
|
return -EINVAL;
|
|
metadata = kbase_hwcnt_virtualizer_metadata(hvirt);
|
|
hlcli = kzalloc(sizeof(*hlcli), GFP_KERNEL);
|
if (!hlcli)
|
return -ENOMEM;
|
|
hlcli->user_dump_buf = (void __user *)(uintptr_t)enable->dump_buffer;
|
|
errcode = kbase_hwcnt_enable_map_alloc(metadata, &hlcli->enable_map);
|
if (errcode)
|
goto error;
|
|
/* Translate from the ioctl enable map to the internal one */
|
phys_em.fe_bm = enable->fe_bm;
|
phys_em.shader_bm = enable->shader_bm;
|
phys_em.tiler_bm = enable->tiler_bm;
|
phys_em.mmu_l2_bm = enable->mmu_l2_bm;
|
kbase_hwcnt_gpu_enable_map_from_physical(&hlcli->enable_map, &phys_em);
|
|
errcode = kbase_hwcnt_dump_buffer_alloc(metadata, &hlcli->dump_buf);
|
if (errcode)
|
goto error;
|
|
errcode = kbase_hwcnt_virtualizer_client_create(
|
hvirt, &hlcli->enable_map, &hlcli->hvcli);
|
if (errcode)
|
goto error;
|
|
*out_hlcli = hlcli;
|
return 0;
|
|
error:
|
kbase_hwcnt_legacy_client_destroy(hlcli);
|
return errcode;
|
}
|
|
void kbase_hwcnt_legacy_client_destroy(struct kbase_hwcnt_legacy_client *hlcli)
|
{
|
if (!hlcli)
|
return;
|
|
kbase_hwcnt_virtualizer_client_destroy(hlcli->hvcli);
|
kbase_hwcnt_dump_buffer_free(&hlcli->dump_buf);
|
kbase_hwcnt_enable_map_free(&hlcli->enable_map);
|
kfree(hlcli);
|
}
|
|
int kbase_hwcnt_legacy_client_dump(struct kbase_hwcnt_legacy_client *hlcli)
|
{
|
int errcode;
|
u64 ts_start_ns;
|
u64 ts_end_ns;
|
|
if (!hlcli)
|
return -EINVAL;
|
|
/* Dump into the kernel buffer */
|
errcode = kbase_hwcnt_virtualizer_client_dump(hlcli->hvcli,
|
&ts_start_ns, &ts_end_ns, &hlcli->dump_buf);
|
if (errcode)
|
return errcode;
|
|
/* Patch the dump buf headers, to hide the counters that other hwcnt
|
* clients are using.
|
*/
|
kbase_hwcnt_gpu_patch_dump_headers(
|
&hlcli->dump_buf, &hlcli->enable_map);
|
|
/* Zero all non-enabled counters (current values are undefined) */
|
kbase_hwcnt_dump_buffer_zero_non_enabled(
|
&hlcli->dump_buf, &hlcli->enable_map);
|
|
/* Copy into the user's buffer */
|
errcode = copy_to_user(hlcli->user_dump_buf, hlcli->dump_buf.dump_buf,
|
hlcli->dump_buf.metadata->dump_buf_bytes);
|
/* Non-zero errcode implies user buf was invalid or too small */
|
if (errcode)
|
return -EFAULT;
|
|
return 0;
|
}
|
|
int kbase_hwcnt_legacy_client_clear(struct kbase_hwcnt_legacy_client *hlcli)
|
{
|
u64 ts_start_ns;
|
u64 ts_end_ns;
|
|
if (!hlcli)
|
return -EINVAL;
|
|
/* Dump with a NULL buffer to clear this client's counters */
|
return kbase_hwcnt_virtualizer_client_dump(hlcli->hvcli,
|
&ts_start_ns, &ts_end_ns, NULL);
|
}
|