目次: Zephyr
前回はRISC-Vの明示的なコンテキストスイッチの既存実装を調べました。今回は新しいコンテキストスイッチを実装します。
従来と新形式のコンテキストスイッチで大きく異なるのは、下記の要素です。
明示的コンテキストスイッチ | 従来 | 新形式 |
---|---|---|
割り込まれた処理の返り値 | 設定必要(thread->arch.swap_return_value) | 設定不要(do_swap() がやってくれる) |
切り替え元スレッド | _kernel.cpu[0].current | a1レジスタ(引数old_thread->switch_handle) |
切り替え先スレッド | _kernel.ready_q.cache | a0レジスタ(引数new_thread->switch_handle) |
プリエンプション | 従来 | 新形式 |
---|---|---|
割り込まれた処理の返り値 | 設定必要(thread->arch.swap_return_value) | 設定不要(do_swap() がやってくれる) |
切り替え元スレッド | _kernel.cpu[0].current | _kernel.cpu[n].current(※) |
切り替え先スレッド | _kernel.ready_q.cache | z_get_next_switch_handle() の返り値 |
(※)初めは切り替え元スレッドですが、z_get_next_switch_handle() を呼ぶと、切り替え先のスレッドに変わります。
割り込まれた処理の返り値を -EINTRに設定するために必要な処理は、do_swap() がやるため実装は必要ありません。従来の処理が間違って発動しないように #ifdefで消しておきます。
// zephyr/arch/riscv/core/isr.Sの差分
+ /* Save stack pointer of current thread. */
+ RV_OP_STOREREG sp, _thread_offset_to_sp(t1) //★スタックポインタ保存は新しい形式でも必要★
+
+#ifndef CONFIG_USE_SWITCH
/*
- * Save stack pointer of current thread and set the default return value
- * of z_swap to _k_neg_eagain for the thread.
+ * Set the default return value of z_swap to _k_neg_eagain for
+ * the thread.
*/
- RV_OP_STOREREG sp, _thread_offset_to_sp(t1)
la t2, _k_neg_eagain
lw t3, 0x00(t2)
sw t3, _thread_offset_to_swap_return_value(t1) //★返り値設定は不要★
+#endif /* !CONFIG_USE_SWITCH */
// zephyr/arch/riscv/core/offsets/offsets.c
//★_thread_offset_to_swap_return_value() マクロを使えるようにする仕掛け★
#ifndef CONFIG_USE_SWITCH
GEN_OFFSET_SYM(_thread_arch_t, swap_return_value); //★いらない★
#endif /* !CONFIG_USE_SWITCH */
// zephyr/include/arch/riscv/thread.h
struct _thread_arch {
#ifndef CONFIG_USE_SWITCH
uint32_t swap_return_value; /* Return value of z_swap() */ //★いらない★
#endif /* !CONFIG_USE_SWITCH */
};
処理を消すだけでも動きますが、swap_return_valueを間違って使うとバグの元なので、変数宣言ごと消します。
従来は常に _kernel変数を見れば良かったので楽でした。新形式では明示的コンテキストスイッチとプリエンプションで切り替え元/切り替え先スレッドの取得方法が異なります。よって、明示的コンテキストスイッチとプリエンプションで、スレッドの扱いを揃える必要があります。
設計する人の自由で決めて構いませんが、今回は合流地点(reschedule)に辿り着く前に切り替え元(old_thread)、切り替え先スレッド(new_thread)を取得し _currentにnew_threadを設定し, t1レジスタにold_threadを設定することとします。
明示的コンテキストスイッチの場合、切り替え元と切り替え先スレッドは引数で渡されます。引数はハンドラの先頭でスタックに保存されますので、スタックからロードできます。
// zephyr/arc/riscv/core/isr.S
#ifdef CONFIG_USE_SWITCH
/*
* Get new_thread and old_thread from stack.
* - a0 = new_thread->switch_handle -> _current
* - a1 = &old_thread->switch_handle -> t1
*/
/* Get reference to _kernel */
la t2, _kernel
/* Get new_thread from stack */
RV_OP_LOADREG t1, __z_arch_esf_t_a0_OFFSET(sp) //★スタックから切り替え先スレッド取得★
/* Set new thread to _current */
RV_OP_STOREREG t1, ___cpu_t_current_OFFSET(t2) //★(2) この関数内でnew_threadを _currentに設定★
/* Get old_thread from stack and set it to t1 */
RV_OP_LOADREG t1, __z_arch_esf_t_a1_OFFSET(sp) //★(1)スタックから切り替え元スレッド取得、t1レジスタにold_threadを設定★
addi t1, t1, -___thread_t_switch_handle_OFFSET //★(A) old_thread->switch_handleの更新★
#endif
/*
* Go to reschedule to handle context-switch
*/
j reschedule
// zephyr/arch/riscv/core/thread.c
void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
char *stack_ptr, k_thread_entry_t entry,
void *p1, void *p2, void *p3)
{
struct __esf *stack_init;
...
#ifdef CONFIG_USE_SWITCH
thread->switch_handle = thread; //★(B) switch_handleの初期値設定★
#endif
}
処理 (1) でnew_threadを _currentに設定していて、処理 (2) でold_threadをt1レジスタに設定してから、rescheduleにジャンプします。
スレッドと直接関係ないもののold_thread->switch_handleを更新する処理も重要です。あとでハマりやすいポイントですので、補足しておきます。
以前、少し言及しましたが(2020年9月30日の日記参照)、switch_handleの更新を実装し忘れるとCONFIG_SMPを有効にしたときにwait_for_switch() で無限ループに陥ってハマります。
Zephyrのドキュメントにてarch_switch() を見ると(Zephyr Project: arch_switch)、スレッドにレジスタを退避後、old_thread->switch_handleをNULL以外の値で書き換える必要があります。これはコンテキストスイッチ処理内で行う (A) の処理に相当します。
実はこれだけではダメです。wait_for_switch() はコンテキストスイッチの「前」に呼ばれるからです。一番最初に発生するコンテキストスイッチのold_thread->switch_handleは誰も書き換えてくれないのでハングします。この問題の対処としてスレッド生成時にswitch_handleを初期化する (B) の処理を実装しています。
次回はプリエンプションの実装をします。
< | 2020 | > | ||||
<< | < | 10 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)