Eu li /dev/sda
usando um tamanho de bloco de 1MiB. O Linux parece limitar as solicitações de IO a512 KiBum tamanho médio de 512KiB. O que esta acontecendo aqui? Existe uma opção de configuração para este comportamento?
$ 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
...
Enquanto meu dd
comando está em execução, rareq-sz
é 512.
rareq-sz O tamanho médio (em kilobytes) das solicitações de leitura que foram emitidas para o dispositivo.
$ 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
...
A versão do kernel é 5.1.15-300.fc30.x86_64
. max_sectors_kb
é 1280.
$ 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
Por padrão eu uso o agendador de E/S BFQ. Eu também tentei repetir o teste depois echo 0 | sudo tee wbt_lat_usec
. Eu também tentei repetir o teste depois echo mq-deadline|sudo tee scheduler
. Os resultados permaneceram os mesmos.
Além do WBT, usei as configurações padrão para ambos os agendadores de E/S. Por exemplo, para mq-deadline
, iosched/read_expire
é 500, o que equivale a meio segundo.
Durante o último teste (mq-deadline, WBT desativado), executei btrace /dev/sda
. Ele mostra que todas as solicitações foram divididas em duas metades desiguais:
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 -- split Em [software] raid ou configurações de mapeador de dispositivo, um i/o de entrada pode abranger um dispositivo ou zona interna e precisa ser dividido em pedaços menores para serviço. Isso pode indicar um problema de desempenho devido a uma configuração ruim desse dispositivo raid/dm, mas também pode ser apenas parte das condições normais de limite. dm é notavelmente ruim nisso e clonará muitas E/S.
Coisas para ignorar emiostat
Ignore o %util
número. Está quebrado nesta versão. ( `dd` está rodando a toda velocidade, mas só vejo 20% de utilização do disco. Por quê? )
Eu pensei que aqu-sz
também é afetado por ser baseado em %util . Embora eu achasse que isso significava que seria cerca de três vezes maior aqui (100/34,27).
Ignore o svtm
número. "Aviso! Não confie mais neste campo. Este campo será removido em uma versão futura do sysstat."
Suponho que a E/S está sendo limitada a "cerca de" 512 KiB devido à maneira como está sendo enviada e vários limites sendo atingidos (neste caso
/sys/block/sda/queue/max_segments
). O questionador teve tempo para incluir várias informações secundárias (como a versão do kernel e ablktrace
saída) que nos permite adivinhar esse mistério, então vamos ver como cheguei a essa conclusão.É fundamental observar que o questionador disse cuidadosamente "sobre" no título. Enquanto a
iostat
saída nos faz pensar que devemos procurar valores de 512 KiB:o
blktrace
(viablkparse
) nos dá alguns valores exatos:(Normalmente, esperamos que um único setor tenha 512 bytes de tamanho) Portanto, a E/S de leitura do
dd
setor 6496256 que foi dimensionado para 2048 setores (1 MiByte) foi dividida em duas partes - uma lida começando no setor 6496256 para 1344 setores e outra leia começando no setor 6497600 para 704 setores. Portanto, o tamanho máximo de uma solicitação antes de ser dividida é um pouco mais de 1024 setores (512 KiB) ... mas por quê?O questionador menciona uma versão do kernel do
5.1.15-300.fc30.x86_64
. Fazer uma pesquisa no Google por linux split block i/o kernel mostra o "Capítulo 16. Block Drivers" de Linux Device Drivers, 3rd Edition e que mencionaEmbora não estejamos dividindo
bio
s porque pretendemos enviá-los para dispositivos diferentes (da forma como md ou mapeador de dispositivos), isso ainda nos dá uma área para explorar. A pesquisa na fonte do kernel Linux 5.1.15 do LXRbio_split
inclui um link para o arquivoblock/blk-merge.c
. Dentro desse arquivo existeblk_queue_split()
e para I/Os não especiais que a função chamablk_bio_segment_split()
.(Se você quiser fazer uma pausa e explorar o LXR, agora é um bom momento. Continuarei a investigação abaixo e tentarei ser mais conciso daqui para frente)
Na
blk_bio_segment_split()
variávelmax_sectors
, em última análise, vem alinhando o valor retornadoblk_max_size_offset()
e que olhaq->limits.chunk_sectors
e se for zero, então apenas retornaq->limits.max_sectors
. Clicando ao redor, vemos comomax_sectors
é derivado demax_sectors_kb
emqueue_max_sectors_store()
que está emblock/blk-sysfs.c
. De voltablk_bio_segment_split()
, amax_segs
variável vem dequeue_max_segments()
onde retornaq->limits.max_segments
. Continuando para baixoblk_bio_segment_split()
, vemos o seguinte:De acordo com
block/biovecs.txt
estamos iterando sobre bvec de várias páginas.Portanto, se o tamanho de E/S for maior que
max_sectors_kb
(que é 1280 KiB no caso do questionador), ele será dividido (se houver segmentos sobressalentes e espaço de setor, preencheremos a E/S atual o máximo possível antes de dividir por dividindo-o em segmentos e somando o máximo possível). Mas no caso do questionador a E/S é "apenas" 1 MiB que é menor que 1280 KiB então não estamos neste caso... Mais abaixo vemos:queue_max_segment_size()
retornaq->limits.max_segment_size
. Dado o que vimos anteriormente (if (sectors + (bv.bv_len >> 9) > max_sectors)
)bv.bv_len
será em termos de bytes (caso contrário, por que temos que dividi-lo por 512?) e o questionador disse que/sys/block/sda/queue/max_segment_size
era 65336. Se soubéssemos qual era o valorbv.bv_len
...Portanto, para cada um
bv
, verificamos se é um bvec de página única ou de várias páginas (verificando se seu tamanho é <=PAGE_SIZE
). Se for um bvec de página única, adicionamos um à contagem de segmentos e fazemos alguma contabilidade. Se for um bvec de várias páginas, verificamos se ele precisa ser dividido em segmentos menores (o código embvec_split_segs()
faz comparações com oget_max_segment_size()
que, neste caso, significa que ele dividirá o segmento em vários segmentos não maiores que 64 KiB (anteriormente dissemos que/sys/block/sda/queue/max_segment_size
era 65336), mas há não deve ter mais de 168 (max_segs
) segmentos. Sebvec_split_segs()
atingiu o limite de segmento e não cobriu todo obv
comprimento do , então pularemos parasplit
. No entanto, SE assumirmos que tomamos ogoto split
caso só geremos 1024 / 64 = 16 segmentos, então, no final das contas, não teríamos que enviar menos de 1 MiB de E/S, então esse não é o caminho pelo qual a E/S do questionador passou ...Trabalhando para trás, se assumirmos que havia "apenas segmentos de tamanho de página única", isso significa que podemos deduzir
bv.bv_offset + bv.bv_len
<= 4096 e comobv_offset
é umunsigned int
então significa 0 <=bv.bv_len
<= 4096. Assim, também podemos deduzir que nunca pegamos o corpo de condição que levougoto new_segment
antes. Em seguida, concluímos que o biovec original deve ter tido 1024 / 4 = 256 segmentos. 256 > 168 então teríamos causado um salto parasplit
logo apósnew_segment
gerando uma E/S de 168 segmentos e outra de 88 segmentos. 168 * 4096 = 688128 bytes, 88 * 4096 = 360448 bytes, mas e daí? Nós iremos:Quais são os números que vimos na
blktrace
saída:Então, proponho que a
dd
linha de comando que você está usando está fazendo com que a E/S seja formada em bvecs de página única e, como o número máximo de segmentos está sendo alcançado, a divisão de E/S acontece em um limite de 672 KiB para cada I /O.Suspeito que se tivéssemos enviado E/S de uma maneira diferente (por exemplo, via E/S em buffer) de modo que bvecs de várias páginas fossem gerados, teríamos visto um ponto de divisão diferente.
Sort of -
/sys/block/<block device>/queue/max_sectors_kb
é um controle sobre o tamanho máximo que uma E/S normal submetida através da camada de bloco pode ter antes de ser dividida, mas é apenas um dos muitos critérios - se outros limites forem atingidos (como o máximo de segmentos), então um A E/S baseada em bloco pode ser dividida em um tamanho menor. Além disso, se você usar comandos SCSI brutos, é possível enviar uma E/S de/sys/block/<block device>/queue/max_hw_sectors_kb
tamanho maior, mas você estará ignorando a camada de bloco e E/S maiores serão rejeitadas.Na verdade, você pode Ilya Dryomov descrevendo essa
max_segments
limitação em um tópico Ceph Users de junho de 2015 "krbd dividindo E/S grandes em E/S menores" e uma correção foi feita posteriormente pararbd
dispositivos (que foi corrigida posteriormente ).A validação adicional do acima vem através de um documento intitulado " Quando 2 MB se transforma em 512 KB " pelo mantenedor da camada de bloco do kernel Jens Axboe, que tem uma seção intitulada "Limitações do dispositivo" cobrindo a limitação máxima de segmentos de forma mais sucinta.