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

ドキュメントの情報

はじめに

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

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

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

3.  マルチスレッド

4.  プロパティー

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

6.  ドライバの自動構成

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

8.  割り込みハンドラ

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

DMA モデル

デバイス DMA の種類

バスマスター DMA

サードパーティー DMA

ファーストパーティー DMA

ホストプラットフォームの DMA の種類

DMA ソフトウェアコンポーネント: ハンドル、ウィンドウ、cookie

DMA 操作

バスマスター DMA 転送の実行

ファーストパーティー DMA 転送の実行

サードパーティー DMA 転送の実行

DMA 属性

ddi_dma_attr 構造体

SBus の例

ISA バスの例

DMA リソースの管理

オブジェクトのロック

DMA ハンドルの割り当て

DMA リソースの割り当て

デバイスレジスタ構造体

DMA コールバックの例

最大バーストサイズの決定

プライベート DMA バッファーの割り当て

リソース割り当てエラーの処理

DMA エンジンのプログラミング

DMA リソースの解放

DMA ハンドルの解放

DMA コールバックの取り消し

メモリーオブジェクトの同期

キャッシュ

ddi_dma_sync() 関数

DMA ウィンドウ

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

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

12.  電源管理

13.  Oracle Solaris ドライバの強化

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

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

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

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

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

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

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

20.  USB ドライバ

21.  SR-IOV ドライバ

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

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

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

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

パート IV 付録

A.  ハードウェアの概要

B.  Solaris DDI/DKI サービスのサマリー

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

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

E.  pci.conf ファイル

索引

DMA リソースの管理

このセクションでは、DMA リソースを管理する方法について説明します。

オブジェクトのロック

DMA リソースをメモリーオブジェクトに割り当てる前に、そのオブジェクトを移動できないようにする必要があります。そうしないと、デバイスがオブジェクトへの書き込みを試している間に、システムがそのオブジェクトをメモリーから削除する可能性があります。オブジェクトが見つからないと、データ転送に失敗し、システムが破壊される恐れがあります。DMA 転送中にメモリーオブジェクトが移動できないようにするプロセスは、オブジェクトのロックダウンと呼ばれます。

次のオブジェクトタイプは、明示的なロックが必要ありません。

ユーザー空間から生成されるバッファーなど、その他のオブジェクトについては、physio(9F) または ddi_umem_lock(9F) を使用してロックダウンする必要があります。これらの関数を使ったオブジェクトのロックダウンは通常、文字デバイスドライバの read(9E) または write(9E) ルーチン内で実行されます。「データ転送方法」を参照してください。

DMA ハンドルの割り当て

DMA ハンドルは、そのあとに割り当てられる DMA リソースへの参照として使用される不透明なオブジェクトです。DMA ハンドルは通常、ddi_dma_alloc_handle(9F) を使用する、ドライバの attach() エントリポイントで割り当てられます。ddi_dma_alloc_handle() 関数は、dip によって参照されるデバイス情報と、ddi_dma_attr(9S) 構造体によってパラメータとして記述されたデバイスの DMA 属性を取得します。ddi_dma_alloc_handle() 関数の構文は次のとおりです。

int ddi_dma_alloc_handle(dev_info_t *dip,
    ddi_dma_attr_t *attr, int (*callback)(caddr_t),
    caddr_t arg, ddi_dma_handle_t *handlep);

各表記の意味は次のとおりです。

dip

デバイスの dev_info 構造体へのポインタ。

attr

ddi_dma_attr(9S) 構造体へのポインタです (「DMA 属性」 を参照)。

callback

リソース割り当てエラーに対処するためのコールバック関数のアドレスです。

arg

コールバック関数に渡される引数です。

handlep

返されたハンドルを格納するための DMA ハンドルへのポインタです。

DMA リソースの割り当て

DMA リソースは次の 2 つのインタフェースで割り当てられます。

DMA リソースは通常、ドライバの xxstart() ルーチンで割り当てられます (xxstart() ルーチンが存在する場合)。xxstart() については、「非同期データ転送 (ブロックドライバ)」を参照してください。これらの 2 つのインタフェースの構文は次のとおりです。

int ddi_dma_addr_bind_handle(ddi_dma_handle_t handle,
    struct as *as, caddr_t addr,
    size_t len, uint_t flags, int (*callback)(caddr_t),
    caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp);

int ddi_dma_buf_bind_handle(ddi_dma_handle_t handle,
    struct buf *bp, uint_t flags,
    int (*callback)(caddr_t), caddr_t arg,
    ddi_dma_cookie_t *cookiep, uint_t *ccountp);

