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.  割り込みハンドラ

割り込みハンドラの概要

デバイス割り込み

高レベルの割り込み

レガシー割り込み

標準メッセージシグナル割り込みと拡張メッセージシグナル割り込み

MSI 割り込み

MSI-X 割り込み

ソフトウェア割り込み

DDI 割り込み関数

割り込み許可フラグ関数

割り込み初期化関数と割り込み破棄関数

優先順位管理関数

ソフト割り込み関数

割り込み関数の例

割り込みの登録

レガシー割り込みの登録

MSI 割り込みの登録

割り込みリソース管理

割り込みリソース管理機能

コールバックのインタフェース

コールバックハンドラ関数の登録

コールバックハンドラ関数の登録解除

コールバックハンドラ関数

割り込み要求のインタフェース

割り込みの割り当て

要求する割り込みベクターの数の変更

割り込みの使用率と柔軟性

割り込みリソース管理の実装例

割り込みハンドラの機能

高レベルの割り込みの処理

高レベルの mutex

高レベルの割り込み処理の例

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

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

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

12.  電源管理

13.  Solaris ドライバの強化

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

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

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

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

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

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

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

20.  USB ドライバ

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

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

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

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

パート IV 付録

A.  ハードウェアの概要

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

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

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

索引

割り込みリソース管理

この節では、多数の異なる割り込み条件を生成する可能性のあるデバイスのドライバで、割り込みリソース管理機能を利用して割り込みベクターの割り当てを最適化する方法について説明します。

割り込みリソース管理機能

割り込みリソース管理機能を使用すると、ドライバの割り込み構成を動的に管理することで、デバイスドライバで使用する割り込みリソースを増やすことができます。割り込みリソース管理機能を使用しないと、割り込み処理の構成は通常、ドライバの attach (9E) ルーチンでのみ行われます。割り込みリソース管理機能では、システムの変更を監視し、その変更に応答して各デバイスに許可する割り込みベクターの数を再計算して、ドライバの新しい割り込みベクター割り当ての影響を受ける、関連するドライバに通知します。関連するドライバとは、コールバックハンドラを登録したドライバのことです (「コールバックのインタフェース」を参照)。割り込みベクターの再割り当ての原因になる可能性のある変更には、デバイスの追加や削除、明示的な要求が含まれます (「要求する割り込みベクターの数の変更」を参照)。

割り込みリソース管理機能は、すべての Solaris プラットフォームで利用できるわけではありません。この機能は、MSI-X 割り込みを利用する PCIe デバイスでのみ利用できます。


注 - 割り込みリソース管理機能を利用するドライバは、この機能が利用できないときに適切に順応できる必要があります。


割り込みリソース管理機能が利用できる場合、ドライバは、この機能が利用できない場合に割り当てられるよりも多くの割り込みベクターにアクセスできます。ドライバで利用できる割り込みベクターの数が増えると、ドライバが割り込み条件を処理する効率が向上することがあります。

割り込みリソース管理機能では、次の制約に応じて、関連するドライバに許可される割り込みベクターの数を動的に調整します。

指定された任意の時点でデバイスで利用できるようになる割り込みベクターの数は、次の条件により異なることがあります。

ドライバは、割り込みリソース管理機能を利用するために次のサポートを提供する必要があります。

コールバックのインタフェース

ドライバは、次のインタフェースを使用してコールバックサポートを登録する必要があります。

表 8-1 コールバックサポートのインタフェース

インタフェース
データ構造体
説明
ddi_cb_register()
ddi_cb_flags_tddi_cb_handle_t
コールバックハンドラ関数を登録して、特定のタイプのアクションを受信します。
ddi_cb_unregister()
ddi_cb_handle_t
コールバックハンドラ関数の登録を解除します。
(*ddi_cb_func_t)()
ddi_cb_action_t
処理する各アクションに関連のあるコールバックアクションと特定の引数を受信します。

コールバックハンドラ関数の登録

ドライバのコールバックハンドラ関数を登録するときは、ddi_cb_register(9F) 関数を使用します。

int
ddi_cb_register (dev_info_t *dip, ddi_cb_flags_t cbflags,
                 ddi_cb_func_t cbfunc, void *arg1, void *arg2,
                 ddi_cb_handle_t *ret_hdlp);

ドライバは、1 つのコールバック関数のみを登録できます。この 1 つのコールバック関数が、個々のすべてのコールバックアクションを処理するために使用されます。cbflags パラメータは、アクションが発生したときにドライバが受信するアクションのタイプを判定します。cbfunc() ルーチンは、関連するアクションをドライバが処理するときに常に呼び出されます。ドライバは、cbfunc() ルーチンを実行するたびに自分に送信する 2 つのプライベート引数 (arg1arg2) を指定します。

