tcmu は LIO のバックエンドの一つで、UIO を使って SCSI をコマンドをユーザーランドと通信する。
ドキュメントがかなり詳しく書いているのでわかりやすい。
https://www.kernel.org/doc/Documentation/target/tcmu-design.txt
SCSI コマンドやデータのやり取りは、UIO デバイスを mmap したメモリ領域に書き込むことで行う。
このメモリ領域は、メタデータ、コマンドキュー、データ領域にわけられている。
キューにコマンドがエンキューされたことは、UIO デバイスを read または poll することで検知できる。
またコマンドを処理したことをカーネルに伝えるには、UIO デバイスに 4 byte write してやることで実現する。
NBD と違い、SCSI コマンドを解釈する必要があるので実装は面倒くさいが、
カーネルとの通信は mmap した領域に書き込むだけなのでオーバーヘッドが少ないことが予想される。
いつものように rust でメモリバックエンドのデバイスを実装してみた。
github.com
SCSI コマンドは linux がデバイスを動かせる最低限しか実装していない。
それでもかなり面倒くさかったのでデバイスを作る開発者には頭が下がる。
さて、fio でのベンチマークは以下となる。
バージョンやパラメータが以前と異なるが、まあ大きく問題ではないはず。
rw: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
...
fio-3.1
Starting 3 processes
rw: Laying out IO files (8 files / total 250MiB)
rw: Laying out IO files (8 files / total 250MiB)
rw: Laying out IO files (8 files / total 250MiB)
Jobs: 3 (f=24): [m(3)][100.0%][r=166MiB/s,w=167MiB/s][r=42.4k,w=42.7k IOPS][eta 00m:00s]
rw: (groupid=0, jobs=3): err= 0: pid=1031: Sun Oct 15 02:40:33 2017
read: IOPS=50.0k, BW=195MiB/s (205MB/s)(11.5GiB/60020msec)
slat (nsec): min=1006, max=8783.3k, avg=11616.59, stdev=53570.35
clat (usec): min=20, max=300906, avg=1902.37, stdev=8166.04
lat (usec): min=26, max=300908, avg=1914.10, stdev=8167.55
clat percentiles (usec):
| 1.00th=[ 347], 5.00th=[ 486], 10.00th=[ 529], 20.00th=[ 594],
| 30.00th=[ 668], 40.00th=[ 734], 50.00th=[ 938], 60.00th=[ 1188],
| 70.00th=[ 1729], 80.00th=[ 2245], 90.00th=[ 2704], 95.00th=[ 3032],
| 99.00th=[ 4359], 99.50th=[ 74974], 99.90th=[149947], 99.95th=[160433],
| 99.99th=[210764]
bw ( KiB/s): min= 1680, max=150592, per=33.35%, avg=66720.66, stdev=21592.10, samples=360
iops : min= 420, max=37648, avg=16680.15, stdev=5398.03, samples=360
write: IOPS=49.0k, BW=195MiB/s (205MB/s)(11.4GiB/60020msec)
slat (nsec): min=1489, max=8732.8k, avg=12720.29, stdev=53613.83
clat (usec): min=9, max=301225, avg=1910.20, stdev=8189.98
lat (usec): min=24, max=301228, avg=1923.05, stdev=8191.48
clat percentiles (usec):
| 1.00th=[ 347], 5.00th=[ 486], 10.00th=[ 529], 20.00th=[ 594],
| 30.00th=[ 676], 40.00th=[ 734], 50.00th=[ 947], 60.00th=[ 1188],
| 70.00th=[ 1729], 80.00th=[ 2245], 90.00th=[ 2704], 95.00th=[ 3064],
| 99.00th=[ 4424], 99.50th=[ 74974], 99.90th=[149947], 99.95th=[160433],
| 99.99th=[210764]
bw ( KiB/s): min= 1480, max=150488, per=33.35%, avg=66684.45, stdev=21528.53, samples=360
iops : min= 370, max=37622, avg=16671.08, stdev=5382.13, samples=360
lat (usec) : 10=0.01%, 50=0.01%, 100=0.06%, 250=0.37%, 500=6.07%
lat (usec) : 750=34.44%, 1000=12.39%
lat (msec) : 2=21.49%, 4=24.07%, 10=0.30%, 20=0.23%, 50=0.07%
lat (msec) : 100=0.33%, 250=0.20%, 500=0.01%
cpu : usr=4.07%, sys=16.96%, ctx=1419522, majf=0, minf=43
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.0%
issued rwt: total=3002198,3000568,0, short=0,0,0, dropped=0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
Run status group 0 (all jobs):
READ: bw=195MiB/s (205MB/s), 195MiB/s-195MiB/s (205MB/s-205MB/s), io=11.5GiB (12.3GB), run=60020-60020msec
WRITE: bw=195MiB/s (205MB/s), 195MiB/s-195MiB/s (205MB/s-205MB/s), io=11.4GiB (12.3GB), run=60020-60020msec
Disk stats (read/write):
sdb: ios=2966618/2964788, merge=26473/26652, ticks=4184010/4205964, in_queue=8400100, util=99.91%
これを見ると NBD を使った実装よりも遅いことがわかる。
perf などで調べてみたが、UIO デバイスへの write、すなわち kernel への command completion の通知が重いらしい。
Samples: 31K of event 'cycles:ppp', Event count (approx.): 29291463791
Children Self Command Shared Object Symbol
+ 71.40% 0.22% tcmu-mem [kernel.vmlinux] [k] entry_SYSCALL_64_fastpath
+ 70.75% 0.22% tcmu-mem libpthread-2.26.so [.] __libc_write
+ 70.08% 0.08% tcmu-mem [kernel.vmlinux] [k] sys_write
+ 69.78% 0.56% tcmu-mem [kernel.vmlinux] [k] vfs_write
+ 69.09% 0.24% tcmu-mem [kernel.vmlinux] [k] __vfs_write
+ 68.79% 0.58% tcmu-mem [uio] [k] uio_write
+ 67.84% 0.20% tcmu-mem [target_core_user] [k] tcmu_irqcontrol
+ 56.89% 2.83% tcmu-mem [target_core_user] [k] tcmu_handle_completions
+ 31.39% 0.00% tcmu-mem [unknown] [k] 0x0000000000000001
+ 22.22% 21.87% tcmu-mem libc-2.26.so [.] __memmove_avx_unaligned_erms
+ 21.27% 0.83% tcmu-mem [target_core_mod] [k] target_complete_cmd
+ 20.45% 1.52% tcmu-mem [kernel.vmlinux] [k] preempt_schedule
+ 20.28% 1.17% tcmu-mem [kernel.vmlinux] [k] ___preempt_schedule
+ 19.63% 1.66% tcmu-mem [kernel.vmlinux] [k] try_to_wake_up
+ 19.47% 1.28% tcmu-mem [kernel.vmlinux] [k] __schedule
+ 19.05% 0.59% tcmu-mem [kernel.vmlinux] [k] queue_work_on
+ 18.86% 0.40% tcmu-mem [kernel.vmlinux] [k] preempt_schedule_common
+ 17.40% 1.08% tcmu-mem [kernel.vmlinux] [k] _raw_spin_unlock
+ 16.96% 1.89% tcmu-mem [kernel.vmlinux] [k] __queue_work
+ 15.17% 0.77% tcmu-mem [kernel.vmlinux] [k] insert_work
+ 14.36% 0.12% tcmu-mem [kernel.vmlinux] [k] wake_up_process
+ 13.08% 4.32% tcmu-mem tcmu-mem [.] tcmu_mem::main