JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
デバイスドライバの記述     Oracle Solaris 10 8/11 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

パート I Solaris プラットフォーム用デバイスドライバの設計

1.  Solaris デバイスドライバの概要

2.  Solaris カーネルとデバイスツリー

3.  マルチスレッド

4.  プロパティー

5.  イベントの管理とタスクのキュー

6.  ドライバの自動設定

7.  デバイスアクセス: プログラム式入出力

8.  割り込みハンドラ

9.  ダイレクトメモリーアクセス (DMA)

10.  デバイスメモリーおよびカーネルメモリーのマッピング

11.  デバイスコンテキスト管理

12.  電源管理

13.  Solaris ドライバの強化

14.  階層化ドライバインタフェース (LDI)

パート II 特定の種類のデバイスドライバの設計

15.  文字デバイスのドライバ

16.  ブロックデバイスのドライバ

17.  SCSI ターゲットドライバ

ターゲットデバイスの概要

Sun Common SCSI Architecture の概要

一般的な制御フロー

SCSA 関数

ハードウェア構成ファイル

宣言とデータ構造体

scsi_device 構造体

scsi_pkt 構造体 (ターゲットドライバ)

SCSI ターゲットドライバの自動構成

probe() エントリポイント (SCSI ターゲットドライバ)

attach() エントリポイント (SCSI ターゲットドライバ)

detach() エントリポイント (SCSI ターゲットドライバ)

getinfo() エントリポイント (SCSI ターゲットドライバ)

資源割り当て

scsi_init_pkt() 関数

scsi_sync_pkt() 関数

scsi_destroy_pkt() 関数

scsi_alloc_consistent_buf() 関数

scsi_free_consistent_buf() 関数

コマンドの構築とトランスポート

コマンドの構築

ターゲット機能の設定

コマンドのトランスポート

同期 scsi_transport() 関数

コマンドの完了

パケットの再利用

自動要求検知モード

ダンプの処理

SCSI オプション

18.  SCSI ホストバスアダプタドライバ

19.  ネットワークデバイスのドライバ

20.  USB ドライバ

パート III デバイスドライバの構築

21.  ドライバのコンパイル、ロード、パッケージ化、およびテスト

22.  デバイスドライバのデバッグ、テスト、およびチューニング

23.  推奨されるコーティング方法

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスの概要

C.  64 ビットデバイスドライバの準備

D.  コンソールフレームバッファードライバ

索引

コマンドの構築とトランスポート

ホストバスアダプタドライバは、デバイスへのコマンドの転送を担当します。さらに、ドライバは低レベル SCSI プロトコルの処理も担当します。scsi_transport(9F) ルーチンは、転送のためにパケットをホストバスアダプタドライバに渡します。ターゲットドライバは、有効な scsi_pkt(9S) 構造体の作成を担当します。

コマンドの構築

ルーチン scsi_init_pkt(9F) は、この例で示すように、SCSI CDB に領域を割り当て、必要な場合には DMA 資源を割り当てて、pkt_flags フィールドを設定します。

pkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
CDB_GROUP0, 1, 0, 0, SLEEP_FUNC, NULL);

この例では、渡された buf(9S) 構造体ポインタで指定されているように DMA 資源を割り当てるとともに、新しいパケットを作成しています。SCSI CDB はグループ 0 (6 バイト) のコマンドに割り当てられています。pkt_flags フィールドは 0 に設定されていますが、pkt_private フィールドに割り当てられた領域はありません。scsi_init_pkt(9F) に対するこの呼び出しは、SLEEP_FUNC パラメータのため、使用できる資源が現在ない場合はいつまでも資源を待機します。

次のステップは、scsi_setup_cdb(9F) 関数を使用して SCSI CDB を初期化することです。

    if (scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
     SCMD_READ, bp->b_blkno, bp->b_bcount >> DEV_BSHIFT, 0) == 0)
     goto failed;

この例では、グループ 0 のコマンド記述子ブロックを構築しています。この例の pkt_cdbp フィールドには次のように値が格納されます。


注 - scsi_setup_cdb(9F) は、ターゲットデバイスの論理ユニット番号 (LUN) を、SCSI コマンドブロックのバイト 1 の 5-7·ビットで設定することをサポートしていません。この要件は SCSI-1 で定義されています。コマンドブロック内に LUN ビットが設定されている必要がある SCSI-1 デバイスの場合は、scsi_setup_cdb(9F) ではなく、makecom_g0(9F) または同等の関数を使用します。


SCSI CDB を初期化したら、パケット内にあるほかの 3 つのフィールドを初期化し、パケットへのポインタとして状態構造体内に格納します。

pkt->pkt_private = (opaque_t)bp;
pkt->pkt_comp = xxcallback;
pkt->pkt_time = 30;
xsp->pkt = pkt;

buf(9S) ポインタは、後から完了ルーチンで使用するため、pkt_private フィールドに保存されます。

ターゲット機能の設定

