Solaris 8 のソフトウェア開発 (追補)

第 4 章 SPARC: ドライバ強化のテストハーネス

Solaris 8 4/01 リリースの SPARC 版で、ドライバ強化のテストハーネスが新規に追加されました。Solaris デバイスドライバの作成方法についての詳細は、『Writing Device Drivers』を参照してください。

ドライバ強化テストハーネスは、Solaris デバイスドライバの開発ツールです。開発中のドライバがハードウェアにアクセスする際に、テストハーネスは模擬的なハードウェア障害 (シミュレーション) を投入します。この章では、テストハーネスの構成方法、エラー投入仕様 (errdefs) の作成方法、デバイスドライバでのテストの実行方法について説明します。


注 –

最新のマニュアルページを参照するには、man コマンドを使用してください。Solaris 8 Update リリースのマニュアルページには、「Solaris 8 Reference Manual Collection」には記載されていない新しい情報も提供されています。


テストハーネスについて

強化されたデバイスドライバは、可能性として考えられるハードウェアの障害に対し耐性があります。デバイスドライバの耐性は、ドライバの開発工程の一環としてテストしておく必要があります。こうしたテストでは、ハードウェアによくあるさまざまな障害に対し、ドライバはきちんと管理された方法で繰り返し対処できることを確認します。ドライバ強化テストハーネスを使用すると、ドライバの開発者はソフトウェアでハードウェア障害のシミュレーション (模擬的なテスト) が行えます。

テストハーネスはさまざまな DDI ルーチンに対するドライバからの呼び出しを横取りし、あたかもハードウェアが原因であるかのように、呼び出しの結果を破壊します。ハーネスでは、特定のレジスタへのアクセスに対する破壊を定義することができますし、よりランダムなタイプの破壊を定義することもできます。


注 –

Solaris DDI/DKI に準拠するためには、ドライバは DDI ルーチンを使用して、すべての I/O アクセスを実行する必要があります。


テストハーネスでは、指定された作業負荷の実行時におけるすべてのレジスタアクセスとダイレクトメモリーアクセス (DMA)、割り込みの使用状況を追跡することによって、テストスクリプトが自動的に生成できます。生成されるスクリプトでは、その作業負荷が再実行されると同時に、アクセスのたびに一連の障害が投入されます。

ドライバのテスト担当者はさらにテストケースを追加して、一層不明確な障害経路を発生させなければなりません。また、生成されたスクリプトから重複しているテストケースを削除する必要もあります。

テストハーネスは bofi というデバイスドライバと、ユーザーレベルの 2 つのユーティリティである th_define(1M) と th_manage(1M) として実装されています。bofi は、bus_ops 障害投入 (fault injection) を意味しています。

テストハーネスは次の作業を行います。

障害投入

ドライバ強化テストハーネスは、ドライバがハードウェアにアクセスするたびにそれを横取りし、必要に応じてアクセスを破壊します。この節では、ドライバの耐性テストで障害を作成するとき、理解しておく必要のある事柄について説明します。

データアクセス関数

Solaris デバイスはデバイス (devinfo) ツリーというツリー状の構造内で管理されます。devinfo ツリーの各ノードには、システム内にあるデバイスの特定のインスタンスに関する情報が保存されています。各末端ノードはデバイスドライバに対応しています。それ以外のノードは連結 (nexus) ノードといいます。通常は、1 つの連結 (nexus) が 1 つのバスを表します。バスノードは末端ドライバをバスへの依存関係から分離し、その結果、アーキテクチャ上独立したドライバが作成できます。

DDI 関数の多く (特にデータアクセス関数 (DAF)) は、結果としてバス連結ドライバに対する上位呼び出しになります。末端ドライバはハードウェアにアクセスすると、アクセスルーチンにハンドルを渡します。バス連結はハンドルの操作方法を知っており、要求に応じます。DDI 準拠のドライバはこれらの DDI アクセスルーチンだけを使用して、ハードウェアにアクセスします。テストハーネスはこれらの上位呼び出しが指定されたバス連結に到達する前に横取りします。データアクセスがドライバのテスト担当者で指定されている基準に一致すると、そのアクセスは破壊されます。データアクセスが基準に一致しなかった場合は、バス連結に渡され、通常どおりに処理されます。

