現在細かい修正とエラーリカバリー対応を行っています。
今後のバージョンでは次のような動作をするようになります。
互換性のため従来の方法も残す方向で作業しています。
リードの場合はサイズに関係なく読み込み完了したデータとサイズを
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という所か。

