Jaa


STATUS_PENDING の取り扱い方

久方ぶりです。まさかたです。

先日、素晴らしいお天気の中、東京マラソン 2011 が開催されましたね。みなさんは東京マラソンに参加されたことがありますでしょうか?

私はありません。

 

さて、今回の話題は、STATUS_PENDING です。

この STAUS_PENDING は、ご存知の通り、あるドライバーが、ドライバースタックの下位ドライバーに対して、IoCallDriver() を使って IRP を投げた場合に、その戻り値として返ってくる可能性のあるステータスの一つです。

STATUS_PENDING が返ってくる可能性のある処理の詳細については、以下をご参照ください。

 

Handling IRPs: What Every Driver Writer Needs to Know - Asynchronous I/O Responses

https://msdn.microsoft.com/en-us/library/ms810023.aspx#irp_ha_topic7

 

A driver must return STATUS_PENDING if:

  • Its dispatch routine for an IRP might return before the IRP is completed.
  • It completes the IRP on another thread.
  • The dispatch routine cannot determine the IRP's completion status before it returns.

IRP を受け取った下位ドライバーは、さらにそのディスパッチルーチン内で、すぐに処理を完了させるか、それとも、ひとまず STATUS_PENDING を返して、しばらくしてから IRP を完了させるか、のいずれかを行います。

上位のドライバーは、下位ドライバーがすぐに IRP を完了させた場合も、STATUS_PENDING を返してきた場合も、どちらの場合に対しても、何らかの適切な処理を行う必要があります。

 

下位ドライバーから STATUS_PENDING が返ってきた場合、その関数名から、なんとなく IoMarkIrpPending() を呼び出さなければいけないような気がしてしまいますが、必ずしもそうではなく、ドライバー内でその後どのような処理を行うかに応じた処理を行わなくてはなりません。

ただ、多くのドライバーでは、ディスパッチルーチン内ですぐに処理を終えることが多く、STATUS_PENDING が返ってくる場合が少ないためか、実際 STATUS_PENDING が返ってきた場合にどのように IRP を取り扱ったらいいのかについて、私たち WDK サポートにもご質問をいただくことが何度かありました。

 

そこで、今回は、一般的に STATUS_PENDING が返ってきた場合に、状況毎に必要な処理について、簡単にまとめてみたいと思います。

まず、大きく分けると、STATUS_PENDING のハンドリングを行う場合には、主に以下のようなシチュエーションが考えられ、それぞれの状況に合わせたハンドリングが必要になります。

 

1. クライアント ドライバーで Pending の処理をする場合

2. Stack Location をスキップする場合

3. 完了ルーチンを設定した上で IRP を上位ドライバに通知する場合

 

 

1. クライアント ドライバーで Pending の処理をする場合

クライアント ドライバー内で、下位のドライバーが IRP を完了するまで待機する場合には、IoMarkIrpPending() を呼び出す必要はありません

というのも、この場合、IRPの IoStatus.Statusを、ディスパッチルーチンの戻り値に上書きするため、STATUS_PENDINGが上位ドライバーから返すステータス(戻り値)となるわけではないからです。

そもそも、IoMarkIrpPending() は、ドライバーが、IoCompleteRequest() で IRP を完了させたり、下位ドライバーに IRP を渡したりせず、IRP を キューに入れたりするなどした場合に、ディスパッチルーチンの戻り値として STATUS_PENDING を戻り値とするときには、呼び出す必要がある関数ですが、下位ドライバーが STATUS_PENDING を返して来たからといって、必ず呼ばないといけない関数ではないからです。

逆に、IoMarkIrpPending() を呼び出したのなら、必ず STATUS_PENDING を返さないといけなくなります。

 

IoMarkIrpPending

https://msdn.microsoft.com/en-us/library/ff549422(VS.85).aspx

After calling IoMarkIrpPending, the dispatch routine must return STATUS_PENDING, even if some routine completes the IRP (by calling IoCompleteRequest) before the dispatch routine that called IoMarkIrpPending returns.

 

なお、下記の技術情報で言えば、「シナリオ 2 : 転送して IRP が完了するまで同期的に待つ」が、この場合に該当します。

 

     IRP のさまざまな処理方法 (パート 1/2)

     https://support.microsoft.com/kb/320275/ja

     シナリオ 2 : 転送して IRP が完了するまで同期的に待つ

 

2. Stack Location をスキップする場合

次に、下位ドライバーから STATUS_PENDING が返ってきても、何も処理を行わず、Stack Location をスキップしたい場合があります。

このような場合には、IoSkipCurrentIrpStackLocation() で、Stack Location をスキップすることになりますが、スキップするのですから、IO_STACK_LOCATION の変更を行ってはいけません。

