私のIOリクエストサイズが約512Kに制限されているのはなぜですか?

私のIOリクエストサイズが約512Kに制限されているのはなぜですか?

私は/dev/sda読み取りに1MiBブロックサイズを使用します。 LinuxはIO要求を制限しているようです。512KiB平均サイズは512KiBです。ここで何が起こっているのでしょうか?この動作の設定オプションはありますか?

$ sudo dd iflag=direct if=/dev/sda bs=1M of=/dev/null status=progress
1545601024 bytes (1.5 GB, 1.4 GiB) copied, 10 s, 155 MB/s
1521+0 records in
1520+0 records out
...

私のddコマンドが実行されるとrareq-sz512です。

珍しいサイズ デバイスへの読み取り要求の平均サイズ(KB)。

-man iostat

$ iostat -d -x 3
...
Device            r/s     w/s     rkB/s     wkB/s   rrqm/s   wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util
sda            309.00    0.00 158149.33      0.00     0.00     0.00   0.00   0.00    5.24    0.00   1.42   511.81     0.00   1.11  34.27
dm-0             0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
dm-1             0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
dm-2             0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
dm-3             0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
...

カーネルバージョンは5.1.15-300.fc30.x86_64.is max_sectors_kb1280です。

$ cd /sys/class/block/sda/queue
$ grep -H . max_sectors_kb max_hw_sectors_kb max_segments max_segment_size optimal_io_size logical_block_size chunk_sectors
max_sectors_kb:1280
max_hw_sectors_kb:32767
max_segments:168
max_segment_size:65536
optimal_io_size:0
logical_block_size:512
chunk_sectors:0

デフォルトではBFQ I / Oスケジューラを使用します。私も後でテストを繰り返してみましたecho 0 | sudo tee wbt_lat_usec。その後、後でテストを繰り返してみましたecho mq-deadline|sudo tee scheduler。結果はまだ同じです。

WBTに加えて、両方のI / Oスケジューラはデフォルト設定を使用しました。たとえば、500mq-deadlineiosched/read_expire0.5秒に対応します。

最後のテスト(mq-deadline、WBTを無効にする)中に、以下を実行しましたbtrace /dev/sda。すべての要求が2つの異なる部分に分割されていることがわかりました。

  8,0    0     3090     5.516361551 15201  Q   R 6496256 + 2048 [dd]
  8,0    0     3091     5.516370559 15201  X   R 6496256 / 6497600 [dd]
  8,0    0     3092     5.516374414 15201  G   R 6496256 + 1344 [dd]
  8,0    0     3093     5.516376502 15201  I   R 6496256 + 1344 [dd]
  8,0    0     3094     5.516388293 15201  G   R 6497600 + 704 [dd]
  8,0    0     3095     5.516388891 15201  I   R 6497600 + 704 [dd]
  8,0    0     3096     5.516400193   733  D   R 6496256 + 1344 [kworker/0:1H]
  8,0    0     3097     5.516427886   733  D   R 6497600 + 704 [kworker/0:1H]
  8,0    0     3098     5.521033332     0  C   R 6496256 + 1344 [0]
  8,0    0     3099     5.523001591     0  C   R 6497600 + 704 [0]

X——分割[ソフトウェア] raidまたはデバイスマッパー設定からの着信I / Oは、デバイスまたは内部領域にまたがる可能性があり、サービスのためにより小さな部分に分割する必要があります。これは、不適切なraid / dmデバイス設定によるパフォーマンスの問題を示す可能性がありますが、通常の境界条件の一部である可能性があります。 dmは、特に多くのI / Oを複製するのには不十分です。

-man blkparse

無視すべきことiostat

この%util番号は無視してください。このバージョンでは壊れました。 (`dd`は最高速度で実行されていますが、ディスク使用率はわずか20%です。なぜ?)

アイデア aqu-szまた影響を受ける%utilに基づいているので。私の考えにはこれがここサイズの約3倍(100/34.27)という意味だと思います。

このsvtm番号は無視してください。 「警告!このフィールドを信頼しないでください。このフィールドは将来のsysstatリリースで削除される予定です。」

答え1

私のIOリクエストサイズが約512Kに制限されているのはなぜですか?