ドライバは ddi_map_regs_setup (diprsetmaoffsetsizehandle) 関数を使用して、アクセスハンドルを取得します。引数では、対応付け (マッピング) 対象の「オフボード」メモリーを指定します。ハンドルはドライバをバス階層構造の詳細から分離するためのものなので、対応付けされた I/O アドレスをドライバが参照するときは、返されたハンドルを使用する必要があります。したがって、返される対応付けされたアドレスである ma を直接使用しないようにしてください。対応付けされたアドレスを直接使用すると、DAF の機構を使用しないことになります。

プログラムされた I/O では、DAF の組み合わせは次のようになります。

Xrepcnt は、転送されるバイト数です。X は 8 バイト、16 バイト、32 バイト、または 64 バイトのバス転送サイズです。

DMA には類似した、さらに充実した一群の DAF があります。

テストハーネスの設定

ドライバ強化テストハーネスは Solaris 開発者クラスタや全体ディストリビューションクラスタに含まれています。どちらの Solaris クラスタもインストールしていない場合は、プラットフォームに合ったテストハーネスパッケージを手作業でインストールする必要があります。

テストハーネスのインストール

テストハーネスパッケージ (SUNWftduuSUNWftdur、および SUNWftdux) をインストールするときは、pkgadd(1M) を使用します。

パッケージの存在するディレクトリに移動し、スーパーユーザーとして次のように実行します。


# pkgadd -d . SUNWftduu SUNWftdur SUNWftdux

テストハーネスの構成

テストハーネスをインストールしたら、/kernel/drv/bofi.conf ファイルを編集して、ドライバに作用できるようにハーネスを構成します。テストハーネスの属性については、次の節の説明を参照してください。

ハーネスの構成が完了したら、システムを再起動してハーネスドライバを読み込みます。

テストハーネスの属性

テストハーネスの動作は、/kernel/drv/bofi.conf 構成ファイルに設定されている起動時の属性によって制御されます。

ハーネスを最初にインストールしたときに、次の属性を設定して、ドライバへの DDI アクセスをハーネスが横取りするようにします。

bofi-nexus

PCI バスなどのバス連結の種類

bofi-to-test

テスト中のドライバの名前

たとえば xyznetdrv という PCI バスネットワークドライバをテストする場合は、次の属性値を設定します。


bofi-nexus="pci"
bofi-to-test="xyznetdrv"

上記以外の他の属性は、PIO を使用している周辺装置からの読み書き、および DMA を使用している周辺装置とデータのやりとりをする、Solaris DDI データアクセスメカニズムの使用状況やハーネスの検査に関する属性です。

bofi-range-check

この属性を設定すると、テストハーネスは PIO DAF に渡される引数の整合性が検査されます。

bofi-ddi-check

この属性を設定すると、ddi_map_regs_setup() によって返される対応付けされたアドレスが DAF のコンテキスト以外では使用されていないことを、テストハーネスが確認します。

bofi-sync-check

この属性を設定すると、テストハーネスは DMA 関数の使い方が正しいことを検証し、ドライバが標準に準拠して ddi_dma_sync() を使用していることを確認します。

ドライバのテスト

この節では、th_define(1M) コマンドと th_manage(1M) コマンドを使用して、障害を作成し投入する方法を説明します。

障害の作成

th_define(1M) ユーティリティは、errdef を定義するための、bofi デバイスドライバへのインタフェースを提供します。1 つの errdef が、デバイスドライバのハードウェアへのアクセスを破壊する方法に関する 1 つの仕様に対応しています。th_define のコマンド行引数によって、投入する障害の正確な種類を指定します。指定した引数によって矛盾のない errdef が定義される場合は、th_define プロセスは bofi ドライバに errdef を格納します。errdef で定められている基準に達するまで、プロセスは停止状態になります。実際には、アクセスカウントがゼロ (0) になると、停止状態が終了します。

