A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Linux

Administração de Largura de Banda no Linux II

Dom

22

Abr

2007


18:09

(7 votos, média 4.14 de 5) 


A famosa qdisc CBQ

A CBQ é a qdisc mais complexa, a mais conhecida, a menos compreendida e, provavelmente, a mais difícil de fazer funcionar. Isto não é porque seus autores sejam carrascos ou incompetentes, muito pelo contrário, é porque o algoritmo CBQ não é muito preciso e não combina com o jeito do Linux trabalhar.

Além se ser uma qdisc com classe, a CBQ também é um modelador e, justamente por este fato, ela não trabalha muito bem. Deveria ser o seguinte: se tentarmos modelar uma conexão de 100mbit/s para 1mbit/s, o link deveria ficar ocioso 90% do tempo; se não ficar, é preciso fazer um estrangulamento para que ele FIQUE 90% do tempo ocioso.

A ociosidade é difícil de ser medida, por isto a CBQ deriva o tempo ocioso do número de microsegundos decorridos entre solicitações de dados feitas pela camada de hardware. Isto pode ser usado para obter um valor aproximado de quanto o link está cheio ou vazio.

Este método é um tanto tortuoso e nem sempre fornece resultados apropriados. Por exemplo, qual será o tempo ocioso se a velocidade atual do link de uma interface não for suficiente para transmitir a carga total de 100mbit/s de dados? Um driver que não esteja bem implementado ou uma placa de rede PCMCIA, que também nunca chega nos 100mbit/s devido à arquitetura do bus, são duas situações nas quais o cálculo da ociosidade fica muito complicado.

A coisa fica ainda pior se considerarmos dispositivos de rede not-quite-real (não-bem-reais) como PPP sobre Ethernet ou PPTP sobre TCP/IP. A largura de banda efetiva nestes casos provavelmente é determinada pela eficiência de pipes para o espaço do usuário - que é imensamente grande.

Quem fizer estas medições vai descobrir que a CBQ nem sempre é precisa e que, algumas vezes, o resultado está completamente fora.

Em muitas circunstâncias, no entanto, a CBQ funciona bem. Com a documentação fornecida a seguir você deveria ser capaz de configurá-la para que funcione bem na maioria dos casos.

Modelagem CBQ em detalhes

A CBQ trabalha assegurando-se de que o link esteja ocioso o tempo suficiente para que ela possa diminuir a largura de banda para a taxa que foi configurada. Para isto, ela calcula o tempo que deveria decorrer entre a média de pacotes.

Durante as operações, o tempo ocioso efetivo é medido usando uma média exponencial, a exponential weighted moving average (EWMA), a qual considera que pacotes recentes sejam exponencialmente mais importantes que pacotes mais antigos. A loadaverage (média de carga) do UNIX é calculada da mesma forma.

O tempo ocioso calculado é subtraido do tempo calculado pela EWMA e o valor resultante é chamado de 'avgidle'. Um link perfeitamente carregado tem uma avgidle de zero: os pacotes chegam nos exatos intervalos calculados.

Um link com excesso de carga tem uma avgidle negativa. Se ela se tornar negativa em excesso, a CBQ interrompe seu trabalho por um tempo, ficando no estado 'overlimit' (acima do limite).

Por outro lado, um link ocioso pode acumular uma enorme avgidle, o que acaba ermitindo uma largura de banda infinita depois de algumas horas de silêncio. Para impedir que isto aconteça, a avgidle é truncada quando alcança o valor maxidle (ociosidade máxima).

Teoricamente, se a CBQ estiver em 'overlimit', ela poderia ajustar-se para o exato período de tempo calculado para a passagem dos pacotes, passar um pacote e depois ajustar-se novamente. Poderia... mas veja o parâmetro 'minburst' a seguir.