I / Oは、コミットされた方法と到達したさまざまな制限(この場合)のために「/sys/block/sda/queue/max_segmentsおよそ」512KiBに制限されていると思います。質問者は、blktrace私たちがこの謎を推測できるように、さまざまな補助情報(カーネルバージョンや出力など)を提供するのに時間を費やしました。

なぜ[… ]は次のように制限されます。~について512K?

重要なのは、質問者がタイトルに「about」を注意深く言及したことに注意することです。出力結果を見ると、iostat512KiBの値を見つける必要があると思うようになります。

Device         [...] aqu-sz rareq-sz wareq-sz  svctm  %util
sda            [...]   1.42   511.81     0.00   1.11  34.27

blktrace(経由)はblkparseいくつかの正確な値を提供します。

  8,0    0     3090     5.516361551 15201  Q   R 6496256 + 2048 [dd]
  8,0    0     3091     5.516370559 15201  X   R 6496256 / 6497600 [dd]
  8,0    0     3092     5.516374414 15201  G   R 6496256 + 1344 [dd]
  8,0    0     3093     5.516376502 15201  I   R 6496256 + 1344 [dd]
  8,0    0     3094     5.516388293 15201  G   R 6497600 + 704 [dd]
  8,0    0     3095     5.516388891 15201  I   R 6497600 + 704 [dd]

(通常、単一のセクタサイズは512バイトであると予想されます。)したがって、ddサイズ2048セクタ(1MiByte)のセクタ6496256の読み取りI / Oは2つの部分に分割されます。 1 つはセクター 6496256 1344 セクターから始まり、もう 1 つは 1344 セクターを読み取ります。セクター 6496256 で始まり、セクター 6497600 で始まる 704 セクター。だから分割前に要求された最大サイズは、1024セクタ(512KiB)よりわずかに大きいです。...しかしなぜ?

質問者が言及しました5.1.15-300.fc30.x86_64Google検索Linux分割ブロックI / Oカーネル現れるLinuxデバイスドライバ、第3版の「第16章ブロックドライバ」そして言及

[...]複数のデバイスに送信するためにbio_splitファイルをチャンクに分割するために使用できる呼び出しbio

biosを別のデバイスに送信しようとしているので(mdやデバイスマッパーができる方法で)分割するのではなく、まだナビゲートする領域を提供します。検索LXRの5.1.15 Linuxカーネルソースコードbio_splitファイルへのリンクが含まれています。block/blk-merge.c。このファイルには以下が含まれています。blk_queue_split()関数呼び出しのための非特殊I/Oblk_bio_segment_split()

(休憩を持ってLXRを探索したい場合は、今が良い時です。下記で調査を続け、より簡潔に努力します)

blk_bio_segment_split()変数のmax_sectorsソートから返された最終値blk_max_size_offset()レポートq->limits.chunk_sectors0の場合は返しますq->limits.max_sectors。クリックすると、max_sectorsそれがどのようにmax_sectors_kb派生するかを確認できます。queue_max_sectors_store()この時間はblock/blk-sysfs.cblk_bio_segment_split()に戻り、max_segs変数は次から来ます。queue_max_segments()Returnq->limits.max_segments引き続きblk_bio_segment_split()次のことがわかります。

    bio_for_each_bvec(bv, bio, iter) {

~によるとblock/biovecs.txt私たちはマルチページbvecを繰り返しています。

        if (sectors + (bv.bv_len >> 9) > max_sectors) {
            /*
             * Consider this a new segment if we're splitting in
             * the middle of this vector.
             */
            if (nsegs < max_segs &&
                sectors < max_sectors) {
                /* split in the middle of bvec */
                bv.bv_len = (max_sectors - sectors) << 9;
                bvec_split_segs(q, &bv, &nsegs,
                        &seg_size,
                        &front_seg_size,
                        &sectors, max_segs);
            }
            goto split;
        }

したがって、I / Oサイズが(質問の場合は1280KiB)より大きい場合はmax_sectors_kb分割されます(使用可能なセグメントとセクタスペースがある場合は、分割する前に現在のI / Oを最大限に埋めます)。部分に分けて、できるだけ追加してください)。ただし、質問者の場合、I / Oは「単なる」1MiBで1280KiB未満なので、その場合は該当しません。もっと下から見ることができます。

        if (bvprvp) {
            if (seg_size + bv.bv_len > queue_max_segment_size(q))
                goto new_segment;
        [...]

queue_max_segment_size()返品q->limits.max_segment_size。以前に見たこと(if (sectors + (bv.bv_len >> 9) > max_sectors))がバイト単位であることbv.bv_lenを考慮すると(他の理由は何ですか?512で割る必要がありますか?)/sys/block/sda/queue/max_segment_sizeその価値が何であるかを知っていればbv.bv_len...

[...]
new_segment:
        if (nsegs == max_segs)
            goto split;

        bvprv = bv;
        bvprvp = &bvprv;

        if (bv.bv_offset + bv.bv_len <= PAGE_SIZE) {
            nsegs++;
            seg_size = bv.bv_len;
            sectors += bv.bv_len >> 9;
            if (nsegs == 1 && seg_size > front_seg_size)
                front_seg_size = seg_size;
        } else if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
                    &front_seg_size, &sectors, max_segs)) {
            goto split;
        }
    }

    do_split = false;

