
		TOPPERS/FMP3カーネル
		設計メモ

		対応バージョン: Release 3.1.0
		最終更新: 2019年3月30日（作成中）

このドキュメントは，TOPPERS/FMP3カーネルの設計メモである．作成中のもの
であり，網羅的ではない．

----------------------------------------------------------------------
 TOPPERS/FMP Kernel
     Toyohashi Open Platform for Embedded Real-Time Systems/
     Flexible Multiprocessor Profile Kernel

 Copyright (C) 2005-2019 by Embedded and Real-Time Systems Laboratory
             Graduate School of Information Science, Nagoya Univ., JAPAN
 
 上記著作権者は，以下の(1)～(4)の条件を満たす場合に限り，本ソフトウェ
 ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
     権表示，この利用条件および下記の無保証規定が，そのままの形でソー
     スコード中に含まれていること．
 (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
     用できる形で再配布する場合には，再配布に伴うドキュメント（利用
     者マニュアルなど）に，上記の著作権表示，この利用条件および下記
     の無保証規定を掲載すること．
 (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
     用できない形で再配布する場合には，次のいずれかの条件を満たすこ
     と．
   (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
       作権表示，この利用条件および下記の無保証規定を掲載すること．
   (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
       報告すること．
 (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
     害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
     また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
     由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
     免責すること．
 
 本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 の責任を負わない．
 
 @(#) $Id: design.txt 178 2019-10-08 13:55:00Z ertl-honda $
----------------------------------------------------------------------
======================================================================
凡例
======================================================================
メモリアクセスの凡例 
  R    : リード
  W    : ライト
  (I) : カーネル初期化時にアクセス
  (E) : カーネル終了時にアクセス

ROMデータ  : 固定値の変数．ROMに配置することを想定．
RAMデータ  : 可変値の変数．RAMに配置することを想定．

======================================================================
目次
======================================================================
・TOPPERS/FMP3カーネルの実装設計方針
・動作アーキテクチャ
・コンフィギュレーション項目
  ・スピンロックアーキテクチャ
  ・タイマアーキテクチャ
  ・メモリアーキテクチャ
・クラス 
  ・概念
  ・クラスの定義
・データ構造
  ・基本方針
  ・プロセッサコントロールブロック（PCB）
  ・プロセッサコントロールブロック（PCB）へのアクセス
  ・タイムイベントコントロールブロック(TEVTCB)
  ・タイムイベントヒープ
  ・タイムイベント処理プロセッサの判断方法
  ・オブジェクトのコントロールブロック
  ・オブジェクトの初期化ブロック
  ・待ち情報ブロック
・RAMデータの配置指定
  ・オブジェクトの管理ブロック
  ・プロセッサ毎の変数
  ・システムサービスが使用するデータ
・ロック単位の設計
・ロック関数
  ・プロセッサ内の排他制御とプロセッサ間の排他制御
・プロセッサ間割込み
・タスクマイグレーション
  ・タスクマイグレーションシステムコール（mig_tsk()）の仕様検討
  ・タスクマイグレーションシステムコール（mig_tsk()）の仕様の拡張案
  ・タスク起動とマイグレーションシステムコール（mact_tsk）の仕様検討
  ・mig_tsk()実装
  ・mact_tsk()実装
  ・その他マイグレーションが発生する箇所
・タイムイベントハンドラのマイグレーション
  ・システムコール一覧
  ・追加データ
  ・msta_cyc(ID cycid, ID prcid)の実装
  ・msta_alm(ID almid, RELTIM almtim, ID prcid)の実装
・ter_tsk()関連
  ・ter_tsk()の制約
・タスク状態不整合問題
・ティックレスタイマのマルチプロセッサ拡張
・アイドル処理で用いるスタック
・make_non_runanble()の変更
・ターゲット依存部
  ・プロセッサIDアクセスのvolatile化
  ・CPUロック解除状態で呼び出す関数
・コード記述上のルール
  ・自プロセッサ固有データへのアクセス
  ・割込み番号のチェックのタイミング
  ・自タスクの指定
  ・API入り口でのエラーチェック
  ・その他のルール

======================================================================
TOPPERS/FMP3カーネルの実装設計方針
======================================================================
TOPPERS/FMP3カーネル（以下，FMP3カーネル）は，TOPPERS/ASP3カーネル（以
下，ASP3カーネル）をマルチプロセッサ拡張したリアルタイムカーネルであ
る．

以下では，FMP3カーネルの実装設計方針について述べるが，仕様設計方針とも
関連しており，明確に分離できない部分もある．

FMP3カーネルの実装設計を行うにあたり，ASP3カーネルで定めた方針に加えて，
次の方針を設定する．

(1) 様々なアーキテクチャのマルチプロセッサシステムのサポート

マルチプロセッサシステムのアーキテクチャは多種多様であり，その上で動作
するOSは，それらのアーキテクチャを有効に用いて動作する必要がある．マル
チプロセッサシステムのアーキテクチャのバリエーション例を以下に示す．

・プロセッサ数
  ・最大4プロセッサ程度

・プロセッサ間の排他制御機構
  ・排他制御命令
  ・専用ハードウェア
    ・同時に作成可能な排他制御区間（ロック）の個数

・メモリ
  ・プライベートメモリ
    ・特定のプロセッサのみアクセス可能なメモリ
  ・ローカルメモリ
    ・全プロセッサからアクセス可能であるが，特定のプロセッサからのアク
      セスが高速なメモリ
  ・グローバルメモリ
    ・全プロセッサから同じ速度でアクセス可能なメモリ

・タイマ
  ・プロセッサ毎の個別のタイマ
  ・システムに単一のタイマ

これらのアーキテクチャに対して効率のよい実装となるため，FMP3カーネルで
は，カーネルの機能や実装を以下のバリエーションの組み合わせ（コンフィギ
ュレーション項目）から選択可能である．

・スピンロックアーキテクチャ
  ・ネイティブ方式
  ・エミュレーション方式

・タイマアーキテクチャ
  ・タイムイベント処理プロセッサの指定

・メモリアーキテクチャ
  ・RAMデータの配置指定

なお，RAMデータと異なり，ROMデータの配置は細かく指定しないことにする．
これは，全てのプロセッサがプライベートメモリにコピーを持つことを許容で
きるからである．また，マルチプロセッサシステムにおいて，RAMデータに対
してキャッシュを有効とするためには，高価なコヒーレントキャッシュが必須
なのに対して，ROMデータのキャッシュは通常のキャッシュでよい．そのため，
多くのターゲットにおいてROMデータのキャッシュを持つことを前提とする．

オブジェクトの初期化ブロックは配列で宣言することとする．一方，管理ブロ
ック(RAMデータ)は，データの配置場所を細かく配置可能とするため，個別の
変数で宣言する．

======================================================================
動作アーキテクチャ
======================================================================
FMP3カーネルは次のアーキテクチャのマルチプロセッサシステムで動作する．

(a)各プロセッサで，プログラムやROMデータに対して，同一アドレスでアク
   セス可能であること．それぞれのプロセッサがアクセスする物理的なメモ
   リが異なっていても，同じ内容を保持していればよい．

(b)全プロセッサから，同一のアドレスでアクセス可能なRAMがあること．
   
(c)全てのプロセッサから，任意のプロセッサに対して割込み（プロセッサ間
   割込み）を発生可能であること

(d)プロセッサ間での排他制御のための機構を持つこと
   例 : test & set 命令， Mutex回路

(e)プロセッサ間の排他制御機構を用いてロックを最低2個作成可能であること

(f)各プロセッサがユニークなIDを持ち，ソフトウェアから自プロセッサを判
   別可能であること．

(g)高分解能タイマの実現のため次の仕様のタイマを持つこと
・システム上の全てのプロセッサからカウント値を読み込み可能なタイマを1
    個持つこと．
・タイムイベント処理プロセッサとするプロセッサにはそれぞれ，操作可能か
     つ，そのプロセッサに割込み発生可能なタイマを1個持つこと．

======================================================================
コンフィギュレーション項目
======================================================================
FMP3カーネルは以下の機能や実装のバリエーション（コンフィギュレーショ
ン）を持つ． 

----------------------------------------------------------------------
スピンロックアーキテクチャ
----------------------------------------------------------------------
スピンロック機能の実現方法．

●ネイティブ方式

ターゲットプロセッサが持つ排他制御機能（CAS,Test&Set命令,LL/SC,Mutex回
路）を直接用いる方式．スピンロックの数だけ，排他制御機能が必要となる．

●エミュレーション方式

他のシステムコールと同様にOS内部のロックを用いて実現する方式．

FMPカーネルと異なり，両者を組み合わせてスピンロック機能を実現する．

----------------------------------------------------------------------
タイマアーキテクチャ
----------------------------------------------------------------------
システム時刻の管理方法．

●タイムイベント処理プロセッサの指定

タイムイベント処理プロセッサとするプロセッサを指定することが可能であ
る．タイムイベント処理プロセッサとしたプロセッサでは，タイムイベントハ
ンドラを実行可能である．タイムイベント処理プロセッサでないプロセッサで
タスクが待ち状態となった場合は，待ち状態はタイムイベントマスタプロセッ
サで管理される．

----------------------------------------------------------------------
メモリアーキテクチャ
----------------------------------------------------------------------
オブジェクトのコントロールブロックやタスクスタック等のRAMデータの配置
先の指定が可能．指定方法は，後述するクラスや，ターゲット依存部のコンフ
ィギュレータテンプレートファイル（target.trb）内で定義する．

======================================================================
クラス
======================================================================
----------------------------------------------------------------------
概念
----------------------------------------------------------------------
タスクの初期割付けプロセッサやOSが用いるRAMデータの配置場所の指定のた
めに，クラスの概念を導入する．

 ・システムには複数のクラスが定義されている．
 ・オブジェクトはいずれかのクラスに属する必要がある．
 ・オブジェクトが属するクラスは静的に指定する(動的に変更できない)．
 ・クラスはID（数値）で識別される．

クラス毎に以下の内容が定義されている．この定義はターゲット依存部のコン
フィギュレータテンプレートファイル（target.trb）内で定義する．

　・初期割付けプロセッサ
　・割付け可能プロセッサ（複数のプロセッサを指定可能，初期割付けプロセッ
    サを含む）
　・各オブジェクトのコントロールブロックの配置場所
　・その他に必要なメモリ領域（タスクのスタック領域やデータキューのデー
    タキュー管理領域など）の配置場所
　・その他の管理情報（ロック単位など）

オブジェクトが属するクラスの指定は，コンフィギュレーションファイルでク
ラスの囲みにオブジェク  トの生成の静的APIを記述することで指定する．

   CLASS(<クラスID>){
      CRE_TSK(TASK1, .....);
      CRE_SEM(SEM1, ....);
   }

----------------------------------------------------------------------
クラスの定義
----------------------------------------------------------------------
クラス数は，ターゲット依存部のコンフィギュレーションテンプレートファイ
ル中で$clsDataの配列で定義する．例を以下に示す．

  $clsData = {
    1 => { clsid: NumStr.new(1, "CLS_ALL_PRC1"),
           initPrc: 1, affinityPrcList: [ 1,2 ] },
    2 => { clsid: NumStr.new(2, "CLS_ALL_PRC2"),
           initPrc: 2, affinityPrcList: [ 1,2 ] },
    3 => { clsid: NumStr.new(3, "CLS_PRC1"),
           initPrc: 1, affinityPrcList: [ 1 ] },
    4 => { clsid: NumStr.new(4, "CLS_PRC2"),
           initPrc: 2, affinityPrcList: [ 2 ] },
  }

クラスの内容は，次の通りである．
clsid           : クラスID
initPrc         : 初期割付けプロセッサ
affinityPrcList : マイグレーション可能なプロセッサのリスト

各オブジェクトのコントロールブロックやその他に必要なメモリ領域の配置場
所は，kernel_cfg.c 生成時のそれぞれのメモリ領域の宣言時に宣言用の関数
が呼び出されるため，そこに記述する．

target_kernel.hで，クラスのID番号をクラス名のマクロで定義する．

======================================================================
データ構造
======================================================================
----------------------------------------------------------------------
基本方針
----------------------------------------------------------------------
ASP3カーネルでグローバル変数として宣言されているカーネル内の変数の多く
は，FMP3カーネルではプロセッサ毎に独立した変数として持つ必要がある．こ
れらの変数は，アクセスオーバヘッドや後述のメモリ配置の制御を考慮して，
プロセッサ毎の構造体としてまとめる．

RAMデータに関しては，メモリアーキテクチャに対して実装の最適化が可能な
ように設計する．具体的には，配列ではなく個々の変数として宣言し，それぞ
れの配置場所を指定可能とする．

----------------------------------------------------------------------
プロセッサコントロールブロック（PCB）
----------------------------------------------------------------------
プロセッサ毎に持つ変数を保持する構造体であり，kerflg, p_runtsk, 
p_schedtsk 等が含まれる．

基本方針から，kernel_cfg.c でプロセッサ毎に異なる変数として生成する．
各プロセッサのPCBをアクセスするため，PCBのポインタの配列を 
kernel_cfg.c に生成する(p_pcb_table[])．
  
    PCB _kernel_pcb_prc1;
    PCB _kernel_pcb_prc2;

    PCB* const p_pcb_table[TNUM_PRCID] = {
      &_kernel_pcb_prc1,
      &_kernel_pcb_prc2
    };
 
マイグレーションのサポートのため，TCB/CYCCB/ALMCBはPCBのポインタ変数を
メンバとして持ち，割り付けられているプロセッサのPCBへのポインタを登録
する．PCBのポインタ変数の内容は，処理単位がマイグレーション時に書き換
えられるため，RAMデータとして持つ．

ターゲット依存でプロセッサ毎に持つデータを構造体 TPCB としてまとめ，
PCB内のメンバーとする．ディスパッチャ内等では，PCBのデータとターゲット
依存でプロセッサ毎に持つデータへのアクセスが同じタイミングで発生する可
能性があるため，PCBのメンバーとして置いた方が，アクセスコストが低くな
る可能性があるためである．

PCBの内容とアクセスは，次のようになっている．

・プロセッサID
  変数
    ID  prcid;
  アクセス
    自プロセッサ : R/W(I)
    他プロセッサ : R
    ロック取得   : 必要なし

   書き込みは，起動時に自プロセッサが初期化のために行う．
   ROMデータとして持つことも可能だが，現状この変数しか該当しないため，
   RAMデータとしている．

・実行状態のタスク
  変数
    TCB  *p_runtsk;
  アクセス
     自プロセッサ : R/W
     他プロセッサ : R
     ロック取得   : 必要なし

・最高優先順位のタスク
  変数
     TCB  *p_schedtsk;
  アクセス
     自プロセッサ : R/W
     他プロセッサ : R/W
     ロック取得   : 必要あり

・ディスパッチ許可状態
  変数
     bool_t enadsp;
  アクセス
     自プロセッサ : R/W
     他プロセッサ :  -
     ロック取得   : 必要なし
  他のデータと同時にアクセスしているため，実質ロックは取得している．
  
・タスクディスパッチ可能状態
  変数
     bool_t dspflg;
  アクセス
     自プロセッサ : R/W
     他プロセッサ : R 
     ロック取得   : 必要なし
         
・実行すべきタスクの更新フラグ
  変数
     bool_t update_schedtsk;
  アクセス
     自プロセッサ : R/W
     他プロセッサ : R/W
     ロック取得   : 必要あり

・レディキュー
  変数
    QUEUE  ready_queue[TNUM_TPRI];
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R/W
    ロック取得   : 必要あり

・レディキューサーチのためのビットマップ
  変数
    uint16_t ready_primap;
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R/W
    ロック取得   : 必要あり
    
・取得しているスピンロック
  変数
    const SPNINIB *p_locspn;
  アクセス
    自プロセッサ : R/W
    他プロセッサ :  - 
    ロック取得   : 必要あり    

・タイムイベントコントロールブロックへのポインタ
  変数
    TEVTCB   *p_tevtcb;
  アクセス
    自プロセッサ : R/W(I)
    他プロセッサ : R
    ロック取得   : 必要なし

   カーネル起動時に初期化．

・ターゲット依存のプロセッサコントロールブロック
  変数
    TPCB  target_pcb;
  アクセス
    ターゲット依存．
    
・カーネル動作状態フラグ
  変数
   bool_t  kerflg;
  アクセス
    自プロセッサ : R/W(I/E)
    他プロセッサ : -
    ロック取得   : 必要なし

   自プロセッサのみがアクセス．OSの起動終了処理で変更．カーネル動作中
   は参照のみ．
   現状のOSの起動・終了の仕様では，グローバルに1つとしても問題ない．そ
   の場合，マスタプロセッサが書き込みを行う．

----------------------------------------------------------------------
プロセッサコントロールブロック（PCB）へのアクセス
----------------------------------------------------------------------
処理単位が実行されているプロセッサのPCBを取得する関数を get_my_pcb()と
して，特定のプロセッサのPCBを取得する関数を get_pcb(prcid) として用意
する．システムコールでは，取得したPCBへのポインタをローカル変数
p_my_pcb へ保存して，この変数を経由してプロセッサ固有のデータへアクセ
スする．これらの関数内でのpcbの取得は，最適化の対象とならないようにす
る．

get_my_pcb() は共通部で，get_my_prcidx()を用いてプロセッサINDEXからPCB
のアドレスを求めるインライン関数として定義される．ターゲット依存の最適
化として，PCBのアドレスを直接保持するレジスタ等を用意して，そのレジス
タをアクセスする方法を可能とする．具体には，ターゲット依存部で 
get_my_pcb() を定義すればよい．

例えば，MPCore依存部の場合は，PCBのアドレスをCP15の Thread ID レジスタ
に設定することが考えられる．Nios II の場合は，cpuid レジスタに同様PCB
のアドレスを設定する方法が考えられる．

----------------------------------------------------------------------
タイムイベントコントロールブロック(TEVTCB)
----------------------------------------------------------------------
タイムイベントに関連したデータ構造(systime_offset, current_time)等が含
まれている構造体．タイムイベント処理プロセッサ毎に生成するため，PCBと
は別の構造体とし，そのポインタをPCBに登録する．

各プロセッサのタイムイベントコントロールブロックにアクセスするため，タ
イムイベントコントロールブロックへのポインタの配列を kernel_cfg.c に生
成する(p_tevtcb_table[])．

   TEVTCB   _kernel_tevtcb_prc1;
   TEVTCB   _kernel_tevtcb_prc2;

   TEVTCB* const _kernel_p_tevtcb_table[TNUM_PRCID] = {
         &_kernel_tevtcb_prc1,
         &_kernel_tevtcb_prc2
   };

ただし，p_tevtcb_table[]はカーネル起動時の初期化で用いるだけあり，カー
ネル起動中はPCB経由でアクセスする．

タイムイベントコントロールブロックの内容とアクセスは，次のようになって
いる．

タイムイベントコントロールブロック

・高分解能タイマ割込みの処理中であることを示すフラグ
  変数
    bool_t in_signal_time
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・タイムイベントヒープへのポインタ
  変数
    TMEVTN *p_tmevt_heap;
  アクセス
    自プロセッサ : R/W(I)
    他プロセッサ : R
    ロック取得   : 必要なし

----------------------------------------------------------------------
タイムイベントヒープ
----------------------------------------------------------------------
タイムイベントヒープには，TEVTCB とは独立して宣言して，そのポインタを 
TEVTCB に登録する．タイムイベントハンドラはマイグレーション可能である
ため，タイムイベントヒープのサイズは，システム中の全てのタイムイベント
ハンドラを登録可能なサイズとする．割付け可能プロセッサの情報を用いて最
適化することは可能である．

タイムイベントヒープのアクセスは，次のようになっている．

・タイムイベントヒープ
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R/W
    ロック取得   : 必要あり

----------------------------------------------------------------------
タイムイベント処理プロセッサの判断方法
----------------------------------------------------------------------
タイムイベント処理プロセッサとノンタイムイベント処理プロセッサの判定方
法．案としては次の2案がある．

・案1)PCB.p_tevtcb をNULLにする．
・案2)PCB.p_tevtcb にタイムマスタプロセッサのタイムイベントコントロールブロックを登録．

案1を採用する．

----------------------------------------------------------------------
オブジェクトのコントロールブロック
----------------------------------------------------------------------
基本方針から，オブジェクト毎に個別の変数とする．ID番号からコントロール
ブロックを取得するために，オブジェクトの種類毎に，各オブジェクトのコン
トロールブロックへのポインタを登録したテーブル（アクセステーブル，ROM
データ）を作成する．

   TCB _kernel_tcb_TASK1_1;
   TCB _kernel_tcb_TASK1_2;

   TCB* const p_tcb_table[TNUM_TSKID] ={
    	&_kernel_tcb_TASK1_1,
    	&_kernel_tcb_TASK1_2,
        ...
   }

----------------------------------------------------------------------
オブジェクトの初期化ブロック
----------------------------------------------------------------------
ROMデータであるオブジェクト初期化ブロックは，ASPカーネルと同様に列配と
して宣言する．また，単体の変数として宣言した場合は，アクセステーブルが
必要となるが，このアクセステーブルが必要となるのは，初期化時のみである
ため，無駄が大きい．

----------------------------------------------------------------------
待ち情報ブロック
----------------------------------------------------------------------
ASPカーネルでは，WINFO（WINFO_WOBJ）やTMEVTB は，タスクスタック上に確
保している．この方法では，タスクが割り付けられたプロセッサのプライベー
トメモリにタスクスタックを配置した場合，他のプロセッサからアクセスする
ことができない．そのため，FMPカーネルでは，待ち情報ブロックをTCB内に確
保することとする．

ASPカーネルでは，オブジェクトがタイムイベント待ちでない場合は，WINFOの
p_tmevtb にNULLを指定していた．待ち情報ブロックをTCB内に確保すると，
NULLを指定できないため，TMEVTBのcallbackにNULLを入れることにより，タイ
ムイベント待ちでないことを指定する．

======================================================================
RAMデータの配置指定
======================================================================
----------------------------------------------------------------------
オブジェクトの管理ブロック
----------------------------------------------------------------------
オブジェクト管理ブロックに関してはクラス単位で指定する．オブジェクトの
種類毎に配置先をさらに細かく配置先を指定するかは，ターゲット依存とする．
配置先はターゲット依存部のコンフィギュレータテンプレートファイル（
target.trb）内で定義する．

----------------------------------------------------------------------
プロセッサ毎の変数
----------------------------------------------------------------------
プロセッサ毎の変数の配置先はターゲット依存部のコンフィギュレータテンプ
レートファイル（target.trb）内で定義する．

オブジェクトの管理ブロックの指定と共に，kernel_cfg.c の生成時に，コン
フィギュレーションテンプレートファイル（kernel.tf）から，RAMデータの宣
言のために関数が呼び出されるため，その関数で配置先を含めた宣言を行う．
詳細は porting.txt を参照のこと．

----------------------------------------------------------------------
システムサービスが使用するデータ
----------------------------------------------------------------------
システムサービス（ログタスク・シリアルドライバ）が使用するデータの
配置場所の指定に対しては，target_syssvc.h で指定する．typeには型，
variableには変数名が渡される．

 ・ログタスク
   ・SEC_PRC1_LOGTASK(type, variable)
   ・SEC_PRC2_LOGTASK(type, variable)
     ...

 ・シリアルインタフェースドライバが使用する変数
   ・SEC_SERIAL1(type, variable)
   ・SEC_SERIAL2(type, variable)
     ..

 ・システムログ機能が使用する変数
   ・SEC_PRC1_SYSLOG(type, variable)
   ・SEC_PRC2_SYSLOG(type, variable)
     ..

======================================================================
ロック単位の設計
======================================================================
OSのコード（主にシステムコール）を実行する際にプロセッサ間の排他的な実
行が必要な箇所を排他制御するためのロックの単位．ロックの単位の粒度が粗
いと取得・解放のオーバヘッドは小さくなるが，実行の並列性が低下する．ロ
ックの単位の粒度が細かいと複数のロックを取得・解放するため，実行オーバ
ヘッドは大きくなるが，プロセッサ間の実行の並列性が向上する．なお，ロッ
クの単位の粒度が細かい場合には，システムコールによっては，デッドロック
回避のために，実行時間が極端に長くなる場合がある．そのため，プロセッサ
数が少ない場合（2個程度）は，ロック単位が粗い方が有利であり，プロセッ
サが多くなるにつれて，粒度の細かいロックの方が有利である．

FMPカーネルでは，次の3種類のロック単位をサポートしていた．

●ジャイアントロック方式
 
システムで1個のロックを持つ．全てのシステムコールは逐次的に実行される．

●プロセッサロック方式

プロセッサ毎にタスクロックとオブジェクトロックを持つ．タスクはいずれか
のプロセッサのタスクロックに関連づけられ，タスク以外のオブジェクトはい
ずれかのオブジェクトロックに関連づけられる．異なるプロセッサのロックに
関連づけられたオブジェクトに対するシステムコールはお互い影響を与えず並
列に実行可能である．

●細粒度ロック方式

プロセッサ毎にタスクロックを，オブジェクト毎にオブジェクトロックを持つ．
タスクはいずれかのプロセッサのタスクロックに関連づけられ，タスク以外の
オブジェクトはそれぞれのオブジェクトロックに関連づけられる．異なるロッ
クに関連づけられたオブジェクトに対するシステムコールはお互い影響を与え
ず並列に実行可能である．

FMP3カーネルで上記のロック方式を実現可能か確認した結果，次の理由により
ジャイアントロック方式以外のロック方式を実現するのは困難であることか
ら，ジャイアンロック方式のみをサポートする．

1.タスク終了機能のサポートによりタスクの終了処理の箇所の増加

プロセッサロック方式，細粒度ロック方式ではタスク終了処理に対して，デッ
トロック回避の対策が必要となる．FMPカーネルでは，ter_tsk()とext_tsk()
が対象となっていた．一方，FMP3カーネルでは，タスク終了機能を新たにサ
ポートしたことから，タスクの終了処理の箇所が増加した．具体的には，以下
の箇所で必要となる．

  ・chg_ipm()
  ・ena_dsp()
  ・ras_ter()
  ・ena_ter()
  ・ter_tsk()
  ・ext_tsk()

2.ミューテックス機能

ミューテックス機能のmutex_release_all()はタスク終了時に呼び出されて，
複数のタスクのロックを取得する必要があるため，デットロック回避が必要と
なる．

上記の全ての箇所にデットロック回避の対策を入れるのは現実的でないため，
ジャイアントロック方式のみのサポートとした．

======================================================================
ロック関数（共通部）
======================================================================
----------------------------------------------------------------------
プロセッサ内の排他制御とプロセッサ間の排他制御
----------------------------------------------------------------------
システムコールの実行には，プロセッサ間での排他制御と，プロセッサ内での
排他制御が必要となる．FMP3カーネルでは，前者は前述のロック（スピンロッ
ク）で後者は割込み禁止で実現する．

この二つの排他制御はお互いに関連しており，リアルタイム性を確保するため
には適切に扱わなければならない．例えば，ロックの取得と割込みの禁止とい
う順に実行すると，ロックの取得と割込みの禁止の間に割込みが入りそれを受
け付ける可能性がある．割込みを受け付けると，ロックを取得しているため，
割込みを受け付けている間は他のプロセッサを待たせてしまいプロセッサ数に
対するスケーラビリティが確保できない．一方，割込みを禁止してロックの取
得を試みると，ロックの取得を試みる間，割込みが禁止となって割込み禁止時
間が長くなってしまう．このようにどちらを先に実行しても問題が発生してし
まう．理想的な実現方法は二つを同時ロックすることであるが，1命令でロッ
クの取得と割込みの禁止を行えるプロセッサは存在しないため，実現は困難で
ある．

FMP3カーネルでは，ジャイアントロックのみをサポートしているため，ター
ゲット依存部の関数において，ロックの試行に失敗した後に一旦割込みを許可
するように実装する．ロック取得の関数として次の関数を用意する．

  acquire_lock()

======================================================================
プロセッサ間割込み
======================================================================
カーネルでプロセッサ間割込みにより他のプロセッサに通知する内容は次の通りである．

・ディスパッチ要求
・高分解能タイマの設定
・カーネル終了要求

依存部で，1から3本プロセッサ間割込みで実現可能なよう，共通部の要求関数
とハンドラを要因毎に用意して，依存部でどれを使うか決める

要求関数
・request_dispatch_prc(ID prcid) : 他コアへのディスパッチ要求
・request_set_hrt_event(ID prcid) : 高分解能タイマの設定
・request_ext_ker(ID prcid)       : カーネル終了要求

ハンドラ
・dispatch_handler()       : ディスパッチハンドラ
・set_hrt_event_handler()  : 高分解能タイマー設定ハンドラ
・ext_ker_handler()        : カーネル終了ハンドラ

======================================================================
タスクマイグレーション
======================================================================
システムコールにより，タスクはプロセッサを移動（タスクマイグレーション）
可能とする．マイグレーション可能なタイミングは要検討である．

タスクマイグレーションの導入により，カーネルのリアルタイム性や実行効率
に大きな影響を与えないことを目標とする．

----------------------------------------------------------------------
タスクマイグレーションシステムコール（mig_tsk()）の仕様検討
----------------------------------------------------------------------
●対象とするタスク

タスクマイグレーションの対象とするタスクにより実現の困難さが異なる．

・実現が容易なケース（○）
  (a)タスクコンテキストから同じプロセッサに割り付けられている他のタス
     ク（実行可能状態，待ち状態，休止状態）に対して．

  (b)タスクコンテキストから自タスクに対して  

  (c)非タスクコンテキストから同じプロセッサに割り付けられている実行   
     可能状態・待ち状態・休止状態のタスクに対して．

  (d)異なるプロセッサに割り付けられている 待ち状態・休止状態 のタスク
     に対して．

・仕様が問題となるケース（△）
  (e)非タスクコンテキストから同じプロセッサに割り付けられている実行   
     状態のタスクに対して．

・実現が困難なケース（×）
  (f)異なるプロセッサに割り付けられている 実行状態・実行可能状態 のタスク
     に対して．

                自プロセッサ(タスク)  自プロセッサ(非タスク)  他プロセッサ
実行状態              (b)○                 (e)△               (f)×
実行可能状態          (a)○                 (c)○               (f)×
待ち状態              (a)○                 (c)○               (d)○
休止状態              (a)○                 (c)○               (d)○


●実現方法の検討（実現が容易なケース）

ロックの取得は省略している．

(a)タスクコンテキストから同じプロセッサに割り付けられている他のタスク
  （実行可能状態，待ち状態，休止状態）に対して．
 ・実行可能状態ならマイグレーション元のレディキューから外す．
 ・TCBの p_pcb を移動先のプロセッサのPCBを示すように設定する．
 ・実行可能状態ならマイグレーション先のレディキューに入れる．
 ・マイグレーション先でディスパッチが必要ならディスパッチリクエストを出す．

(b)タスクコンテキストから自タスクに対して  
 ・マイグレーション元のレディキューから外す．
 ・TCBの p_pcb を移動先のプロセッサのPCBを示すように設定する．
 ・マイグレーション先のレディキューに入れる．
 ・コンテキスト保存
 ・非タスクコンテキストのスタックに切り替え
 ・マイグレーション先でディスパッチが必要ならディスパッチリクエストを出す．
 ・ディスパッチャを呼び出す

(c)非タスクコンテキストから同じプロセッサに割り付けられている実行   
   可能状態・待ち状態・休止状態のタスクに対して．
 ・（a）と同様．

(d)異なるプロセッサに割り付けられている 待ち状態・休止状態 のタスクに
   対して．
 ・TCBの p_pcb を移動先のプロセッサのPCBを示すように設定する．

●実現方法の検討（仕様が問題となるケース）

(e)非タスクコンテキストから同じプロセッサに割り付けられている実行   
   状態のタスクに対して．

 ・割込みハンドラ実行時は，割込み応答性を向上させるため，実行状態のタ
   スクは最小限のコンテキスト（スクラッチレジスタ）のみを保存された状
   態であり，その他のレジスタは割込みハンドラによって，必要に応じて非
   タスクコンテキストのスタックに保存されている（割込みの下敷き）．そ
   のため，マイグレーションは即時には実行できず，割込みの出口でマイグ
   レーションを行わなければならない．

 ・即時にマイグレーションさせる方法としては，割込みの入口でコンテキ  
   ストを全て保存する方法があるが，割込み応答時間が悪化することや，場  
   合によっては，ロックの取得が必要であり最悪時間が押さえられない場合  
   がある．

 ・割込みの出口でマイグレーションを行うためには，ロックの取得が必要に
   なる．ロックアルゴリズムによっては，ロックの取得までの上限時間が定
   まらないため，好ましくない．

 ・対象タスクが実行状態のときのみAPIの実行が遅延してしまう．タスク状態
   によりシステムコールの振る舞いが変わる仕様は許容されるか．

●実現方法の検討（実現が困難なケース）

(f)異なるプロセッサに割り付けられている 実行状態・実行可能状態 のタスク
   に対して．

 ・実行状態のタスクに関しては，対象タスクが実行されているプロセッサが
   CPUロック状態やディスパッチ禁止状態の場合に，FDMPカーネルにおける他
   プロセッサのタスクに対する ter_tsk と同様の過渡的な状態が発生する．

 ・さらに，これらの状態のタスクに対してのマイグレーションは，即時に実
   行されないため，マイグレーションのシステムコールからリターンした時
   点で対象のタスクがマイグレーションしていない場合がある．

 ・実行状態のタスクが割込みの下敷きとなっている場合にも同様に，即時に
   マイグレーションが実行されない．

 ・実行可能状態のタスクに関しては，現状の実装では，ディスパッチャはロ
   ックを取得せずに実行するため，実行可能状態から実行状態への遷移をロ
   ックの取得で防ぐことができない．そのため，実行状態に遷移するタイミ
   ング（ディスパッチ中に）でマイグレーションを行いマイグレーション先
   のプロセッサで実行状態となった場合に，両方のプロセッサでタスクを実
   行してしまう問題が発生する（実行状態のタスクに対してマイグレーショ
   ンを実行することになる）．この問題を解決するためには，ディスパッチ
   ャをタスクロックを取得して実行するようにすればよい．
 
●iter_tsk()が用意されていない理由．

ITRON/TOPPERS仕様では，非タスクコンテキストからタスクを終了する
iter_tsk() が用意されていない．その理由としては，実行状態のタスクに発
行すると，対象タスクがディスパッチ禁止中の振る舞いが問題となるため（過
度的な状態をサポートするか）である．

対象タスクを実行可能・待ち状態のタスクに限定する方法もあるが（実行状態
の場合にエラーとする），タスクが実行状態か実行可能状態であるかは，シス
テムの他のタスクの振る舞いに影響され，対象タスクを確実に終了できないた
め，使いにくいと考えサポートしていない．

●結論

(c)(e)の非タスクコンテキストから自プロセッサのタスクに対してのマイグレ
ーションに関しては，まず，(e)を即時に実行する方針は，マイグレーション
のサポートのために，割込みの応答性を悪化させることは許容させれないため，
採用しない．そのため，(e)の実行は遅延するが，iter_tsk()を用意しないポ
リシーを考慮すると，対象タスクが実行状態か実行可能状態かによってシステ
ムコールの振る舞いが変わるのは好ましくない．さらに，割込みの出口でロッ
クを取得するのも実行時間の上限が定まらない可能性がある．そのため，今回
はサポートしない．

(d)(f)の他のプロセッサのタスクに対してのマイグレーションに関しては，(f)
の実行状態のタスクは困難であるためサポートしない．実行可能状態のタスク
に関しては，ディスパッチャをロックを取得して実行する必要がある．ロック
の取得期間は短い方が好ましいが，ディスパッチャはロックを取得して実行す
るとロック取得期間が長くなってしまう．例えば，ディスパッチを伴う 
act_tsk をARMプロセッサで実行した場合は，1/4の実行時間がディスパッチ時
間である．そのため，マイグレーションのサポートにより，全てのディスパッ
チを伴うシステムコールに影響を及ぼすため，サポートしない．(d)はサポー
ト可能であるが，待ち状態・休止状態のタスクをマイグレーションするケース
はまれであると考えられるため，サポートしない．なお，休止状態のタスクに
対しては，後述する mact_tsk() により起動とマイグレーションをサポートす
る．

●システムコール仕様

・mig_tsk(ID tskid, ID prcid)
  ・tskid で指定したタスクを prcid のタスクにマイグレーションする．
  ・タスクコンテキストから，自タスクもしくは，自タスクと同じプロ
    セッサに割り付けられたタスクを指定可能．
  ・prcidにTPRC_INI（＝0）を指定すると，初期割付けプロセッサを対象とする．

----------------------------------------------------------------------
タスクマイグレーションシステムコール（mig_tsk()）の仕様の拡張案
----------------------------------------------------------------------
仕様の拡張案について考える．

●非タスクコンテキストから同一プロセッサに割り付けられているタスク（実
  行可能・待ち・休止状態）のタスクマイグレーション

実装は特に問題はない．実行状態のタスクを指定した場合にはエラーとする．
前述の仕様検討では，iter_tsk()と同様のポリシーで，対象タスクが実行可能
状態か実行状態かによって，振る舞いが異なるのは使い勝手が悪いという理由
でサポートしなかった．しかしながら，タスクマイグレーション機能を用いて
ロードバランスを実現する場合は，オーバヘッドを考慮して，周期ハンドラ等
の非タスクコンテキストでロードバランスの必要性を判断する．そして，必要
ならマイグレーション用のタスクに対してデータキュー等によりマイグレーシ
ョンの指示を行い，マイグレーション用のタスクでマイグレーションを行う方
法が一般的である．非タスクコンテキストからのマイグレーションがサポート
されれば，マイグレーション用のタスクが不必要になりオーバヘッドが低減可
能である．

ロードバランスの必要性を判断の結果，実行状態のタスクがマイグレーション
の対象となることはまれであることや（実行状態ということは既にプロセッサ
が割り当てられているためマイグレーションする必要性がない），失敗したと
しても，他のタスクを指定することが可能であることや，次回のロードバラン
スのタイミングでマイグレーションすればよいという考え方もある．そのため，
実行状態ならエラーとなるスペックであっても，有効性が高いと言える．


●他プロセッサのタスク（実行可能・待ち・休止状態）のタスクマイグレーシ
  ョン(ToDo)

前述のように，実行状態のタスクに対してのマイグレーションはエラーとする
ことを許容できるとして，他プロセッサの実行可能・待ち・休止状態のタスク
のタスクマイグレーションの実現方法を検討する．

待ち・休止状態のタスクのタスクマイグレーションは特に問題はない．実行可
能状態のタスクのタスクマイグレーションは，ディスパッチャをロックを取得
して実行することで実現可能である．

ディスパッチャをロックを取得して実行することが可能か検討する．ディスパ
ッチャが呼び出されるタイミングは次の通りである．

  ・カーネル起動時
  ・システムコール
  ・割込みの出口（遅延ディスパッチ）

システムコールは既にタスクロックを取得しているため問題ない．カーネル起
動時も特に問題はない．割込みの出口（遅延ディスパッチ）は，reqflgをチェ
ックした後にタスクロックを取得する必要があるが，この時点ではスタックは
割込み前のタスクのスタックとなっているため，割込み応答性の上限を定める
ためのロック取得を行うためには，非タスクコンテキストのスタックに変更し
て，非タスクコンテキストとしてタスクロックの取得を行う必要がある．ユー
ザー割込みハンドラ終了時にタスクロックを取得する方法もあるが，割込みハ
ンドラの平均実行時間が悪化し，ロック取得の上限が定まらないロックアルゴ
リズムを用いている場合は，割込みハンドラの実行時間の上限が定まらなくな
るため，避けた方がよい．

次にロックの解放が必要なディスパッチャの出口は，次の通りである．

  ・start_r
  ・dispatch_r
  ・ret_int_r

以上より，実現は可能であるが，ソースコードの変更量が大きく，ディスパッ
チを伴うシステムコール全てを変更する必要がある．また，割込みの実行時間
へも影響を与える．

●非タスクコンテキストから同一プロセッサに割り付けられているタスク（実
  行状態）のタスクマイグレーション(ToDo)

実行が遅延することを許容する．

TCBにmigflgを追加．実行状態のタスクの場合はmigflgに移動先のプロセッサ
のプロセッサIDを入れ，reqflg を trueにする．          

割込みハンドラの出口でタスクマイグレーションを行う．

 ・dspflgのチェック，terflgをチェック終了後，タスクコンテキストを保存．
 ・非タスクコンテキストのスタックに切り替え．
 ・migrate_task()
   ・ロックの取得
   ・自プロセッサのレディキューから出す
   ・移動先のプロセッサのレディキューに入れる
   ・プロセッサ間割込みの発生
   ・ロックの解放
   ・exit_and_dispatch()

●他プロセッサのタスク（実行状態）のタスクマイグレーション(ToDo)

実行が遅延することを許容する．

TCBにmigflgを追加．実行状態のタスクの場合はmigflgに移動先のプロセッサ
のプロセッサIDを入れ，対象タスクが動作しているプロセッサに対してプロセ
ッサ間割込みを入れる．

----------------------------------------------------------------------
タスク起動とマイグレーションシステムコール（mact_tsk）の仕様検討
----------------------------------------------------------------------
mig_tsk()の仕様をタスクコンテキストから自タスクと同一のプロセッサに割
り付けられているタスクに限定したため，他プロセッサに割り付けられたタス
クのマイグレーション方法として，休止状態のタスクに対して起動とタスクマ
イグレーションを行う（mact_tsk()）を用意する．制御系のシステムの場合は，
周期やイベントにより，タスクを起動するシステムが多いため，そのタイミン
グで起動するプロセッサを指定するのは，有用であると考えられる．一方，タ
スクの起床とマイグレーションに関しては，待ち解除を行うシステムコールは
多いため，サポートしない．

●mact_tsk()の仕様案1（見送り）

 ・[i]act_tsk(ID tskid)
   ・タスクを割付けプロセッサで起動する．
   ・キューイングした場合は，タスク終了後，終了時に割り付けらている
     プロセッサでタスクを起動する．

 ・[i]mact_tsk(ID tskid, ID prcid)
   ・プロセッサを指定してタスクを起動する．
   ・休止状態のタスクにのみ発行可能である．そのため，キューイングは発
     生しない
   ・prcidにTPRC_INI（＝0）を指定すると，初期割付けプロセッサで起動する．

 ・ter_tsk(ID tskid)
   ・ASPカーネルから変更なし．

休止状態（実行継続中）のタスクに対してmact_tsk()した場合の振る舞いが問
題となる．休止状態（実行継続中）のタスクに対して，mact_tsk()を実行した
結果，そのタスクが移動先のプロセッサで最高優先度のタスクになった場合，
ディスパッチ処理が開始される．この際，対象タスクが，休止状態 （実行継
続中）であると，二つのプロセッサでタスクが動作することになってしまい，
タスクスタックを破壊してしまう．mact_tsk()対象のタスクが，休止状態か休
止状態 （実行継続中）のどちらかの判断は他のプロセッサからはできない．
（terflg とタスクが実行されているプロセッサの p_runtsk がそのタスクを
示している場合であっても，すでにディスパッチャが動作している場合がある
ので，判断には使用できない）．

また，キューイングは発生しないとしたのは，キューイングすると，
ter_tsk()内でタスクマイグレーションの必要が発生するため，他のプロセッ
サに割り付けられたタスクに対してタスクマイグレーションを行うことになる．
他のプロセッサに割り付けられたタスクのタスクマイグレーションは，タスク
マイグレーションのシステムコールで議論した通り困難であるため，キューイ
ングはしないとした．

●mact_tsk()の仕様案2（採用）

 ・[i]act_tsk(ID tskid)
   ・タスクを割付けプロセッサで起動する．
   ・キューイングした場合は，タスク終了後，終了時に割り付けられてい
     るプロセッサでタスクを起動する．

 ・[i]mact_tsk(ID tskid, ID prcid)
   imact_tsk(ID tskid, ID prcid)
   ・プロセッサを指定してタスクを起動する．
   ◎キューイングした場合は，タスク終了後，指定したプロセッサでタスク
     を実行する．
   ・prcidにTPRC_INI（＝0）を指定すると，初期割付けプロセッサで起動する．

  ・ter_tsk(ID tskid)
   ◎自タスクと同じプロセッサに割り付けられたタスクのみを指定可能．
   ◎実行時間の上限が定まらない（内部でリトライする）

ter_tsk()を自タスクと同じプロセッサに割り付けられたタスクのみを指定可
能とすることで，タスクは過渡的な状態とならないため，仕様案1の問題は発
生しない．

キューイングされた起動処理を行うのは，ext_tsk() と ter_tsk() である．
ext_tsk()に関しては，自タスクに対するマイグレーションと同等の処理を行
えば問題ない．

ter_tsk()に関しては，ter_tsk()を自タスクと同じプロセッサに割り付けられ
たタスクのみを指定可能とすることで，仕様案1で発生する他プロセッサから
のタスクマイグレーションを避けることができる．

mact_tsk()のサポートのためだけに，ter_tsk()に制約を加えるのは互換性の
点では避けた方がよい．しかしながら，ter_tsk()に制約を設けることで，タ
スクの状態遷移の拡張が必要なくなることや，ディスパッチャでロックを取得
する必要がなくなるメリットがある（詳細はter_tsk()の制約の節を参照のこ
と）．そのため，ter_tsk()に制約を加えることとした．

また，ter_tsk()におけるタスクマイグレーションの実現のため，ter_tsk()は
実行時間の上限は定まらない実装とする．詳細については"ter_tsk()のリトラ
イの許容"の節を参照のこと．
   
----------------------------------------------------------------------
mig_tsk()実装
----------------------------------------------------------------------
タスクコントロールブロック（TINIB)への追加
  ID     iprcid   : タスクの初期割付けプロセッサ
  uint_t affinity : タスクの割付け可能プロセッサ 

ER
mig_tsk(ID tskid, ID prcid)
{
  ...
  割込み禁止
  ロック取得
  if (異なるプロセッサに割り付けられているタスク) {
      エラー
  }
  else if (対象タスクが自タスクで，ディスパッチ保留状態の場合) {
      エラー
  else if (実行可能状態) {
       マイグレーション元のプロセッサのレディキューから外す．
       TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
              
      if (自タスクに発行) {
            dispatch_and_migrate()によりマイグレーション．
            移動先のプロセッサでリターンする．
      }
      else {
        マイグレーション先のプロセッサのレディキューに入れる．
      }
  }
  else if (休止状態&強制待ち状態&タイムアウトのない待ち状態) {
     TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
  }
  else {
     /* 時間待ちの場合 */
    マイグレーション元のプロセッサのタイムイベントヒープから削除
    移動先のプロセッサの方が時間が進んでいればタイムアウト時間を延長
    マイグレーション先のプロセッサのタイムイベントヒープに挿入
    TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
  }
  ロック解放
  割込み許可
}

ターゲット依存部でアセンブラにより記述する．

void
dispatch_and_migrate(PCB* p_my_pcb, TCB* p_selftsk, PCB* p_new_pcb)
{
	スクラッチレジスタを除くすべてのレジスタをスタックに保存する
	スタックポインタを自タスク（自プロセッサのPCBのp_runtsk）のTCBに保存する
	dispatch_rを，実行再開番地として自タスクのTCBに保存する 
	スタックを非タスクコンテキスト用のスタックに切り替える
	migrate_self(prcid)
}

migrate_self()は共通部の関数である．

void
migrate_self(PCB *p_my_pcb, TCB *p_selftsk, PCB *p_new_pcb)
{
   マイグレーション元のプロセッサのレディキューから外す．
   TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
   マイグレーション先のプロセッサのレディキューに入れる．
   if (マイグレーション先で最高優先順位) {
      ディスパッチリクエスト
   }    
   マイグレーション元と先のプロセッサのタスクロックを解放．
}

----------------------------------------------------------------------
mact_tsk()実装
----------------------------------------------------------------------
タスクコントロールブロック（TCB）への追加．

  ID actprc : 次回の起動先のプロセッサ
              (0で現在割付けのプロセッサ．act_tsk()がキューイングした
              場合に設定される)
              
ER
mact_tsk(ID tskid, ID prcid)
{
   ...
   割込みの禁止
   ロックの取得
   マイグレーション元と先のプロセッサのタスクロックを取得．
   if (休止状態) {
     TCBの p_pcb をマイグレーション先のプロセッサのPCBへ変更．
     実行可能状態へ（make_active）
     if (自タスクのディスパッチが必要な場合) {
       if (タスクコンテキストの場合) {
         ロック解放         
         ディスパッチ
       }
       else {
        ディスパッチリクエスト       
       }
     }
   }
   else if (起動要求数が超えていたら) {
      E_QOVERエラー
   }   
   else {
      TCBの actque を trueに．
      マイグレーション要求をキューイング(actprcにプロセッサIDを指定).
   }

   ロックの解放
   割込みの許可
}

----------------------------------------------------------------------
その他マイグレーションが発生する箇所
----------------------------------------------------------------------
mig_tsk()/mact_tsk()以外のマイグレーションが発生する箇所としては，
mac_tsk()による起動要求がキューイングしている場合に，以下の箇所で起動
時マイグレーションが発生する．

ext_tsk() : 自タスクの終了処理
chg_ipm() : タスク終了要求フラグの処理（自タスクの終了処理）
ena_dsp() : タスク終了要求フラグの処理（自タスクの終了処理）
ena_ter() : タスク終了要求フラグの処理（自タスクの終了処理）
ter_tsk() : タスク終了要求フラグの処理（同一コアの他タスクの終了処理）
ras_ter() : タスク終了要求フラグの処理（同一コアの他タスクの終了処理）

全ての処理で対象タスクに対して，task_terminate()を呼び出す．
task_terminate()では，actprc をチェックして，0以外ならタスクマイグレー
ション要求がキューイングされているため，TCB中のp_pcbをマイグレーション
先のプロセッサに書き換える．更に 他タスクが対象，または自タスクが対象
で同一プロセッサへの再起動処理の場合にはmake_active()を呼び出して再起
動処理を行う．

自タスクの終了処理では，task_terminate()呼び出し後に，TCB中のp_pcbが自
プロセッサから変更されているかチェックして，変更されていれば，ロックを
取得したまま，exit_and_migrate()を呼び出す．

exit_and_migrate()はターゲット依存部で定義され，アセンブラで記述するこ
とを想定する．

void
exit_and_migrate(PCB *p_my_pcb, TCB *p_selftsk)
{
	スタックを非タスクコンテキスト用のスタックに切り替える
	migrate_activate_self(p_my_pcb, p_selftsk)
}

migrate_activate_self()は共通部の関数である．

void
migrate_activate_self(PCB *p_my_pcb, TCB *p_selftsk)
{
	タスクを実行可能状態に．
	ロックを解放
	exit_and_dispatch() を呼び出す．
}

======================================================================
タイムイベントハンドラのマイグレーション
======================================================================
----------------------------------------------------------------------
システムコール一覧
----------------------------------------------------------------------
  ER msta_cyc(ID cycid, ID prcid)
   ・割付けプロセッサ指定での周期通知の動作開始
  ER msta_alm(ID almid, RELTIM almtim, ID prcid)
   ・割付けプロセッサ指定でのアラーム通知の動作開始

----------------------------------------------------------------------
追加データ
----------------------------------------------------------------------
CYCINIB
  ID      iprcid;    /* 通知ハンドラの初期割付けプロセッサ */
  uint_t  affinity;  /* 通知ハンドラの割付け可能プロセッサ */

ALMINIB
  ID      iprcid;    /* 通知ハンドラの初期割付けプロセッサ */
  uint_t  affinity;  /* 通知ハンドラの割付け可能プロセッサ */

----------------------------------------------------------------------
msta_cyc(ID cycid, ID prcid)の実装
----------------------------------------------------------------------
ER
msta_cyc(ID cycid, ID prcid)
{
   ...
   割込みの禁止
   ロックの取得
   if (動作していれば) {
      タイムイベントヒープから外す
   }
   else {
      動作状態とする
   }
   周期通知コントロールブロックの p_pcb に移動先のプロセッサのPCBを登録
   移動先のプロセッサのタイムイベントヒープに挿入
   ロックの解放
   割込みの許可
}

----------------------------------------------------------------------
msta_alm(ID almid, RELTIM almtim, ID prcid)の実装
----------------------------------------------------------------------
ER
msta_alm(ID almid, RELTIM almtim, ID prcid)
{
   ...  
   割込みの禁止
   ロックの取得
   if (動作していれば) {
      タイムイベントヒープから外す
   }
   else {
      動作状態とする
   }
   アラームコントロールブロックの p_pcb に移動先のプロセッサのPCBを登録
   移動先のプロセッサのタイムイベントヒープに挿入
   ロックの解放．
} 

======================================================================
ter_tsk()関連
======================================================================
----------------------------------------------------------------------
ter_tsk()の制約
----------------------------------------------------------------------
FMP3カーネルでは，FMPカーネルと同様にter_tsk()はAPIを呼び出したタスク
と同じプロセッサに割り付けられたタスクのみを対象とするよう制限を設ける．

FDMPカーネルでは，この様な制限は設けていないが，その代わり，タスクの状
態遷移が複雑化していた（FDMPカーネル仕様書を参照のこと）．

FDMPカーネルで導入していた，複雑な状態遷移は，ユーザーから見た場合，利
用の足かせとなる．通常のタスク終了は，他のプロセッサに割り付けられてい
るタスクに対しても発行できるタスク例外を使用するとして，ter_tsk()はあ
くまでもタスクを停止させる場合の最終手段と捉えると，制約を設けてサーバ
ータスク経由で発行しても問題がないと考える．

また，ter_tsk()に制限を設けない場合には，ディスパッチャや割込みの出口
でディスパッチが必要になった場合にタスクロックの取得が必要となるデメリ
ットもある．

なお，ras_ter()も同様の扱いとする．

======================================================================
タスク状態不整合問題
======================================================================
CPUロック状態/ディスパッチ禁止状態で実行中のタスクに対してsus_tskが発
行された場合の振る舞い(セマンティクス)を決定する．ter_tsk()に関しては，
FMP3カーネルでは，他プロセッサのタスクに対するter_tsk()を禁止したため，
該当しない．

 sus_tsk(ID tskid)の仕様
   CPUロック/ディスパッチ禁止中のタスクに対してsus_tsk()を発行すると，
   タスクは一時的に過渡的な状態(run-suspend)になる．

- CPUロック状態の実行状態のタスクに対してsus_tsk()を発行
   - 特に対策の必要はなし
   - sus_tsk()の実行後，プロセッサ間割込みを発生する
   - CPUロック中はAPIが発行できない．
   - CPUロックを解除した時点で割込みを受け付ける
   - タスクの状態はsuspendに移行しているため，rsm_tsk()は正しく
     動作する．

- ディスパッチ禁止状態の実行状態のタスクに対して発行
   - 特に対策の必要はなし
   - sus_tsk()実行後，プロセッサ間割込みを発生する
   - 割込みは許可されているので，割込みルーチンに入るが，
      ディスパッチ禁止中のため，何もせずにリターン．
   - ena_dsp()が実行されると，ディスパッチを行う．ena_dsp()の
     中でタスクロックをとるため，sus_tsk()とのレーシングは発生
     しない．
   - タスクの状態はsuspendに移行しているため，rsm_tsk()は正しく
      動作する．

- 過渡状態（run-suspend）のタスクからのAPIの発行
   - ディスパッチ禁止状態では，自タスクを待ち状態にするAPIは発行できな
     い．CPUロック状態ではほとんどのAPIを実行できない．いいかえると，
     自分の状態は変更できない．そのため，API発行は問題ない．また，
     ext_tsk()は原則エラーだが，TOPPERSではワーニングにして処理．

runanbleチェックについて

タスクを休止状態とするAPIはディスパッチ禁止状態で呼び出すことが可能で
あるため，FMPカーネルにおいては，過渡状態から休止状態の遷移は，タスク
の状態遷移を拡張してサポートしていた．一方，自タスクの待ち状態の遷移に
関して，ロック取得後に自タスクが実行状態であるかのチェックをするための
ロック取得関数を用意していた．このロック取得関数はロック取得後自タスク
が実行状態でなければ，ロックを一旦解放して割込みを許可する（runanbleチ
ェックロック）．runanbleチェックロックは以下のように無限ループとなって
おり，何らかの不具合でプロセッサ割込みが入らない場合は，無限ループとる
可能性がある．

-------------------------------
runanbleチェック有りのロック取得関数

Inline void
acquire_glock_runnable_check(void)
{
	PCB *p_my_pcb;

	while(true) {
		acquire_lock(&giant_lock);
		p_my_pcb = get_p_my_pcb();
		if (!TSTAT_RUNNABLE(p_my_pcb->p_runtsk->tstat)) { 
			/* RUNNABLEでない場合 */
			release_lock(&giant_lock);
			unlock_cpu();
			lock_cpu();
		} else {
			break;
		}
	}
}
-------------------------------

一方，休止状態と同様に強制待ち状態から二重待ち状態への遷移を許容すれば，
runanbleチェックロックは必要なくなる．ユーザーから見れば，両者のAPIの
順序が逆に発行されたと見えるだけで大きな問題はない．そのため，チェック
は行わず，runanbleチェックロックは使用しない様にする．

この変更により，待ち状態に遷移するAPIでは，強制待ち状態で実行される可
能性がある．そのため，ASP3からコードを変更する必要がある．

具体的には，APIの層ではtstatを直接変更せず，
make_wait()/wobj_make_wait() で強制待ち状態であるかチェックして，強制
待ち状態であれば，tstatにORで待ち状態をセットする．


-------
Inline void
make_wait(TCB *p_selftsk, PCB *p_my_pcb)
{
	make_non_runnable(p_selftsk, p_my_pcb);
	(p_selftsk->tmevtb).callback = NULL;
}
->
Inline void
make_wait(uint_t tstat, TCB *p_selftsk, PCB *p_my_pcb)
{
	if (!TSTAT_SUSPENDED(p_runtsk->tstat) {
		p_runtsk->tstat = tstat;
		make_non_runnable(p_selftsk, p_my_pcb);
	}
	else {
		/* 過渡状態で呼び出された場合 */
		p_runtsk->tstat |= tstat;
	}
	(p_selftsk->tmevtb).callback = NULL;
}
-------
void
make_wait_tmout(TMO tmout, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_non_runnable(p_selftsk, p_my_pcb);
	if (tmout == TMO_FEVR) {
		(p_selftsk->tmevtb).callback = NULL;
	}
	else {
		assert(tmout <= TMAX_RELTIM);
		p_selftsk->tmevtb.callback = (CBACK) wait_tmout;
		p_selftsk->tmevtb.arg = (void *) p_selftsk;
		tmevtb_enqueue(&(p_selftsk->tmevtb), (RELTIM) tmout,
					   (p_my_pcb->p_tevtcb == NULL)? P_TM_PCB : p_my_pcb);
	}
}
->
void
make_wait_tmout(TMO tmout, uint_t tstat, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_wait(p_selftsk, tstat, p_my_pcb)
	if (tmout == TMO_FEVR) {
		(p_selftsk->tmevtb).callback = NULL;
	}
	else {
		assert(tmout <= TMAX_RELTIM);
		p_selftsk->tmevtb.callback = (CBACK) wait_tmout;
		p_selftsk->tmevtb.arg = (void *) p_selftsk;
		tmevtb_enqueue(&(p_selftsk->tmevtb), (RELTIM) tmout,
					   (p_my_pcb->p_tevtcb == NULL)? P_TM_PCB : p_my_pcb);
	}
}
-------

void
wobj_make_wait(WOBJCB *p_wobjcb, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_wait(p_selftsk, p_my_pcb);
	wobj_queue_insert(p_wobjcb, p_selftsk);
	p_selftsk->p_wobjcb = p_wobjcb;
	LOG_TSKSTAT(p_selftsk);
}
->
void
wobj_make_wait(WOBJCB *p_wobjcb, uint_t tstat, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_wait(p_selftsk, tstat, p_my_pcb);
	wobj_queue_insert(p_wobjcb, p_selftsk);
	p_selftsk->p_wobjcb = p_wobjcb;
	LOG_TSKSTAT(p_selftsk);
}
-------
void
wobj_make_wait_tmout(WOBJCB *p_wobjcb, TMO tmout, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_wait_tmout(tmout, p_selftsk, p_my_pcb);
	wobj_queue_insert(p_wobjcb, p_selftsk);
	p_selftsk->p_wobjcb = p_wobjcb;
	LOG_TSKSTAT(p_selftsk);
}
->
void
wobj_make_wait_tmout(WOBJCB *p_wobjcb, TMO tmout, uint_t tstat, TCB *p_selftsk, PCB *p_my_pcb)
{
	make_wait_tmout(tmout, tstat, p_selftsk, p_my_pcb);
	wobj_queue_insert(p_wobjcb, p_selftsk);
	p_selftsk->p_wobjcb = p_wobjcb;
	LOG_TSKSTAT(p_selftsk);
}
-------

・対象API
 ・slp_tsk/tslp_tsk
 ・dly_tsk
 ・wai_sem/twai_sem
 ・wai_flg/twai_flg
 ・snd_dtq/tsnd_dtq/rcv_dtq/trcv_dtq
 ・snd_pdq/tsnd_pdq/rcv_pdq/trcv_pdq
 ・loc_mtx/tloc_mtx
 ・get_mpf/tget_mpf
 
======================================================================
ティックレスタイマのマルチプロセッサ対応
======================================================================
ティックタイマとの違い

ローカルタイマ方式の場合，他のコアのタイムイベントキューに対して，挿入・
削除する場合，ティックタイマでは，タイムイベントキューに対する操作のみ
を行えばよい．ティックレスタイマの場合は，更に高分解能タイマの操作が必
要になる．

高分解能タイマハードウェアに関する操作は次の通りである．

タイムイベントが発生するまでの時間の計算(tmevt_lefttim())

呼び出し元API : mig_tsk()/ref_tsk()/ref_alm()/ref_cyc()
高分解能タイマへの操作 
・update_current_evttim() 
  -> target_hrt_get_current() 

タイムイベントの登録解除(tmevtb_dequeue())

呼び出し元API : sta_alm()/msta_alm()/stp_alm()/sta_cyc()/msta_cyc()/stp_cyc()/mig_tsk()
呼び出し元関数 : wait_dequeue_tmevtb() : 各APIの待ち解除を経由して呼び出される
・update_current_evttim()
  -> target_hrt_get_current() 
・set_hrt_event()
  -> target_hrt_set_event()   
  -> target_hrt_raise_event() 

相対時間指定によるタイムイベントの登録(tmevtb_enqueue())

呼び出し元API : sta_alm()/msta_alm()/sta_cyc()/msta_cyc()/mig_tsk()/dly_tsk()
呼び出し元関数 : make_wait_tmout() -> 各APIの待ち状態
・update_current_evttim()
  -> target_hrt_get_current() 
・set_hrt_event()
  -> target_hrt_set_event()   
  -> target_hrt_raise_event() 

タイムイベントの登録(tmevtb_register) 

呼び出し元関数
・initialize_cyclic()
・call_cyclic()
タイムイベント処理プロセッサでのみ呼び出させるので検討の必要なし．

以上より，高分解能タイマの操作関数は次の通りとなっている．

 ・target_hrt_get_current()  : 高分解能タイマのカウント値の読み込み
 ・target_hrt_set_event()    : 高分解能タイマの設定
 ・target_hrt_raise_event()  : 高分解能タイマの割込みの発生

ハードウェア要件として，高分解能タイマのカウント値は全てのプロセッサか
ら呼び出し可能としたため，target_hrt_get_current()は全てのプロセッサか
ら呼び出し可能である．

set_hrt_event() に関しては，target_hrt_set_event() と
target_hrt_raise_event() を呼び出すため，タイムマスタプロセッサ以外か
ら高分解能タイマを設定可能なプロセッサと設定不可能なプロセッサでコード
を分ける．

set_hrt_event() の引数には，対象のタイムイベント処理プロセッサのPCBへ
のポインタを指定する．

設定不可能なプロセッサにおける set_hrt_event() は次のようになる．

if (自プロセッサに対する要求（p_pcb == p_my_pcb）か？) {
  自プロセッサの高分解能タイマの設定(set_my_hrt_event(p_my_pcb));
}
else {
  高分解能タイマの設定要求（プロセッサ間割込み）を発生．
}

また，プロセッサ間割込みで実行されるハンドラ（高分解能タイマ設定ハンド
ラ）を用意する．このハンドラ(set_hrt_event_handler())では，タイムイベ
ント処理プロセッサ用のイベントセット関数（set_my_hrt_event()）を呼び出
す．

======================================================================
アイドル処理で用いるスタック
======================================================================
ASP3カーネルでは，アイドル処理では「最後に実行状態であったタスク」のス
タックをアイドル処理で使用することにしている．さらに，カーネルの動作開
始時は，「最後に実行状態であったタスク」が存在しないため，IDが1のタス
クのスタックを使用している．一方，FMP3カーネルでは，「最後に実行状態で
あったタスク」が他のコアで動作している可能性があることや，起動時はタス
クが割り付けられていないプロセッサが存在する可能性がある．

そのため，FMP3カーネルでは，アイドル処理用のスタックを用意して用いるこ
ととする．

======================================================================
make_non_runanble()の変更
======================================================================
ASP3カーネルのmake_non_runanble()では，対象タスクが実行されているプロ
セッサがディスパッチ禁止状態で，sus_tskを呼び出すとmake_non_runnable()
のassert(dspflg)がエラーとなる．ASP3カーネルの場合は，ディスパッチ禁止
状態でassert()が実行されることがないため問題ない．

FMP3カーネルでは，dspflgをチェックしてfalseならp_schedtskを更新しない
ようにする．

======================================================================
ターゲット依存部
======================================================================
----------------------------------------------------------------------
プロセッサIDアクセスのvolatile化
----------------------------------------------------------------------
マイグレーションを考慮すると，ロック取得前に参照した自タスクが割り付け
られたプロセッサのプロセッサIDはロック取得後に再度参照する必要がある．
そのため，プロセッサIDの参照は最適化の対象としない（volatile） ．

----------------------------------------------------------------------
CPUロック解除状態で呼び出す関数
----------------------------------------------------------------------
以下に示す内部関数は，CPUロック解除状態で呼び出すため，自プロセッサの
固有データをアクセスするものに関しては，実行中にマイグレーションされて
も問題ないように，CPUロック状態として固有データをアクセスすること．

・void lock_cpu(void)
・bool_t VALID_INTPRI_CHGIPM(PRI intpri)


======================================================================
コード記述のチェックポイント
======================================================================
----------------------------------------------------------------------
自プロセッサ固有データへのアクセス
----------------------------------------------------------------------
自プロセッサの固有データへのアクセスする場合は，必ず割込みを禁止してマ
イグレーションを抑制して行うこと．

PCB等の自プロセッサの固有のデータへアクセスする場合は，CPUID等のプロ
セッサを識別するレジスタにアクセスしてから，その情報を基にアクセスする
べきデータを決定する．割込みが許可された状態でこの方法でプロセッサ固有
のデータへアクセスすると，誤ったプロセッサの固有データへアクセスする問
題が発生する可能性がある．具体的には，プロセッサを識別するレジスタにア
クセスした直後に割込みが入り，その先でマイグレーションされた場合，マイ
グレーション前に割り付けられていたプロセッサの固有データにアクセスして
しまう．

----------------------------------------------------------------------
割込み番号のチェックのタイミング
----------------------------------------------------------------------
ASP3カーネルでは，以下の割込み番号のチェックマクロは，CPUロック解除状
態で呼び出されている．

VALID_INTNO_DISINT
VALID_INTNO_CLRINT
VALID_INTNO_RASINT
VALID_INTNO_PRBINT
VALID_INTNO_CREISR

FMP3カーネルでは，コア毎に操作可能な割込み番号が異なる場合がある．その
ため，これらのマクロの実行後にマイグレーションが発生すると，マイグレー
ション後，誤った番号に対して操作を行う可能性がある．

そのため，CPUロック状態とした後に，割込み番号のチェックを行うようにす
る．

----------------------------------------------------------------------
自タスクの指定
----------------------------------------------------------------------
コード中のp_runtskの意味には，自タスク(APIを呼び出したタスク)と実行中
のタスクの両方の意味がある．ASP3カーネルでは両者は一致しているが，FMP3
カーネルでは異なるため，次のように使い分ける.

  ・自タスク     : p_selftsk
  ・実行中タスク  : p_runtsk

自タスク(p_selftsk)となる箇所は次の通りである．

チェックマクロを使用する箇所
・CHECK_TSKCTX_UNL_MYSTATE(TCB **pp_selftsk, bool_t *p_context)
・CHECK_UNL_MYSTATE(TCB **pp_selftsk)  

その他のAPI
・ena_dsp()/slp_tsk()/tslp_tsk()/dly_tsk()/ext_tsk()/ena_ter()/
  snd_dtq()/tsnd_dtq()/rcv_dtq()/trcv_dtq()/wai_flg()/twai_flg()/
  chg_ipm()/get_mpf()/tget_mpf()/loc_mtx()/ploc_mtx()/tloc_mtx()/
  unl_mtx()/snd_pdq()/tsnd_pdq()/rcv_pdq()/trcv_pdq()/wai_sem()/
  twai_sem()

----------------------------------------------------------------------
API入り口でのエラーチェック
----------------------------------------------------------------------
APIの入り口では静的エラーチェックをCPUロック解除状態で実施する．ASP3の
act_tskの例を次に示す．

------------------------------------------------------------
・ASP3カーネルのact_tsk()の静的APIチェック
	LOG_ACT_TSK_ENTER(tskid);
	CHECK_UNL();								/*［NGKI1114］*/
	if (tskid == TSK_SELF && !sense_context()) {
		p_tcb = p_runtsk;						/*［NGKI1121］*/
	}
	else {
		CHECK_ID(VALID_TSKID(tskid));			/*［NGKI1115］*/
		p_tcb = get_tcb(tskid);
	}

・ASP3カーネルのCHECK_UNL
#define CHECK_UNL() do {									\
	if (sense_lock()) {										\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
} while (false)
------------------------------------------------------------

この際，以下の依存部の関数は，タスクマイグレーション対応として，これら
の関数はCPUロック状態で呼び出し・アクセスする必要がある（自プロセッサ
のPCBにアクセスする）．

・sense_context()
・sense_lock()
・dspflg

ASP3と可能な限りコードを共有するため，APIからは上記の関数を直接は呼び
出さず，チェックと共に自プロセッサの状態を取得する関数を用意して呼び出
す様にする．

依存部で，check_unl_mystate()を効率的に実装できる場合は，
OMIT_CHECK_UNL_MYSTATE を定義してから，独自に実装すればよい．

------------------------------------------------------------
・FMP3カーネルのact_tsk()の静的APIチェック
	LOG_ACT_TSK_ENTER(tskid);
	CHECK_UNL_MYSTATE(&p_selftsk, &context);	/*［NGKI1114］*/
	if (tskid == TSK_SELF && !context) {
		p_tcb = p_selftsk;						/*［NGKI1121］*/
	}
	else {
		CHECK_ID(VALID_TSKID(tskid));			/*［NGKI1115］*/
		p_tcb = get_tcb(tskid);
	}

・FMP3カーネルのOMIT_CHECK_UNL_MYSTATE
#ifndef OMIT_CHECK_UNL_MYSTATE
Inline bool_t
check_unl_mystate(TCB **pp_selftsk, bool_t *p_context){
	bool_t	state;
	PCB		*p_my_pcb;
	SIL_PRE_LOC;

	SIL_LOC_INT();
	p_my_pcb = get_p_my_pcb();
	*p_context = sense_context(p_my_pcb);
	*pp_selftsk = p_my_pcb->p_runtsk;
	SIL_UNL_INT();

	state = sense_lock();

	return state;
}
#endif /* OMIT_CHECK_UNL_MYSTATE */

/*
 *  CPUロック状態のチェック（E_CTX）
 */
#define CHECK_UNL_MYSTATE(pp_selftsk, p_context) do {		\
	if (check_unl_mystate(pp_selftsk, p_context)) {			\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
} while (false)	
------------------------------------------------------------

======================================================================
その他
======================================================================
----------------------------------------------------------------------
自タスクを対象としたsus_tskのレーシング
----------------------------------------------------------------------
sus_tsk同士がレーシングする状況を考える．

他タスクを対象とし，その他タスクが強制待ち状態となったとしても，カーネ
ル処理の不可分性から，問題がない（そもそも防げないため）．

自タスクを対象とした場合には，強制待ち状態で sus_tsk を実行することに
なり，実行の結果，E_QOVRが返ることになる．

E_QOVRが返るということは，強制待ち状態で sus_tsk を呼び出したというこ
とになるので，避けた方がよいと考えられる．避けるためには，対象タスクが
自タスクか他タスクかによって，使用するロック関数を変更する必要がある．
そもそも自分自身に対してsus_tsk を呼び出すこと自体がレアケースと考えら
れるため，E_QOVRが返ってもよいという考えられるため，仕様を以下のように
変更し，対象タスクの判定は行わない．

----------------------------------------
マルチプロセッサ対応カーネルでは，対象タスクが自タスクの場合にも，
E_QOVRエラーとなる場合がある．この状況は，自タスクに対してsus_tskを発行
するのと同じタイミングで，他のプロセッサで実行されているタスクから同じ
タスクに対してsus_tskが発行された場合に発生する可能性がある．
----------------------------------------

以上