cbflags() パラメータは、ドライバがサポートするアクションを指定する列挙型です。

typedef enum {
        DDI_CB_FLAG_INTR
} ddi_cb_flags_t;

割り込みリソース管理アクションのサポートを登録するには、ドライバでハンドラを登録して DDI_CB_FLAG_INTR フラグを含める必要があります。コールバックハンドラが正常に登録されると、ret_hdlp パラメータを介して不透明なハンドルが返されます。ドライバでコールバックハンドラの処理が終了すると、ドライバは ret_hdlp パラメータを使用してコールバックハンドラの登録を解除できます。

コールバックハンドラをドライバの attach(9F) エントリポイントで登録します。不透明なハンドルをドライバのソフト状態に保存します。ドライバの detach(9F) エントリポイントでコールバックハンドラの登録を解除します。

コールバックハンドラ関数の登録解除

ドライバのコールバックハンドラ関数の登録を解除するときは、ddi_cb_unregister(9F) 関数を使用します。

int
ddi_cb_unregister (ddi_cb_handle_t hdl);

この呼び出しは、ドライバの detach(9F) エントリポイントで行います。この呼び出しのあと、ドライバはコールバックアクションを受信しなくなります。

ドライバは、登録されたコールバック処理関数があることによって取得した、システムによる追加サポートも失います。たとえば、ドライバで前に利用できるようになった一部の割り込みベクターは、ドライバがコールバック処理関数の登録を解除するとすぐに元に戻されます。正常に復帰する前に、ddi_cb_unregister() 関数は、システムのサポートを失うことによって発生する、最終的なアクションをドライバに通知します。

コールバックハンドラ関数

コールバックアクションを受信し、処理する各アクションに固有の引数を受信するときは、登録されたコールバック処理関数を使用します。

typedef int (*ddi_cb_func_t)(dev_info_t *dip, ddi_cb_action_t cbaction,
                             void *cbarg, void *arg1, void *arg2);

cbaction パラメータは、ドライバがコールバックを受信するときの処理対象となるアクションを指定します。

typedef enum {
        DDI_CB_INTR_ADD,
        DDI_CB_INTR_REMOVE
} ddi_cb_action_t;

DDI_CB_INTR_ADD アクションは、ドライバで使用可能な割り込みが増えることを意味します。DDI_CB_INTR_REMOVE アクションは、ドライバで使用可能な割り込みが減ることを意味します。追加または削除された割り込みの数を判定するときは、cbarg パラメータを int にキャストします。cbarg 値は、利用可能な割り込みの数の変化を表します。

たとえば、利用可能な割り込みの数の変化を取得する場合は、次のようになります。

count = (int)(uintptr_t)cbarg;

cbactionDDI_CB_INTR_ADD である場合は、cbarg 個の割り込みベクターを追加します。cbactionDDI_CB_INTR_REMOVE である場合は、cbarg 個の割り込みベクターを解放します。

arg1 および arg2 の説明については、ddi_cb_register(9F) を参照してください。

コールバック処理関数は、関数が登録される期間全体を通じて、正しく実行できる必要があります。コールバック関数は、コールバック関数の登録が正常に解除される前に破棄される可能性のあるデータ構造体に依存することはできません。

コールバック処理関数は、次のいずれかの値を返す必要があります。

割り込み要求のインタフェース

ドライバは、次のインタフェースを使用して、システムからの割り込みベクターを要求する必要があります。

表 8-2 割り込みベクター要求のインタフェース

インタフェース
データ構造体
説明
ddi_intr_alloc()
ddi_intr_handle_t
割り込みを割り当てます。
ddi_intr_set_nreq()
要求する割り込みベクターの数を変更します。

割り込みの割り当て

最初に割り込みを割り当てるときは、ddi_intr_alloc(9F) 関数を使用します。

int
ddi_intr_alloc (dev_info_t *dip, ddi_intr_handle_t *h_array, int type,
                int inum, int count, int *actualp, int behavior);

ドライバは、この関数を呼び出す前に、要求する数の割り込みを格納するのに十分な大きさの空のハンドル配列を割り当てる必要があります。ddi_intr_alloc() 関数は、count 個の割り込みハンドルを割り当てて、inum パラメータで指定されたオフセットから始まる割り当て済みの割り込みベクターを使って配列を初期化しようとします。actualp パラメータは、割り当てられた割り込みベクターの実際の数を返します。

