
		TOPPERS/FMPカーネル
		設計メモ

		対応バージョン: Release 1.4.0
		最終更新: 2015年1月27日（作成中）

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

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

 Copyright (C) 2005-2015 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 1214 2017-04-25 07:02:15Z ertl-honda $
----------------------------------------------------------------------
======================================================================
ToDo
======================================================================
・ターゲット依存部の追記事項を考える．

======================================================================
凡例
======================================================================
メモリアクセスの凡例 
  R    : リード
  W    : ライト
  (I) : カーネル初期化時にアクセス
  (E) : カーネル終了時にアクセス

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

======================================================================
目次
======================================================================
・TOPPERS/FMPカーネルの実装設計方針
・動作アーキテクチャ
・コンフィギュレーション項目
  ・OS内部のロック単位
  ・スピンロックアーキテクチャ
  ・タイマアーキテクチャ
  ・メモリアーキテクチャ
・クラス 
  ・概念
  ・クラスの定義
・データ構造
  ・基本方針
  ・プロセッサコントロールブロック（PCB）
  ・プロセッサコントロールブロック（PCB）へのアクセス
  ・タイムイベントコントロールブロック(TEVTCB)
  ・タイムイベントヒープ
  ・オブジェクトのコントロールブロック
  ・オブジェクトの初期化ブロック
  ・待ち情報ブロック
・RAMデータの配置指定
  ・オブジェクトの管理ブロック
  ・プロセッサ毎の変数
  ・システムサービスが使用するデータ
・ロック単位の設計
  ・基本方針
  ・カーネルの分析
  ・ロック単位の方式
  ・2個タスクロックの取得に伴うデッドロックの回避
・ロックの実装
  ・ロック方式の切り替え
  ・ロック変数の記憶場所
  ・ソースコードの記述の方針
  ・ロック方式によりソースコードの差異が発生する箇所
・ロック関数（共通部）
  ・プロセッサ内の排他制御とプロセッサ間の排他制御
  ・ロック取得時のマイグレーションチェック
  ・ロック取得時のRUNNABLEチェック
  ・ロック関数でのPCBへのポインタの取得
  ・ロック関数の外部仕様
  ・システムコールで使用するロック関数
  ・ロック関数の実装
・ロック関数（ターゲット依存部）
  ・割込みの発生の有無のチェック
  ・テストの容易化
  ・ロック関数（ターゲット依存部）の仕様
  ・ロック関数（ターゲット依存部）の実装例
・カーネルティック
  ・カーネルティック方式の切り替え
  ・グローバルタイマ方式とロック方式
  ・カーネルティック方式によりソースコードの差異が発生する箇所
  ・グローバルタイマ方式でプロセッサ・細粒度ロックのサポート（未実装）
・タスクマイグレーション
  ・タスクマイグレーションシステムコール（mig_tsk()）の仕様検討
  ・タスクマイグレーションシステムコール（mig_tsk()）の仕様の拡張案
  ・タスク起動とマイグレーションシステムコール（mact_tsk）の仕様検討
  ・mig_tsk()実装
  ・mact_tsk()実装
  ・マイグレーションが発生する箇所（ter_tsk()/ext_tsk()）
・タイムイベントハンドラのマイグレーション
  ・システムコール一覧
  ・追加データ
  ・msta_cyc(ID cycid, ID prcid)の実装
  ・[i]msta_alm(ID almid, RELTIM almtim, ID prcid)の実装
・TOPPERS/SMPカーネルで追加した排他制御用の機能の扱い
  ・スピンロック機能
  ・プロセッサ占有状態API
・スピンロック機能
  ・変更箇所
  ・ネイティブ方式の実装
  ・エミュレーション方式の実装
・デッドロック回避
  ・対象システムコール・内部関数
  ・TCBへの追加エントリ
  ・システムコール・内部関数の実装
・ter_tsk()関連
  ・ter_tsk()の制約
  ・ter_tsk()のリトライの許容  
・タスク状態不整合問題
・内部関数（共通部）
  ・新規追加した内部関数
  ・変更した内部関数
  ・削除した内部関数
・ターゲット依存部
  ・プロセッサIDアクセスのvolatile化
  ・CPUロック解除状態で呼び出す関数
・コード記述のチェックポイント
  ・プロセッサ固有データへのアクセス
  ・システムコールでのPCBの取得
  ・使用するロック関数
  ・ena_int()/dis_int()の割込み番号のチェックのタイミング
・その他
  ・チェックマクロの変更
  ・自タスクを対象としたsus_tskのレーシング
  ・複数のタスクの待ち解除に関する留意点
  ・[i]mrot_rdq()の必要性


======================================================================
TOPPERS/FMPカーネルの実装設計方針
======================================================================
TOPPERS/FMPカーネル（以下，FMPカーネル）は，TOPPERS新世代カーネルの基
盤となるTOPPERS/ASPカーネル（以下，ASPカーネル）をマルチプロセッサ拡張
したリアルタイムカーネルである．

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

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

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

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

・プロセッサ数
  ・2プロセッサ
  ・3プロセッサ以上

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

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

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

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

・OS内部のロック単位
  ・ジャイアントロック方式
  ・プロセッサロック方式
  ・細粒度ロック方式

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

・タイマアーキテクチャ
  ・グローバルタイマ方式
  ・ローカルタイマ方式

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

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

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


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

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

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

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

(e)プロセッサ間の排他制御機構を用いてロックを最低2個作成可能であること
   ・2個のみの場合はジャイアントロック方式のみサポート可能
   ・プロセッサ毎に2個+1個作成可能であるとプロセッサロック方式をサポート可能
   ・ロックの作成個数に上限がないと細粒度ロック方式もサポート可能

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


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

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

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

●プロセッサロック方式

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

●細粒度ロック方式

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

----------------------------------------------------------------------
スピンロックアーキテクチャ
----------------------------------------------------------------------
FMPカーネルで新たに導入されたスピンロック機能の実現方法．

●ネイティブ方式

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

●エミュレーション方式

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

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

●グローバルタイマ方式

システムで単一のシステム時刻を使用する．ハードウェアタイマは1個使用し，
タイマ割込みの受け付けとシステム時刻の更新は単一のプロセッサが行う．こ
のプロセッサはシステム時刻管理プロセッサと呼び，静的に決定する．タイム
イベントハンドラは，システム時刻管理プロセッサでのみ動作する．

●ローカルタイマ方式

各プロセッサで独立したシステム時刻を使用する．ハードウェアタイマはプロ
セッサ個数分必要となり，いずれかのプロセッサに関連づけられ，そのプロセ
ッサのシステム時刻を更新するためのに用いられる．

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

----------------------------------------------------------------------
コンフィギュレーションの組み合わせ
----------------------------------------------------------------------
以下のコンフィギュレーションは組み合わせて指定不可能である．

・タイマアーキテクチャのグローバルタイマ方式とOS内部のロック単位のプロ
  セッサ・細粒度ロック方式

・スピンロックアーキテクチャのエミュレーション方式と，OS内部のロック単
  位の細粒度ロック方式

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

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

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

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

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

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

----------------------------------------------------------------------
クラスの定義
----------------------------------------------------------------------
クラス数は，ターゲット依存部のコンフィギュレーションテンプレートファイ
ル中で定義する

 ・NUM_CLASS : クラス数

クラスの内容は，ターゲット依存部のコンフィギュレーションテンプレートフ
ァイル中で，連想配列を定義し，各クラスの定義内容をIDが対応する連想配列
の要素に設定する．

 ・CLASS_AFFINITY_INIT
   ・初期割付けプロセッサ
 ・CLASS_AFFINITY_MASK
   ・マイグレーション可能なプロセッサのマスク指定
   ・0ビットがINDEX0のプロセッサに対応，"1"でマイグレーション可能
 ・CLASS_OBJ_LOCK
   ・プロセッサロックの場合に使用するオブジェクトロック

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

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


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

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

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

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

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

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

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

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

   書き込みは，起動時に自プロセッサが初期化のために行う．
   ROMデータとして持つことも可能だが，現状この変数しか該当しないため，
   RAMデータとしている．
     
・カーネル動作状態フラグ
  変数
   bool_t  kerflg;
  アクセス
    自プロセッサ : R/W(I/E)
    他プロセッサ : -
    ロック取得   : 必要なし

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

・タスクロック
 変数
    LOCK  tsk_lock;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要なし

   カーネル起動時に初期化．読み込みはロック取得時に発生．書き込みはタ
   ーゲット依存部によっては発生する．

・オブジェクトロック
 変数
    LOCK  obj_lock;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要なし

   カーネル起動時に初期化．読み込みはロック取得時に発生．書き込みはタ
   ーゲット依存部によっては発生する．

・1段目の取得中のロック
 変数
   LOCK *p_firstlock;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : -
    ロック取得   : 必要なし

・2段目の取得中のロック
 変数
   LOCK *p_secondlock;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : -
    ロック取得   : 必要なし

・実行状態のタスク
 変数
   TCB  *p_runtsk;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要なし
     
   書き込みはディスパッチャ内のみで行う．他プロセッサからの読み込みは，
   ref_tsk()でのみ行っている．

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

・ディスパッチ／タスク例外処理ルーチン起動要求フラグ
 変数
   bool_t  reqflg;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : -
    ロック取得   : 必要なし

   ロック取得は必要ないが，他のデータと同時にアクセスしているため，実
   質ロックは取得している．
   他のプロセッサにディスパッチを要求する場合は，対象プロセッサのプロセ
   ッサ間割込みハンドラで reqflg を true するため，他のプロセッサの 
   reqflg をアクセスすることはない．

・ディスパッチ禁止状態
 変数
   bool_t  disdsp;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : -
    ロック取得   : 必要なし

   ロック取得は必要ないが，他のデータと同時にアクセスしているため，実
   質ロックは取得している．

・タスクディスパッチ可能状態
 変数
   bool_t  dspflg;
 アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・スピンロック取得しているか
 変数
   ID  locspnid;
 アクセス
   自プロセッサ : R/W
   他プロセッサ : -
   ロック取得   : 必要なし
   
・レディキュー
 変数
   QUEUE  ready_queue[TNUM_TPRI];
 アクセス
   自プロセッサ : R/W
   他プロセッサ : R/W
   ロック取得   : 必要あり

・レディキューサーチのためのビットマップ
 変数
   uint16_t ready_primap;
 アクセス
   自プロセッサ : R/W
   他プロセッサ : R/W
   ロック取得   : 必要あり

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

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

・ターゲット依存のプロセッサコントロールブロック
 変数
   TPCB  target_pcb;
 アクセス
   ターゲット依存．

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

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

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