障害の投入

テストハーネスはデータアクセスのレベルで動作します。データアクセスには次のような特性があります。

テストハーネスはデータのアクセスを横取りし、該当する障害をドライバに投入します。th_define(1M)コマンドで指定された errdef によって、次の情報がエンコードされます。

-a acc_chk オプションを使用すると、errdef でフレームワーク障害をシミュレートできます。

障害投入プロセス

障害を投入するプロセスには、次の 2 つの段階があります。

  1. th_define コマンドを使用して errdef を作成します。

    テストの定義を bofi ドライバに渡して、errdef を作成します。bofi ドライバは定義を保持するので、th_manage(1M) を使用して定義にアクセスできます。

  2. 作業負荷を作成し、th_manage を使用して errdef を起動し管理します。

    th_manage(1M) コマンドは、bofi ハーネスドライバが認知するさまざまな ioctl に対するユーザーインタフェースです。th_manage はドライバ名とインスタンスのレベルで動作します。th_manage には、アクセスハンドルを一覧表示する get_handles、errdef を起動する start、errdef を中止する stop などのコマンドがあります。

    errdef を起動すると、対象となるデータのアクセスに障害が発生します。th_manage ユーティリティでは、errdef の現在の状態を表示する broadcast や errdef をクリアする clear_errors などのコマンドがサポートされています。

    詳細は、th_define(1M) および th_manage(1M) のマニュアルページを参照してください。

テストハーネスの警告

テストハーネスは、警告メッセージを次の方法で処理するように設定できます。

2 つめの方法を取ると、問題の根本原因を突き止めやすくなります。

bofi-range-check 属性値を warn に設定すると、ドライバによる DDI 関数の範囲違反をハーネスが検出したときに、次のメッセージが出力されます(panic に設定されている場合はパニック状態になります)。


ddi_getX() out of range addr %x not in %x
ddi_putX() out of range addr %x not in %x
ddi_rep_getX() out of range addr %x not in %x
ddi_rep_putX() out of range addr %x not in %x

X は 8、16、32、または 64 です。

ハーネスが 1000 を超える割り込みを挿入するように要求された場合には、ドライバが割り込みジャバーを検出しなかった場合に、次のメッセージが出力されます。


undetected interrupt jabber - %s %d

スクリプトによるテストプロセスの自動化

th_define ユーティリティでアクセスログタイプを指定して、障害投入テストスクリプトが作成できます。


# th_define -n name -i instance -a log [-e fixup_script]

th_define はインスタンスをオフラインにし、またオンラインに戻します。次に、修正スクリプト (fixup_script) が記述している作業負荷を実行し、ドライバインスタンスが行う I/O アクセスをログに記録します。

修正スクリプトは省略可能ないくつかの引数を指定されて 2 度呼び出されます (インスタンスがオフラインにされる直前と、オンラインに戻された後の 2 回です)。次の変数が、呼び出された実行可能ファイルの環境に渡されます。

DRIVER_PATH

インスタンスのデバイスパス

DRIVER_INSTANCE

ドライバのインスタンス番号

DRIVER_UNCONFIGURE

インスタンスがこれからオフラインになるときは、1 に設定

DRIVER_CONFIGURE

インスタンスがオンラインに戻されたときは、1 に設定

通常、修正スクリプトはテスト中のデバイスがオフラインとなってよい状態 (未構成)にあるか、あるいはエラー投入が適切な状態 (構成済み、エラーなし、および作業負荷を処理中) であることを確認します。次に、ネットワークドライバ用の最小限のスクリプトの例を示します。


#!/bin/ksh
driver=xyznetdrv
ifnum=$driver$DRIVER_INSTANCE

if [[ $DRIVER_CONFIGURE = 1 ]]; then
   ifconfig $ifnum plumb	
   ifconfig $ifnum ...	
   ifworkload start $ifnum
elif [[ $DRIVER_UNCONFIGURE = 1 ]]; then	
   ifworkload stop $ifnum	
   ifconfig $ifnum down	
   ifconfig $ifnum unplumb