となると、IoMarkIrpPending() を呼び出すことも、結果的に Current Stack Location を変更することになるため、この条件の場合には IoMarkIrpPendin() を呼び出すことはできない、ということになります。

実際、IoMarkIrpPending() は WDK の WDM.H 内に定義されていますが、その中身を見ても、IoGetCurrentIrpStackLocation() で Stack Location を変更しているのが分かります。

 

FORCEINLINE

VOID

IoMarkIrpPending(

    __inout PIRP Irp

)

/*++

Routine Description:

 

    This routine marks the specified I/O Request Packet (IRP) to indicate

    that an initial status of STATUS_PENDING was returned to the caller.

    This is used so that I/O completion can determine whether or not to

    fully complete the I/O operation requested by the packet.

 

Arguments:

 

    Irp - Pointer to the I/O Request Packet to be marked pending.

 

Return Value:

 

    None.

 

--*/

{

    IoGetCurrentIrpStackLocation( (Irp) )->Control |= SL_PENDING_RETURNED;

}

 

IoSkipCurrentIrpStackLocation Macro

https://msdn.microsoft.com/en-us/library/ff550355(VS.85).aspx

 

If your driver calls IoSkipCurrentIrpStackLocation, be careful not to modify the IO_STACK_LOCATION structure in a way that could unintentionally affect the lower driver or the system's behavior with respect to that driver.

Examples include modifying the IO_STACK_LOCATION structure's Parameters union or calling IoMarkIrpPending.

 

  ですので、実際のコード例は、下記技術情報の「シナリオ1 : 転送して終了する」が該当するかと思います。

 

  IRP のさまざまな処理方法 (パート 1/2)

  https://support.microsoft.com/kb/320275/ja

  シナリオ1: 転送して終了する

 

3. 完了ルーチンを設定した上で IRP を上位ドライバに通知する場合

完了ルーチンを設定した上で、Stack Location をスキップせずに IRP を上位のドライバに通知する場合、IoCompletion ルーチンで、IRP の PendingReturned をチェックし、TRUE である場合には IoMarkIrpPending() を呼び出す必要があります。

この点については、IoMarkIrpPending() の技術情報の中に書かれている通りです。

 

IoMarkIrpPending

https://msdn.microsoft.com/en-us/library/ff549422(VS.85).aspx

 

If a driver sets an IoCompletion routine for an IRP and then passes the IRP down to a lower driver, the IoCompletion routine should check the IRP->PendingReturned flag.

If the flag is set, the IoCompletion routine must call IoMarkIrpPending with the IRP.

(IoCompletion routines do not return STATUS_PENDING, however. For more information, see Implementing an IoCompletion Routine.)

 

  このシナリオは、「シナリオ3 : 完了ルーチンを設定して転送するが、IRP が完了するまで待たない」が該当するかと思いますので、実装例などご参考としていただければと思います。

 

  IRP のさまざまな処理方法 (パート 1/2)

  https://support.microsoft.com/kb/320275/ja

  シナリオ3: 完了ルーチンを設定して転送するが、IRP が完了するまで待たない

 

ちなみに、ドライバーで STATUS_PENDING が返ってきた場合の処理が適切に行われているかどうかをテストするために、Driver Verifier の “Force Pending I/O Requests” というテストが役に立つかと思います。

Driver Verifier については、過去の Blog (「ドライバー検証ツール」 by さなえすさん)にも書いておりますので、そちらも併せてご参考としていただければ幸いです。

 

Force Pending I/O Requests

https://msdn.microsoft.com/en-us/library/ff546145(VS.85).aspx

 

では、また。

__________

【閑話休題】

と、ここで、東京マラソンの話の続きを。

私は出ませんでしたが、実は、知り合いが見事当選して出場することになったというので、応援に行ってきました。

応募してもなかなか走ることができない東京マラソンということで、はりきって予めコースマップを印刷して持って行き、走るペースからどのくらいの時間に何キロ地点を通るのかを計算し、地下鉄で先回りをして、勇姿を見ようという算段でした。

さて、いざに応援するポイントに到着して一生懸命 知り合いのランナーを探しますが、他にランナーが多過ぎて見分けがつきません!

そうこうしている内に、私たちが応援していたポイントを通り過ぎてしまい、はるか先のポイントをすでに通過したという知らせが届き、慌てて次のポイントへ…ということが何回か続きました。

そして、せめてゴールくらいは出迎えたい!と、東京ビッグサイトに向かうものの、出遅れた上に電車の方が時間がかかってこれまた間に合わず

結局、勇姿を見れたのは一番初めのポイントくらいでした。

ランナーがコスプレしたりするのは、見つけてもらいやすいようにということを、この時悟りました。

みなさんも、マラソン大会で応援してもらいたい時には、コスプレされることをお勧めいたします。