----------------------------------------------------------------------
タイムイベントコントロールブロック(TEVTCB)
----------------------------------------------------------------------
タイムイベントに関連したデータ構造(systime_offset, current_time)等が含
まれている構造体．ローカルタイマ方式の場合はプロセッサ毎に生成する．一
方，グローバルタイマ方式の場合は1個生成する．各方式で生成個数が異なる
ため，PCBとは別の構造体とし，そのポインタをPCBに登録する．

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

   TEVTCB   _kernel_prc1_tevtcb;
   TEVTCB   _kernel_prc2_tevtcb;

   TEVTCB* const _kernel_p_tevtcb_table[TNUM_PRCID] = {
         &_kernel_prc1_tevtcb,
         &_kernel_prc2_tevtcb
   };

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

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

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

・現在のシステム時刻
  変数
    EVTTIM current_time;
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・次のタイムティックのシステム時刻
  変数
    EVTTIM next_time;
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・システム時刻積算用変数
  変数
    uint_t next_subtime
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・システム時刻が未更新であることを示すフラグ
  変数
    bool_t pend_update
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R
    ロック取得   : 必要あり

・タイムイベントヒープの最後の使用領域のインデックス
  変数
    uint_t last_index
  アクセス
    自プロセッサ : R/W
    他プロセッサ : R/W
    ロック取得   : 必要あり

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

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

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

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

----------------------------------------------------------------------
オブジェクトのコントロールブロック
----------------------------------------------------------------------
基本方針から，オブジェクト毎に個別の変数とする．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.tf）内で定義する．

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

オブジェクトの管理ブロックの指定と共に，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)
     ..


======================================================================
ロック単位の設計
======================================================================
----------------------------------------------------------------------
基本方針
----------------------------------------------------------------------
最も大きなロック単位はシステム全体で一つのロックを用いる方法（ジャイア
ントロック）であるが，この方法ではお互いに関連しない処理を阻害してしま
い，プロセッサ数に対するスケーラビリティが確保できない．また，ロックの
取得までの時間に上限がないロックアルゴリズムを使用した場合は，リアルタ
イム性が悪くなる．例えばプロセッサ1とプロセッサ2でそれぞれプロセッサ内
に閉じた管理ブロックの操作をしている場合でも，ロック取得により，お互い
の実行を阻害してしまう．そのため，少なくとも各プロセッサのロック単位は
別々にするべきある．これは自プロセッサのオブジェクト操作で他のプロセッ
サの処理を阻害するべきではないからである．しかしながら，あまり細かい粒
度でロック単位を設定してしまうと，ロック取得のネスト段数が多くなりオー
バーヘッド（最悪時性能）が増大してしまう．

また，できるだけ多くの箇所でロックの取得順序が一定になるようにロック単
位を決定する必要がある．これにより，デッドロック回避が必要なシステムコ
ールを最小限にする．例えばロックA,Bがあり，あるタスクAがロックをA->Bの
順序で取得し，他のタスクがロックをB->Aの順序で取得すると，デッドロック
が発生するため，システムでロックの取得順序を一定に定め，そのままでは，
定めた順序でロックを取得しない箇所に関しては，デットロック回避のための
処理を加える．

割込みの出入口ではロックを取得しないとする．割込みの入り口でロックを取
得すると，ロック取得までの時間に上限がないロックアルゴリズムを使用した
場合，割込みの応答時間の上限が定まらなくなってしまう．

割込みの出口に関しては，ロック取得の試行中の割込み応答性を低下させない
ためには，ロック取得の試行と割込みの許可を繰り返して実行する必要がある．
しかしながら，割込みの出口では割込みを許可できないターゲットが多いため，
割込み禁止時間の上限が定められなくなる．

FMPカーネルでは，OSによるタスクのプロセッサへの自動割付けをサポートし
ないため，割込みの入口では，ロックによる排他制御が必要なデータ構造にア
クセスする必要はない．割込みの出口に関しては，割込みの出口でディスパッ
チが必要でない場合は，ロックによる排他制御は必要ない．一方，ディスパッ
チが必要な場合は，ディスパッチャをロック取得状態で実行しなければならな
いとすると，ロックを取得する必要がある．ディスパッチャをロック取得状態
で実行するべきかはタスクマイグレーションの章で議論する．

----------------------------------------------------------------------
カーネルの分析
----------------------------------------------------------------------
ロック単位を決定するため，ASPカーネルをマルチプロセッサ拡張した場合に，
カーネル内の共有されるデータ構造と，それに対するアクセスパターンを整理
する．

カーネル内の主なデータ構造として，次のものを持つ．

  ・タスクコントロールブロック（TCB）
  ・レディキュー
  ・オブジェクトのコントロールブロック
  ・タイマイベントキュー

各システムコールと内部変数でのこれらのデータ構造に対するアクセスパター
ンは，次のように分類できる．

（a）タスクに対する通常の操作
  アクセス対象のタスク（または自タスク）のTCBおよびレディキューのみを
  アクセスする（TCBとレディキューのどちらかしかアクセスしないケースも含む）．

（b）タスクに対する特殊操作
  アクセス対象のタスクのTCBおよびレディキューと，そのタスクが持ってい
  る同期・通信オブジェクトのコントロールブロックをアクセスする．カーネ
  ル仕様のバリエーションによっては，その同期・通信オブジェクトを持って
  いる他のタスクのTCBとレディキューもアクセスする．

（c） 同期・通信オブジェクトに対する単純操作
  アクセス対象の同期・通信オブジェクトのコントロールブロックのみをアク
  セスする．

（d）同期・通信オブジェクトに対する待ちを伴う操作
  アクセス対象の同期・通信オブジェクトのコントロールブロックをアクセス
  し，必要な場合（自タスクが待ちに入る場合）には自タスクのTCBおよびレ
  ディキューもアクセスする．

（e）同期・通信オブジェクトに対するタスクの待ち解除を伴う操作
  アクセス対象の同期・通信オブジェクトのコントロールブロックと，そのオ
  ブジェクトを待っているタスクがあれば，そのTCBおよびレディキューをア
  クセスする．

（f）タイムイベントキューに対する操作

（g）PCBのみに対する操作

（h）その他
  これらのデータ構造をアクセスしない．

システムコール及び内部変数に対してこの分類を行った（design.xlsのアクセ
スパターンのシートを参照）．

TCBとレディキューは同時にアクセスされる場合が多いことがわかったため，
TCBとレディキューは同じロック単位に入れることとした．また，タイマイベ
ントキューをアクセスする場合には，TCBやレディキューにもアクセスする場
合が多いため，タイマイベントキューもTCBやレディキューと同じロック単位
に入れることとした．このロック単位をタスクロックと呼ぶ．

同期・通信オブジェクトのコントールブロックに対するロック単位を，TCBと
レディキューを含むロック単位とは別にする．このロック単位をオブジェクト
ロックと呼ぶ．タスクロックとオブジェクトロックの両方を取る必要のある場
合には（（b）（d）（e）），デッドロック回避のため，ロック単位の取得順
序を一意に決めなければならない．タスクロック，オブジェクトロックの順で
ロックを取得する（b）のシステムコールより，オブジェクトロック，タスク
ロックの順で取得する（d）（e）のシステムコールが多く，(b)のシステムコ
ールは利用頻度が低いと考えられるため，オブジェクトロック，タスクロック
の順で取得することを原則とする．そのため，（b）の分類のシステムコール
の実装に際しては，デッドロック回避の仕組みを入れる必要がある．

デッドロック回避が必要なシステムコールと内部関数は次の通りである．

 ・ext_tsk
 ・ter_tsk
 ・chg_pri
 ・rel_wai
 ・irel_wai
 ・make_non_wait（内部関数）
 ・wait_tmout（内部関数）

----------------------------------------------------------------------
ロック単位の方式
----------------------------------------------------------------------
前述の議論では，プロセッサ毎にタスクロックとオブジェクトロックを持つ方
式がロックの段数とデッドロック回避の少なさから適切と述べた．しかしなが
ら，2プロセッサ程度の場合はジャイアントロックで十分な場合が多いことや，
ハードウェアが排他制御機構を多く持たないため，ジャイアントロックしか実
現できないケースが考えられる．そこで，FMPカーネルでは3種類のロック単位
の方式を選択可能とする．

システムで単一のロックを用いる方式をジャイアントロック方式と呼ぶ．

カーネルの分析から，ジャイアントロックの次に粒度が細かいロック単位とし
て，タスクロックとオブジェクトロックをプロセッサ毎に持つロック単位を採
用し，これをプロセッサロック方式と呼ぶ．

さらに，オブジェクトロックに関しては，個々のオブジェクト毎に独立したオ
ブジェクトロックを持つ方式も考えられ，これを細粒度ロック方式と呼ぶ．タ
スクロックに関しては，タスク関連のデータ構造を操作するシステムコールの
多くは，レディキューを操作するため，タスク毎にロックを持つように細分化
したとしても，レディキューのためのロックが必要となり，実行オーバヘッド
は増加するが，並列性は向上しないと予想される．そのため，細粒度ロック方
式でも，タスクロックはプロセッサ毎に持つとする．

----------------------------------------------------------------------
2個タスクロックの取得に伴うデッドロックの回避
----------------------------------------------------------------------
プロセッサ・細粒度ロック方式の場合，タスクやタイムイベントハンドラのマ
イグレーションの際には，マイグレーション元とマイグレーション先の2個の
タスクロックを同時に取得する必要がある．その際，デッドロックを回避する
ため，プロセッサIDの小さいプロセッサのタスクロックから取得することにす
る．


======================================================================
ロックの実装
======================================================================
----------------------------------------------------------------------
ロック方式の切り替え
----------------------------------------------------------------------
ターゲット依存部の target_config.h 内で，マクロ TTYPE_KLOCK を G_KLOCK
（ジャイアントロック方式）/P_KLOCK（プロセッサロック方式）/F_KLOCK（細
粒度ロック方式）のいずれかに定義する．

----------------------------------------------------------------------
ロック変数の記憶場所
----------------------------------------------------------------------
ロック変数に格納する内容は，ターゲット依存である．CAS,Test&Set,LL/SCと
いった命令を持つ場合は，直接ロックのための領域として使い，専用回路を用
いる場合は，アドレスを格納することを想定している．ロック変数の型や初期
化関数は，ターゲット依存部で定義する．

・ジャイアントロック方式
  ・グローバル変数として確保．

・プロセッサロック方式
  ・タスクロック 
    ・PCB内に確保．TCBにPCBへのポインタを格納して，TCB経由でアクセス．
  ・オブジェクトロック
    ・PCB内に確保．各オブジェクトの初期化ブロックにオブジェクトロック
      へのポインタを保持する領域を用意．

・細粒度ロック方式
  ・タスクロック 
    ・PCB内に確保．TCBにPCBへのポインタを格納して，TCB経由でアクセス．
  ・オブジェクトロック
    ・各オブジェクトのコントロールブロック内に確保．

----------------------------------------------------------------------
ソースコードの記述の方針
----------------------------------------------------------------------
ソースコードは，細粒度ロックを前提に記述する．ロック取得・解放部分は関
数とし，各関数はタスクロックを取得・解放するのか，オブジェクトロックを
取得・解放するのか，二段目のロックの取得・解放なのかに分けて定義する．

