diff --git a/drivers/soc/qcom/hab/Makefile b/drivers/soc/qcom/hab/Makefile index 77825be16fc4..0ad19931776c 100644 --- a/drivers/soc/qcom/hab/Makefile +++ b/drivers/soc/qcom/hab/Makefile @@ -10,6 +10,7 @@ msm_hab-objs = \ hab_pipe.o \ qvm_comm.o \ hab_qvm.o \ - hab_parser.o + hab_parser.o \ + khab_test.o obj-$(CONFIG_MSM_HAB) += msm_hab.o diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c index 37afe025a97a..52de57b766f2 100644 --- a/drivers/soc/qcom/hab/hab.c +++ b/drivers/soc/qcom/hab/hab.c @@ -122,7 +122,7 @@ struct virtual_channel *hab_get_vchan_fromvcid(int32_t vcid, return NULL; } -static struct hab_device *find_hab_device(unsigned int mm_id) +struct hab_device *find_hab_device(unsigned int mm_id) { int i; diff --git a/drivers/soc/qcom/hab/hab.h b/drivers/soc/qcom/hab/hab.h index 2a07da728e00..facb0a068221 100644 --- a/drivers/soc/qcom/hab/hab.h +++ b/drivers/soc/qcom/hab/hab.h @@ -510,6 +510,8 @@ bool hab_is_loopback(void); int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids, char *names, size_t name_size, uint32_t flags); +struct hab_device *find_hab_device(unsigned int mm_id); + /* Global singleton HAB instance */ extern struct hab_driver hab_driver; diff --git a/drivers/soc/qcom/hab/khab_test.c b/drivers/soc/qcom/hab/khab_test.c new file mode 100644 index 000000000000..3773211aeec7 --- /dev/null +++ b/drivers/soc/qcom/hab/khab_test.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2018, 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" +#include "khab_test.h" +#include "hab_pipe.h" +#include "hab_qvm.h" +#include +#include + +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 = 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, struct kernel_param *kp); +static int get_hab_perf_result(char *buffer, struct kernel_param *kp); + +module_param_call(perf_test, kick_hab_perf_test, get_hab_perf_result, + NULL, S_IRUSR | S_IWUSR); + +static int kick_hab_perf_test(const char *val, 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, struct kernel_param *kp) +{ + return strlcpy(buffer, g_perf_test_result, + strlen(g_perf_test_result)+1); +} diff --git a/drivers/soc/qcom/hab/khab_test.h b/drivers/soc/qcom/hab/khab_test.h new file mode 100644 index 000000000000..bc2080ed08c7 --- /dev/null +++ b/drivers/soc/qcom/hab/khab_test.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2018, 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. + * + */ +#ifndef __KHAB_TEST_H +#define __KHAB_TEST_H + +int hab_perf_test(long testId); + +#endif /* __KHAB_TEST_H */