ドライバは、次の 2 つの方法で ddi_intr_alloc() 関数を使用できます。

割り込みリソース管理機能を使用している場合は、 ddi_intr_alloc() を 1 回呼び出して、すべての割り込みベクターを一度に割り当てます。count パラメータは、ドライバが要求する割り込みベクターの合計数です。actualp の値が count の値より小さい場合、システムは要求を完全に満たすことはできません。割り込みリソース管理機能では、この要求 (countnreq になる - 下記を参照) を保存するため、あとでさらに多くの割り込みベクターをこのドライバに割り当てることができるようになる場合があります。


注 - 割り込みリソース管理機能を使用すると、ddi_intr_alloc() を追加で呼び出しても、要求される割り込みベクターの合計数は変化しません。要求する割り込みベクターの数を変更するときは、ddi_intr_set_nreq(9F) 関数を使用します。


要求する割り込みベクターの数の変更

要求する割り込みベクターの数を変更するときは、ddi_intr_set_nreq(9F) 関数を使用します。

int
ddi_intr_set_nreq (dev_info_t *dip, int nreq);

割り込みリソース管理機能が利用できるとき、ドライバは ddi_intr_set_nreq() 関数を使用して、要求する割り込みベクターの合計数を動的に調整できます。ドライバは、ドライバが接続されると存在するようになる実際のロードに応答してこれを行うことができます。

ドライバは、最初に ddi_intr_alloc(9F) を呼び出して、割り込みベクターの最初の数を要求する必要があります。ddi_intr_alloc() 呼び出しのあとで、ドライバは ddi_intr_set_nreq() を呼び出してその要求サイズをいつでも変更できます。指定した nreq 値は、ドライバの、要求する割り込みベクターの新しい合計数です。割り込みリソース管理機能では、この新しい要求に応答して、システムの各ドライバに割り当てられた割り込みの数のバランスをとり直すことがあります。割り込みリソース管理機能によって、ドライバに割り当てられた割り込みの数のバランスがとり直されると、影響を受ける各ドライバは常に、ドライバが使用可能な割り込みベクターが増えたまたは減ったというコールバック通知を受信します。

たとえば、処理中の特定のトランザクションに関連して割り込みを使用する場合、ドライバは要求する割り込みベクターの合計数を動的に調整することがあります。ストレージドライバは、DMA エンジンを進行中の各トランザクションに関連付け、その理由で割り込みベクターを要求することがあります。ドライバは、open(9F) および close(9F) ルーチンで ddi_intr_set_nreq() を呼び出して、ドライバの実際の使用に応じて割り込みの使用率を調整することがあります。

割り込みの使用率と柔軟性

多数の異なる割り込み条件をサポートするデバイスのドライバは、それらの条件を任意の数の割り込みベクターにマッピングできる必要があります。そのようなドライバは、割り当てられる割り込みベクターが利用可能なままであると想定することはできません。現在利用可能な一部の割り込みは、システム内のほかのドライバの必要に対応するため、あとでシステムによって元に戻されることがあります。

ドライバは、次のことができる必要があります。

まとめると、ドライバは、一連の割り込み処理関数を選択し、ハードウェアをプログラムして必要性と割り込みの可用性に応じて割り込みを生成できる必要があります。場合によっては、複数の割り込みが同じベクターのターゲットになることがあります。また、その割り込みベクターの割り込みハンドラは、発生した割り込みを判定する必要があります。デバイスのパフォーマンスは、ドライバが割り込みベクターに割り込みをマッピングする能力の影響を受けることがあります。

割り込みリソース管理の実装例

割り込みリソース管理の好例となるデバイスドライバのタイプの 1 つは、ネットワークデバイスのドライバです。ネットワークデバイスのハードウェアは、複数の送受信チャネルをサポートしています。

ネットワークデバイスは、デバイスがいずれかの受信チャネルでパケットを受信するか、いずれかの送信チャネルでパケットを送信すると常に、一意の割り込み条件を生成します。ハードウェアは、発生する可能性のあるイベントごとに特定の MSI-X 割り込みを送信できます。ハードウェア内のテーブルによって、イベントごとに生成する MSI-X 割り込みが判定されます。

パフォーマンスを最適化するために、ドライバはシステムからの十分な割り込みを要求して、別個の各割り込みに独自の割り込みベクターを提供します。ドライバは、attach(9F) ルーチンで最初に ddi_intr_alloc(9F) を呼び出すときにこの要求を行います。