Estes são os parâmetros que podem ser utilizados para configurar modelagens:

  • avpkt: (average packet) É o tamanho médio dos pacotes, medido em bytes. É necessário para calcular maxidle, que é derivado de maxburst, o qual é especificado em pacotes.
  • bandwidth: (largura de banda) A largura de banda física do dispositivo, necessária para cálculos do tempo ocioso.
  • cell: (célula) O tempo que um pacote leva para ser transmitido através de um dispositivo pode aumentar em passos, baseados no tamanho do pacote. Por exemplo, um pacote de tamanho 800 e um pacote de 806 podem levar o mesmo tempo para serem transmitidos - isto determina a granularidade. Geralmente fixada em '8'. Precisa ser um número inteiro, uma potência de dois (2, 4, 8, 16, etc).
  • maxburst: (rajada máxima) Este número de pacotes é usado para calcular maxidle de modo que, quando avgidle for igual a maxidle, este número de pacotes médios podem ser disparados antes que avgidle caia para zero. Aumente este número para ser mais tolerante com as rajadas. Não é possível definir maxidle diretamente, só através deste parâmetro.
  • minburst: (rajada mínima) Como já foi explicado, a CBQ precisa de um ajuste quando chega no overlimit. A solução ideal é fazer isto durante exatamente o tempo ocioso calculado e depois passar um pacote. Para kernels Unix, no entanto, geralmente é difícil programar eventos mais curtos do que 10ms. Por isto é melhor se ajustar durante um período maior, depois passar a quantidade de pacotes definida em minburst numa leva só e, a seguir, "dormir" por mais um tempo minburst.
    O tempo de espera é chamado de offtime. Valores maiores de minburst resultam numa modelagem mais precisa a longo prazo, mas também a rajadas maiores na escala de tempo de milisegundos.
  • minidle: (ociosidade mínima) Se a avgidle estiver abaixo de zero, estamos em overlimit e será preciso aguardar até que a avgidle alcance o valor suficiente para enviar um pacote. Para evitar que uma rajada súbita feche o link por um período de tempo muito prolongado, a avgidle recebe o valor de minidle se ela estiver com um valor muito baixo.
    A minidle é especificada em microsegundos negativos: 10 significa que a avgidle é truncada em -10µs.
  • mpu: (minimum packet unit) Tamanho mínimo do pacote. É necessário porque um pacote, mesmo tendo tamanho zero, é ampliado para 64 bytes pela Ethernet e leva um certo tempo para ser transmitido. A CBQ precisa conhecer este valor para poder calcular com precisão o tempo ocioso.
  • rate: (taxa) A taxa desejada de tráfego que sai desta qdisc - este é o acelerador!

Internamente, a CBQ possui uma porção de ajustes finos. Por exemplo, as classes que sabidamente não têm dados na fila não são solicitadas. As classes em overlimit são penalizadas baixando sua prioridade efetiva. Tudo muito esperto... mas tudo muito complicado smile

Comportamento da BCQ

Além da modelagem, que usa aproximações de ociosidade conforme já mencionado, a CBQ também atua como uma fila PRIO no sentido de que as classes possuem prioridades diferentes e que valores mais baixos de prioridade são apurados antes dos valores mais altos.

Cada vez que a camada de hardware requisita um pacote para ser enviado para fora da rede, um processo de ciranda (round robin) ponderado (Weighted Round Robin - WRR) é iniciado, começando pelas classes de prioridade mais baixa. Estas são agrupadas e inquiridas se possuem dados disponíveis. Se tiverem, recebem autorização para despachar. Depois que uma classe despachou um certo número de bytes, é a vez da próxima com o mesma prioridade.