デッドロック回避が必要なシステムコールでは，ロック方式によって，実装方
法が大きく変わる．その場合には，可読性を考慮して，システムコール単位で
ifdefで切り替える．

----------------------------------------------------------------------
ロック方式によりソースコードの差異が発生する箇所
----------------------------------------------------------------------
・各オブジェクトの初期化ブロック
  ・プロセッサロック方式の場合，ロック変数へのポインタを格納する変数

・各オブジェクトの管理ブロック
  ・細粒度ロック方式の場合，ロック変数を確保する．

・各オブジェクトの初期化ルーチン
  ・細粒度ロック方式の場合，ロック変数を初期化する

・ロック関数
  ・ジャイアントロック方式と，プロセッサ・細粒度ロック方式で異なる．

・オブジェクトロック取得マクロ（GET_OBJLOCK）
  ・ジャイアントロック方式 : ジャイアントロックを返す
  ・プロセッサロック方式   : PCB内のオブジェクトロックを返す
  ・細粒度ロック方式       : 各オブジェクトのコントロールブロックの
                             オブジェクトロックを返す

・プロセッサコントロールブロック（PCB）
  ・プロセッサロック方式 : タスクロックとオブジェクトロック
  ・細粒度ロック方式     : タスクロック
  ・プロセッサロックと細粒度ロック方式 : 取得中のロックの記憶領域（
                                         p_firstlock, p_secondlock）

・ジャイアントロック用のロック変数
  ・ジャイアントロック方式の場合

・タスク管理ブロック（TCB）
  ・プロセッサ・細粒度ロック方式の場合，デッドロック回避用の変数が3種
    類追加される．

・デッドロック回避が必要な関数
  ・プロセッサ・細粒度ロック方式の場合は，デッドロック回避を行うコード
    とする．


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

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

●検討案（採用見送り）

FMPカーネルでは，まず割込みを禁止してから，ロックの取得を試みる．そし
て，ロックの取得を試みるたびに（ロックの取得を試みた結果ロックが取得で
きなかった場合に），割込み要求をチェックし，割込み要求があれば，割込み
を許可して割込みを受け付ける．

割込みを先に禁止することにより，ロックを取得したまま割込みハンドラを実
行することを防ぎ，ロックの取得を試みるたびに割込み要求をチェックするこ
とにより割込み応答性の悪化を防いでいる．

 retry:
    /* (1) オブジェクトロックの取得 */
    disable_interrupt();
    while(!test_and_set(obj lock)){
        /* ロックが取得できずかつ割込みが発生して */
        /* いれば，割込みを受け付け，その後 retry */
        /* から再開する */
        if(interrupt_request()){
            enable_interrupt();
            goto retry;
        }
     }
     /* (2) この箇所のコードは繰り返し実行される可能 */
     /* 性があるため，カーネル内部のデータを変更して */
     /* はならない */
     ...
     /* (3) タスクロックの取得 */
     while(!test_and_set(tsk lock)){
         if(interrupt_request()){
             /* ロックが取得できずかつ割込みが発生して */
             /* いればオブジェクトロックを解放し，割込 */
             /* みを受け付け，その後 retry から再開する */
             release_lock(obj_lock);
             enable_interrupt();
             goto retry;
          }
     }
     /* (4)Critical Section */
     ...

----------------------------------------------------------------------
ロック取得時のマイグレーションチェック
----------------------------------------------------------------------
システムコール実行の際にロックを取得する必要があるが，そのバリエーショ
ンは幾つかに分類される．そのうち，特定のタスクに関連づけられたタスクロ
ックを取得するには，TCBに登録されたPCBのタスクロック用の変数を用いてタ
スクロックを取得する．FMPカーネルはタスクやタイムイベントハンドラのマ
イグレーションをサポートしているため，ロックを取得したとしても，その取
得の試行中に対象のタスクやタイムイベントハンドラがマイグレーションする
可能性がある．マイグレーションが発生すると，取得したタスクロックが異な
るプロセッサに対するものとなるため，ロックの取り直しが必要となる．以下
では，各バリエーションについて，ロック取得時のマイグレーションのチェッ
クの必要性の有無についてまとめる．

●タスクコンテキストから自タスクのタスクロックの取得

自タスクのタスクロックを取得する場合は，以下の条件を満たすことで，ロッ
ク取得後のマイグレーションのチェックは必要なくなる．

  CPUロック状態でプロセッサIDを取得して，PCBへアクセスし，ロックすべき
  ロックを取得する．

FMPカーネルのマイグレーションの制約により，同じプロセッサに割り付けら
れているタスクのみマイグレーション可能である．そのため，CPUロック状態
とした後には，マイグレーションされることはないため，上記の条件によりロ
ック取得後の自タスクのマイグレーションのチェックは必要なくなる．

いいかえると，CPUロック解除状態で取得したプロセッサIDはマイグレーショ
ンにより無効となっている可能性がある．

ただし，依存部のロック取得関数内で割込みを許可する実装とした場合は，自
タスクのロックを取得する場合にも，自タスクのマイグレーションのチェック
は必要となる．

●タスクコンテキストから任意タスク（自タスクも含む）のロックを取得

任意タスク（自タスクも含む）のロックを取得する場合は，ロック取得後，対
象タスクがマイグレーションしているかチェックする必要がある．

チェック方法は，ロック取得時に使用したPCBと，ロック取得後のTCBに登録さ
れているPCBが同一であるかにより判断する．

●タスクコンテキストから二段目のロックとしてタスクロックを取得する場合

タスクロックを二段目のロックとして取得する場合，ロック取得後に，マイグ
レーションが発生していないかチェックする必要がある．これは，mig_tskに
おいて，オブジェクト待ちのタスクをマイグレーションする際に，オブジェク
トロックは取得せず，タスクロックのみを取得してマイグレーションを実施す
るためである．

逆に，mig_tskでマイグレーション対象のタスクがオブジェクト待ちの場合，
オブジェクトロックも取得するようにすれば，二段目のロック取得時にマイグ
レーションのチェックが必要なくなる．しかしながら，mig_tskにデッドロッ
ク回避ルーチンが入り，ter_tskと同様に処理の代行もできないため，上限時
間が抑えられなくなる．

他にマイグレーションを発生させるAPIとしては，mact_tsk / imact_tsk / 
ext_tsk / ter_tsk があるが，mact_tsk/imact_tskは，待ち状態のタスクに対
しては，キューイングビットをセットするだけであるので問題ない．ext_tsk
は，実行状態でのみ呼び出されるため該当しない．ter_tskは，オブジェクト
ロックとタスクロックを取得するため問題ない．

ロックマクロでのマイグレーションのチェックは軽いと見積もられるため，
mig_tsk でのオブジェクトロックの取得は行わない．

●非タスクコンテキストから自プロセッサのタスクロックの取得

割込みハンドラやタイムイベントハンドラは実行中にマイグレーションされる
ことはないため，マイグレーションのチェックは必要ない．

●非タスクコンテキストから任意タスクのタスクロックの取得

任意タスクのロックを取得する場合は，ロック取得後，対象タスクがマイグレ
ーションしているかチェックする必要がある．

チェック方法は，ロック取得時に使用したPCBと，ロック取得後のTCBに登録さ
れているPCBが同一であるかにより判断する．

----------------------------------------------------------------------
ロック取得時のRUNNABLEチェック
----------------------------------------------------------------------
CPUロック状態としてロックを取得する間に，他のプロセッサのタスクによっ
て，自タスクがSUSPEND状態に変更される可能性があるため，ロック取得後に
対象タスクの状態をチェックする必要がある．システムコールの分類を
"design.xls"のRUNNABLEチェックのシートに示す．

●タスクコンテキストから自タスクのタスクロックの取得

 PCB* t_acquire_tsk_lock_self(void);
 PCB* t_acquire_nested_tsk_lock_self(LOCK *p_objlock)
 
自タスクのタスクロックを取得する場合は，割込みを禁止してから，ロックを
取得するまでの間に他のプロセッサに割り付けられているタスクから，
sus_tskが発行され，強制待ち状態となる可能性がある．その場合，API実行を
行うと，不整合が発生するため（強制待ち状態から二重待ち状態になる等..），
ロック取得後，自タスクの状態がRUNNABLEかチェックする必要がある．自タス
クの状態がRUNNABLEでない場合は，ロックを解放して一旦割込みを許可する．

ただし，ディスパッチ禁止状態による過度状態（run-suspend）のタスクが呼
び出し可能なシステムコールに関しては問題が発生する．具体的には，，タス
クの状態がRUNNABLEでないため，RUNNABLEかチェックに引っかかり割込みを許
可してしまうが，ディスパッチ禁止状態なので，割込みハンドラの出口でディ
スパッチが発生せずに戻り，無限ループとなっしまうため，対策が必要である．

タスクコンテキストから自タスクのタスクロックの取得を取得するAPIは次の
通りである．

分類
 ●ディスパッチ禁止状態で呼び出せないAPI
 ・ディスパッチ禁止状態で呼び出せるAPI
 
●snd_dtq(ID dtqid, intptr_t data)
●tsnd_dtq(ID dtqid, intptr_t data, TMO tmout)
●rcv_dtq(ID dtqid, intptr_t *p_data)
●trcv_dtq(ID dtqid, intptr_t *p_data, TMO tmout)
●snd_pdq(ID pdqid, intptr_t data, PRI datapri)
●tsnd_pdq(ID pdqid, intptr_t data, PRI datapri, TMO tmout)
●rcv_pdq(ID pdqid, intptr_t *p_data, PRI *p_datapri)
●trcv_pdq(ID pdqid, intptr_t *p_data, PRI *p_datapri, TMO tmout)
●wai_flg(ID flgid, FLGPTN waiptn, MODE wfmode, FLGPTN *p_flgptn)
●twai_flg(ID flgid, FLGPTN waiptn, MODE wfmode, FLGPTN *p_flgptn, TMO tmout)
●rcv_mbx(ID mbxid, T_MSG **ppk_msg)
●trcv_mbx(ID mbxid, T_MSG **ppk_msg, TMO tmout)
●get_mpf(ID mpfid, void **p_blk)
●tget_mpf(ID mpfid, void **p_blk, TMO tmout)
●wai_sem(ID semid)
●twai_sem(ID semid, TMO tmout)
・rot_rdq(PRI tskpri)
・dis_dsp(void)
・ena_dsp(void)
・call_texrtn(void)
・dispatch_call_texrtn(void)
・dis_tex(void)
・ena_tex(void)
・ext_tsk(void)
・get_inf(intptr_t *p_exinf)
●slp_tsk(void)
●tslp_tsk(TMO tmout)
●dly_tsk(RELTIM dlytim)
・get_tim(SYSTIM *p_systim)