次の引数は、ddi_dma_addr_bind_handle(9F)ddi_dma_buf_bind_handle(9F) の両方に共通です。

handle

DMA ハンドルと、リソースを割り当てるためのオブジェクトです。

flags

転送方向などの属性を指定するフラグセットです。DDI_DMA_READ はデバイスからメモリーへのデータ転送を指定します。DDI_DMA_WRITE はメモリーからデバイスへのデータ転送を指定します。利用できるフラグの詳細は、ddi_dma_addr_bind_handle(9F) または ddi_dma_buf_bind_handle(9F) のマニュアルページを参照してください。

callback

リソース割り当てエラーに対処するためのコールバック関数のアドレスです。ddi_dma_alloc_handle(9F) のマニュアルページを参照してください。

arg

コールバック関数に渡される引数です。

cookiep

このオブジェクトの最初の DMA cookie へのポインタです。

ccountp

このオブジェクトの DMA cookie の数へのポインタです。

ddi_dma_addr_bind_handle(9F) の場合、オブジェクトは、次のパラメータでアドレス範囲を指定することによって記述します。

as

アドレス空間構造体へのポインタです。as の値は NULL になります。

addr

オブジェクトのベースカーネルアドレスです。

len

オブジェクトの長さをバイト単位で指定します。

ddi_dma_buf_bind_handle(9F) の場合、オブジェクトは、bp が示す buf(9S) 構造体で記述します。

デバイスレジスタ構造体

DMA 対応デバイスには、前述の例で使用したものよりも多くのレジスタが必要になります。

scatter/gather 機能を使わずに DMA 対応デバイスをサポートするには、デバイスレジスタ構造体で次のフィールドを使用します。

uint32_t      dma_addr;      /* starting address for DMA */
uint32_t      dma_size;      /* amount of data to transfer */

scatter/gather 機能を使って DMA 対応デバイスをサポートするには、デバイスレジスタ構造体で次のフィールドを使用します。

struct sglentry {
    uint32_t    dma_addr;
    uint32_t    dma_size;
} sglist[SGLLEN];

caddr_t       iopb_addr;     /* When written, informs the device of the next */
                             /* command's parameter block address. */
                             /* When read after an interrupt, contains */
                             /* the address of the completed command. */

DMA コールバックの例

例 9-1 では、コールバック関数として xxstart() を使用します。また、xxstart() の引数としてデバイスごとの状態構造体を使用します。xxstart() 関数は、コマンドを開始しようとします。リソースが利用できないためにコマンドを開始できない場合、xxstart() はあとでリソースが利用可能になったときに呼び出されるようにスケジュールされます。

xxstart() は DMA コールバックとして使用されるため、xxstart() は DMA コールバックに課せられている次の規則に従う必要があります。

例 9-1 DMA コールバックの例