ターゲットドライバは scsi_ifsetcap(9F) を使用して、ホストバスアダプタドライバの機能を設定します。cap は名前と値の組で、NULL で終わる文字列と整数値から構成されます。機能の現在の値は、scsi_ifgetcap(9F) を使用して取得できます。scsi_ifsetcap(9F) では、バス上のすべてのターゲットに対して機能を設定できます。

ただし、一般に、ターゲットドライバによって所有されていないターゲットの機能を設定することは推奨されません。この運用方法は、HBA ドライバで汎用的にサポートされているわけではありません。切断と同期などの一部の機能は、デフォルトでは HBA ドライバによって設定できます。その他の機能は、ターゲットドライバによって明示的に設定する必要が生じる可能性があります。たとえば、wide-xfer とタグ付きキューイングはターゲットドライバで設定する必要があります。

コマンドのトランスポート

scsi_pkt(9S) 構造体に値が格納されたら、scsi_transport(9F) を使用して、構造体をバスアダプタドライバに渡します。

    if (scsi_transport(pkt) != TRAN_ACCEPT) {
     bp->b_resid = bp->b_bcount;
     bioerror(bp, EIO);
     biodone(bp);
     }

scsi_transport(9F) からのほかの戻り値は次のとおりです。


注 - scsi_device(9S) 構造体内の sd_mutex という mutex は、scsi_transport(9F) の呼び出し全体にわたって保持してはいけません。


scsi_transport(9F)TRAN_ACCEPT を返す場合、パケットはホストバスアダプタドライバで管理されるものになっています。パケットは、コマンド完了ルーチンが呼び出されるまで、ターゲットドライバからアクセスしてはいけません。

同期 scsi_transport() 関数

パケット内に FLAG_NOINTR が設定されている場合、コマンドが完了するまで scsi_transport(9F) は復帰しません。コールバックは実行されません。


注 - 割り込みのコンテキストで FLAG_NOINTR を使わないでください。


コマンドの完了

ホストバスアダプタドライバがコマンドを完了すると、ドライバはパケットの完了コールバックルーチンを呼び出します。ドライバは次に、scsi_pkt(9S) 構造体へのポインタをパラメータとして渡します。パケットのデコード後、完了ルーチンが適切な操作を実行します。

例 17-5は、簡単な完了コールバックルーチンを表しています。このコードは、トランスポートの失敗をチェックします。失敗の場合、ルーチンはコマンドを再試行するのではなく、実行を断念します。ターゲットがビジー状態の場合、あとでコマンドを再送信するために、追加のコードが必要です。

コマンドがチェック条件になった場合、要求の自動検知が有効になっている場合を除き、ターゲットドライバが要求検知コマンドを送信する必要があります。

その他の場合、コマンドは成功したことになります。コマンドの処理の最後に、コマンドはパケットを破棄し、biodone(9F) を呼び出します。

バスのリセットやパリティーの問題など、トランスポートエラーが発生した場合、ターゲットドライバは scsi_transport(9F) を使用してパケットを再送信できます。再送信の前に、パケット内の値を変更する必要はありません。

次の例では、完了しなかったコマンドの再試行は試みていません。


注 - 割り込みのコンテキストでは、通常、ターゲットドライバのコールバック関数が呼び出されます。結果としてコールバック関数は、スリープすることがあってはなりません。


例 17-5 SCSI ドライバの完了ルーチン

static void
xxcallback(struct scsi_pkt *pkt)
{
    struct buf        *bp;
    struct xxstate    *xsp;
    minor_t           instance;
    struct scsi_status *ssp;
    /*
     * Get a pointer to the buf(9S) structure for the command
     * and to the per-instance data structure.
     */
    bp = (struct buf *)pkt->pkt_private;
    instance = getminor(bp->b_edev);
    xsp = ddi_get_soft_state(statep, instance);
    /*
     * Figure out why this callback routine was called
     */
    if (pkt->pkt_reason != CMP_CMPLT) {
       bp->b_resid = bp->b_bcount;
       bioerror(bp, EIO);
       scsi_destroy_pkt(pkt);          /* Release resources */
       biodone(bp);                    /* Notify waiting threads */ ;
    } else {
       /*
        * Command completed, check status.
        * See scsi_status(9S)
        */
       ssp = (struct scsi_status *)pkt->pkt_scbp;
       if (ssp->sts_busy) {
           /* error, target busy or reserved */
       } else if (ssp->sts_chk) {
           /* Send a request sense command. */
       } else {
        bp->b_resid = pkt->pkt_resid;  /* Packet completed OK */
        scsi_destroy_pkt(pkt);
        biodone(bp);
       }
    }
}

パケットの再利用

ターゲットドライバは次の方法でパケットを再利用できます。

自動要求検知モード

キューイングが使用される場合、タグ付きのキューキングであるか、タグなしのキューイングであるかにかかわらず、自動要求検知モードを使用することが推奨されます。CAC (Contingent Allegiance Condition) は後続のコマンドによってクリアされ、結果として検知データは失われます。ほとんどの HBA ドライバは、ターゲットドライバのコールバックを実行する前に次のコマンドを開始します。その他の HBA ドライバは、別の優先順位が低いスレッドを使用してコールバックを実行できます。この方法では、パケットがチェック条件で完了したことをターゲットドライバに通知するために必要な時間が増加する可能性があります。この場合、ターゲットドライバは、検知データの取得に間に合うように要求検知コマンドを送信できないことがあります。