fi
exit $?

注 –

ifworkload は作業負荷をバックグラウンドタスクとして開始する必要があります。障害の投入は、修正スクリプトがテスト中のドライバを構成してオンラインに戻した後で行われます (DRIVER_CONFIGURE は 1 に設定されます)。


-e fixup_script オプションを指定する場合は、コマンド行の最後に指定しなければなりません。指定しないと、デフォルトのスクリプトが使用されます。デフォルトのスクリプトは、テスト中のデバイスのオフラインとオンラインを繰り返し試行します。従って、作業負荷は、ドライバのアタッチとデタッチの処理となります。

出来上がったログは、自動障害投入テストに適した、いくつかの実行可能なスクリプトに変換されます。これらのスクリプトは、driver.test.id という名前で、現在のディレクトリの下のサブディレクトリに作成されます。スクリプトはドライバに障害を 1 度に 1 つずつ投入します。同時に、修正スクリプトが記述している作業負荷を実行します。

ドライバのテスト担当者は、テスト自動化プロセスで生成された errdef を大部分制御できます。詳細は、th_define(1M) のマニュアルページを参照してください。

テスト担当者がテストスクリプトに適した作業負荷の範囲を選択すれば、ハーネスはドライバの強化の多くの局面をテストできます。しかし、すべての局面を網羅するためには、テスト担当者は別のテストケースを手作業で作成しなければならなくなる場合があります。これらのケースは、テストスクリプトに追加します。テストを適切な時間内に完了させるには、テスト担当者は重複しているテストケースを手作業で削除する必要がある場合があります。

自動テストプロセス

自動テストのプロセスは、次のようになります。

  1. ドライバの何をテストするかを確認します。

    次のようにドライバがハードウェアに作用する部分は、すべてテストします。

    • アタッチとデタッチ

    • スタック下の接続と切り離し

    • 通常のデータ転送

    • 文書化されたデバッグモード

    使用モードごとに、作業負荷スクリプト (fixup_script) を生成する必要があります。

  2. 使用モードごとに、デバイスの構成と構成の解除を行い、作業負荷を作成し終了する、実行可能なプログラム (fixup_script) を作成します。

  3. errdef によってアクセスの種類を -a log と指定し、th_define を実行します。

  4. ログがいっぱいになるまで待ちます。

    ログには、bofi ドライバの内部バッファのダンプが入っています。このデータはスクリプトの最初に記載されます。

    ログの作成には数秒から数分かかるので、th_manage broadcast コマンドを使用して進行状況を検査します。

  5. 作成されたテストディレクトリに移動し、マスターテストスクリプトを実行します。

    マスタースクリプトは、生成されたテストスクリプトを順次実行します。レジスタセットごとに、テストスクリプトが生成されます。

  6. 分析結果を保存します。

    success (corruption reported)success (corruption undetected) などの正常に終了したテスト結果は、テスト中のドライバが正常に動作していることを示しています。

    出力に test not triggered という失敗が 2、3 含まれても問題ではありませんが、そうした失敗がそれ以上になった場合には、テストがうまくいっていないことを示します。テストスクリプトが生成されたときと同じレジスタにドライバがアクセスしていないときは、こうした失敗が発生することがあります。

  7. ドライバの複数のインスタンスに対して同時にテストを実行し、エラーパスのマルチスレッド化をテストします。

    たとえば、th_define コマンドごとに、テストスクリプトとマスタースクリプトが入ったディレクトリを作成します。


    # th_define -n xyznetdrv -i 0 -a log -e script
    # th_define -n xyznetdrv -i 1 -a log -e script
    

    マスタースクリプトを作成したら、それらを同時に実行します。


    注 –

    生成されたスクリプトでは、ログ対象の errdef がアクティブであった間に記録されたログ内容に基づいた障害投入のシミュレーションだけが生成されます。作業負荷を定義するときは、必要な結果がログに記録されることを確認し、出来上がったログや障害投入の仕様も分析します。出来上がったテストスクリプトが行ったハードウェアのアクセスの範囲が、要望どおりであることを確認する必要があります。