static int
xxstart(caddr_t arg)
{
    struct xxstate *xsp = (struct xxstate *)arg;
    struct device_reg *regp;
    int flags;
    mutex_enter(&xsp->mu);
    if (xsp->busy) {
        /* transfer in progress */
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    xsp->busy = 1;
    regp = xsp->regp;
    if ( /* transfer is a read */ ) {
        flags = DDI_DMA_READ;
    } else {
        flags = DDI_DMA_WRITE;
    }
    mutex_exit(&xsp->mu);
    if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp,flags, xxstart,
        (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) {
        /* really should check all return values in a switch */
        mutex_enter(&xsp->mu);
        xsp->busy=0;
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    /* Program the DMA engine. */
    return (DDI_DMA_CALLBACK_DONE);
}

最大バーストサイズの決定

ドライバは、そのデバイスがサポートする DMA バーストサイズを、ddi_dma_attr(9S) 構造体の dma_attr_burstsizes フィールドで指定します。このフィールドは、サポートされるバーストサイズのビットマップです。ただし、システムでは、DMA リソースが割り当てられたときに、デバイスが実際に使用する可能性のあるバーストサイズに対してさらなる制限を加えることがあります。ddi_dma_burstsizes(9F) ルーチンを使用すると、許容されるバーストサイズを取得できます。このルーチンは、デバイスの適切なバーストサイズビットマップを返します。ドライバは、DMA リソースが割り当てられるときに、DMA エンジンに使用する適切なバーストサイズをシステムに問い合わせることができます。

例 9-2 バーストサイズの決定

#define BEST_BURST_SIZE 0x20 /* 32 bytes */

    if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
        (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) {
        /* error handling */
    }
    burst = ddi_dma_burstsizes(xsp->handle);
    /* check which bit is set and choose one burstsize to */
    /* program the DMA engine */
    if (burst & BEST_BURST_SIZE) {
        /* program DMA engine to use this burst size */
    } else {
        /* other cases */
    }

プライベート DMA バッファーの割り当て

一部のデバイスドライバでは、ユーザースレッドやカーネルから要求された転送を実行するのに加え、DMA 転送用のメモリーを割り当てることが必要になる場合があります。プライベート DMA バッファーを割り当てる例として、デバイスとの通信用の共有メモリーを設定したり、中間転送バッファーを割り当てたりすることが挙げられます。DMA 転送用のメモリーを割り当てるには、ddi_dma_mem_alloc(9F) を使用します。

int ddi_dma_mem_alloc(ddi_dma_handle_t handle, size_t length,
    ddi_device_acc_attr_t *accattrp, uint_t flags,
    int (*waitfp)(caddr_t), caddr_t arg, caddr_t *kaddrp,
    size_t *real_length, ddi_acc_handle_t *handlep);

各表記の意味は次のとおりです。

handle

DMA ハンドルです

length

目的とする割り当ての長さ (バイト単位) です

accattrp

デバイスアクセス属性構造体へのポインタです

flags

データ転送モードフラグです。使用できる値は、DDI_DMA_CONSISTENT DDI_DMA_STREAMING です。

waitfp

リソース割り当てエラーに対処するためのコールバック関数のアドレスです。ddi_dma_alloc_handle(9F) のマニュアルページを参照してください。

arg

コールバック関数に渡される引数です

kaddrp

割り当てられたストレージのアドレスを含む正常な復帰へのポインタです

real_length

割り当てられた長さ (バイト単位) です

handlep

データアクセスハンドルへのポインタです

デバイスが非順次方式でアクセスする場合、flags パラメータは DDI_DMA_CONSISTENT に設定されます。ddi_dma_sync(9F) を使用する同期手順は、小さなオブジェクトに頻繁に適用されるため、できるだけ軽量にします。このようなアクセスは一般に、一貫性アクセスと呼ばれています。一貫性アクセスは、デバイスとドライバ間の通信に使用される入出力パラメータブロックに特に役立ちます。

x86 プラットフォームでは、物理的に連続したメモリーである DMA メモリーを割り当てる際に次の要件があります。

次の例は、IOPB メモリーと、そのメモリーへのアクセスに必要な DMA リソースの割り当て方法を示しています。DMA リソースを引き続き割り当て、DDI_DMA_CONSISTENT フラグを割り当て関数に渡す必要があります。

例 9-3 ddi_dma_mem_alloc(9F) の使用

if (ddi_dma_mem_alloc(xsp->iopb_handle, size, &accattr,
    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &xsp->iopb_array,
    &real_length, &xsp->acchandle) != DDI_SUCCESS) {
    /* error handling */
    goto failure;
}
if (ddi_dma_addr_bind_handle(xsp->iopb_handle, NULL,
    xsp->iopb_array, real_length,
    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
    NULL, &cookie, &count) != DDI_DMA_MAPPED) {
    /* error handling */
    ddi_dma_mem_free(&xsp->acchandle);
    goto failure;
}

メモリー転送が順次、単方向、ブロックサイズ、およびブロック整列の方式で行われる場合、flags パラメータは DDI_DMA_STREAMING に設定されます。このようなアクセスは一般に、ストリーミングアクセスと呼ばれています。

場合によっては、入出力キャッシュを使用すると入出力転送の速度を上げることができます。入出力キャッシュは、最低でも 1 つのキャッシュラインを転送します。ddi_dma_mem_alloc(9F) ルーチンでは、データの破壊を避けるために size を四捨五入してキャッシュラインの倍数にします。

ddi_dma_mem_alloc(9F) 関数は、割り当てられたメモリーオブジェクトの実際のサイズを返します。パディングと配置の要件のために、実際のサイズは要求されたサイズよりも大きくなることがあります。ddi_dma_addr_bind_handle(9F) 関数には実際の長さを指定する必要があります。

ddi_dma_mem_alloc(9F) で割り当てられたメモリーを解放するには、ddi_dma_mem_free(9F) 関数を使用します。


注 - ドライバは、バッファーが適切に配置されるようにする必要があります。下方バインドされた DMA バッファーの配置要件が設定されているデバイスのドライバは、それらの要件を満たすドライバの中間バッファーにデータをコピーしてから、その中間バッファーを DMA 用の DMA ハンドルにバインドすることが必要な場合があります。ドライバの中間バッファーを割り当てるには、ddi_dma_mem_alloc(9F) を使用します。アクセスするデバイスにメモリーを割り当てるには、kmem_alloc(9F) ではなく、必ず ddi_dma_mem_alloc(9F) を使用します。


リソース割り当てエラーの処理

リソース割り当てルーチンは、割り当てエラーの処理時にいくつかのオプションをドライバに提供します。waitfp 引数は、次の表に示すように、割り当てルーチンがブロックする、すぐに復帰する、コールバックをスケジュールするのいずれになるかを示します。

表 9-1 リソース割り当て処理

waitfp
指示された処理
DDI_DMA_DONTWAIT
ドライバはリソースが利用できるようになるのを待たない
DDI_DMA_SLEEP
ドライバはリソースが利用できるようになるまで待ち続ける
その他の値
リソースが利用できるようになった可能性があるときに呼び出される関数のアドレス

DMA エンジンのプログラミング

リソースの割り当てが正常に完了したら、デバイスをプログラミングする必要があります。DMA エンジンのプログラミングはデバイスごとに異なりますが、開始アドレスと転送カウントはすべての DMA エンジンに必要です。デバイスドライバはこれらの 2 つの値を、ddi_dma_addr_bind_handle(9F)ddi_dma_buf_bind_handle(9F)、または ddi_dma_getwin(9F) の呼び出しが正常に行われた場合に返されるDMA cookie から取得します。これらの関数はすべて、最初の DMA cookie と、DMA オブジェクトが複数の cookie で構成されているかどうかを示す cookie カウントを返します。cookie カウント N が 1 よりも大きければ、ddi_dma_nextcookie(9F)N-1 回だけ呼び出して残りの cookie をすべて取得します。

DMA cookie の型は ddi_dma_cookie(9S) です。この型の cookie には次のフィールドがあります。

uint64_t    _dmac_ll;       /* 64-bit DMA address */
uint32_t    _dmac_la[2];    /* 2 x 32-bit address */
size_t      dmac_size;      /* DMA cookie size */
uint_t      dmac_type;      /* bus specific type bits */

dmac_laddress フィールドには、デバイスの DMA エンジンのプログラミングに適した 64 ビットの入出力アドレスを指定します。デバイスに 64 ビットの DMA アドレスレジスタがある場合、ドライバはこのフィールドを使用して DMA エンジンをプログラミングします。dmac_address フィールドには、32 ビットの DMA アドレスレジスタを持つデバイスに使用される 32 ビットの入出力アドレスを指定します。dmac_size フィールドには、転送カウントが入ります。cookie の dmac_type フィールドがドライバで必要になるかどうかは、バスアーキテクチャーによって決まります。ドライバは、cookie に対して論理操作や算術操作などの操作は一切行いません。

例 9-4 ddi_dma_cookie(9S) の例

ddi_dma_cookie_t            cookie;

     if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
     (caddr_t)xsp, &cookie, &xsp->ccount) != DDI_DMA_MAPPED) {
         /* error handling */
      }
     sglp = regp->sglist;
     for (cnt = 1; cnt <= SGLLEN; cnt++, sglp++) {
     /* store the cookie parms into the S/G list */
     ddi_put32(xsp->access_hdl, &sglp->dma_size,
         (uint32_t)cookie.dmac_size);
     ddi_put32(xsp->access_hdl, &sglp->dma_addr,
         cookie.dmac_address);
     /* Check for end of cookie list */
     if (cnt == xsp->ccount)
         break;
     /* Get next DMA cookie */
     (void) ddi_dma_nextcookie(xsp->handle, &cookie);
     }
    /* start DMA transfer */
     ddi_put8(xsp->access_hdl, &regp->csr,
     ENABLE_INTERRUPTS | START_TRANSFER);

