406 lines
10 KiB
C
406 lines
10 KiB
C
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
#include "hab.h"
|
|
#if !defined CONFIG_GHS_VMM && defined(CONFIG_QTI_GVM_QUIN)
|
|
#include <asm/cacheflush.h>
|
|
#include <linux/list.h>
|
|
#include <linux/rtc.h>
|
|
#include "hab_pipe.h"
|
|
#include "hab_qvm.h"
|
|
#include "khab_test.h"
|
|
|
|
static char g_perf_test_result[256];
|
|
|
|
enum hab_perf_test_type {
|
|
HAB_SHMM_THGPUT = 0x0,
|
|
};
|
|
|
|
#define HAB_PERF_TEST_MMID 802
|
|
#define PERF_TEST_ITERATION 50
|
|
#define MEM_READ_ITERATION 30
|
|
|
|
static int hab_shmm_throughput_test(void)
|
|
{
|
|
struct hab_device *habDev;
|
|
struct qvm_channel *dev;
|
|
struct hab_shared_buf *sh_buf;
|
|
struct physical_channel *pchan;
|
|
struct timeval tv1, tv2;
|
|
int i, counter;
|
|
void *test_data;
|
|
unsigned char *source_data, *shmm_adr;
|
|
|
|
register int sum;
|
|
register int *pp, *lastone;
|
|
int throughput[3][2] = { {0} };
|
|
int latency[6][PERF_TEST_ITERATION];
|
|
int ret = 0, tmp, size;
|
|
|
|
habDev = find_hab_device(HAB_PERF_TEST_MMID);
|
|
if (!habDev || list_empty(&(habDev->pchannels))) {
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
}
|
|
|
|
pchan = list_first_entry(&(habDev->pchannels),
|
|
struct physical_channel, node);
|
|
dev = pchan->hyp_data;
|
|
if (!dev) {
|
|
ret = -EPERM;
|
|
return ret;
|
|
}
|
|
|
|
sh_buf = dev->pipe_ep->tx_info.sh_buf;
|
|
|
|
/* pChannel is of 128k, we use 64k to test */
|
|
size = 0x10000;
|
|
|
|
if (!sh_buf) {
|
|
pr_err("Share buffer address is empty, exit the perf test\n");
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
}
|
|
shmm_adr = (unsigned char *)sh_buf->data;
|
|
|
|
test_data = kzalloc(size, GFP_ATOMIC);
|
|
if (!test_data) {
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
}
|
|
|
|
source_data = kzalloc(size, GFP_ATOMIC);
|
|
if (!source_data) {
|
|
ret = -ENOMEM;
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++) {
|
|
/* Normal memory copy latency */
|
|
flush_cache_all();
|
|
do_gettimeofday(&tv1);
|
|
memcpy(test_data, source_data, size);
|
|
do_gettimeofday(&tv2);
|
|
latency[0][i] = (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
|
|
/* Share memory copy latency */
|
|
flush_cache_all();
|
|
do_gettimeofday(&tv1);
|
|
memcpy(shmm_adr, source_data, size);
|
|
do_gettimeofday(&tv2);
|
|
latency[1][i] = (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
|
|
/* Normal memory read latency */
|
|
counter = MEM_READ_ITERATION;
|
|
sum = 0;
|
|
latency[2][i] = 0;
|
|
flush_cache_all();
|
|
while (counter-- > 0) {
|
|
pp = test_data;
|
|
lastone = (int *)((char *)test_data + size - 512);
|
|
do_gettimeofday(&tv1);
|
|
while (pp <= lastone) {
|
|
sum +=
|
|
pp[0] + pp[4] + pp[8] + pp[12]
|
|
+ pp[16] + pp[20] + pp[24] + pp[28]
|
|
+ pp[32] + pp[36] + pp[40] + pp[44]
|
|
+ pp[48] + pp[52] + pp[56] + pp[60]
|
|
+ pp[64] + pp[68] + pp[72] + pp[76]
|
|
+ pp[80] + pp[84] + pp[88] + pp[92]
|
|
+ pp[96] + pp[100] + pp[104]
|
|
+ pp[108] + pp[112]
|
|
+ pp[116] + pp[120]
|
|
+ pp[124];
|
|
pp += 128;
|
|
}
|
|
do_gettimeofday(&tv2);
|
|
latency[2][i] += (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
flush_cache_all();
|
|
}
|
|
|
|
/* Share memory read latency*/
|
|
counter = MEM_READ_ITERATION;
|
|
sum = 0;
|
|
latency[3][i] = 0;
|
|
while (counter-- > 0) {
|
|
pp = (int *)shmm_adr;
|
|
lastone = (int *)(shmm_adr + size - 512);
|
|
do_gettimeofday(&tv1);
|
|
while (pp <= lastone) {
|
|
sum +=
|
|
pp[0] + pp[4] + pp[8] + pp[12]
|
|
+ pp[16] + pp[20] + pp[24] + pp[28]
|
|
+ pp[32] + pp[36] + pp[40] + pp[44]
|
|
+ pp[48] + pp[52] + pp[56] + pp[60]
|
|
+ pp[64] + pp[68] + pp[72] + pp[76]
|
|
+ pp[80] + pp[84] + pp[88] + pp[92]
|
|
+ pp[96] + pp[100] + pp[104]
|
|
+ pp[108] + pp[112]
|
|
+ pp[116] + pp[120]
|
|
+ pp[124];
|
|
pp += 128;
|
|
}
|
|
do_gettimeofday(&tv2);
|
|
latency[3][i] += (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
flush_cache_all();
|
|
}
|
|
|
|
/* Normal memory write latency */
|
|
flush_cache_all();
|
|
do_gettimeofday(&tv1);
|
|
memset(test_data, 'c', size);
|
|
do_gettimeofday(&tv2);
|
|
latency[4][i] = (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
|
|
/* Share memory write latency */
|
|
flush_cache_all();
|
|
do_gettimeofday(&tv1);
|
|
memset(shmm_adr, 'c', size);
|
|
do_gettimeofday(&tv2);
|
|
latency[5][i] = (tv2.tv_sec - tv1.tv_sec)*1000000
|
|
+ (tv2.tv_usec - tv1.tv_usec);
|
|
}
|
|
|
|
/* Calculate normal memory copy throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[0][i];
|
|
throughput[0][0] = (tmp != 0) ? size*PERF_TEST_ITERATION/tmp : 0;
|
|
|
|
/* Calculate share memory copy throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[1][i];
|
|
throughput[0][1] = (tmp != 0) ? size*PERF_TEST_ITERATION/tmp : 0;
|
|
|
|
/* Calculate normal memory read throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[2][i];
|
|
throughput[1][0] = (tmp != 0) ?
|
|
size*PERF_TEST_ITERATION*MEM_READ_ITERATION/tmp : 0;
|
|
|
|
/* Calculate share memory read throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[3][i];
|
|
throughput[1][1] = (tmp != 0) ?
|
|
size*PERF_TEST_ITERATION*MEM_READ_ITERATION/tmp : 0;
|
|
|
|
/* Calculate normal memory write throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[4][i];
|
|
throughput[2][0] = (tmp != 0) ?
|
|
size*PERF_TEST_ITERATION/tmp : 0;
|
|
|
|
/* Calculate share memory write throughput by average */
|
|
tmp = 0;
|
|
for (i = 0; i < PERF_TEST_ITERATION; i++)
|
|
tmp += latency[5][i];
|
|
throughput[2][1] = (tmp != 0) ?
|
|
size*PERF_TEST_ITERATION/tmp : 0;
|
|
|
|
kfree(test_data);
|
|
kfree(source_data);
|
|
|
|
snprintf(g_perf_test_result, sizeof(g_perf_test_result),
|
|
"cpy(%d,%d)/read(%d,%d)/write(%d,%d)",
|
|
throughput[0][0], throughput[0][1], throughput[1][0],
|
|
throughput[1][1], throughput[2][0], throughput[2][1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int hab_perf_test(long testId)
|
|
{
|
|
int ret;
|
|
|
|
switch (testId) {
|
|
case HAB_SHMM_THGPUT:
|
|
ret = hab_shmm_throughput_test();
|
|
break;
|
|
default:
|
|
pr_err("Invalid performance test ID %ld\n", testId);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int kick_hab_perf_test(const char *val, const struct kernel_param *kp);
|
|
static int get_hab_perf_result(char *buffer, const struct kernel_param *kp);
|
|
|
|
module_param_call(perf_test, kick_hab_perf_test, get_hab_perf_result,
|
|
NULL, 0600);
|
|
|
|
static int kick_hab_perf_test(const char *val, const struct kernel_param *kp)
|
|
{
|
|
long testId;
|
|
int err = kstrtol(val, 10, &testId);
|
|
|
|
if (err)
|
|
return err;
|
|
memset(g_perf_test_result, 0, sizeof(g_perf_test_result));
|
|
return hab_perf_test(testId);
|
|
}
|
|
|
|
static int get_hab_perf_result(char *buffer, const struct kernel_param *kp)
|
|
{
|
|
return strlcpy(buffer, g_perf_test_result,
|
|
strlen(g_perf_test_result)+1);
|
|
}
|
|
#endif
|
|
|
|
static struct kobject *hab_kobject;
|
|
|
|
static int vchan_stat;
|
|
static int context_stat;
|
|
static int pid_stat;
|
|
|
|
static ssize_t vchan_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return hab_stat_show_vchan(&hab_driver, buf, PAGE_SIZE);
|
|
}
|
|
|
|
static ssize_t vchan_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret;
|
|
|
|
ret = sscanf(buf, "%du", &vchan_stat);
|
|
if (ret < 1) {
|
|
pr_err("failed to read anything from input %d", ret);
|
|
return 0;
|
|
} else
|
|
return vchan_stat;
|
|
}
|
|
|
|
static ssize_t ctx_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return hab_stat_show_ctx(&hab_driver, buf, PAGE_SIZE);
|
|
}
|
|
|
|
static ssize_t ctx_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret;
|
|
|
|
ret = sscanf(buf, "%du", &context_stat);
|
|
if (ret < 1) {
|
|
pr_err("failed to read anything from input %d", ret);
|
|
return 0;
|
|
} else
|
|
return context_stat;
|
|
}
|
|
|
|
static ssize_t expimp_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return hab_stat_show_expimp(&hab_driver, pid_stat, buf, PAGE_SIZE);
|
|
}
|
|
|
|
static ssize_t expimp_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret;
|
|
char str[36] = {0};
|
|
|
|
ret = sscanf(buf, "%35s", str);
|
|
if (ret < 1)
|
|
pr_err("failed to read anything from input %d", ret);
|
|
|
|
if (strnlen(str, strlen("dump_pipe")) == strlen("dump_pipe") &&
|
|
strcmp(str, "dump_pipe") == 0) {
|
|
/* string terminator is ignored */
|
|
dump_hab();
|
|
return strlen("dump_pipe");
|
|
}
|
|
|
|
ret = sscanf(buf, "%du", &pid_stat);
|
|
if (ret < 1)
|
|
pr_err("failed to read anything from input %d", ret);
|
|
else
|
|
return pid_stat; /* good result stored */
|
|
return -EEXIST;
|
|
}
|
|
|
|
static struct kobj_attribute vchan_attribute = __ATTR(vchan_stat, 0660,
|
|
vchan_show,
|
|
vchan_store);
|
|
|
|
static struct kobj_attribute ctx_attribute = __ATTR(context_stat, 0660,
|
|
ctx_show,
|
|
ctx_store);
|
|
|
|
static struct kobj_attribute expimp_attribute = __ATTR(pid_stat, 0660,
|
|
expimp_show,
|
|
expimp_store);
|
|
|
|
int hab_stat_init_sub(struct hab_driver *driver)
|
|
{
|
|
int result;
|
|
|
|
hab_kobject = kobject_create_and_add("hab", kernel_kobj);
|
|
if (!hab_kobject)
|
|
return -ENOMEM;
|
|
|
|
result = sysfs_create_file(hab_kobject, &vchan_attribute.attr);
|
|
if (result)
|
|
pr_debug("cannot add vchan in /sys/kernel/hab %d\n", result);
|
|
|
|
result = sysfs_create_file(hab_kobject, &ctx_attribute.attr);
|
|
if (result)
|
|
pr_debug("cannot add ctx in /sys/kernel/hab %d\n", result);
|
|
|
|
result = sysfs_create_file(hab_kobject, &expimp_attribute.attr);
|
|
if (result)
|
|
pr_debug("cannot add expimp in /sys/kernel/hab %d\n", result);
|
|
|
|
return result;
|
|
}
|
|
|
|
int hab_stat_deinit_sub(struct hab_driver *driver)
|
|
{
|
|
sysfs_remove_file(hab_kobject, &vchan_attribute.attr);
|
|
sysfs_remove_file(hab_kobject, &ctx_attribute.attr);
|
|
sysfs_remove_file(hab_kobject, &expimp_attribute.attr);
|
|
kobject_put(hab_kobject);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dump_hab_get_file_name(char *file_time, int ft_size)
|
|
{
|
|
struct timeval time;
|
|
unsigned long local_time;
|
|
struct rtc_time tm;
|
|
|
|
do_gettimeofday(&time);
|
|
local_time = (unsigned int)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
|
|
rtc_time_to_tm(local_time, &tm);
|
|
|
|
snprintf(file_time, ft_size, "%04d_%02d_%02d-%02d_%02d_%02d",
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
|
|
tm.tm_min, tm.tm_sec);
|
|
|
|
return 0;
|
|
}
|
|
|