この検知データの損失を回避するには、チェック条件が検出された場合に HBA ドライバまたはコントローラが要求検知コマンドを発行する必要があります。このモードは、自動要求検知モードと呼ばれます。すべての HBA ドライバが自動要求検知モードに対応しているわけではなく、一部のドライバは自動要求検知モードが有効な場合にのみ動作可能であることに注意してください。

ターゲットドライバは、scsi_ifsetcap(9F) を使用して自動要求検知モードを有効にします。次に、自動要求検知を有効にする例を示します。

例 17-6 自動要求検知モードの有効化

static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    struct xxstate *xsp;
    struct scsi_device *sdp = (struct scsi_device *)
    ddi_get_driver_private(dip);
    /*
     * Enable auto-request-sense. An auto-request-sense command might
     * fail due to a BUSY condition or transport error. Therefore,
     * it is recommended to allocate a separate request sense
     * packet as well.
     * Note that scsi_ifsetcap(9F) can return -1, 0, or 1
     */
    xsp->sdp_arq_enabled =
    ((scsi_ifsetcap(ROUTE, "auto-rqsense", 1, 1) == 1) ? 1 : 0);
    /*
     * If the HBA driver supports auto request sense then the
     * status blocks should be sizeof (struct scsi_arq_status).
     * Else, one byte is sufficient.
     */
    xsp->sdp_cmd_stat_size =  (xsp->sdp_arq_enabled ?
    sizeof (struct scsi_arq_status) : 1);
    /* ... */
}

scsi_init_pkt(9F) を使用してパケットが割り当てられていて、このパケットで自動要求検知が必要な場合、追加の領域が必要になります。ターゲットドライバは、自動要求検知構造体を保持するため、ステータスブロック用のこの領域を要求する必要があります。要求検知コマンドで使用される検知の長さは、struct scsi_extended_sensesizeof です。ステータスブロックに対して struct scsi_status から sizeof を割り当てることで、個々のパケットごとに自動要求検知を無効にできます。

パケットは通常どおり、scsi_transport(9F) を使用して送信されます。このパケットでチェック条件が発生すると、ホストバスアダプタドライバは次のステップを実行します。

ターゲットドライバのコールバックルーチンでは、pkt_state 内の STATE_ARQ_DONE ビットをチェックして、検知データを使用可能であることを検証する必要があります。STATE_ARQ_DONE は、チェック条件が発生したこと、および要求検知が実行されたことを意味します。パケットで自動要求検知が一時的に無効にされている場合、以降の検知データの取得を保証することはできません。

その後、ターゲットドライバで自動要求検知コマンドが正常に完了し、検知データがデコードされたかどうかを検証する必要があります。

ダンプの処理

dump(9E) エントリポイントは、システム障害やチェックポイント操作が発生した場合に、仮想アドレス空間の一部を、指定されたデバイスに直接コピーします。cpr(7)dump(9E) のマニュアルページを参照してください。dump(9E) エントリポイントは、割り込みを使わずにこの操作を実行できる必要があります。

dump() の引数は次のとおりです。

dev

ダンプデバイスのデバイス番号

addr

ダンプを開始するカーネルの仮想アドレス

blkno

デバイス上の最初の出力先ブロック

nblk

ダンプするブロックの数

例 17-7 dump(9E) ルーチン

static int
xxdump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk)
{
    struct xxstate     *xsp;
    struct buf         *bp;
    struct scsi_pkt    *pkt;
    int    rval;
    int    instance;

    instance = getminor(dev);
    xsp = ddi_get_soft_state(statep, instance);

    if (tgt->suspended) {
    (void) pm_raise_power(DEVINFO(tgt), 0, 1);
    }

    bp = getrbuf(KM_NOSLEEP);
    if (bp == NULL) {
    return (EIO);
    }

/* Calculate block number relative to partition. */
    
bp->b_un.b_addr = addr;
    bp->b_edev = dev;
    bp->b_bcount = nblk * DEV_BSIZE;
    bp->b_flags = B_WRITE | B_BUSY;
    bp->b_blkno = blkno;

    pkt = scsi_init_pkt(ROUTE(tgt), NULL, bp, CDB_GROUP1,
    sizeof (struct scsi_arq_status),
    sizeof (struct bst_pkt_private), 0, NULL_FUNC, NULL);
    if (pkt == NULL) {
    freerbuf(bp);
    return (EIO);
    }
    (void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
        SCMD_WRITE_G1, blkno, nblk, 0);
    /*
     * While dumping in polled mode, other cmds might complete
     * and these should not be resubmitted. we set the
     * dumping flag here which prevents requeueing cmds.
     */
    tgt->dumping = 1;
    rval = scsi_poll(pkt);
    tgt->dumping = 0;

    scsi_destroy_pkt(pkt);
    freerbuf(bp);

    if (rval != DDI_SUCCESS) {
    rval = EIO;
    }

    return (rval);
}