ディスパッチ禁止状態で呼び出せるAPIは，過度状態で呼び出せるため，すな
わち，自タスクの状態を変更しないため，RUNNABLEチェックを行う必要がない．

そこで，RUNNABLEチェックを行わない自プロセッサのタスクロック取得関数を
加える．

 PCB* t_acquire_tsk_lock_self_without_runnable_check(void);
 
●タスクコンテキストから任意タスク（自タスクも含む）のタスクロックの取得

他タスクを対象とした場合は，強制待ち状態のタスクからシステムコールを発
行することになるが，カーネル処理の不可分性の観点からは問題ない．

そもそも，強制待ち状態からのシステムコールの発行を抑止するには，タスク
が呼び出す全てのシステムコールにおいて，自タスクの割り付けられたプロセ
ッサのタスクロックを取得するようにしなければならない．

自タスクを対象とした場合も，問題はないと考えられるが，個別のAPIで確認
する必要がある．

ref_tsk()は，強制待ち状態が読み取れる可能性があるため，統合仕様書の
ref_tsk()に説明を加える．

どちらの場合でも，システムコール実行中に強制待ち状態になっても正しく動
作するテストが必要である．

----------------------------------------------------------------------
ロック関数でのPCBへのポインタの取得
----------------------------------------------------------------------
マイグレーションを考慮すると，自プロセッサが割り付けられたプロセッサ
PCBへのポインタの取得は，CPUロック状態で行う必要がある．

システムコールの処理において，CPUロック状態となるのは，ロックを取得し
た後である．ロックを取得する前に一旦CPUロック状態とするが，ロックの試
行において割込み応答性の確保のために割込みを許可するため，この時点で取
得したPCBへのポインタはロック取得後に有効でない場合がある．

他プロセッサに割り付けられたタスクが割り付けられているプロセッサのPCB
の取得に関しては，ロック取得後に有効となる．そのため，ロック取得後に取
得したロックが対象タスクが割り付けられたプロセッサのロックか再度確認す
る必要がある．

このように，PCBのポインタを取得するタイミングは注意が必要であるため，
ミスを軽減するために，PCBへのポインタは，システムコール内で取得するこ
とはせず，ロック取得関数内で適切に取得し，ロック取得関数の戻り値とする
ことにする．システムコールでは，ロック関数から受け取ったPCBへのポイン
タを用いることを基本とする．

----------------------------------------------------------------------
ロック関数の外部仕様
----------------------------------------------------------------------
PCB*を返すものは，ロックが取得できない場合はNULLを，ロックが取得できた
場合はpcbへのポインタを返す．t_acquire_nested_dual_tsk_lock()のみ，
bool_tを返し，ロックが取得できた場合は false を，ロック取得中に割込み
が入った場合は，true を返す．名称中の nested は，2段目のロックであるこ
とを示す．

●特定プロセッサのタスクロックの取得（タスクコンテキスト）
 PCB* t_acquire_tsk_lock_prcid(ID prcid)

●自プロセッサのタスクロックの取得（タスクコンテキスト）
 PCB* t_acquire_tsk_lock_self(void);
 PCB* t_acquire_nested_tsk_lock_self(LOCK *p_objlock)
 
●RUNNABLEチェックを行わない自プロセッサのタスクロックの取得
（タスクコンテキスト） 
 PCB* t_acquire_tsk_lock_self_without_runnable_check(void);

●任意タスク（自タスクも含む）のタスクロックの取得（タスクコンテキスト）
 PCB* t_acquire_tsk_lock(TCB *p_tcb)
 PCB* t_acquire_nested_tsk_lock(TCB *p_tcb, LOCK *p_objlock)

●任意タイムイベントハンドラのタスクロックの取得（タスクコンテキスト）
 PCB* t_acquire_tsk_lock_cyc(ALMCB *p_cyccb)
 PCB* t_acquire_tsk_lock_alm(CYCCB *p_almcb)

●2個のタスクロックの取得（タスクコンテキスト）
 void t_acquire_dual_tsk_lock(TCB *p_tcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)
 void t_acquire_dual_tsk_lock_alm(ALMCB *p_almcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)
 void t_acquire_dual_tsk_lock_cyc(CYCCB *p_almcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)
 bool_t t_acquire_nested_dual_tsk_lock(TCB *p_tcb, ID dstprcid, LOCK *p_objlock,
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)

●特定プロセッサのタスクロックの取得（非タスクコンテキスト）
 PCB* i_acquire_tsk_lock_prcid(ID prcid)

●自プロセッサのタスクロックの取得（非タスクコンテキスト）
 PCB* i_acquire_tsk_lock_self(void)

●任意タスクのタスクロックの取得（非タスクコンテキスト）
 PCB* i_acquire_tsk_lock(TCB *p_tcb)
 PCB* i_acquire_nested_tsk_lock(TCB *p_tcb, LOCK *p_objlock)

●任意タイムイベントハンドラのタスクロックの取得（非タスクコンテキスト）
 PCB* i_acquire_tsk_lock_alm(ALMCB *p_almcb)

●2個のタスクロックの取得（非タスクコンテキスト）
 void i_acquire_dual_tsk_lock(TCB *p_tcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)
 void i_acquire_dual_tsk_lock_alm(ALMCB *p_almcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)

●プリエンプションしない自プロセッサのタスクロックの取得
 PCB* acquire_tsk_lock_without_preemption_self(void)

●プリエンプションしない2段目のタスクロックの取得
 PCB* acquire_nested_tsk_lock_without_preemption(TCB *p_tcb)
 
●プリエンプションしないタスクの2個のタスクロックの取得（タスクコンテキスト）
 void t_acquire_dual_tsk_lock_without_preemption(TCB *p_tcb, ID dstprcid, 
                                    PCB **pp_srcpcb, PCB **pp_dstpcb)

●オブジェクトロックの取得（タスクコンテキスト）
 void t_acquire_obj_lock(LOCK *p_objlock)

●オブジェクトロックの取得（非タスクコンテキスト）
 void i_acquire_obj_lock(LOCK *p_objlock)

○ロック返却関数

●タスクロックの解放
 void release_tsk_lock(PCB *p_pcb);

●タスクロックの解放（2段目）
 void release_nested_tsk_lock(PCB *p_pcb);

●2個のタスクロックの解放
 void release_dual_tsk_lock(PCB *p_srcpcb, PCB *p_dstpcb);

●オブジェクトロックの解放
 void release_obj_lock(LOCK *p_objlock);

----------------------------------------------------------------------
システムコールで使用するロック関数
----------------------------------------------------------------------
システムコール及び内部関数で使用するロック関数を"design.xls"のロック関
数（1.1.0）のシートにまとめる．

----------------------------------------------------------------------
ロック関数の実装
----------------------------------------------------------------------
ジャイアントロック方式とプロセッサ・細粒度ロック方式とでは，実装方法が
大きく異なるため，ifdef で実装を切り替えている．

ジャイアントロック方式では，ロック関数の実装が小さく，2段目のロック取
得関数に関してはほぼ空であるかことから，実行効率を考慮してインライン関
数として実装する．一方，プロセッサ・細粒度ロック方式では，マイグレーシ
ョンチェックや2段目のロック取得等でロック関数の実装が大きいため，関数
として実装する．

●ロック取得関数

CPUロック状態で呼び出される．ターゲット依存部の関数によりロックを取得
した後，必要に応じてマイグレーションチェック，RUNNABLEチェックを行う．
タスクマイグレーションでタスクロックを2個取得する場合には，デッドロッ
クを回避するため，プロセッサIDの小さいプロセッサのタスクロックから取得
する．

2段目のロックを取得する場合は，ロック取得前に1段目のロック変数を自プロ
セッサのPCBの p_firstlock へ登録する．2段目のロックの取得後は NULL に
する．

●ロック返却関数

CPUロック状態で呼び出される．ロックを解放して，CPUロック状態のままリタ
ーンする．


======================================================================
ロック関数（ターゲット依存部）
======================================================================
----------------------------------------------------------------------
割込みの発生の有無のチェック
----------------------------------------------------------------------
理想的なターゲット依存部のロック取得関数，t_acquire_obj_lock() / 
t_acquire_tsk_lock() の仕様は次の通りである．

ロックを取得できればfalseを返す．ロックが取得出来なければ，割込みの発
生の有無をチェックして，割込みが発生していれば，true を返す．割込みが
発生していなければ，ロックの取得を再度試みる．

割込みの発生の有無がチェックできないターゲットについては，ロックの取得
に成功するまでロックの取得を繰り返すか，それとも1回の試行の結果，ロッ
クが取得できなければtrueを返すかのどちらかを選択することになる．

割込みの発生の有無がチェックできないプロセッサの場合，ロックの取得に成
功するまでロックの取得を繰り返すと割込み応答性が悪くなり，1回の試行の
結果，ロックが取得できなければtrueを返すと，一段目のロックも解放するこ
とになるので，スループットが悪化する可能性がある．

FMPカーネルが現状サポートしているターゲットで，低オーバヘッドで割込み
の発生確認の有無が可能かは次のようになっている．

  NiosII    : 可能
  MPCore    : 不可能
  SHX-3     : 不可能
  SH2A-DUAL : 不可能

NiosII に関しても，割込み処理モデルの実現方法によっては，有効な割込み
の判断が低オーバヘッドで実現できなくなる可能性がある．そのため，現状，
割込み発生確認の有無が低オーバヘッドで可能なプロセッサは一般的ではない
と言える．そのため，FMPカーネルでは割込みの発生の有無を低オーバヘッド
で実現できないことを前提とする．

割込みの発生の有無が判定出来ないプロセッサにおいて，割込み応答性とスル
ープットを確保するためには次の方法が考えられる．

まず，プロセッサ毎に取得したロックを記憶するグローバル変数（取得済みロ
ック管理変数）を用意する．1段目のロック（オブジェクトロック）を取得す
ると，そのポインタを取得済みロック管理変数に保存する．2段目のロック取
得ルーチンでは，ロックの取得を試み，ロックが取得できればそのまま進む．
ロックが取得できなかった場合は，一旦割込みを許可する．割込みハンドラの
先頭では，取得済みロック管理変数をチェックして，ロックを取得している場
合は，ロックを解放して，割込みハンドラを実行する．2段目のロック取得ル
ーチンでは，割込みの禁止後，取得済みロック管理変数をチェックして，1段
目のロックが解放されていなければ2段目のロックの再試行を行う．1段目のロ
ックが解放されていれば，trueをリターンする．

----------------------------------------------------------------------
テストの容易化
----------------------------------------------------------------------
テストの容易化のため，ターゲット依存のロック関連の関数の数は可能な限り
少なくする方針とする．

1.0.Xでは，タスクロックとオブジェクトロックは異なる型としているが，違
う必要はなく，同一の型にすると，ロック関連関数の数を減らせるため，同一
のLOCK型にする．

----------------------------------------------------------------------
ロック関数（ターゲット依存部）の仕様
----------------------------------------------------------------------
これまでの方針により，ターゲット依存のロックマクロの仕様は次のように
する．

