diff --git a/test/mng_mod/issues/1029/1029.patch b/test/mng_mod/issues/1029/1029.patch new file mode 100644 index 00000000..eeb73885 --- /dev/null +++ b/test/mng_mod/issues/1029/1029.patch @@ -0,0 +1,173 @@ +diff --git a/kernel/include/process.h b/kernel/include/process.h +index 1bd70e1..240fe03 100644 +--- a/kernel/include/process.h ++++ b/kernel/include/process.h +@@ -697,6 +697,9 @@ struct thread { + // for performance counter + unsigned long pmc_alloc_map; + unsigned long extra_reg_alloc_map; ++ ++// TestCode for #1029 ++int started; + }; + + #define VM_RANGE_CACHE_SIZE 4 +diff --git a/kernel/process.c b/kernel/process.c +index cdd4ab3..a3e6c8a 100644 +--- a/kernel/process.c ++++ b/kernel/process.c +@@ -125,6 +125,7 @@ init_process(struct process *proc, struct process *parent) + proc->mpol_threshold = parent->mpol_threshold; + memcpy(proc->rlimit, parent->rlimit, + sizeof(struct rlimit) * MCK_RLIM_MAX); ++#define POSTK_DEBUG_TEMP_FIX_69 1 // TestCode for #1029 + #ifdef POSTK_DEBUG_TEMP_FIX_69 /* Fix problem not to inherit parent cpu_set. */ + memcpy(&proc->cpu_set, &parent->cpu_set, + sizeof(proc->cpu_set)); +@@ -3104,12 +3105,16 @@ out_schedule: + schedule(); + } + ++// TestCode for #1029 ++int cases[13] = {0}; + void schedule(void) + { + struct cpu_local_var *v; + struct thread *next, *prev, *thread, *tmp = NULL; + int switch_ctx = 0; + struct thread *last; ++// TestCode for #1029 ++int runq_cnt = 0, case_num = 0, not_started = 0; + + if (cpu_local_var(no_preempt)) { + kprintf("%s: WARNING can't schedule() while no preemption, cnt: %d\n", +@@ -3139,6 +3144,62 @@ redo: + } + } + ++// TestCode for #1029 ++// flag for thread is started or not ++if (prev) { ++ prev->started = 1; ++} ++ ++// check runq ++runq_cnt = 0; ++not_started = 0; ++list_for_each_entry_safe(thread, tmp, &(v->runq), sched_list) { ++ if (thread->tid == prev->tid) { ++ continue; ++ } ++ runq_cnt++; ++ if (!thread->started) { ++ not_started = 1; ++ } ++} ++ ++// test cases ++if (!prev) { ++ case_num = 0; ++} else if (prev == &cpu_local_var(idle)) { // prev is idle ++ if (runq_cnt == 0) { // runq is empty ++ case_num = 1; ++ } else if (not_started) { // runq has other NOT_started thread ++ case_num = 2; ++ } else { // runq has other started thread ++ case_num = 3; ++ } ++} else if (v->flags & CPU_FLAG_NEED_MIGRATE) { // prev is NEED_MIGRATE ++ if (runq_cnt == 0) { // runq is empty ++ case_num = 4; ++ } else if (not_started) { // runq has other NOT_started thread ++ case_num = 5; ++ } else { // runq has other started thread ++ case_num = 6; ++ } ++} else if (prev->status != PS_EXITED) { // prev is NOT EXITED ++ if (runq_cnt == 0) { // runq is empty ++ case_num = 7; ++ } else if (not_started) { // runq has other NOT_started thread ++ case_num = 8; ++ } else { // runq has other started thread ++ case_num = 9; ++ } ++} else { // prev is NOT EXITED ++ if (runq_cnt == 0) { // runq is empty ++ case_num = 10; ++ } else if (not_started) { // runq has other NOT_started thread ++ case_num = 11; ++ } else { // runq has other started thread ++ case_num = 12; ++ } ++} ++ + if (v->flags & CPU_FLAG_NEED_MIGRATE || + prev->status == PS_EXITED) { + next = &cpu_local_var(idle); +@@ -3172,6 +3233,58 @@ redo: + + set_timer(); + ++// TestCode for #1029 ++switch (case_num) { ++case 0: ++ break; ++case 1: ++ if (!cases[case_num]) { ++ if (!switch_ctx) { ++ kprintf("[OK] CT_%03d not_switch\n", case_num); ++ } else { ++ kprintf("[NG] CT_%03d %d -> %d\n", case_num, prev->tid, next->tid); ++ } ++ cases[case_num] = 1; ++ } ++ break; ++case 4: ++case 5: ++case 6: ++case 7: ++case 10: ++case 11: ++case 12: ++ // switch to idle ++ if (!cases[case_num]) { ++ if (next == &cpu_local_var(idle)) { ++ kprintf("[OK] CT_%03d switch to idle %d => %d\n", case_num, ++ prev ? prev->tid : 0, next ? next->tid : 0); ++ } else { ++ kprintf("[NG] CT_%03d %d => %d\n", case_num, prev->tid, next->tid); ++ } ++ cases[case_num] = 1; ++ } ++ break; ++case 2: ++case 3: ++case 8: ++case 9: ++ // switch to NOT idle ++ if (!cases[case_num]) { ++ if (next != &cpu_local_var(idle)) { ++ kprintf("[OK] CT_%03d %d => %d\n", case_num, ++ prev ? prev->tid : 0, next ? next->tid : 0); ++ } else { ++ kprintf("[NG] CT_%03d\n", case_num); ++ } ++ cases[case_num] = 1; ++ } ++ break; ++ ++default: ++ kprintf("unexpected case_num\n"); ++} ++ + if (switch_ctx) { + dkprintf("schedule: %d => %d \n", + prev ? prev->tid : 0, next ? next->tid : 0); +@@ -3260,6 +3373,7 @@ redo: + + /* Have we migrated to another core meanwhile? */ + if (v != get_this_cpu_local_var()) { ++ switch_ctx = 0; + goto redo; + } + } diff --git a/test/mng_mod/issues/1029/Makefile b/test/mng_mod/issues/1029/Makefile new file mode 100644 index 00000000..ffb6f6c7 --- /dev/null +++ b/test/mng_mod/issues/1029/Makefile @@ -0,0 +1,18 @@ +CC=gcc +MCK_DIR=/home/satoken/ppos +MCEXEC=$(MCK_DIR)/bin/mcexec +TARGET=sched_test go_test +all:: $(TARGET) + +sched_test: sched_test.c + $(CC) -o $@ $< + +go_test: go_test.c + $(CC) -o $@ $< + +test:: $(TARGET) + -$(MCEXEC) ./go_test 2 + -$(MCK_DIR)/sbin/ihkosctl 0 kmsg | grep CT_ | cut -f 2 -d ":" | sort > result.log + +clean:: + rm -f $(TARGET) diff --git a/test/mng_mod/issues/1029/README b/test/mng_mod/issues/1029/README new file mode 100644 index 00000000..f5f81ff8 --- /dev/null +++ b/test/mng_mod/issues/1029/README @@ -0,0 +1,69 @@ +【Issue#1029 動作確認】 +Issue#1029が解決され、既存機能にも影響がないことをストレステスト用いた確認(1項目)と、 +schedule()の基本動作確認(12項目)の計13項目のテストによって確認した。 + +①ストレステストを用いた確認 + 以下のコマンドを実行し、Issue#1029で報告された事象が発生せず、テストがパスすることを確認した。 + # ./mck-mcexec.sh ./killit -np 16 -nosignal - ./signalonfutex + +②schedule()の基本動作確認 +schedule()実行時のコンテキストスイッチ前thread(prev)と、 +runqに積まれている実行待ちthreadの状態の組み合わせで、12項目のテストを実施した。 + +基本動作確認の詳細を以下に示す。 + +1. ファイルの説明 + 1029.patch 動作確認用デバッグプリントを追加するパッチファイル + sched_test.c 修正対象のschedule()の動作を確認するプログラム + 複数の子プロセスをfork()し、それぞれの子プロセスでsched_setaffinity()を行う + go_test.c schedule()の動作確認テストを実施するプログラム + sched_testプログラムを並列実行する + result.log go_testプログラムの実行結果 + +2. テストの実行方法 + 以下の手順でテストを実行する + 1. 1029.patch をMcKernelのソースコードに適用し、ビルドとインストールを行う + 2. MakefileのMCK_DIR変数の内容を、McKernelがインストールされているディレクトリに変更する + 3. McKernelを起動する + 4. sh make test を実行する + +3. テスト項目 +schedule()実行時のコンテキストスイッチ前thread(prev)と、 +runqに積まれている実行待ちthreadの状態の以下の組み合わせで、 +schedule()が想定どおりの動作をすることを確認する。 + +◆prevがidleのケース +CT_001: runqが空 + ⇒ コンテキストスイッチを行わない +CT_002: runqに実行開始前のthreadが存在する + ⇒ 非idleのthreadにスイッチする +CT_003: runqに実行開始前のthreadが存在しない + ⇒ 非idleのthreadにスイッチする + +◆schedule時点で当該CPUのCPU_FLAGS_NEED_MIGRATEが活性化しているケース +CT_004: runqが空 + ⇒ idleにスイッチする +CT_005: runqに実行開始前のthreadが存在する + ⇒ idleにスイッチする +CT_006: runqに実行開始前のthreadが存在しない + ⇒ idleにスイッチする + +◆prevがidle以外で、statusがPS_EXITED以外: +CT_007: runqが空 + ⇒ idleにスイッチする +CT_008: runqに実行開始前のthreadが存在する + ⇒ 非idleのthreadにスイッチする +CT_009: runqに実行開始前のthreadが存在しない + ⇒ 非idleのthreadにスイッチする + +◆prevがidle以外で、statusがPS_EXITED: +CT_010: runqが空 + ⇒ idleにスイッチする +CT_011: runqに実行開始前のthreadが存在する + ⇒ idleにスイッチする +CT_012: runqに実行開始前のthreadが存在しない + ⇒ idleにスイッチする + +4. 結果 +テストプログラムの実行結果はresult.log に出力される。 +上記12項目で[OK]が出力されていることを確認した。 diff --git a/test/mng_mod/issues/1029/go_test.c b/test/mng_mod/issues/1029/go_test.c new file mode 100644 index 00000000..5961ee8a --- /dev/null +++ b/test/mng_mod/issues/1029/go_test.c @@ -0,0 +1,39 @@ +#define _GNU_SOURCE +#include +#include +#include + +#define DEF_LOOPS 10 + +int main(int argc, char** argv) { + pid_t pid[256]; + int result, i, j; + int loops = DEF_LOOPS; + char *_argv[3]; + + if (argc > 1) { + loops = atoi(argv[1]); + } + + for (i = 0; i < loops && (pid[i] = fork()) > 0; i++); + + if (i == loops) { // parent + for (i = 0; i < loops; i++) { + waitpid(pid[i], NULL, 0); + } + } + else if (pid[i] == 0) { + _argv[0] = "./sched_test"; + _argv[1] = "4"; + _argv[2] = NULL; + execve(_argv[0], _argv, NULL); + + perror("execve"); + } + else { + perror("Error fork()"); + return(1); + } + + return(0); +} diff --git a/test/mng_mod/issues/1029/result.log b/test/mng_mod/issues/1029/result.log new file mode 100644 index 00000000..e92c5464 --- /dev/null +++ b/test/mng_mod/issues/1029/result.log @@ -0,0 +1,12 @@ + [OK] CT_001 not_switch + [OK] CT_002 0 => 16997 + [OK] CT_003 1 => 17007 + [OK] CT_004 switch to idle 17017 => 2 + [OK] CT_005 switch to idle 17047 => 1 + [OK] CT_006 switch to idle 17018 => 1 + [OK] CT_007 switch to idle 16997 => 0 + [OK] CT_008 17007 => 17018 + [OK] CT_009 17017 => 17018 + [OK] CT_010 switch to idle 17017 => 1 + [OK] CT_011 switch to idle 17047 => 1 + [OK] CT_012 switch to idle 17018 => 1 diff --git a/test/mng_mod/issues/1029/sched_test.c b/test/mng_mod/issues/1029/sched_test.c new file mode 100644 index 00000000..639d91f6 --- /dev/null +++ b/test/mng_mod/issues/1029/sched_test.c @@ -0,0 +1,73 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +#define DEF_LOOPS 10 + +int main(int argc, char** argv) { + pid_t pid[256]; + cpu_set_t cpu_set; + int result, i, j; + int loops = DEF_LOOPS; + + if (argc > 1) { + loops = atoi(argv[1]); + } + + CPU_ZERO(&cpu_set); + CPU_SET(1, &cpu_set); + + result = sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set); + + if (result != 0) { + perror("Error sched_setaffinity()"); + return(1); + } + + for (i = 0; i < loops && (pid[i] = fork()) > 0; i++); + + if (i == loops) { // parent + for (i = 0; i < loops; i++) { + waitpid(pid[i], NULL, 0); + } + } + else if (pid[i] == 0) { + cpu_set_t child_set; + + CPU_ZERO(&child_set); + CPU_SET(2, &child_set); + result = sched_setaffinity(0, sizeof(cpu_set_t), &child_set); + if (result != 0) { + perror("Error sched_setaffinity() on child"); + } + + result = sched_yield(); + if (result != 0) { + perror("Error sched_yield()"); + } + + CPU_ZERO(&child_set); + CPU_SET(1, &child_set); + result = sched_setaffinity(0, sizeof(cpu_set_t), &child_set); + if (result != 0) { + perror("Error sched_setaffinity() on child"); + } + + result = sched_yield(); + if (result != 0) { + perror("Error sched_yield()"); + } + + printf("child[%d] is done.\n", i); + return(0); + } + else { + perror("Error fork()"); + return(1); + } + printf("parent is done.\n"); + + return(0); +}