次に、ドライバは、actualpddi_intr_alloc() から受信した割り込みの実際の数を評価します。ドライバは、要求したすべての割り込みを受信することもあり、要求より少ない割り込みを受信することもあります。

ドライバ内の別個の関数は、利用可能な割り込みの合計数を使用して、イベントごとに生成する MSI-X 割り込みを計算します。この関数は、その結果に従ってハードウェア内のテーブルをプログラムします。

ドライバには、2 つの異なる割り込みハンドラ関数があるはずです。

この節のドライバ例では、関数 xx_setup_interrupts() は、利用可能な割り込みベクターの数を使用してハードウェアをプログラムし、それらの割り込みベクターごとに適切な割り込みハンドラを呼び出します。xx_setup_interrupts() 関数は、2 か所で呼び出されます。ddi_intr_alloc()xx_attach () で呼び出されたあとと、割り込みベクター割り当てが xx_cbfunc() コールバックハンドラ関数で調整されたあとです。

int
xx_setup_interrupts(xx_state_t *statep, int navail, xx_intrs_t *xx_intrs_p);

xx_setup_interrupts() 関数は、xx_intrs_t データ構造体の配列を使って呼び出されます。

typedef struct {
        ddi_intr_handler_t      inthandler;
        void                    *arg1;
        void                    *arg2;
} xx_intrs_t;

この xx_setup_interrupts() 機能は、割り込みリソース管理機能が利用できるかどうかに関係なくドライバに存在している必要があります。ドライバは、接続時に要求する数より少ない割り込みベクターで機能できる必要があります。割り込みリソース管理機能が利用できる場合は、利用可能な割り込みベクターの新しい数に動的に調整されるようにドライバを変更できます。

割り込みリソース管理機能が利用できるかどうかに関係なくドライバが提供する必要のある別の機能には、ハードウェアを休止および再開する機能が含まれます。休止と再開は、電源管理とホットプラグによる取り付けに関係する特定のイベントに必要です。休止と再開は、割り込みコールバックアクションの処理にも必要です。

休止関数は、xx_detach() で呼び出されます。

int
xx_quiesce(xx_state_t *statep);

再開関数は、xx_attach() で呼び出されます。

int
xx_resume(xx_state_t *statep);

このデバイスドライバを拡張して割り込みリソース管理機能を使用するときは、次の変更を行います。

/*
 * attach(9F) routine.
 *
 * Creates soft state, registers callback handler, initializes
 * hardware, and sets up interrupt handling for the driver.
 */
xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
        xx_state_t              *statep = NULL;
        xx_intr_t               *intrs = NULL;
        ddi_intr_handle_t       *hdls;
        ddi_cb_handle_t         cb_hdl;
        int                     instance;
        int                     type;
        int                     types;
        int                     nintrs;
        int                     nactual;
        int                     inum;

        /* Get device instance */
        instance = ddi_get_instance(dip);

        switch (cmd) {
        case DDI_ATTACH:

                /* Get soft state */
                if (ddi_soft_state_zalloc(state_list, instance) != 0)
                        return (DDI_FAILURE);
                statep = ddi_get_soft_state(state_list, instance);
                ddi_set_driver_private(dip, (caddr_t)statep);
                statep->dip = dip;

                /* Initialize hardware */
                xx_initialize(statep);

                /* Register callback handler */
                if (ddi_cb_register(dip, DDI_CB_FLAG_INTR, xx_cbfunc,
                    statep, NULL, &cb_hdl) != 0) {
                        ddi_soft_state_free(state_list, instance);
                        return (DDI_FAILURE);
                }
                statep->cb_hdl = cb_hdl;

                /* Select interrupt type */
                ddi_intr_get_supported_types(dip, &types);
                if (types & DDI_INTR_TYPE_MSIX) {
                        type = DDI_INTR_TYPE_MSIX;
                } else if (types & DDI_INTR_TYPE_MSI) {
                        type = DDI_INTR_TYPE_MSI;
                } else {
                        type = DDI_INTR_TYPE_FIXED;
                }
                statep->type = type;

                /* Get number of supported interrupts */
                ddi_intr_get_nintrs(dip, type, &nintrs);

                /* Allocate interrupt handle array */
                statep->hdls_size = nintrs * sizeof (ddi_intr_handle_t);
                statep->hdls = kmem_zalloc(statep->hdls_size, KMEM_SLEEP);

                /* Allocate interrupt setup array */
                statep->intrs_size = nintrs * sizeof (xx_intr_t);
                statep->intrs = kmem_zalloc(statep->intrs_size, KMEM_SLEEP);

                /* Allocate interrupt vectors */
                ddi_intr_alloc(dip, hdls, type, 0, nintrs, &nactual, 0);
                statep->nactual = nactual;

                /* Configure interrupt handling */
                xx_setup_interrupts(statep, statep->nactual, statep->intrs);

                /* Install and enable interrupt handlers */
                for (inum = 0; inum < nactual; inum++) {
                        ddi_intr_add_handler(&hdls[inum],
                            intrs[inum].inthandler,
                            intrs[inum].arg1, intrs[inum].arg2);
                        ddi_intr_enable(hdls[inum]);
                }

                break;

        case DDI_RESUME:

                /* Get soft state */
                statep = ddi_get_soft_state(state_list, instance);
                if (statep == NULL)
                        return (DDI_FAILURE);

                /* Resume hardware */
                xx_resume(statep);

                break;
        }

        return (DDI_SUCESS);
}