●タスクコンテキスト用

void t_acquire_lock(LOCK *p_lock)
  1段目のロック取得関数．p_lockで渡されたロックを取得する．
  
bool_t t_acquire_nested_lock(LOCK *p_lock)
  2段目以上のロック取得関数．
  p_lockで渡されたロックを取得する．ロックを取得できれば
  falseを返す．ロックが取得ができなければ，一旦割込みを許可
  した後禁止して，自プロセッサのPCBの取得済みロック管理変数
  をチェックして，NULLならtrueを返す．

●非タスクコンテキスト用

void i_acquire_lock(LOCK *p_lock)
  1段目のロック取得関数．p_lockで渡されたロックを取得する．
  
bool_t i_acquire_nested_lock(LOCK *p_lock)
  2段目以上のロック取得関数．
  p_lockで渡されたロックを取得する．ロックを取得できれば
  falseを返す．ロックが取得ができなければ，一旦割込みを許可
  した後禁止して，自プロセッサのPCBの取得済みロック管理変数
  をチェックして，NULLならtrueを返す．

●タスク/非タスクコンテキスト両用

void x_release_lock(LOCK *p_lock)
  p_lockで渡されたロックを解放する．

●割込みを許可しないロック取得関数

void x_acquire_lock_without_preemption(LOCK *p_lock)
 ロック取得に失敗した場合も割込みを許可しない．
 set_flg/iset_flg/ini_xxxでリトライができないために使用される 

----------------------------------------------------------------------
ロック関数（ターゲット依存部）の実装例
----------------------------------------------------------------------
●プロセッサコントロールブロック（PCB）

   LOCK *p_firstlock  = NULL;
   LOCK *p_secondlock = NULL;

●割込みエントリー処理

CPUロック状態を解除する前に，p_firstlock/p_secondlockをチェックして，
NULL でなければロックを解放して NULL とする．

割込エントリー処理()
{
	....
	p_pcb = get_my_p_pcb(); 
	if (p_pcb->p_firstlock != NULL) {
		x_release_lock(p_pcb->p_firstlock);
		p_pcb->p_firstlock = NULL;
		if (p_pcb->p_secondlock != NULL) {
			x_release_lock(p_pcb->p_secondlock);
			p_pcb->p_secondlock = NULL;
		}
	}
	....
}

●ターゲット依存部の関数

Test&Set機能(test_and_set())を持つターゲットでの例．

void
t_acquire_lock(LOCK *p_lock)
{
	while (test_and_set(p_lock)) {
		t_unlock_cpu();
		t_lock_cpu();
	}
}

bool_t
t_acquire_nested_lock(LOCK *p_lock)
{
	PCB		*p_pcb;

	while (test_and_set(p_lock)) {
		t_unlock_cpu();
		t_lock_cpu();
		/* マイグレーションする可能性があるのでここでPCBを毎回取得 */
		p_pcb = get_my_p_pcb(); 
		if (p_pcb->p_firstlock == NULL) {
			return(true);
		}
	}
	return(false);
}

void
i_acquire_lock(LOCK *p_lock)
{
	while (test_and_set(p_lock)) {
		i_unlock_cpu();
		i_lock_cpu();
	}
}

bool_t
i_acquire_nested_lock(LOCK *p_lock)
{
	PCB		*p_pcb;

	while (test_and_set(p_lock)) {
		i_unlock_cpu();
		i_lock_cpu();
		/* 
		 * マイグレーションしないため，while前に実行してもよいが，
		 * 1回でロックがとれた場合，効率が悪いので，ここで取得
		 */
		p_pcb = get_my_p_pcb();
		if (p_pcb->p_firstlock == NULL) {
			return(true);
		}
	}
	return(false);
}

void
x_release_lock(LOCK *p_lock)
{
	*p_lock = NULL;
}

void 
acquire_lock_without_preemption(LOCK *p_lock)
{
	while (test_and_set(p_lock));
}


======================================================================
カーネルティック
======================================================================
カーネルティック（システム時刻）の実現方法として，ハードウェアタイマを
1個使用し，システムで1つのシステム時刻を使用するグローバルタイマ方式と，
プロセッサ個数分のハードウェアタイマを用い，各プロセッサで独自のシステ
ム時刻を使用する，ローカルタイマ方式をサポートする．

----------------------------------------------------------------------
タイマアーキテクチャの切り替え
----------------------------------------------------------------------
ローカルタイマ方式とグローバルタイマ方式の切り替えは，target_kernel.h 
およびそこからインクルードされるファイルにおいて，ローカルタイマ方式を
有効にする場合は，TOPPERS_SYSTIM_LOCAL を グローバルタイマ方式を有効
にする場合は，TOPPERS_SYSTIM_GLOBAL を定義する．

グローバルタイマ方式の場合，システム時刻管理プロセッサのIDを 
TOPPERS_SYSTIM_PRCID に定義する．

----------------------------------------------------------------------
タイマアーキテクチャとOS内部のロック単位
----------------------------------------------------------------------
グローバルタイマ方式では，ジャイアントロック方式のみをサポートする．こ
れは，プロセッサ・細粒度ロックの場合はにタイムイベントコントロールやタ
イムイベントヒープの排他制御が問題となるからである．具体的には，システ
ム時刻管理プロセッサのプロセッサロックで排他制御を行うとすると，他のプ
ロセッサに割り付けられたタスクが待ち状態になる場合には，タスクロックを
二つ取得する必要があり，他の方式とコードの共通化が困難になるためである．

----------------------------------------------------------------------
カーネルティック方式によりソースコードの差異が発生する箇所
----------------------------------------------------------------------
・タイムイベントコントロールブロック・タイムイベントヒープ
  ・グローバルタイマ方式
    ・システムで単一に持つ（グローバル）．
  ・ローカルタイマ方式
    ・プロセッサ毎に持つ

・プロセッサコントロールブロック（PCB）への登録
  ・グローバルタイマ方式
    ・グローバルのタイムイベントコントロールブロック・タイムイベントヒープを登録
  ・ローカルタイマ方式
    ・タスクやタイムイベントハンドラが割り付けられたプロセッサの持つタ
      イムイベントコントロールブロック・ヒープを登録

・時間待ち時に使用するタイムイベントコントロールブロック・タイムイベントヒープ
  タスクやタイムイベントハンドラが時間待ちになる際に使用される．
  ・グローバルタイマ方式
    ・グローバルのタイムイベントコントロールブロック・タイムイベントヒープ
  ・ローカルタイマ方式
    ・タスクやタイムイベントハンドラが割り付けられたプロセッサの持つタ
      イムイベントコントロールブロック・ヒープ

・ハードウェアタイマの関連処理や割込みハンドラ
  ・グローバルタイマ方式
    ・システム時刻管理プロセッサでのみ実行
  ・ローカルタイマ方式
    ・各プロセッサで実行

・タイムイベントハンドラの初期化
  ・グローバルタイマ方式
    ・システム時刻管理プロセッサでのみ実行
  ・ローカルタイマ方式
    ・各プロセッサで実行

・タイムイベントハンドラのマイグレーション関連のシステムコール
  ・グローバルタイマ方式
    ・ノンサポート（E_NOSPTを返す）
  ・ローカルタイマ方式
    ・サポート

・タスクマイグレーション時（mig_tsk()）
  ・ローカルタイマ方式
    ・対象タスクが時間待ちの場合，マイグレーション先のプロセッサのタイ
      ムイベントヒープに挿入する．

・性能評価用システム時刻の参照（get_utm）
  ・グローバルタイマ方式
    ・ノンサポート（コンパイルエラー）
  ・ローカルタイマ方式
    ・サポート

----------------------------------------------------------------------
グローバルタイマ方式でプロセッサ・細粒度ロックのサポート（未実装）
----------------------------------------------------------------------
専用ロックを用意する．signal_timeは，このロックを取得する．

loop タイムアウトがなければリターン
・タイムアウトがあれば
 ・TMEVTにロック情報のエントリ2個追加（タスクロックとオブジェク
   トロック）
 ・NULLなら(周期ハンドラかアラームハンドラ)エントリを削除してコ
   ールバック
   コールバックされた側では，CYCCBを操作した後でロックを解放してハン
   ドラを呼び出す．
 ・NULLでない場合
   ・一旦タイムイベントロックを外す
   ・指定されたロック（タスクロック）を取る
   ・タイムイベントロックを取得
   ・先頭イベントが変化していないかチェック
     ・変化していれば，指定されたロックを解放し loop へ
   ・変化していなければ，エントリを削除してコールバック


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

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

----------------------------------------------------------------------
タスクマイグレーションシステムコール（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() により起動とマイグレーションをサポートす
る．

参考)同一プロセッサのタスクに対するact_tsk()の実行
 評価環境 : CT11MPCore 550Mhz
 ・ディスパッチあり : 2000[usec]
 ・ディスパッチなし : 1500[usec]


●システムコール仕様

・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     iafinity      : タスクの初期割付けプロセッサ
  uint_t affinity_mask : タスクの割付け可能プロセッサ 

ER
mig_tsk(ID tskid, ID prcid)
{
   ...
   t_lock_cpu()
   マイグレーション元と先のプロセッサのタスクロックを取得．
   if (異なるプロセッサに割り付けられているタスク) {
       エラー
   }
   else if (実行可能状態) {
       if (自タスクに発行) {
          if (ディスパッチ禁止中) {
             エラー
          }
          else if(同一プロセッサを指定) {
             優先順位を同一優先度で最低とする
          }
          else {
             dispatch_and_migrate()によりマイグレーション．
             移動先のプロセッサでリターンする．
          }
       }
       else {
          /* 他タスクの場合 */
          if (同一プロセッサを指定) {
             優先順位を同一優先度で最低とする
          }
          else {
             マイグレーション元のプロセッサのレディキューから外す．
             TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
             マイグレーション先のプロセッサのレディキューに入れる．
             if (マイグレーション先で最高優先順位) {
                ディスパッチリクエスト
             }
          }
       }
   }
   else if (休止状態) {
      TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
   }
   else {
      /* 待ち状態 */
      if (時間待ちでない場合) {
         TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
      }
      else {
         /* ローカルタイマ方式の場合のみ */
         マイグレーション元のプロセッサのタイムイベントヒープから削除
         移動先のプロセッサの方が時間が進んでいればタイムアウト時間を延長
         マイグレーション先のプロセッサのタイムイベントヒープに挿入
         TCB の p_pcb をマイグレーション先のプロセッサのPCBに書き換え．
      }
   }
   マイグレーション元と先のプロセッサのタスクロックを解放．
}

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

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

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

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

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

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

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

どちらの場合でも対象タスクのTCBの actprc をチェックして，0以外ならタス
クマイグレーション要求がキューイングされているため，タスクマイグレーシ
ョン処理を行う．

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