DMA リソースの解放

DMA 転送の完了後、通常は割り込みルーチンで、ドライバは ddi_dma_unbind_handle(9F) を呼び出して、DMA リソースを解放できます。

「メモリーオブジェクトの同期」で説明しているように、ddi_dma_unbind_handle(9F)ddi_dma_sync(9F) を呼び出すことで、明示的に同期を行う必要がなくなります。ddi_dma_unbind_handle(9F) の呼び出し後、DMA リソースは無効になり、それ以上そのリソースを参照しても結果は保証されません。次の例は、ddi_dma_unbind_handle(9F) の使用方法を示しています。

例 9-5 DMA リソースの解放

static uint_t
xxintr(caddr_t arg)
{
     struct xxstate *xsp = (struct xxstate *)arg;
     uint8_t    status;
    volatile   uint8_t   temp;
     mutex_enter(&xsp->mu);
     /* read status */
     status = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
     if (!(status & INTERRUPTING)) {
        mutex_exit(&xsp->mu);
        return (DDI_INTR_UNCLAIMED);
     }
     ddi_put8(xsp->access_hdl, &xsp->regp->csr, CLEAR_INTERRUPT);
      /* for store buffers */
     temp = ddi_get8(xsp->access_hdl, &xsp->regp->csr);
     ddi_dma_unbind_handle(xsp->handle);
     /* Check for errors. */
     xsp->busy = 0;
     mutex_exit(&xsp->mu);
     if ( /* pending transfers */ ) {
        (void) xxstart((caddr_t)xsp);
     }
     return (DDI_INTR_CLAIMED);
}