Os seguintes parâmetros controlam o processo WRR:

  • allot: (designar) Quando a CBQ externa é chamada para enviar um pacote através de uma interface, ela tentará todas as qdics internas (das classes), uma de cada vez, de acordo com a prioridade. Quando chega a vez de uma classe, ela pode despachar apenas uma quantidade limitada de dados. 'Allot' é a unidade básica desta quantidade. Veja o parâmetro 'weight' para mais informações.
  • prio: (prioridade) A CBQ também pode atuar como um dispositivo PRIO. Classes internas com prioridade mais alta são inquiridas primeiro e, enquanto tiverem tráfego, outras classes não são apuradas.
  • weight: (peso) O peso ajuda no processo ponderado de ciranda (Weighted Round Robin). Todas as classes, uma depois da outra, têm a possibilidade de enviar pacotes. Se houver classes com uma largura de banda significantemente maior do que outras, faz sentido permitir que elas enviem uma quantidade maior de dados em cada rodada do que as outras.
    Uma CBQ soma todos os pesos sob uma classe e os normaliza, o que permite usar números arbitrários: apenas as proporções são importantes. O pessoal tem chutado 'taxa/10', o que parece funcionar bem. O peso re-normalizado é multiplicado pelo parâmetro 'allot' para determinar o volume de dados que pode ser enviado numa rodada.

Por favor, observe que todas as classes na hierarquia CBQ precisam compartilhar o mesmo número maior (major)!

Parâmetros CBQ que determinam o compartilhamento e o empréstimo de links

Além de simplesmente limitar certos tipos de tráfego, também é possível especificar quais classes podem emprestar capacidade de outras classes ou, por outro lado, quais podem emprestar largura de banda.

  • isolated/sharing: (isolada/compartilhando) A classe que é configurada com 'isolated' não irá compartilhar largura de banda com as classes irmãs. Use isto se houver agências competindo ou pouco amigáveis.
    O programa de controle tc (traffic control) também aceita 'sharing', que é o contrário de 'isolated'.
  • bounded/borrow: (travada/emprestar) Uma classe também pode ser 'bounded', o que significa que não tentará emprestar largura de banda de classes irmãs. O tc também aceita 'borrow', o contrário de 'bounded'.

Uma situação típica pode ser a de um link com duas agências, ambas 'isolated' e 'bouded'. Neste caso, as duas ficam realmente limitadas às taxas que lhes foram designadas e também não vão existir empréstimos entre elas.

Dentro de uma classe de agenciamento deste tipo pode haver outras classes para as quais é permitido negociar largura de banda.

Exemplo de configuração

A configuração a seguir limita o tráfego do servidor web em 5mbit e o tráfego SMTP em 3mbit. Juntos, eles não podem ultrapassar 6mbit. A NIC é de 100mbit e as classes podem emprestar largura de banda.

               1:           qdisc raiz
               |
              1:1           classe filha
             /   \
            /     \
          1:3     1:4       classes folha
           |       |
          30:     40:       qdiscs
         (sfq)   (sfq)

Esta parte instala a classe raiz e a costumeira 1:1. A classe 1:1 é travada (bouded) de modo que o total da largura de banda não exceda 6mbit.

# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit         \
  avpkt 1000 cell 8
# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit  \
  rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20      \
  avpkt 1000 bounded

Como já foi dito anteriormente, a CBQ exige um "montão" de configurações. A configuração da HTB é muito mais simples.

A seguir vêm as duas classes folha. Observe como o peso é escalonado de acordo com a taxa. As duas classes não são travadas, mas estão conectadas à classe 1:1 que está travada. Deste modo, a soma da largura de banda das duas classes nunca será maior do que 6mbit. As IDs das classes precisam estar dentro do mesmo número maior (major) que o da qdisc pai.

As duas classes têm uma qdisc FIFO por default, mas estas são subsituidas por filas SFQ para que cada fluxo de dados seja tratado igualmente.

# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
# tc qdisc add dev eth0 parent 1:4 handle 40: sfq

Estes comando, anexados diretamente à raiz, enviam o tráfego para as qdiscs certas.

# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
  sport 80 0xffff flowid 1:3
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
  sport 25 0xffff flowid 1:4

Observe que 'tc class add' é usado para CRIAR classes dentro de uma qdisc, mas, para adicionar qdiscs a estas classes, usa-se 'tc qdisc add'.

Talvez você esteja se perguntando o que acontece com o tráfego que não é classificado por uma destas duas regras. Parece que, neste caso, os dados não serão limitados e serão processados por 1:0.