void
exit_and_migrate(ID prcid)
{
	スタックを非タスクコンテキスト用のスタックに切り替える
	exit_and_migrate_self(prcid)
}

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

void
exit_and_migrate_self(ID prcid) {
   マイグレーション元のプロセッサのレディキューから外す．
   休止状態とする(make_dormant())．
   TCB の p_pcb をマイグレーション先のプロセッサのPCBへ変更．
   TCB の actque を false へ初期化．
   TCB の actprc を 0 へ初期化．
   if (マイグレーション先で最高優先順位) {
      ディスパッチリクエスト
   }   
  タスクロックを解放．
  ディスパッチャーへ（exit_and_dispatch()）;
}


======================================================================
タイムイベントハンドラのマイグレーション
======================================================================
ローカルタイマ方式の場合は，タイムイベントハンドラはいずれかのプロセッ
サに割り付けられる．動作が開始されると，割り付けられたプロセッサのタイ
ムイベントコントロールブロック及びタイムイベントヒープによって管理され，
タイムアウトすると割り付けられたプロセッサで実行される．そこで，割り付
けられたプロセッサを変更するシステムコールを用意する．

----------------------------------------------------------------------
システムコール一覧
----------------------------------------------------------------------
  ER msta_cyc(ID cycid, ID prcid)
   ・割付けプロセッサ指定での周期ハンドラの動作開始
  ER msta_alm(ID almid, RELTIM almtim, ID prcid)
   ・割付けプロセッサ指定でのアラームハンドラの動作開始
  ER imsta_alm(ID almid, RELTIM almtim, ID prcid)
   ・割付けプロセッサ指定でのアラームハンドラの動作開始（非タスクコン
     テキスト）

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

ALMINIB
  ID      iaffinity;      /* アラームハンドラの初期割付けプロセッサ */
  uint_t  affinity_mask;  /* アラームハンドラの割付け可能プロセッサ */

----------------------------------------------------------------------
msta_cyc(ID cycid, ID prcid)の実装
----------------------------------------------------------------------
ER
msta_cyc(ID cycid, ID prcid)
{
   ...  
   t_lock_cpu()
   移動元と移動先のプロセッサのタスクロックを取得．
   if (動作していれば) {
      タイムイベントヒープから外す
   }
   else {
      cycsta = true;
   }
   周期ハンドラコントロールブロックの p_pcb に移動先のプロセッサのPCBを登録
   移動先のプロセッサのタイムイベントヒープに挿入
   全ロックを解放．
} 

----------------------------------------------------------------------
[i]msta_alm(ID almid, RELTIM almtim, ID prcid)の実装
----------------------------------------------------------------------
ER
[i]msta_alm(ID almid, RELTIM almtim, ID prcid)
{
   ...  
   t_lock_cpu()
   移動元と移動先のプロセッサのタスクロックを取得．
   if (動作していれば) {
      タイムイベントヒープから外す
   }
   else {
      almsta = true;
   }
   アラームコントロールブロックの p_pcb に移動先のプロセッサのPCBを登録
   移動先のプロセッサのタイムイベントヒープに挿入
   全ロックを解放．
} 

======================================================================
TOPPERS/SMPカーネルで追加した排他制御用の機能の扱い
======================================================================
----------------------------------------------------------------------
スピンロック機能
----------------------------------------------------------------------
異なるプロセッサのタスクと割込みハンドラ間で排他制御が必要となるため，
FMPカーネルでもサポートする．

----------------------------------------------------------------------
プロセッサ占有機能
----------------------------------------------------------------------
マイグレーションは，自プロセッサの他のタスクもしくは自分自身に限定され
ているため，CPUロック機能で実現可能．

他のプロセッサから実行状態のタスクのマイグレーションをサポートする場合
であっても，最終的には割込みを入れるので，割込み禁止で実現可能．


======================================================================
スピンロック機能
======================================================================
スピンロック機能は，異なるプロセッサで実行されている処理間の排他制御に
使用する事を想定している．このスピンロック取得状態は，「タスク間」「タ
スク・割込みハンドラ間」「割込みハンドラ間」で使用可能である．なお，
「タスク間」は，一般的にはセマフォで行うが，短い処理（例えばエリア更新
のみなど）区間で使用する事は認める．

実装は，ハードウェアが持つ排他制御機構で作成可能なロックの数の制約より，
ネイティブ方式とエミュレーション方式の2種類をサポートする．エミュレー
ション方式は，他のシステムコールで用いるオブジェクトロックを用いるため
（システムコール実行時のみ，スピンロック取得後は解放する），他のシステ
ムコールとの競合が発生する．ネイティブ方式は他のシステムコールとの競合
は発生しない．

切り替えは，マクロ SPN_TYPE を，ネイティブ方式の場合は NATIVE_SPN，エ
ミュレーション方式の場合は EMULATE_SPN と定義する．

----------------------------------------------------------------------
変更箇所
----------------------------------------------------------------------
ネイティブ方式，エミュレーション方式共に次の変更が必要となる．

・プロセッサ管理ブロック（PCB）
  ・ID locspnid : 0ならスピンロック解除状態．スピンロックを取得してい
                  れば，取得しているスピンロックのIDを保持する．

・スピンロックの強制解放
  ・タスク
  　・タスク終了時（ext_tsk()）
　   ・タスク例外ハンドラからのリターン時

　 ・割込み
　  ・割込みハンドラからのリターン時
　  ・割込みサービスルーチンからのリターン時

　  ・タイムイベントハンドラ
  　　・アラームハンドラからのリターン時
  　　・周期ハンドラからのリターン時

  ・CPU例外
　   ・CPU例外ハンドラからのリターン時
 　   ・CPU例外発生時にスピンロックを取得していない場合

  ・カーネル終了時（ext_ker()）

・unl_cpu/iunl_cpuの変更
  ・スピンロック取得状態をチェックして，取得していればアンロックしない．

----------------------------------------------------------------------
ネイティブ方式の実装
----------------------------------------------------------------------
スピンロックのオブジェクトに1個に対してハードウェアのロックを1つ割り付
ける方法．ハードウェアのロックの数に制約がない場合に用いる．

作成可能なスピンロックの数は，マクロ TMAX_NATIVE_SPN  に定義する．
また，ロック変数の型をマクロ SPNLOCK に定義する．

・スピンロック初期化ブロック
   ATR  spnatr : スピンロック属性

・スピンロック管理ブロック
    SPNLOCK       spn_lock   : ハードウェアのロック
    const SPNINIB *p_spninib : 初期化ブロックへのポインタ
    bool_t        lock_flg   : スピンロックの現在のロック状態(ref_spn)

・ターゲット依存部の関数
  ・void x_initialize_spin(ID spnid, SPNLOCK *p_spn_lock)
    ・起動時の初期化．スピンロック管理ブロックの spn_lock エントリへの
      ポインタを渡す．

  ・void [t|i]_lock_spin(SPNLOCK *p_spn_lock)
    ・CPUロック状態で呼び出される．ハードウェアスピンロックが取得を試
      み，取得できればリターンする．取得できない場合は，一旦割込みを許可
      した後，再び割込みを禁止した後にハードウェアスピンロックが取得を試
      みる．スピンロック取得状態で呼び出されることはなく，呼び出された場
      合の動作は保証する必要がない．

  ・bool_t x_try_lock_spin(SPNLOCK *p_spn_lock)
    ・ハードウェアスピンロックの取得を試みる．取得に成功した場合falseを
      返し，取得 に失敗した場合は，trueを返す．CPUロック状態で呼び出さ
      れる．

  ・void x_unlock_spin(ID spnid)
    ・スピンロックを返却する

----------------------------------------------------------------------
エミュレーション方式の実装
----------------------------------------------------------------------
他のシステムコールと同様にOS内部のロックを用いて実現する方法．ハードウ
ェアのロックの数以上にスピンロックが必要な場合に用いる．細粒度ロックの
場合は，ネイティブ方式が使用可能であるため，細粒度ロックとエミュレーシ
ョン方式を同時に有効にするとエラーとする．

・スピンロック初期化ブロック
   ATR  spnatr : スピンロック属性
   LOCK *p_obj_lock : ロックへのポインタ（プロセッサロックの場合）

・スピンロック管理ブロック
   QUEUE         dummy      : 他の同期オブジェクトと同じマクロを使うためのダミー 
   const SPNINIB *p_spninib : 初期化ブロックへのポインタ
   bool_t        lock_flg   : スピンロックの現在のロック状態


======================================================================
デッドロック回避
======================================================================
----------------------------------------------------------------------
対象システムコール・内部関数
----------------------------------------------------------------------
 ・ext_tsk(void)
 ・ter_tsk(ID tskid)
 ・rel_wai(ID tskid)/irel_wai(ID tskid)
 ・chg_pri(ID tskid, PRI tskpri)  
 ・wait_tmout()（内部関数）
 ・make_non_wait()（内部関数）

ter_tsk()については，マイグレーション対応のためリトライを許容する．
ext_tsk()も同様にマイグレーション対応のため，リトライを許容する．その
他のシステムコールに関しては，リトライしないための機構を組み込む．

make_non_wait() に関しては，この関数自体はデッドロック回避は必要としな
いが，他のシステムコールのデッドロック回避におけるリトライの回避の実現
のために，make_non_wait() を変更する必要がある．

----------------------------------------------------------------------
TCBへの追加エントリ
----------------------------------------------------------------------
・bool_t pend_relwai : タスクの待ち解除実行中を表す
・bool_t pend_chgpri : chg_pri(ID tskid, PRI tskpri)実行中を表す．
・UINT   pend_newpri : 優先度変更保留の新優先度（内部表現）

----------------------------------------------------------------------
システムコール・内部関数の実装
----------------------------------------------------------------------
●ext_tsk(void)

ER
ext_tsk(void)
{
  retry:
   ...
   自タスクのタスクロックの取得．
   ...
   if (再起動時のマイグレーション要求なし) {
      起動要求キューイングがあれば再起動．
   }
   else {
      移動先のプロセッサのPCBを取得．
      一旦自タスクのタスクロックを解放する．

      現在割り付けられているプロセッサと移動先のプロセッサのタスク
      ロックをIDの小さい順に取得．
     
      if(マイグレーション先のプロセッサが変更された場合) {
         ロックを全て解放してリトライ.
         (can_act()により発生する可能性がある)
      }
   }
}

●ter_tsk(ID tskid)