DMA リソースが解放されます。次の転送で別のオブジェクトが使用される場合、DMA リソースを再割り当てする必要があります。ただし、同じオブジェクトが常に使用される場合、リソースの割り当ては一度で構いません。途中の ddi_dma_sync(9F) の呼び出しが残っているかぎり、リソースの再利用が可能です。

DMA ハンドルの解放

ドライバが切り離される場合は、DMA ハンドルを解放する必要があります。ddi_dma_free_handle(9F) 関数は、DMA ハンドルと、システムがそのハンドル上でキャッシュしている残りのリソースをすべて破棄します。DMA ハンドルをそれ以上参照しても結果は保証されません。

DMA コールバックの取り消し

DMA コールバックを取り消すことはできません。DMA コールバックを取り消すには、ドライバの detach(9E) エントリポイントにコードを追加する必要があります。未処理のコールバックが存在する場合は、detach() ルーチンが DDI_SUCCESS を返すことはありません。例 9-6 を参照してください。DMA コールバックが発生すると、detach() ルーチンはコールバックが実行されるのを待つ必要があります。コールバックが完了したら、detach() はコールバックが再スケジュールを行わないようにする必要があります。次の例に示すように、状態構造体で追加のフィールドを指定することによって、コールバックが再スケジュールを行わないようにすることができます。

例 9-6 DMA コールバックの取り消し

static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
     /* ... */
     mutex_enter(&xsp->callback_mutex);
     xsp->cancel_callbacks = 1;
     while (xsp->callback_count > 0) {
        cv_wait(&xsp->callback_cv, &xsp->callback_mutex);
     }
     mutex_exit(&xsp->callback_mutex);
     /* ... */
 }

static int
xxstrategy(struct buf *bp)
{
     /* ... */
     mutex_enter(&xsp->callback_mutex);
       xsp->bp = bp;
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
         xxdmacallback, (caddr_t)xsp, &cookie, &ccount);
     if (error == DDI_DMA_NORESOURCES)
       xsp->callback_count++;
     mutex_exit(&xsp->callback_mutex);
     /* ... */
}

static int
xxdmacallback(caddr_t callbackarg)
{
     struct xxstate *xsp = (struct xxstate *)callbackarg;
     /* ... */
     mutex_enter(&xsp->callback_mutex);
     if (xsp->cancel_callbacks) {
        /* do not reschedule, in process of detaching */
        xsp->callback_count--;
        if (xsp->callback_count == 0)
           cv_signal(&xsp->callback_cv);
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);    /* don't reschedule it */
     }
     /*
      * Presumably at this point the device is still active
      * and will not be detached until the DMA has completed.
      * A return of 0 means try again later
      */
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
         DDI_DMA_DONTWAIT, NULL, &cookie, &ccount);
     if (error == DDI_DMA_MAPPED) {
        /* Program the DMA engine. */
        xsp->callback_count--;
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);
     }
     if (error != DDI_DMA_NORESOURCES) {
        xsp->callback_count--;
        mutex_exit(&xsp->callback_mutex);
        return (DDI_DMA_CALLBACK_DONE);
     }
     mutex_exit(&xsp->callback_mutex);
     return (DDI_DMA_CALLBACK_RUNOUT);
}

メモリーオブジェクトの同期

メモリーオブジェクトにアクセスする過程で、ドライバはさまざまなキャッシュに対してメモリーオブジェクトを同期させることが必要な場合があります。このセクションでは、メモリーオブジェクトの同期を行うタイミングとその方法に関するガイドラインを示します。