Se juntos, SMTP e web, tentarem ultrapassar o limite de 6mbit/s, a largura de banda será dividida de acordo com o parâmetro 'weight' (peso), o que dá 5/8 do tráfego para o servidor web e 3/8 para o servidor de email.

Com esta configuração também é possível dizer que o tráfego do servidor web terá sempre no mínimo 5/8 * 6 mbit = 3.75 mbit.

Outros parâmetros CBQ: split e defmap

Uma qdisc com classe precisa chamar filtros para determinar qual das classes deve receber um pacote. Além de acionar o filtro, a CBQ oferece outras opções: split e defmap. Este negócio é um tanto complicado de entender e felizmente não é vital. Em todo caso, aqui vai uma explicação.

Como é frequente filtrar pacotes apenas pelo campo TOS (Type Of Service - Tipo de Serviço), existe uma sintaxe especial. Sempre que a CBQ precisar descobrir a fila na qual um pacote precisa entrar, ela verifica se este nó é um 'split node' (nó dividido). Se for, uma das sub-qdiscs indicou que quer receber todos os pacotes com uma determinada prioridade, o que pode ser obtido através do campo TOS ou ser uma opção do socket determinada por aplicativos.

É feita uma operação AND com os bits da prioridade dos pacotes e o campo defmap para ver se existe uma coincidência. Em outras palavras, este é um atalho para criar um filtro muito rápido que identifica certas prioridades. Um defmap (mapa de definição) com um valor hexadecimal FF coincide com todas as prioridades e não vai filtrar coisa nenhuma. Um exemplo de configuração talvez esclareça melhor as coisas:

# tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 \
  cell 8 avpkt 1000 mpu 64
 
# tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit    \
  rate 10Mbit allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20        \
  avpkt 1000

Este é o preâmbulo padrão da CBQ. Como sempre, um montão de números!

O defmap se refere a bits TC_PRIO, que são definidos como:

TC_PRIO..          Num  Corresponde ao TOS
-------------------------------------------------
BESTEFFORT         0    Maximizar Confiança        
FILLER             1    Minimizar Custo              
BULK               2    Maximizar Throughput (0x8)  
INTERACTIVE_BULK   4                               
INTERACTIVE        6    Minimizar Delay (0x10)      
CONTROL            7                               

O TC_PRIO.. número corresponde a bits contados da direita para a esquerda. Veja pfifo_fast no tutorial Administração de Largura de Banda no Linux para maiores detalhes.

Agora as classes interativa e bulk:

# tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit     \
  rate 1Mbit allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20        \
  avpkt 1000 split 1:0 defmap c0

# tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit     \
  rate 8Mbit allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20        \
  avpkt 1000 split 1:0 defmap 3f

O 'split qdisc' (qdisc dividido) é 1:0 e é onde a escolha será feita. C0 (hexa) é 1100 0000 binário e 3F é 0011 1111. Estes dois binários, em conjunto, cobrem todos os bits. A primeira classe analisa os dois bits à esquerda (bits 6 e 7) que correspondem a 'interactive' e controle de tráfego. A segunda classe confere os bits restantes.

O nó 1:0 tem agora uma tabela como esta:

prioridade	enviar para
0		1:3
1		1:3
2		1:3
3		1:3
4		1:3
5		1:3
6		1:2
7		1:2

Só para se divertir, você também pode passar uma 'change mask' (máscara de mudança) que indique exatamente quais a prioridades que você quer mudar. Você só vai precisar desta máscara se estiver rodando 'tc class change'. Por exemplo, para adicionar 'best effort traffic' (tráfego do melhor esforço) a 1:2, podemos rodar o seguinte:

# tc class change dev eth1 classid 1:2 cbq defmap 01/01

O mapa de prioridades em 1:0 agora é o seguinte:

prioridade	enviar para
0		1:2
1		1:3
2		1:3
3		1:3
4		1:3
5		1:3
6		1:2
7		1:2

Informações adicionais