ER
ter_tsk(ID tskid)
{
   ...
   t_lock_cpu();
  retry:
   対象タスクのタスクロックの取得
   ...
   if (オブジェクト待ち以外) {
      if (再起動時のマイグレーションなし) {
         ...
      }
      else {
         /* 再起動時のマイグレーションあり */
         移動先のプロセッサのPCBを取得．
         対象タスクのタスクロックを解放する．

         現在割り付けられているプロセッサと移動先のプロセッサのタスク
         ロックをIDの小さい順に取得．
     
         if (マイグレーション先のプロセッサが変更された ||
             対象タスクと自タスクが割り付けられているプロセッサが異なる) {
             ロックを全て解放してリトライ.
             (can_act()により発生する可能性がある)
         }
         if (!(対象タスクが実行状態 || オブジェクト待ち状態)) {
             ロックを全て解放してリトライ.
             (他のタスクにより休止状態となっている場合がある)
         }
      }
   }
   else {
      /* オブジェクト待ち状態 */
      if (再起動時のマイグレーションなし) {
         タスクロックを解放．
         オブジェクトロック，タスクロックの順でロックを取得
         if (異なるオブジェクトに対して待ちとなっている) { 
            ロックを全て解放してリトライ.            
         }
      }
      else {
         /* 再起動時のマイグレーションあり */
         移動先のプロセッサのPCBを取得．
         対象タスクのタスクロックを解放．
         
         オブジェクトロックを取得
         現在割り付けられているプロセッサと移動先のプロセッサのタスク
         ロックをIDの小さい順に取得．
         取得に失敗した場合はリトライ．
         if (異なるオブジェクトに対する待ちとなっている ||
               対象タスクと自タスクが割り付けられているプロセッサが異なる) {
               ロックを全て解放してリトライ.
         }
         マイグレーション処理
      }
   }
}

●rel_wai(ID tskid)/irel_wai(ID tskid)

ER
rel_wai(ID tskid)
{
    ...
    t_lock_cpu();
    retry:
     if (オブジェクト待ちでない場合) {
        待ち解除（wait_release()）
     }
     else {
        /* オブジェクト待ちの状態 */
        p_tcb->pend_relwai = true;
        タスクロックを解放．
        オブジェクトロック，タスクロックの順でロックを取得．
        if (!p_tcb->pend_relwai) {
            全てのロックを解放．
            E_OBJで終了．
        }
        p_tcb->pend_relwai = false;
        ...
     }
}

●chg_pri(ID tskid, PRI tskpri) 

最初のロックでnewpriをTCBに登録してロックを開放．他のルーチンでの待ち
状態解除時にはnewpriを見て優先度を変更．再ロック後にnewpriの値が有効な
ら同じ待ちのはずなので通常の優先度変更を行う．

ER
chg_pri(ID tskid, PRI tskpri)
{
    ...
    t_lock_cpu();
  retry:
    if (休止状態) {
       E_OBJを返す．
    }
    else {
       /* 休止状態以外 */
       if (実行可能状態) {
          対象タスクの優先度設定（レディキューの変更）．
       }
       else if (オブジェクト待ちでない) {
          対象タスクの優先度設定（TCBのみ変更）．
       }
       else {
          /* オブジェクト待ち */
          p_tcb->pend_chgpri = true;
          p_tcb->pend_newpri = newpri;          
          タスクロックを解放．
          オブジェクトロック，タスクロックの順でロックを取得．
          ロックの取得を失敗した場合はリトライ．         
          if (!pend_chgpri) {
             全ロックを解放．
             E_OKでリターン．
          }
          優先度の設定．
       }
    }
}

●wait_tmout(TCB *tcb) （内部関数）

再ロック後に待ちでなければ何もしなくてもよい．ローカルタイマ方式の場合
は，タイムアウト処理は対象タスクと同一プロセッサで動作しているので，対
象タスクはこの間に動作せず，他のオブジェクト待ちになることはない．

グローバルタイマ方式の場合は，ジャイアントロックであるため，そもそもデ
ッドロック回避が必要ではない．

void
wait_tmout(TCB *p_tcb) 
{
    if (オブジェクト待ち) {
        p_tcb->pend_relwai = true;
        タスクロックを解放．
       retry:
        オブジェクトロック，タスクロックの順でロックを取得．
        タスクロックの取得に失敗したらリトライ．
        if (!p_tcb->pend_relwai) {
           オブジェクトロックを解放
           リターン
        }
        p_tcb->pend_relwai = false;
        オブジェクト待ちから解放
    }

}

●make_non_wait() （内部関数）

待ち状態を解除する際に通るルーチン．pend_chgpri/pend_relwaiをチェック
する．

Inline bool_t
make_non_wait(TCB *p_tcb)
{
   ...
   /* 優先度変更フラグチェック */
   if (pend _chgpri) {
       p_tcb->priority = p_tcb->pend_newpri;
       p_tcb->pend_chgpri = false;
   } 
   /* タスク強制待ち解除保留クリア */
   p_tcb->pend_relwai = false;

   if (!強制待ち状態) {
      対象タスクを実行可能状態へ移行．
   }
   else {
      /* 強制待ち状態 */
      対象タスクを強制待ち状態へ移行．
   }
}


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

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

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

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

----------------------------------------------------------------------
ter_tsk()のリトライの許容
----------------------------------------------------------------------
プロセッサロック方式または細粒度ロック方式でter_tskでオブジェクト待ち
のタスクを終了させる場合，デッドロック回避のため，一旦タスクロックを解
放してから，オブジェクトロック，タスクロックの順でロックを取得する．

両方のロックを取得した時点でタスクがオブジェクト待ちになっていない場合
や，他のオブジェクトの待ちになっている場合は，リトライするため，実行時
間の上限が定まらない．一方，FDMPカーネルではTCBに pend_tertsk を導入し
て，リトライを発生させずに実行上限時間を抑えている．

pend_tertsk を導入すると，ter_tsk で pend_tertsk をセット後，タスクロ
ックを解放して，オブジェクトロック->タスクロックの順で取得を試みる．ロ
ックを取得する前に，他のシステムコールが実行され，そのシステムコールで
待ち状態を解除する場合は，その箇所（make_non_wait()）で，pend_tertskを
チェックして，trueなら終了と再起動処理をする必要がある．

FMPカーネルでは，mact_tskが導入されており，mact_tskの要求はペンディン
グされる．ペンディングされた場合，再起動時にマイグレーションが発生する．
上記の様に，デッドロック回避時に他のシステムコールが実行された場合は，
そのシステムコールによる再起動処理でマイグレーションが発生する．

他のシステムコールは，ter_tskの対象となっているタスクが割り付けられて
いるプロセッサとは異なるプロセッサから実行される可能性があるため，異な
るプロセッサによるマイグレーションが発生することになる．

FMPカーネルでは，現状異なるプロセッサによるマイグレーションは許可して
いない．その理由としては，FMPカーネルではディスパッチャはロック取得せ
ずに動作するため，ディスパッチ中のタスクをマイグレーションしてしまうと，
OS内部のデータ構造が不整合を起こしてしまうためである．

一方，待ち状態や休止状態のタスクに対するマイグレーションは可能である．
pend_tertskを用いたデッドロック回避では，対象とするタスクは待ち状態で
あり，その待ち状態のタスクを再起動させる際にマイグレーションさせるのは
問題ない．

しかしながら，マイグレーションを実行するためには，移動先のプロセッサの
タスクロックを取得する必要があり，デッドロック回避のため，現状取得して
いる移動元のプロセッサのタスクロックをいったん解放する必要がある．

make_non_wait()は以下の関数から呼び出される内部関数である．

そのため，割込みを許可できず，割込み応答性を上限を悪化させないロックの
取得が出来ない．

 wait_complete()
 wait_tmout()
 wait_tmout_ok()
 wait_release()
 init_wait_queue()

そのため，FMPカーネルでは，pend_tertsk を導入せず，ter_tsk はリトライ
することにする．


======================================================================
タスク状態不整合問題
======================================================================
CPUロック状態/ディスパッチ禁止状態で実行中のタスクに対してsus_tskが発
行された場合の振る舞い(セマンティクス)を決定する．ter_tsk()に関しては，
FMPカーネルでは，他プロセッサのタスクに対する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ではワーニングにして処理．


======================================================================
内部関数（共通部）
======================================================================
共通部の内部関数について，ASPカーネルとの差異について説明する．

----------------------------------------------------------------------
新規追加した内部関数
----------------------------------------------------------------------
mp.c
 ・ロック取得・返却関数
 ・void initialize_pcb(void)
   ・PCBの初期化．
 ・void ipi_handler(void)
   ・プロセッサ間割込みハンドラ
 ・bool_t dispatch_request(PCB* p_pcb)
   ・ディスパッチリクエスト
 ・void ext_ker_request(void)
   ・カーネル終了リクエスト
 ・void barrier_sync(uint_t phase)
   ・バリア同期関数
 
pcb.h
 ・PCB* get_mp_p_pcb(ID prcid)
 ・TPCB* get_my_p_tpcb(void)

task.c
 ・set_lowest_precedence(TCB *p_tcb, PCB *p_pcb);
   ・優先順位を同一優先度のタスクの中で最低に
 ・void dispatch_call_texrtn(void)
   ・タスクロックを取得して call_texrtn() を呼び出す．
 ・void migrate_self(ID prcid)
   ・自タスクのマイグレーション処理
 ・void exit_and_migrate_self(ID prcid)
   ・自タスクの終了とマイグレーション処理