/*
 * detach(9F) routine.
 *
 * Stops the hardware, disables interrupt handling, unregisters
 * a callback handler, and destroys the soft state for the driver.
 */
xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
        xx_state_t      *statep = NULL;
        int             instance;
        int             inum;

        /* Get device instance */
        instance = ddi_get_instance(dip);

        switch (cmd) {
        case DDI_DETACH:

                /* Get soft state */
                statep = ddi_get_soft_state(state_list, instance);
                if (statep == NULL)
                        return (DDI_FAILURE);

                /* Stop device */
                xx_uninitialize(statep);

                /* Disable and free interrupts */
                for (inum = 0; inum < statep->nactual; inum++) {
                        ddi_intr_disable(statep->hdls[inum]);
                        ddi_intr_remove_handler(statep->hdls[inum]);
                        ddi_intr_free(statep->hdls[inum]);
                }

                /* Unregister callback handler */
                ddi_cb_unregister(statep->cb_hdl);

                /* Free interrupt handle array */
                kmem_free(statep->hdls, statep->hdls_size);

                /* Free interrupt setup array */
                kmem_free(statep->intrs, statep->intrs_size);

                /* Free soft state */
                ddi_soft_state_free(state_list, instance);

                break;

        case DDI_SUSPEND:

                /* Get soft state */
                statep = ddi_get_soft_state(state_list, instance);
                if (statep == NULL)
                        return (DDI_FAILURE);

                /* Suspend hardware */
                xx_quiesce(statep);

                break;
        }

        return (DDI_SUCCESS);
}

/*
 * (*ddi_cbfunc)() routine.
 *
 * Adapt interrupt usage when availability changes.
 */
int
xx_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
    void *arg1, void *arg2)
{
        xx_state_t      *statep = (xx_state_t *)arg1;
        int             count;
        int             inum;
        int             nactual;

        switch (cbaction) {
        case DDI_CB_INTR_ADD:
        case DDI_CB_INTR_REMOVE:

                /* Get change in availability */
                count = (int)(uintptr_t)cbarg;

                /* Suspend hardware */
                xx_quiesce(statep);

                /* Tear down previous interrupt handling */
                for (inum = 0; inum < statep->nactual; inum++) {
                        ddi_intr_disable(statep->hdls[inum]);
                        ddi_intr_remove_handler(statep->hdls[inum]);
                }

                /* Adjust interrupt vector allocations */
                if (cbaction == DDI_CB_INTR_ADD) {

                        /* Allocate additional interrupt vectors */
                        ddi_intr_alloc(dip, statep->hdls, statep->type,
                            statep->nactual, count, &nactual, 0);

                        /* Update actual count of available interrupts */
                        statep->nactual += nactual;

                } else {

                        /* Free removed interrupt vectors */
                        for (inum = statep->nactual - count;
                            inum < statep->nactual; inum++) {
                                ddi_intr_free(statep->hdls[inum]);
                        }

                        /* Update actual count of available interrupts */
                        statep->nactual -= count;
                }

                /* Configure interrupt handling */
                xx_setup_interrupts(statep, statep->nactual, statep->intrs);

                /* Install and enable interrupt handlers */
                for (inum = 0; inum < statep->nactual; inum++) {
                        ddi_intr_add_handler(&statep->hdls[inum],
                            statep->intrs[inum].inthandler,
                            statep->intrs[inum].arg1,
                            statep->intrs[inum].arg2);
                        ddi_intr_enable(statep->hdls[inum]);
                }

                /* Resume hardware */
                xx_resume(statep);

                break;

        default:
                return (DDI_ENOTSUP);
        }

        return (DDI_SUCCESS);
}