TOP
«
»

スレッドを2個使ったiSCSI処理

現在細かい修正とエラーリカバリー対応を行っています。
今後のバージョンでは次のような動作をするようになります。
互換性のため従来の方法も残す方向で作業しています。

リードの場合はサイズに関係なく読み込み完了したデータとサイズを
taskに記録してソケット側に戻ってきます。
それをもとにデータとSCSI応答をイニシエータに返します。

ライトの場合はFirstBurstLength(デフォルト64KB)以下ならば
コマンドと一緒にデータも届くので、書き込みして完了したサイズを
taskに記録してソケット側に戻ってきます。
それをもとにSCSI応答をイニシエータに返します。

FirstBurstLength(デフォルト64KB)を超えるならば
追加データをイニシエータから受け取り、
まとめて一回で書き込みして完了したサイズを
taskに記録してソケット側に戻ってきます。
それをもとにSCSI応答をイニシエータに返します。

現在の仕様を流れ的に書くと以下のような感じで、
リードと64KB以下のライトはそんなに問題はない(と思う)。
でもtranscとexeccを待っている間にコネクションが死ぬと
LUNがデッドロックになりそうな流れ(汗)。
まだ改善の余地がある(と思う)。
少なくともデータOUTフェーズでパケットをキューにも
順次いれないとMCSの場合相当待たされてしまう可能性がある。
特にライトキャッシュ機能のない現状ではLUNライトが
実際にどの程度時間がかかるのかはOS(FS)次第だから。
ライトが遅いのはこのせいではないかと思っている。(未検証)

注意:現在のistgt仕様ではMCSの場合はSocketI/Oスレッドが複数になります。

----------------------------------------------------------------------
リード要求または64KB(FirstBurstLength)以下のライト要求の場合
              +---------------------------------------------+
              | istgt                                       |
Initiator       Socket I/O          Disk I/O(LUNs)
                                   wait(cmdc,cmdq)
iSCSI PDU ---> poll
(Read or       read(socket)
 Write <=FB)   create(task)
               lock(cmdq)
               enqueue(task)  ===>
               unlock(cmdq)
               signal(cmdc)   ---> wakeup
                                    (wait lock)
                                   lock(cmdq)
                                   dequeue(task)
                                   unlock(cmdq)
                                   lock(LU)              XXX
                                   execute(SCSI CDB)
                                   unlock(LU)            XXX
                                   lock(taskq)
                              <=== enqueue(task)
                                   unlock(taskq)
               poll           <--- write(pipe)
               read(pipe)
               lock(taskq)
               dequeue(task)
               unlock(taskq)
if Read
READDATA  <--- DATAIN(task)
endif
          <--- RESPONSE(iSCSI)
               destroy(task)
              | istgt                                       |
              +---------------------------------------------+
----------------------------------------------------------------------
64KB(FirstBurstLength)を超えるライト要求の場合
              +---------------------------------------------+
              | istgt                                       |
Initiator       Socket I/O          Disk I/O(LUNs)
                                   wait(cmdc,cmdq)
iSCSI PDU ---> poll
(Write >FB)    read(socket)
               create(task)
               lock(cmdq)
               enqueue(task)  ===>
               unlock(cmdq)
               signal(cmdc)   ---> wakeup
                                    (wait lock)
                                   lock(cmdq)
                                   dequeue(task)
                                   unlock(cmdq)
                                   lock(trans)
                                   lock(taskq)
                              <=== enqueue(task)
                                   unlock(taskq)
               poll           <--- write(pipe)
               read(pipe)          wait(transc,trans)
               lock(taskq)
               dequeue(task)
               unlock(taskq)
               lock(trans)
WRITEDATA ---> DATAOUT(task)
               signal(transc) ---> wakeup
                                    (wait lock)
               wait(execc,trans)
                                   lock(trans)
                                   lock(LU)              XXX
                                   execute(SCSI CDB)
                                   unlock(LU)            XXX
               wakeup         <--- signal(execc)
                (wait lock)
                                   unlock(trans)
               lock(trans)
               unlock(trans)
          <--- RESPONSE(iSCSI)
               destroy(task)
              | istgt                                       |
              +---------------------------------------------+
----------------------------------------------------------------------
FB:    FirstBurstLength
task:  iSCSI PDU w/local buffer, mutex, status, etc

LU:    mutex for entire LU(LUNs)
cmdq:  mutex for CmdSN queue on LUN
taskq: mutex for task queue on each connection
trans: mutex for transfer/execute

transc: cond for transfer
execc:  cond for execute
----------------------------------------------------------------------

MCSでラウンドロビンした場合は
以下のようなコマンドシーケンスになります。
もちろん事の本質はMCSがマルチスレッドな事ではなく、
DATAOUTが1個しか実行できないのとCmdSN3/CmdSN6を保留する事にある。

MCSスレッド1    MCSスレッド2      LUNスレッド
  (ExpCmdSN=1 全MCSコネクション共有データ)
                CmdSN1 =========> (CmdSN1キュー格納)
  (ExpCmdSN=2 全MCSコネクション共有データ)
                                  CmdSN1実行開始
CmdSN2 =========================> (CmdSN2キュー格納)
  (ExpCmdSN=3 全MCSコネクション共有データ)
                (タスクキュー) <= タスク実行を要求
                DATAOUT(CmdSN3保留)
CmdSN4待機
                DATAOUT完了    => 完了受理
                                  LUNロック
                                  LUNライト実行
                                  LUNロック解除
                CmdSN1結果受理 <= CmdSN1実行結果
                RESPONSE          CmdSN1終了
                (タスク削除)      CmdSN2実行開始
(タスクキュー) <================= タスク実行を要求
                保留CmdSN3処理 => (CmdSN3キュー格納)
  (ExpCmdSN=4 全MCSコネクション共有データ)
CmdSN4待機解除 =================> (CmdSN4キュー格納)
  (ExpCmdSN=5 全MCSコネクション共有データ)
                CmdSN5 =========> (CmdSN5キュー格納)
  (ExpCmdSN=6 全MCSコネクション共有データ)
DATAOUT(CmdSN6保留)
 .
 .
 .

エラーリカバリー対応も結構大変になるかも。
キュー内のコマンドはもちろん現在実行中のロックも
処分しないといけないから。

最新実験結果はこんな感じ。
istgt iSCSI Target (HDDx6 ZFS RAID-Z2)

ローカルに接続したWD5000ABYSに対してはこの程度出る。

MCSのシーケンシャルリードで二本のGbEを使った場合に
二本分としてそこそこ安定して出せるようになりました。
144MB/sなら1000MBもわずか7秒で読める!
次はめざせ150MB/sという所か。

タグ: , , , , ,

コメントをどうぞ