したがって、それぞれがbv単一ページか複数ページbvecかを確認します(サイズが<=であることを確認してくださいPAGE_SIZE)。単一ページbvecの場合は、セグメント数を増やしていくつかの帳簿を実行します。マルチページbvecの場合は、より小さいセグメントに分割する必要があることを確認してください(コードはbvec_split_segs()比較を実行しますget_max_segment_size()。この場合、セグメントを/sys/block/sda/queue/max_segment_size64KiB(以前は65336について話していた)よりも大きくないが、168(max_segs)個のセグメントを超えない複数のセグメントに分割するという意味です。bvec_split_segs()セグメント制限に達したがすべての長さが含まれていない場合bvに移動します。ただし、1024/64 = 16個のセグメントのみを生成すると仮定splitすると、最終的に1MiB未満のI / Oをコミットする必要はありません。goto split質問者がI / Oを通過したパスではありません。

逆に外挿して「単一ページサイズのセグメントのみ」と仮定すると、これはbv.bv_offset + bv.bv_len<= 4096を推論できることを意味します。bv_offsetunsigned intこれは、0 <= bv.bv_len<= 4096を意味します。したがって、我々はgoto new_segment条件付きリードをbeforeとして取らないことを推論することもできます。その後、元のBiovecには1024/4 = 256個のセグメントが必要であると結論付けます。 256> 168だから私たちはsplit次へスキップnew_segmentその結果、1つの168セグメントI / Oと別の88セグメントI / Oが生成されます。 168 * 4096 = 688128バイト、88 * 4096 = 360448バイトしかし、それでどうなりますか?素晴らしい:

688128/512 = 1344

360448/512 = 704

出力に表示される数字は次のとおりですblktrace

[...]   R 6496256 + 2048 [dd]
[...]   R 6496256 / 6497600 [dd]
[...]   R 6496256 + 1344 [dd]
[...]   R 6496256 + 1344 [dd]
[...]   R 6497600 + 704 [dd]
[...]   R 6497600 + 704 [dd]

したがって、dd私が使用することを提案するコマンドラインは、I / Oが単一ページbvecを形成し、最大セグメント数に達したため、次の境界でI / O分割が発生します。672KBすべてのI/Oについて。

複数ページのbvecを生成するためにI / Oを異なる方法で送信する(たとえば、バッファ付きI / Oを介して)、別の分割点が表示されると思います。

この動作の設定オプションはありますか?

順序付け -/sys/block/<block device>/queue/max_sectors_kbブロック層を介して送信された一般I / Oは、分割前に到達できる最大サイズの制御ですが、これは多くの基準の1つにすぎません。他の制限(最大セグメントなど)に達すると、ブロックベースのI / Oはより小さいサイズに分割できます。また、ネイティブSCSIコマンドを使用している場合は/sys/block/<block device>/queue/max_hw_sectors_kb最大サイズまでI / Oを送信できますが、そうするとブロック層を迂回してより大きなI / Oが拒否されます。

実はあなたができますIlya Dryomovはこれらのmax_segments制限事項を説明しています。2015年6月、Cephユーザースレッドで「大きなIOを小さなIOに分割するkrbd」と後でrbd機器を修理(どのそれ自体は後で復元されました)。

上記内容の追加確認は、「2MBが512KBになるとカーネルブロック階層マネージャJens Axboeによって作成された「Device Limits」セクションには、最大セグメント制限をより簡潔に扱うセクションがあります。

関連情報