キャッシュ

CPU キャッシュは、CPU とシステムのメインメモリーの間に位置する非常に高速なメモリーです。入出力キャッシュは、デバイスとシステムのメインメモリーの間に位置します (次の図を参照)。

図 9-1 CPU キャッシュとシステムの入出力キャッシュ

image:図は、キャッシュを使用して、デバイスのデータ転送の速度を上げる方法を示しています。

メインメモリーからデータを読み取ろうとすると、関連付けられたキャッシュは要求されたデータがあるかどうかを確認します。データが利用可能である場合、キャッシュはすぐにデータを提供します。データがキャッシュに含まれていない場合、キャッシュはメインメモリーからそのデータを取り出します。次に、そのデータを要求元に渡し、次の要求に備えてデータを保存します。

同様に、書き込みサイクルでは、データはすぐにキャッシュに格納されます。CPU やデバイスは、実行 (つまりデータの転送) を続けることができます。データをキャッシュに格納するのにかかる時間は、データがメモリーに書き込まれるのを待つ時間よりもはるかに短くてすみます。

このモデルでは、デバイス転送が完了したあとも、引き続きデータを入出力キャッシュに格納でき、メインメモリーにはデータが入っていません。CPU がメモリーにアクセスした場合、CPU は CPU キャッシュから間違ったデータを読み取る可能性があります。ドライバは、同期ルーチンを呼び出して、入出力キャッシュからデータをフラッシュし、新しいデータで CPU キャッシュを更新する必要があります。この処理により、CPU のメモリーのビューの一貫性が確保されます。同様に、CPU によって変更されたデータにデバイスからアクセスする場合も、同期段階が必要です。

デバイスとメモリーの間に、バスエクステンダやブリッジなどの追加のキャッシュやバッファーを作成できます。ddi_dma_sync(9F) を使用して、該当するすべてのキャッシュを同期させます。

ddi_dma_sync() 関数

メモリーオブジェクトには、DMA ハンドルによって、CPU やデバイス用などの複数のマッピングが定義されている場合があります。複数のマッピングを持つドライバは、メモリーオブジェクトの変更にいずれかのマッピングが使用される場合、ddi_dma_sync(9F) を呼び出す必要があります。ddi_dma_sync() を呼び出すと、メモリーオブジェクトが別のマッピングを通じてアクセスされる前に、そのオブジェクトの変更が必ず完了しているようになります。ddi_dma_sync() 関数は、オブジェクトへのキャッシュされた参照が古くなっている場合に、オブジェクトのほかのマッピングに知らせることもできます。また、ddi_dma_sync() は必要に応じて古いキャッシュ参照をフラッシュしたり、無効にしたりします。

通常、ドライバは DMA 転送が完了したときに ddi_dma_sync() を呼び出す必要があります。ただし、ddi_dma_unbind_handle(9F) を使用した DMA リソースの解放により、ドライバに代わって ddi_dma_sync() が暗黙的に実行される場合は、この規則に当てはまりません。ddi_dma_sync() の構文は次のとおりです。

int ddi_dma_sync(ddi_dma_handle_t handle, off_t off,
size_t length, uint_t type);

オブジェクトがデバイスの DMA エンジンによって読み取られようとしている場合は、typeDDI_DMA_SYNC_FORDEV に設定して、デバイスのオブジェクトビューを同期させる必要があります。デバイスの DMA エンジンがメモリーオブジェクトに書き込みを行なったあとで、そのオブジェクトが CPU によって読み取られようとしている場合は、typeDDI_DMA_SYNC_FORCPU に設定して、CPU のオブジェクトビューを同期させる必要があります。

次の例は、CPU のための DMA オブジェクトの同期を示しています。

if (ddi_dma_sync(xsp->handle, 0, length, DDI_DMA_SYNC_FORCPU)
    == DDI_SUCCESS) {
    /* the CPU can now access the transferred data */
    /* ... */
} else {
    /* error handling */
}

メモリーが ddi_dma_mem_alloc(9F) によって割り当てられる場合のように、カーネルのためのマッピングしかない場合は、DDI_DMA_SYNC_FORKERNEL フラグを使用します。システムは、CPU のビューよりもかなり迅速にカーネルのビューを同期させようとします。システムがカーネルビューを高速で同期できない場合、システムは DDI_DMA_SYNC_FORCPU フラグが設定されているかのように動作します。