The real-time kernel of RK3588 is tested here to verify its metrics for subsequent evaluation and reference of real-time indicators related to RK3588. The test results may vary, and the final results are based on actual tests.
1. Compiling the Kernel
Two patches need to be modified for the RT kernel, as follows:
diff --git a/include/trace/hooks/dtask.h b/include/trace/hooks/dtask.h
index 9890bfe5c41d..fc2a51ce547e 100644
--- a/include/trace/hooks/dtask.h
+++ b/include/trace/hooks/dtask.h
@@ -58,7 +58,7 @@ DECLARE_HOOK(android_vh_sched_show_task,
TP_ARGS(task));
DECLARE_HOOK(android_vh_alter_mutex_list_add,
TP_PROTO(struct mutex *lock,
- struct mutex_waiter *waiter,
+ struct rt_mutex_waiter *waiter,
struct list_head *list,
bool *already_on_list),
TP_ARGS(lock, waiter, list, already_on_list));
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index a2506b92e0c1..8d794db5e4d6 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -67,6 +67,7 @@
#ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER
#include <clocksource/arm_arch_timer.h>
+#if 0
static u64 get_local_clock(void)
{
u64 ns;
@@ -76,6 +77,7 @@ static u64 get_local_clock(void)
return ns;
}
+#endif
#else
static inline u64 get_local_clock(void)
{
Compilation command:
export ARCH=arm64
export CROSS_COMPILE=aarch64-none-linux-gnu-
export PATH=$PATH:/path/to/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin
make rockchip_linux_defconfig rockchip_rt.config
make kylin.img -j123
This project has kylin.dts, so the kernel is compiled as kylin.img; other projects can be modified accordingly.
2. Testing Task Context Switch Time
2.1 Testing via mqueue
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <stdbool.h>
#include <pthread.h>
//#include <lock.h>
#include <fcntl.h>
#include <time.h>
#include <mqueue.h>
#include <semaphore.h>
#include <stdarg.h>
#include <sys/mman.h>
#define TESTMQ_NAME "/testmsg"
#define MSG_SIZE 8
char *testmsg = "Testing";
staticint print_mode, TestCount;
mqd_t sendsyncmq, recvsyncmq, testmq;
struct timespec tv_s[10], tv_e[10];
staticint over;
struct mq_attr mqstat;
pthread_t send_tid, recv_tid, send_recv_tid;
staticint tracemark_fd = -1;
#define FILE_TRACEMARK "/sys/kernel/debug/tracing/trace_marker"
staticvoid open_tracemark()
{
if ((tracemark_fd = open(FILE_TRACEMARK, O_WRONLY)) == -1)
printf("unable to open trace_marker file: %s\n", FILE_TRACEMARK);
}
staticvoid close_tracemark()
{
close(tracemark_fd);
}
#define TRACEBUFSIZ 1024
static __thread char tracebuf[TRACEBUFSIZ];
staticvoid tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
staticvoid tracemark(char *fmt, ...)
{
va_list ap;
int len;
/* bail out if we're not tracing */
/* or if the kernel doesn't support trace_mark */
if (tracemark_fd < 0)
return;
va_start(ap, fmt);
len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
va_end(ap);
write(tracemark_fd, tracebuf, len);
}
/*
* * * Terminate ftrace, so that the problem site can be preserved at the bottom of ftrace's ringbuffer
* * */
staticint trace_fd = -1;
#define FILE_TRACEON "/sys/kernel/debug/tracing/tracing_on"
staticvoid open_tracingon()
{
if ((trace_fd = open(FILE_TRACEON, O_WRONLY)) == -1)
printf("unable to open trace_on file: %s\n", FILE_TRACEON);
}
staticvoid close_tracingon()
{
close(trace_fd);
}
staticvoid disable_trace()
{
if (trace_fd < 0)
return;
write(trace_fd, "0", 1);
}
pthread_t test_task_create(char *name, void *thread_func, size_t stack_size, int prio, int core)
{
int ret;
struct sched_param param;
pthread_attr_t attr;
pthread_t thread_id;
cpu_set_t mask;
ret = pthread_attr_init(&attr);
if (ret) {
printf("ERROR: pthread_attr_init failed\n");
return-1;
}
ret = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
if (ret) {
printf("ERROR: pthread_attr_setscope failed\n");
gotoout;
}
ret = pthread_attr_setstacksize(&attr, stack_size);
if (ret) {
printf("ERROR: pthread_attr_setstacksize failed\n");
gotoout;
}
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
printf("ERROR: pthread_attr_setinheritsched failed\n");
gotoout;
}
/*
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (ret) {
printf("ERROR: pthread_attr_setdetachstate failed\n");
goto out;
}
*/
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
printf("ERROR: pthread_attr_setschedpolicy failed\n");
gotoout;
}
param.sched_priority = prio;
ret = pthread_attr_setschedparam(&attr, &param);
if (ret) {
printf("ERROR: pthread_attr_setschedparam failed\n");
gotoout;
}
ret = pthread_create(&thread_id, &attr, thread_func, NULL);
if (ret) {
printf("ERROR: pthread_create %s failed\n", name);
gotoout;
}
CPU_ZERO(&mask);
CPU_SET(core, &mask);
if (pthread_setaffinity_np(thread_id, sizeof(mask), &mask) == -1)
printf("ERROR: %s pthread_setaffinity_np failed, cpu_core = %d\n", name, core);
return thread_id;
out:
pthread_attr_destroy(&attr);
return-1;
}
struct timespec send_tv, recv_tv;
bool flag = false;
bool flag_send = false;
void send_task_pthread(int core)
{
int i, ret;
struct timespec tv1, tv2;
unsignedint send_diff, send_sum;
unsignedint send_mindiff, send_avg, send_maxdiff;
char recvtestmsg[MSG_SIZE];
send_sum = 0;
send_avg =0;
send_mindiff = 100000000;
send_maxdiff = 0;
for (i = 0; i < TestCount; i++) {
if (over)
break;
/*
* send the test message and calculate the cost time
*/
if (i > 1) {
send_diff = (recv_tv.tv_sec * 1000000000 + recv_tv.tv_nsec) - (send_tv.tv_sec * 1000000000 + send_tv.tv_nsec);
send_sum += send_diff;
send_avg = send_sum / (i + 1);
if (send_diff < send_mindiff) {
send_mindiff = send_diff;
}
/*
* From the third time, count the maximum time
* Reason: During the second message sending, if the receiving thread has not been successfully created, mq_send blocks,
* waiting for the receiving thread to be created, the second time calculation is not accurate.
* Therefore, after the receiving thread receives a message, start counting the maximum time
*/
if ((send_diff > send_maxdiff) && (i > 1)) {
send_maxdiff = send_diff;
}
// if (((i % 10) == 0))
printf("Task Switch Time, loop: %d, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", i, send_mindiff, send_diff, send_avg, send_maxdiff);
}
flag_send = false;
ret = mq_send(testmq, testmsg, strlen(testmsg), 1);
clock_gettime(CLOCK_REALTIME, &send_tv);
flag_send = true;
flag = false;
while (!flag) {
usleep(10000);
}
// if (!flag){
// usleep(10000);
// flag = true;
// printf("ERROR: send test message failed\n");
// sleep(1);
// goto retry;
// }
}
over = 1;
printf("mq_send test count: %d \n", i);
printf("Final result, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", send_mindiff, send_diff, send_avg, send_maxdiff);
/*
close_tracemark();
close_tracingon();
*/
pthread_kill(recv_tid, SIGTERM);
return;
}
void recv_task_pthread(int core)
{
int i, ret;
ssize_t recv_length;
struct timespec tv1, tv2;
unsignedint recv_diff, recv_sum;
unsignedint recv_mindiff, recv_avg, recv_maxdiff;
char recvsyncmsg[MSG_SIZE];
char recvtestmsg[MSG_SIZE];
while (!over) {
/*
* receive the test message and calculate the cost time
*/
recv_length = mq_receive(testmq, recvtestmsg, MSG_SIZE, NULL);
while (!flag_send) {
}
clock_gettime(CLOCK_REALTIME, &recv_tv);
flag = true;
if ( recv_length != strlen(testmsg)) {
perror("could not receive test message");
over = 1;
}
}
return;
}
void send_recv_pthread(int core)
{
int i, ret;
ssize_t recv_length;
struct timespec tv1, tv2;
unsignedint recv_diff, recv_sum;
unsignedint recv_mindiff, recv_avg, recv_maxdiff;
char recvtestmsg[MSG_SIZE];
recv_sum = 0;
recv_avg =0;
// recv_mindiff = UINT_MAX;
recv_mindiff = 100000000;
recv_maxdiff = 0;
for (i = 0; i < TestCount; i++) {
/*
* send the test message
*/
ret = mq_send(testmq, testmsg, strlen(testmsg), 1);
if (ret < 0) {
printf("ERROR: send test message failed\n");
over = 1;
}
usleep(200);
/*
* receive the test message and calculate the cost time
*/
clock_gettime(CLOCK_REALTIME, &tv1);
recv_length = mq_receive(testmq, recvtestmsg, MSG_SIZE, NULL);
clock_gettime(CLOCK_REALTIME, &tv2);
if ( recv_length != strlen(testmsg)) {
perror("could not receive test message");
over = 1;
}
recv_diff = (tv2.tv_sec * 1000000000 + tv2.tv_nsec) - (tv1.tv_sec * 1000000000 + tv1.tv_nsec);
recv_sum += recv_diff;
recv_avg = recv_sum / (i + 1);
if (recv_diff < recv_mindiff) {
recv_mindiff = recv_diff;
}
if ((recv_diff > recv_maxdiff) && (i > 0)) {
recv_maxdiff = recv_diff;
}
if (((i % 10) == 0))
printf("MQ Receive Time,loop: %d, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", i, recv_mindiff, recv_diff, recv_avg, recv_maxdiff);
usleep(100);
}
printf("mq_receive test count: %d \n", i);
printf("Final result, Min: %4d ns, Cur: %4d ns, Avg: %4d ns, Max: %4d ns\n\n", recv_mindiff, recv_diff, recv_avg, recv_maxdiff);
return;
}
int main(int argc, char **argv)
{
int SendCore, SendThreadPrio, RecvCore, RecvThreadPrio, mode;
if (argc != 7) {
printf("Wrong CMD, check the parameters, please!\n");
printf("./mq_task_switch 1 90 2 90 10000 1\n");
return-1;
}
if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
perror("mlockall");
return-1;
}
SendCore = atoi(argv[1]);
SendThreadPrio = atoi(argv[2]);
RecvCore = atoi(argv[3]);
RecvThreadPrio = atoi(argv[4]);
TestCount = atoi(argv[5]);
mode = atoi(argv[6]);
over = 0;
memset(&mqstat, 0, sizeof(mqstat));
mqstat.mq_maxmsg = 1;
mqstat.mq_msgsize = 8;
mqstat.mq_flags = 0;
mq_unlink(TESTMQ_NAME);
testmq = mq_open(TESTMQ_NAME, O_RDWR | O_CREAT, 0666, &mqstat);
if (testmq < 0) {
printf("open test message queue failed\n");
return-1;
}
if (mode == 0) {
send_recv_tid = test_task_create("SndRECVTask",
send_recv_pthread,
262144,
SendThreadPrio,
SendCore);
if (send_recv_tid < 0) {
printf("ERROR: Create send_recv_pthread failed!\n");
return-1;
}
pthread_join(send_recv_tid, NULL);
} else {
send_tid = test_task_create("sndTask",
send_task_pthread,
262144,
SendThreadPrio,
SendCore);
if (recv_tid < 0) {
printf("ERROR: Create send_task_pthread failed!\n");
return-1;
}
usleep(10);
recv_tid = test_task_create("RcvTask",
recv_task_pthread,
262144,
RecvThreadPrio,
RecvCore);
if (recv_tid < 0) {
printf("ERROR: Create recv_task_pthread failed!\n");
return-1;
}
pthread_join(recv_tid, NULL);
pthread_join(send_tid, NULL);
}
munlockall();
return0;
}
Compile to obtain the binary mq_task_switch, and start testing.
./mq_task_switch 1 90 2 90 10000 1
Output results
Final result, Min: 10208 ns, Cur: 11084 ns, Avg: 11113 ns, Max: 24791 ns
Here it can be confirmed that the maximum value is 24791ns, and the usual value is 11113ns.
2.2 Passing Tokens via Pipe
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <sched.h>
#include <sys/types.h>
#include <unistd.h> //pipe()
int main()
{
int x, i, fd[2], p[2];
int tv[2]; // For inter-process data communication
char send = 's';
char receive;
pipe(fd);
pipe(p);
pipe(tv);
struct timeval tv_start,tv_end;
struct sched_param param;
param.sched_priority = 0;
while ((x = fork()) == -1);
if (x==0) { // Child process
sched_setscheduler(getpid(), SCHED_FIFO, &param);
gettimeofday(&tv_start, NULL);
write(tv[1], &tv_start, sizeof(tv_start));// Save data to pass to the parent process for calculation
for (i = 0; i < 10000; i++) {
read(fd[0], &receive, 1);
//printf("Child read!\n");
write(p[1], &send, 1);
//printf("Child write!\n");
}
exit(0);
}
else { // Parent process
sched_setscheduler(getpid(), SCHED_FIFO, &param);
for (i = 0; i < 10000; i++) {
write(fd[1], &send, 1);
//printf("Parent write!\n");
read(p[0], &receive, 1);
//printf("Parent read!\n");
}
gettimeofday(&tv_end, NULL);
}
read(tv[0], &tv_start, sizeof(tv_start));
printf("Task Switch Time: %f us\n", (1000000*(tv_end.tv_sec-tv_start.tv_sec) + tv_end.tv_usec - tv_start.tv_usec)/20000.0);
return0;
}
Compile:
gcc task_switch_pipe.c -o task_switch_pipe
Run:
root@kylin:~# while((1));do ./task_switch_pipe ; done
Task Switch Time: 7.925000 us
Task Switch Time: 7.978100 us
Task Switch Time: 7.542050 us
Task Switch Time: 9.470300 us
Task Switch Time: 7.518850 us
Task Switch Time: 8.346700 us
Task Switch Time: 8.005150 us
Task Switch Time: 7.560750 us
Task Switch Time: 7.625400 us
Task Switch Time: 8.143850 us
Task Switch Time: 6.504850 us
Task Switch Time: 8.422350 us
Task Switch Time: 8.029650 us
Task Switch Time: 7.612400 us
Task Switch Time: 7.706200 us
Task Switch Time: 6.831750 us
Task Switch Time: 6.637150 us
Task Switch Time: 9.136150 us
Task Switch Time: 7.259700 us
Task Switch Time: 9.774700 us
Task Switch Time: 6.781400 us
Task Switch Time: 7.425350 us
Task Switch Time: 7.738900 us
Task Switch Time: 8.812100 us
Task Switch Time: 7.271150 us
Task Switch Time: 8.677250 us
Task Switch Time: 7.514600 us
Task Switch Time: 6.683600 us
Task Switch Time: 7.421900 us
Task Switch Time: 8.019550 us
Task Switch Time: 8.250450 us
Task Switch Time: 8.571800 us
Task Switch Time: 7.957400 us
Task Switch Time: 7.837050 us
Task Switch Time: 7.161250 us
Task Switch Time: 8.998750 us
Task Switch Time: 7.160500 us
Task Switch Time: 6.629200 us
Task Switch Time: 7.082150 us
Task Switch Time: 7.570450 us
Task Switch Time: 8.423500 us
Task Switch Time: 7.531950 us
Task Switch Time: 7.856900 us
Task Switch Time: 8.980750 us
Task Switch Time: 7.072300 us
Task Switch Time: 9.112650 us
Task Switch Time: 7.871100 us
Task Switch Time: 7.168500 us
Task Switch Time: 7.774850 us
Task Switch Time: 7.308050 us
Task Switch Time: 6.532000 us
Task Switch Time: 6.994950 us
Task Switch Time: 7.805700 us
Task Switch Time: 7.980750 us
Task Switch Time: 7.990150 us
Task Switch Time: 8.017550 us
Task Switch Time: 6.781300 us
Task Switch Time: 6.755500 us
Task Switch Time: 6.418300 us
Task Switch Time: 8.239600 us
Task Switch Time: 8.661350 us
Task Switch Time: 8.688850 us
Task Switch Time: 7.245000 us
Task Switch Time: 8.793250 us
Task Switch Time: 7.628300 us
Task Switch Time: 6.593100 us
Task Switch Time: 8.685100 us
Task Switch Time: 6.575550 us
Task Switch Time: 8.380500 us
Task Switch Time: 8.607750 us
Task Switch Time: 6.581850 us
Task Switch Time: 6.862200 us
Task Switch Time: 6.401400 us
Task Switch Time: 6.690150 us
Task Switch Time: 7.945100 us
Task Switch Time: 7.820750 us
Task Switch Time: 7.935500 us
Task Switch Time: 8.156100 us
Task Switch Time: 7.198700 us
Task Switch Time: 7.230950 us
Task Switch Time: 7.623400 us
Task Switch Time: 7.438650 us
Task Switch Time: 7.172000 us
Task Switch Time: 8.439000 us
Task Switch Time: 6.927050 us
Task Switch Time: 8.215500 us
Task Switch Time: 7.361300 us
Task Switch Time: 7.231800 us
Task Switch Time: 7.303450 us
Task Switch Time: 8.140700 us
Task Switch Time: 8.331800 us
Task Switch Time: 7.962950 us
Task Switch Time: 6.757600 us
Task Switch Time: 7.251000 us
Here the maximum delay is tested to be 9.774700 us.
3. Testing Message Passing Delay Time
Install the test package
apt install rt-tests
Test command:
pmqtest -p 90 -t 2 -i 1000 -l 7200000
Test results
root@kylin:~# pmqtest -p 90 -t 2 -i 1000 -l 7200000
#0: ID2607, P90, CPU5, I1000; #1: ID2608, P90, CPU4, TO 0, Cycles 782505
#2: ID2609, P89, CPU6, I1500; #3: ID2610, P89, CPU7, TO 0, Cycles 524798
#1 -> #0, Min 3, Cur 6, Avg 6, Max 27
#3 -> #2, Min 3, Cur 6, Avg 7, Max 36
Here it can be confirmed that the minimum value is 3us, the average value is 7us, and the maximum value is 36us.
4. Interrupt Response Time
Test command:
cyclictest -p 90 -m -c 0 -i 1000 -t 4 -l 7200000
Test results:
root@kylin:~# cyclictest -p 90 -m -c 0 -i 1000 -t 4 -l 7200000
policy: fifo: loadavg: 2.61 1.48 0.68 1/288 257policy: fifo: loadavg: 2.91 2.71 2.14 1/285 2595
# /dev/cpu_dma_latency set to 0us
T: 0 ( 2567) P:90 I:1000 C:1217830 Min: 4 Act: 4 Avg: 4 Max: 19
T: 1 ( 2568) P:90 I:1500 C: 811877 Min: 4 Act: 4 Avg: 4 Max: 15
T: 2 ( 2569) P:90 I:2000 C: 608904 Min: 4 Act: 5 Avg: 4 Max: 14
T: 3 ( 2570) P:90 I:2500 C: 487120 Min: 4 Act: 4 Avg: 4 Max: 13
Here it can be confirmed that the maximum delay is 19us, and the average delay is 4us.
5. Conclusion
The current test conclusions are as follows:
The maximum task context switch time for 3588 is: 24.791us, the maximum delay for pipe token testing is: 9.774700 us.
The maximum message passing delay for 3588 is: 36us.
The maximum interrupt response delay for 3588 is: 19us.
The current test results do not represent actual test results, and the test results are for reference only; specific situations depend on system load and hardware conditions.
For other articles, please follow the public account: Kylin Embedded