time_event.c
 ・ulong_t get_my_current_time(void){
   ・自プロセッサの current_time の取得．

----------------------------------------------------------------------
変更した内部関数
----------------------------------------------------------------------
alarm.c
 ・void initialize_alarm(void)
   ・タイムイベントコントロールブロックが構造体となったため．
 ・void call_almhdr(ALMCB *p_almcb)
   ・タスクロックの取得・解放を追加．
   ・スピンロックの強制解放を追加．

cyclic.c
 ・void initialize_cyclic(void)
   ・タイムイベントコントロールブロックが構造体となったため．
 ・void call_cychdr(CYCCB *p_cyccb)
   ・タスクロックの取得・解放を追加．
   ・スピンロックの強制解放を追加．

eventflag.c
 ・void initialize_eventflag(void)
   ・細粒度ロックの場合のロック変数の初期化

interrupt.c
 ・void initialize_interrupt(void)
   ・割込みのプロセッサへの割り付けのため．

mailbox.c
 ・void initialize_mailbox(void)
   ・細粒度ロックの場合のロック変数の初期化

mempfix.c
 ・void initialize_mempfix(void)
   ・細粒度ロックの場合のロック変数の初期化

pridataq.c
 ・void initialize_pridataq(void)
   ・細粒度ロックの場合のロック変数の初期化

task.c
 ・void initialize_task(void)
   ・TCBの変更による．
 ・bool_t primap_empty(PCB *p_pcb)
    ・引数にPCB*を追加
 ・uint_t primap_search(PCB *p_pcb)
    ・引数にPCB*を追加
 ・void primap_set(PCB *p_pcb, uint_t pri)
    ・引数にPCB*を追加
 ・void primap_clear(PCB *p_pcb, uint_t pri)
    ・引数にPCB*を追加
 ・TCB *search_schedtsk(PCB *p_pcb)
    ・引数にPCB*を追加
 ・bool_t make_runnable(TCB *p_tcb)
    ・PCBを取得するように変更．
 ・bool_t make_non_runnable(TCB *p_tcb)
    ・PCBを取得するように変更．
 ・void make_dormant(TCB *p_tcb)
    ・TCBの追加項目の初期化
 ・bool_t change_priority(TCB *p_tcb, uint_t newpri)
    ・PCBを取得するように変更．
 ・bool_t rotate_ready_queue(uint_t pri, PCB *p_pcb)
    ・引数にPCB*を追加
 ・void call_texrtn(void)
    ・PCBを取得するように変更．
    ・タスクロックの取得・解放を追加．
 ・void calltex(void)
    ・PCBを取得するように変更．

・time_event.h
 ・void tmevtb_enqueue(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb, RELTIM time, CBACK callback, void *arg)
    ・引数を変更（TMEVTB->TEVTCB）
 ・void tmevtb_enqueue_evttim(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb, EVTTIM time, CBACK callback, void *arg)
    ・引数を変更（TMEVTB->TEVTCB）
 ・EVTTIM tmevtb_dequeue(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb)
    ・引数を変更（TMEVTB->TEVTCB）

・time_event.c
 ・void initialize_tmevt(void)
   ・タイムイベントコントロールブロックが構造体となったため．
 ・uint_t tmevt_up(TEVTCB *p_tevtcb, uint_t index, EVTTIM time)
   ・引数にTEVTCB*を追加
 ・uint_t tmevt_down(TEVTCB *p_tevtcb, uint_t index, EVTTIM time)
   ・引数にTEVTCB*を追加
 ・void tmevtb_insert(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb, EVTTIM time)
   ・引数にTEVTCB*を追加
 ・EVTTIM tmevtb_delete(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb)
   ・引数にTEVTCB*を追加
 ・void tmevtb_delete_top(TEVTCB* p_tevtcb)
   ・引数にTEVTCB*を追加
 ・RELTIM tmevt_lefttim(TEVTCB *p_tevtcb, TMEVTB *p_tmevtb)
   ・引数にTEVTCB*を追加
 ・void signal_time(void)
   ・タスクロックを取得．

wait.h
 ・void make_wait(TCB *p_runtsk)
   ・待ちコントロールブロックの保持方法を変更したため，引数を変更．
 ・void wait_dequeue_tmevtb(TCB *p_tcb)
   ・待ちコントロールブロックの保持方法を変更したため．
 ・ID wait_tskid(QUEUE *p_wait_queue)
   ・待ちコントロールブロックの保持方法を変更したため．
 
wait.c
 ・void make_wait_tmout(TMO tmout, PCB *p_pcb)
   ・待ちコントロールブロックの保持方法を変更したため，引数を変更．
 ・bool_t make_non_wait(TCB *p_tcb)
   ・デッドロック回避のためのコードを追加．
 ・bool_t wait_complete(TCB *p_tcb)
   ・待ちコントロールブロックの保持方法を変更したため．
 ・void wait_tmout(TCB *p_tcb)
   ・デッドロック回避のためのコードを追加．
 ・void wait_tmout_ok(TCB *p_tcb)
    ・PCBを取得するように変更．
    ・異なるプロセッサに割り付けられたタスクの起床には，dispatch_request()
      を呼び出し．
 ・bool_t wait_release(TCB *p_tcb)
   ・待ちコントロールブロックの保持方法を変更したため．
 ・void wobj_queue_insert(WOBJCB *p_wobjcb, TCB *p_runtsk)
   ・PCBを使うようにしたため．
 ・void wobj_make_wait_tmout(WOBJCB *p_wobjcb, TMO tmout, TCB *p_runtsk)
   ・待ちコントロールブロックの保持方法を変更したため，引数を変更．
 ・bool_t init_wait_queue(QUEUE *p_wait_queue)
   ・起床させるタスクのタスクロックを取得・解放．

----------------------------------------------------------------------
削除した内部関数
----------------------------------------------------------------------
dataqueue.c
  ・bool_t send_data(DTQCB *p_dtqcb, intptr_t data, bool_t *p_reqdsp)
    ・取得するロックの関係で各API内に展開したため削除．
  ・bool_t force_send_data(DTQCB *p_dtqcb, intptr_t data)
    ・取得するロックの関係で各API内に展開したため削除．
  ・bool_t receive_data(DTQCB *p_dtqcb, intptr_t *p_data, bool_t *p_reqdsp)
    ・取得するロックの関係で各API内に展開したため削除．

pridataq.c
  ・bool_t send_pridata(PDQCB *p_pdqcb, intptr_t data, PRI datapri, bool_t *p_reqdsp)
    ・取得するロックの関係で各API内に展開したため削除．
  ・bool_t receive_pridata(PDQCB *p_pdqcb, intptr_t *p_data, PRI *p_datapri, bool_t *p_reqdsp)
    ・取得するロックの関係で各API内に展開したため削除．


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

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

・bool_t sense_context(void)
・void t_lock_cpu(void)
・void x_lock_cpu(void)
・bool_t t_sense_lock(void)
・bool_t x_sense_lock(void)
・bool_t sense_tskctx_unl(void)
・bool_t sense_intctx_unl(void)
・bool_t VALID_INTPRI_CHGIPM(PRI intpri)


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

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

----------------------------------------------------------------------
システムコールでのPCBの取得
----------------------------------------------------------------------
ロック取得関数の戻り値を用いていること．

プロセッサ固有データのアクセスと関連しているが，PCBは最も使用頻度の高
いプロセッサ固有データであるため，ロック取得関数が正しいPCBを返すよう
にしている．

----------------------------------------------------------------------
使用するロック関数
----------------------------------------------------------------------
システムコールで正しいロック関数を取得すること．

ロック取得関数は，コンテキスト，ロック対象，段数の違いにより数種類ある
ため，正しいものを使用すること．

----------------------------------------------------------------------
ena_int()/dis_int()の割込み番号のチェックのタイミング
----------------------------------------------------------------------
ASPカーネルでは，CPUロック状態となる前にCHECK_INTNO_DISINT()で割込み番
号が有効かチェックする．FMPカーネルでは，コア毎に操作可能な割込み番号
が異なる場合がある．そのため，CHECK_INTNO_DISINT()の後にマイグレーショ
ンが発生すると，マイグレーション後，誤った番号に対して操作を行う可能性
がある．

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


======================================================================
その他
======================================================================
----------------------------------------------------------------------
チェックマクロの変更
----------------------------------------------------------------------
ASPカーネルにおいて，システムコールの入口でCPUロック解除状態で実行され
るチェックマクロは，内部でプロセッサ固有データに対してアクセスする．そ
のため，FMPカーネルにおいては，タスクマイグレーションがあるため，これ
らのチェックマクロは正しく動作しない可能性がある．また，ターゲット依存
の関数についても，プロセッサ固有データにアクセスする可能性あるため，同
様の問題が発生する．

具体的には以下の変数や関数がチェックマクロ中でCPUロック解除状態で呼び
出されるため，アクセスや呼び出し時にマイグレーションが発生すると，マイ
グレーション終了後，以前に割り付けられたプロセッサの状態を見てしまう可
能性がある．

プロセッサ固有データ(PCB内の変数)
  dspflg, p_runtsk, locspnid

ターゲット依存の関数
  sense_context(), t_sense_lock(), x_sense_lock()

#define CHECK_TSKCTX() {									\
	if (sense_context()) {									\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_INTCTX() {									\
	if (!sense_context()) {									\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_TSKCTX_UNL() {								\
	if (sense_context() || t_sense_lock()) {				\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_INTCTX_UNL() {								\
	if (!sense_context() || i_sense_lock()) {				\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_DISPATCH() {									\
	if (sense_context() || t_sense_lock() || !dspflg) {	\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_NONSELF(p_tcb) {								\
	if ((p_tcb) == p_runtsk) {								\
		ercd = E_ILUSE;										\
		goto error_exit;									\
	}														\
}

#define CHECK_LOCSPN() {									\
	if (locspnid != 0) {									\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

ターゲット依存部の関数に関しては，それぞれの関数内で割込みを禁止して処
理を行えばよいが，実行オーバヘッドを考慮して，コンテキスト判定とCPUロ
ック状態の両方をチェックするターゲット依存の関数を用意する．この関数で
は，マイグレーションを考慮して，必要なら，割込みを禁止して判定する．リ
ターン時には割込みを元の状態とする．

 ・sense_tskctx_unl() : タスクコンテキスト かつ CPUロック解除状態なら true
 ・sense_intctx_unl() : 非タスクコンテキスト かつ CPUロック解除状態なら true

その他のsense系の依存部の関数はマイグレーションを考慮して，必要なら割
込みを禁止して判定する．

共通部の変数のチェックは，CPUロック後にチェックするようにする．具体的
には，コード中のCHECK_DISPATCH()は，CHECK_TSKCTX_UNL()とする．そして，
dspflgのみをチェックする CHECK_DISPATCH() を用意しCPUロック状態とした
後に呼び出す．

#define CHECK_TSKCTX_UNL() {								\
	if (!sense_tskctx_unl()) {								\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_INTCTX_UNL() {								\
	if (!sense_intctx_unl()) {								\
		ercd = E_CTX;										\
		goto error_exit;									\
	}														\
}

#define CHECK_DISPATCH(p_pcb) {								\
	if (!(p_pcb->dspflg)) {									\
		ercd = E_CTX;										\
		t_unlock_cpu();										\
		goto error_exit;									\
	}														\
}															\

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

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

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

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

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

----------------------------------------------------------------------
複数のタスクの待ち解除に関する留意点
----------------------------------------------------------------------
ASPカーネルでは， 以下のシステムコールは待ち解除されるタスクの数に比例
して長くなる．

 ・init_xxx
 ・イベントフラグ set_flg()

FMPカーネルでは，ロックを取得する関係ため，実行時間の上限が定まらない．

また，ini_xxxにおけるタスクロックの取得に関しては，retryを用いたリトラ
イは行えない（破壊的なコードであるため）．そのため，割込み応答時間の上
限が定まらない．初期化なので，特別扱いしてもかまわないと考える．

同じプロセッサに対して複数回割込みを入れることを許容するなら，
init_wait_queue() で make_non_wait()の戻り値をチェックして割込みを入れ
ればよい．

----------------------------------------------------------------------
[i]mrot_rdq()の必要性
----------------------------------------------------------------------
mrot_rdq()は，指定したプロセッサの特定の優先度のレディキューを回転させ
るシステムコールである．FDMPカーネルから導入され，その必要性については，
今後の課題としていた．irot_rdq()は，周期ハンドラで呼び出すことにより，
ラウンドロビンスケジューリングを実現する使い方がある．FMPカーネルでは，
グローバルタイマ方式の場合は，システム時刻管理プロセッサでのみ周期ハン
ドラが動作するため，システム時刻管理プロセッサ以外でラウンドロビンスケ
ジューリングを実現するためには，imrot_rdq()は必要であると言える．一方，
mrot_rdq()の必要性については今後議論する．

以上
