Programação RDMA e Verbs API no ecossistema Linux

O pacote linux-rdma serve como a implementação de referência para o subsistema InfiniBand no kernel Linux (drivers/infiniband). Ele fornece bibliotecas fundamentais no espaço do usuário, como a libibverbs, que expõe as APIs de Verbs necessárias para operações RDMA (Remote Direct Memory Access) e comunicação de alto desempenho.

Componentes Fundamentais da Arquitetura RDMA

Queue Pair (QP)

O Queue Pair é o endpoint de comunicação no RDMA, análogo aos sockets em redes TCP/IP tradicionais. Cada QP é composto por uma fila de envio (Send Queue) e uma fila de recebimento (Receive Queue). Para que dois nós se comuniquem, seus QPs devem ser configurados e conectados, geralmente com o auxílio de um Gerenciador de Comunicação (CM) para a troca inicial de metadados.

/**
 * Protótipo para criação de um Queue Pair
 */
struct ibv_qp *create_rdma_queue_pair(struct ibv_pd *protection_domain,
                                     struct ibv_qp_init_attr *init_attributes);

Completion Queue (CQ)

A Completino Queue funciona como um mecanismo de notificação. Quando uma solicitação de trabalho (Work Request - WR) é finalizada pelo hardware, um elemento de conclusão (CQE) é inserido na CQ. Isso permite que a aplicação verifique o status da operação (sucesso ou erro), o tamanho dos dados transferidos e a origem.

/**
 * Exemplo de criação de uma Fila de Conclusão (CQ)
 */
struct ibv_cq *setup_completion_queue(struct ibv_context *verbs_ctx, 
                                      int minimum_entries, 
                                      void *user_ctx,
                                      struct ibv_comp_channel *evt_channel, 
                                      int comp_vector);

Registro de Memória (Memory Registration - MR)

Diferente de sockets padrão, o hardware RDMA precisa acessar a memória RAM diretamente via DMA sem intervenção da CPU. O registro de memória permite mapear áreas de memória virtual ou física para o adaptador de rede.

struct memory_region_info {
    struct ibv_context *ctx;
    struct ibv_pd      *pd_ptr;
    void               *buffer_addr;
    size_t             buffer_len;
    uint32_t           local_key;  // lkey
    uint32_t           remote_key; // rkey
};

  • lkey (Local Key): Utilizada pelo adaptador local para acessar a memória durante operações de recebimento ou envio.
  • rkey (Remote Key): Enviada ao nó remoto para permitir que ele acesse a memória local via RDMA Read ou Write.

Protection Domain (PD)

O Domínio de Proteção atua como um container lógico de segurança. Ele garante que recursos como QPs e MRs só possam interagir se pertencerem ao mesmo domínio, prevenindo acessos não autorizados entre diferentes processos ou contextos de rede.

/* Alocação de um novo domínio de proteção */
struct ibv_pd *domain = ibv_alloc_pd(verbs_ctx);

Fluxo de Implementação com libibverbs

Para construir uma aplicação RDMA funcional utilizando a API de Verbs, o desenvolvedor deve seguir este fluxo lógico:

1. Localização do Hardware

O primeiro passo é listar os dispositivos InfiniBand/RoCE disponíveis no host através da função ibv_get_device_list(). Cada dispositivo possui um GUID exclusivo e um nome de sistema (ex: mlx5_0).

2. Abertura do Contexto

Após selecionar o dispositivo, abre-se um contexto de execução (ibv_open_device), que servirá de base para todas as operações subsequentes.

3. Configuração de Recursos de Memória e Proteção

Aloca-se o Protection Domain (PD) e, em seguida, registra-se o buffer de memória (MR) que será usado para o tráfego de dados. É neste momento que as permissões (leitura local, escrita remota, etc.) são definidas.

4. Preparação das Filas

Cria-se a Completion Queue (CQ) para monitorar o status das tarefas e o Queue Pair (QP) para gerenciar o tráfego de entrada e saída.

5. Transição de Estados do QP

Um QP recém-criado não pode transmitir dados imediatamente. Ele deve passar por uma máquina de estados controlada por ibv_modify_qp:

  • RESET: Estado inicial.
  • INIT: Informações básicas configuradas; pronto para receber posts na fila de recebimento.
  • RTR (Ready to Receive): Pronto para receber pacotes do nó remoto.
  • RTS (Ready to Send): Pronto para enviar dados; parâmetros de timeout e retransmissão ativos.

6. Execução e Sincronização

A aplicação posta Work Requests (WR) e utiliza ibv_poll_cq para verificar quando o hardware concluiu o processamento.

7. Finalização

A destruição dos objetos deve ocorrer na ordem inversa à criação para evitar vazamentos de recursos ou referências órfãs (QP > CQ > MR > PD > Contexto).

Exemplo de Verificação de Conectividade

Ferramentas como o ibv_rc_pingpong são úteis para validar se a pilha RDMA e a configuração do Driver (como o Soft-RoCE rxe) estão operacionais.

Comando no Servidor:

# ibv_rc_pingpong -d rxe0 -g 0 -s 1024 -n 5

Comando no Cliente:

# ibv_rc_pingpong -d rxe0 -g 0 192.168.1.10 -s 1024 -n 5

Este teste estabelcee uma conexão Reliable Connected (RC) e mede a latência e o throughput entre os nós, permitindo analisar o comportamento dos pacotes através de ferramentas de captura de rede, onde se observa o fluxo de pacotes RC Send Only e os respectivos ACKs de hardware.

Tags: RDMA InfiniBand RoCE libibverbs linux-kernel

Publicado em 9-11 09:10