InfiniBand / RDMA 심화

InfiniBand 서브넷 아키텍처, RDMA 핵심 개념(QP, CQ, MR), 커널 Verbs API, 사용자 공간 라이브러리(libibverbs, librdmacm), IPoIB, 상위 프로토콜(SRP, iSER, NVMe-oF RDMA), GPUDirect RDMA, 성능 최적화 및 트러블슈팅 종합 가이드.

InfiniBand 개요

InfiniBand(IB)는 고성능 컴퓨팅(HPC), 데이터센터, 스토리지 인터커넥트를 위해 설계된 고대역폭·저지연 네트워크 기술입니다. RDMA(Remote Direct Memory Access)를 네이티브로 지원하여 커널 바이패스, 제로카피, 프로토콜 오프로드를 통해 전통적인 TCP/IP 소켓 대비 극적인 성능 향상을 제공합니다.

InfiniBand vs Ethernet 비교

특성InfiniBandEthernet (RoCE v2)iWARP
RDMA 지원 네이티브 RoCE v1/v2 (컨버전스) TCP 위에 RDMA
전송 계층 IB Transport (신뢰성 내장) UDP/IP (RoCE v2) TCP/IP
최대 대역폭 NDR: 400 Gbps (포트당) 400GbE 100GbE
지연시간 ~0.5 μs (HCA-to-HCA) ~1–2 μs ~5–10 μs
손실 처리 신용 기반 흐름 제어 (Lossless) PFC/ECN 필요 (Lossless 구성) TCP 재전송
서브넷 관리 Subnet Manager (opensm) 표준 이더넷 스위칭 표준 이더넷 스위칭
주요 사용처 HPC, AI/ML 클러스터, 스토리지 데이터센터, 클라우드 엔터프라이즈 스토리지

RDMA 전송 기술 비교

기술하위 계층라우팅Lossless 요구특징
IB Native RDMA InfiniBand L2 IB 서브넷 라우팅 내장 (Credit-based) 최저 지연, HPC 표준
RoCE v1 Ethernet L2 L2 전용 PFC 필수 같은 VLAN 내에서만 동작
RoCE v2 UDP/IP IP 라우팅 가능 PFC + ECN 권장 L3 라우팅, 데이터센터 표준
iWARP TCP/IP IP 라우팅 가능 불필요 (TCP 재전송) Lossy 네트워크 호환, 높은 지연
RDMA 핵심 가치: RDMA는 세 가지 핵심 기술을 결합합니다: (1) 커널 바이패스 — 데이터 경로에서 커널 개입 없이 사용자 공간에서 직접 HW 큐에 접근, (2) 제로카피 — 애플리케이션 메모리와 NIC 간 중간 버퍼 복사 없이 직접 DMA 전송, (3) 프로토콜 오프로드 — 전송 프로토콜 처리(세그멘테이션, 신뢰성, 흐름 제어)를 HCA 하드웨어가 수행. 이를 통해 CPU 사용률을 극적으로 낮추면서 높은 처리량과 낮은 지연시간을 동시에 달성합니다.

InfiniBand 서브넷 아키텍처

서브넷 구성 요소

InfiniBand 서브넷은 다음 구성 요소로 이루어집니다:

물리 계층 및 속도

세대신호 속도1x 대역폭4x 대역폭12x 대역폭인코딩
SDR2.5 Gbaud2 Gbps8 Gbps24 Gbps8b/10b
DDR5 Gbaud4 Gbps16 Gbps48 Gbps8b/10b
QDR10 Gbaud8 Gbps32 Gbps96 Gbps8b/10b
FDR14.0625 Gbaud13.64 Gbps54.54 Gbps163.64 Gbps64b/66b
EDR25 Gbaud25 Gbps100 Gbps300 Gbps64b/66b
HDR50 Gbaud50 Gbps200 Gbps600 Gbps64b/66b + PAM4
NDR100 Gbaud100 Gbps400 Gbps1.2 Tbps64b/66b + PAM4
XDR200 Gbaud200 Gbps800 Gbps2.4 Tbps64b/66b + PAM4

주소 체계

InfiniBand Subnet Host A HCA (Port 1) QP / CQ / MR Host B HCA (Port 1) QP / CQ / MR IB Switch LID-based Forwarding Subnet Manager opensm / Switch SM LID 할당 / 경로 계산 IB Router GID-based L3 Routing SMP (관리)

핵심 자료구조 및 개념

Queue Pair (QP)

QP는 RDMA 통신의 기본 단위로, Send Queue (SQ)Receive Queue (RQ) 한 쌍으로 구성됩니다. 각 QP는 고유한 QP Number (QPN)를 가지며, 원격 QP와 1:1(RC), 1:N(UD) 등의 연결 관계를 맺습니다.

Completion Queue (CQ)

CQ는 WR의 완료 이벤트(Work Completion, WC)를 수신하는 큐입니다. 하나의 CQ를 여러 QP의 SQ/RQ에 공유할 수 있습니다. 완료 확인 방식으로 polling(busy-wait)과 completion channel(이벤트 기반) 두 가지를 지원합니다.

Memory Region (MR)

RDMA 동작에 사용할 메모리 영역을 HCA에 등록하는 객체입니다. 등록 시 물리 페이지가 핀(pin)되고, HCA가 DMA로 직접 접근할 수 있는 가상→물리 주소 변환 테이블이 생성됩니다.

Protection Domain (PD) / Address Handle (AH)

Work Request (WR) / Work Completion (WC)

QP 상태 머신

QP는 엄격한 상태 전이 순서를 따라야 합니다:

RESET INIT RTR RTS SQD ERROR ibv_modify_qp P_Key, Port Remote QPN Drain SQ Resume 에러 발생 시 Reset 데이터 전송 가능 수신 가능
QP 상태 전이 순서 엄수: QP 상태는 반드시 RESET → INIT → RTR → RTS 순서로 전이해야 합니다. INIT 단계에서 P_Key, 포트 번호, 접근 플래그를 설정하고, RTR(Ready to Receive)에서 원격 QPN, LID/GID, PSN 등 수신 파라미터를 설정하며, RTS(Ready to Send)에서 타임아웃, 재시도 횟수 등 송신 파라미터를 설정합니다. 순서를 건너뛰면 ibv_modify_qp()-EINVAL을 반환합니다.
MR 등록 비용과 ODP: ibv_reg_mr()는 물리 페이지를 핀(pin)하므로 대용량 메모리 등록 시 상당한 시간이 소요됩니다. Mellanox/NVIDIA ConnectX HCA는 ODP (On-Demand Paging)을 지원하여 실제 접근 시점에 페이지를 핀하는 지연 등록을 제공합니다. ibv_reg_mr()IBV_ACCESS_ON_DEMAND 플래그를 지정하면 ODP가 활성화됩니다. ODP는 등록 시간을 단축하지만, 첫 접근 시 페이지 폴트 오버헤드가 발생할 수 있습니다.

RDMA 동작 (Operations)

Send / Receive

양측 관여(two-sided) 동작입니다. 송신 측이 IBV_WR_SEND WR을 SQ에 post하면, 수신 측은 미리 RQ에 게시한 Receive WR의 버퍼로 데이터를 수신합니다. 수신 측 CPU가 반드시 관여해야 하므로 RDMA Write/Read 대비 오버헤드가 높지만, 메시지 도착을 수신 측이 즉시 인지할 수 있는 장점이 있습니다.

RDMA Write / RDMA Write with Immediate

단측 관여(one-sided) 동작입니다. 송신 측이 원격 MR의 주소(rkey + remote addr)를 지정하여 데이터를 직접 씁니다. 수신 측 CPU는 전혀 관여하지 않습니다 (커널 바이패스 + 제로카피). RDMA Write with Immediate는 32비트 즉시값(imm_data)을 함께 전달하며, 수신 측 RQ에서 완료 이벤트가 생성되어 데이터 도착을 알릴 수 있습니다.

RDMA Read

단측 관여 동작입니다. 로컬 측이 원격 MR에서 데이터를 읽어옵니다. 원격 MR의 rkey와 주소를 알아야 하며, 원격 측 CPU 개입 없이 HCA가 직접 처리합니다. 원격 MR에 IBV_ACCESS_REMOTE_READ 접근 권한이 설정되어 있어야 합니다.

Atomic 연산 (CAS, FAA)

Atomic 연산은 RC QP에서만 지원되며, 원격 MR에 IBV_ACCESS_REMOTE_ATOMIC 권한이 필요합니다.

연결 모드 — RC, UC, UD, XRC

QP 타입연결 방식신뢰성지원 동작MTU사용 사례
RC 연결 지향 (1:1) 신뢰 (ACK/재전송) Send, RDMA Write/Read, Atomic 최대 4KB 스토리지, 범용 RDMA
UC 연결 지향 (1:1) 비신뢰 (무 ACK) Send, RDMA Write 최대 4KB 벌크 전송 (손실 허용)
UD 비연결 (1:N) 비신뢰 Send 전용 최대 MTU (4KB) 멀티캐스트, 관리 트래픽
XRC 연결 지향 (N:1 SRQ) 신뢰 Send, RDMA Write/Read, Atomic 최대 4KB 다수 프로세스 간 효율적 통신
Host A Application Buffer (MR) SQ RQ CQ HCA (RDMA Engine) IB Fabric (Switch / Router) Credit-based Flow Control Host B Application Buffer (MR) SQ RQ CQ HCA (RDMA Engine) RDMA Write (Zero-Copy) DMA Read DMA Write RDMA Read (reverse direction)
RDMA Write vs Send 선택 기준: RDMA Write는 수신 측 CPU 개입이 없어 처리량이 높고 지연이 낮지만, 수신 측에 데이터 도착을 알리려면 별도 메커니즘(polling, RDMA Write with Immediate)이 필요합니다. Send/Recv는 수신 측이 데이터 도착을 즉시 인지할 수 있어 제어 메시지, 작은 RPC에 적합합니다. 대용량 데이터 전송에는 RDMA Write, 제어/시그널링에는 Send를 사용하는 하이브리드 패턴이 일반적입니다.

RDMA 동작 비교

동작방향관여 측수신 WR 필요원격 rkey 필요CQ 완료 위치
Send송신→수신양측 (two-sided)아니오송신 + 수신 CQ
RDMA Write로컬→원격송신측만 (one-sided)아니오송신 CQ만
RDMA Write+Imm로컬→원격양측예 (imm 수신용)송신 + 수신 CQ
RDMA Read원격→로컬로컬만 (one-sided)아니오로컬 CQ만
Atomic CAS원격 in-place로컬만아니오로컬 CQ만
Atomic FAA원격 in-place로컬만아니오로컬 CQ만

리눅스 커널 InfiniBand 서브시스템

drivers/infiniband/ 소스 구조

/* drivers/infiniband/ 디렉토리 구조 */
drivers/infiniband/
├── core/               /* ib_core: 공통 Verbs 프레임워크 */
│   ├── device.c        /* ib_device 등록/해제, sysfs */
│   ├── verbs.c         /* Verbs API 구현 (QP/CQ/MR 생성) */
│   ├── cq.c            /* CQ 추상화 및 poll */
│   ├── mr_pool.c       /* MR 풀링 */
│   ├── uverbs_main.c   /* ib_uverbs: 사용자 공간 Verbs 인터페이스 */
│   ├── uverbs_cmd.c    /* uverbs ioctl 명령 처리 */
│   ├── umad_main.c     /* ib_umad: 사용자 공간 MAD 접근 */
│   ├── sa_query.c      /* SA (Subnet Administration) 쿼리 */
│   ├── cm.c            /* IB Connection Manager */
│   ├── mad.c           /* MAD (Management Datagram) 처리 */
│   ├── addr.c          /* 주소 해석 (GID ↔ IP) */
│   ├── multicast.c     /* 멀티캐스트 그룹 관리 */
│   └── rdma_core.c     /* uobj 관리, fd 기반 리소스 */
├── hw/                 /* HCA 벤더별 드라이버 */
│   ├── mlx4/           /* Mellanox ConnectX-3 */
│   ├── mlx5/           /* Mellanox/NVIDIA ConnectX-4+ */
│   ├── hfi1/           /* Intel OPA (Omni-Path) */
│   ├── bnxt_re/        /* Broadcom RoCE */
│   ├── irdma/          /* Intel iWARP/RoCE */
│   ├── rxe/            /* Soft-RoCE (소프트웨어 에뮬레이션) */
│   └── siw/            /* Soft-iWARP (소프트웨어 에뮬레이션) */
├── ulp/                /* 상위 프로토콜 (Upper Layer Protocol) */
│   ├── ipoib/          /* IP over InfiniBand */
│   ├── srp/            /* SCSI RDMA Protocol */
│   ├── iser/           /* iSCSI Extensions for RDMA */
│   ├── isert/          /* iSER target */
│   └── rtrs/           /* RDMA Transport Server (rnbd) */
└── sw/                 /* 소프트웨어 RDMA 드라이버 */
    ├── rxe/            /* RXE (Soft-RoCE) 공통 */
    └── siw/            /* SIW (Soft-iWARP) 공통 */

ib_core 모듈

ib_core는 InfiniBand 서브시스템의 핵심 프레임워크입니다. HCA 드라이버가 ib_register_device()로 디바이스를 등록하면, ib_core가 sysfs 항목 생성, 이벤트 알림, Verbs API 디스패치 등을 처리합니다. 모든 RDMA 디바이스(IB, RoCE, iWARP)가 동일한 Verbs 추상화를 통해 접근됩니다.

ib_uverbs / ib_umad

주요 커널 구조체

/* include/rdma/ib_verbs.h - 핵심 구조체 (간략화) */

struct ib_device {
    const char             *name;           /* 디바이스 이름 (mlx5_0 등) */
    struct ib_device_ops    ops;             /* Verbs 콜백 함수 테이블 */
    struct device           dev;             /* sysfs 디바이스 */
    u32                     phys_port_cnt;   /* 물리 포트 수 */
    struct ib_device_attr   attrs;           /* 디바이스 능력 (속성) */
    struct list_head        event_handler_list;
    enum rdma_transport_type (*get_transport)(struct ib_device *, u32);
};

struct ib_qp {
    struct ib_device       *device;
    struct ib_pd           *pd;             /* Protection Domain */
    struct ib_cq           *send_cq;        /* 송신 Completion Queue */
    struct ib_cq           *recv_cq;        /* 수신 Completion Queue */
    struct ib_srq          *srq;            /* Shared Receive Queue (선택) */
    u32                     qp_num;          /* QP Number */
    enum ib_qp_type        qp_type;         /* RC, UC, UD, XRC 등 */
    void                    (*event_handler)(struct ib_event *, void *);
};

struct ib_cq {
    struct ib_device       *device;
    int                     cqe;             /* CQ 엔트리 수 */
    struct ib_uobject      *uobject;
    ib_comp_handler         comp_handler;    /* 완료 콜백 */
    void                   *cq_context;
    int                     comp_vector;     /* IRQ 벡터 */
    enum ib_poll_context   poll_ctx;        /* 폴링 컨텍스트 */
};

struct ib_mr {
    struct ib_device       *device;
    struct ib_pd           *pd;
    u32                     lkey;            /* Local Key */
    u32                     rkey;            /* Remote Key */
    u64                     iova;            /* I/O Virtual Address */
    u64                     length;          /* 등록된 메모리 길이 */
    enum ib_mr_type       type;            /* MEM_REG, DMA, USER 등 */
};

커널 Verbs API

카테고리API 함수설명
디바이스ib_register_device()HCA 드라이버가 디바이스 등록
ib_query_device()디바이스 속성(포트 수, 최대 QP 수 등) 조회
PDib_alloc_pd()Protection Domain 생성
ib_dealloc_pd()PD 해제
QPib_create_qp()QP 생성 (타입, CQ, SRQ 지정)
ib_modify_qp()QP 상태 전이 (RESET→INIT→RTR→RTS)
ib_destroy_qp()QP 파괴
CQib_alloc_cq() / ib_create_cq()CQ 생성
ib_poll_cq()CQ에서 완료 이벤트 폴링
MRib_reg_user_mr()사용자 공간 메모리 등록
ib_dereg_mr()MR 해제
데이터 전송ib_post_send()SQ에 Work Request 게시
ib_post_recv()RQ에 Receive WR 게시

커널 내부에서 QP를 생성하고 데이터를 전송하는 기본 흐름:

/* 커널 Verbs API 사용 예제 (간략화) */
struct ib_pd *pd;
struct ib_cq *cq;
struct ib_qp *qp;

/* 1. Protection Domain 생성 */
pd = ib_alloc_pd(ib_dev, 0);

/* 2. Completion Queue 생성 */
cq = ib_alloc_cq(ib_dev, NULL, 256, 0, IB_POLL_SOFTIRQ);

/* 3. Queue Pair 생성 */
struct ib_qp_init_attr qp_attr = {
    .event_handler = my_qp_event,
    .send_cq = cq,
    .recv_cq = cq,
    .cap = {
        .max_send_wr  = 128,
        .max_recv_wr  = 128,
        .max_send_sge = 1,
        .max_recv_sge = 1,
    },
    .qp_type = IB_QPT_RC,
};
qp = ib_create_qp(pd, &qp_attr);

/* 4. QP 상태 전이: RESET → INIT → RTR → RTS */
struct ib_qp_attr attr;
memset(&attr, 0, sizeof(attr));
attr.qp_state        = IB_QPS_INIT;
attr.pkey_index      = 0;
attr.port_num        = 1;
attr.qp_access_flags = IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ;
ib_modify_qp(qp, &attr, IB_QP_STATE | IB_QP_PKEY_INDEX |
             IB_QP_PORT | IB_QP_ACCESS_FLAGS);
/* RTR, RTS 전이도 유사하게 진행 ... */

/* 5. Work Request 게시 (RDMA Write) */
struct ib_sge sge = {
    .addr   = dma_addr,
    .length = len,
    .lkey   = mr->lkey,
};
struct ib_send_wr wr = {
    .opcode     = IB_WR_RDMA_WRITE,
    .send_flags = IB_SEND_SIGNALED,
    .sg_list    = &sge,
    .num_sge    = 1,
    .wr.rdma    = {
        .remote_addr = remote_addr,
        .rkey        = remote_rkey,
    },
};
const struct ib_send_wr *bad_wr;
ib_post_send(qp, &wr, &bad_wr);

/* 6. 완료 폴링 */
struct ib_wc wc;
while (ib_poll_cq(cq, 1, &wc) == 0)
    /* busy wait */;
if (wc.status != IB_WC_SUCCESS)
    pr_err("WC error: %d\n", wc.status);

사용자 공간 라이브러리

libibverbs (ibv_* API)

libibverbs는 RDMA Verbs API의 사용자 공간 구현체입니다. /dev/infiniband/uverbsN을 통해 커널의 ib_uverbs와 통신하며, 데이터 경로(post_send, poll_cq 등)는 메모리 매핑된 Doorbell/CQ를 통해 커널 바이패스로 동작합니다.

주요 API 함수:

librdmacm (rdma_* API)

librdmacm은 RDMA Connection Manager 라이브러리로, TCP 소켓과 유사한 연결 설정/해제 인터페이스를 제공합니다. QP 상태 전이를 자동으로 처리하므로 개발이 훨씬 간편합니다.

rdma-core 패키지

rdma-core는 libibverbs, librdmacm, 벤더 플러그인(libmlx5, libirdma 등), 관리 도구를 통합한 오픈소스 패키지입니다. 대부분의 Linux 배포판에서 rdma-core 패키지로 설치할 수 있습니다.

# rdma-core 설치 (Ubuntu/Debian)
sudo apt install rdma-core libibverbs-dev librdmacm-dev

# rdma-core 설치 (RHEL/CentOS)
sudo dnf install rdma-core libibverbs-devel librdmacm-devel

코드 예제 — RDMA RC Send/Recv

/* libibverbs RC Send/Recv 전체 흐름 (간략화) */
#include <infiniband/verbs.h>

int main(void)
{
    struct ibv_device **dev_list;
    struct ibv_context *ctx;
    struct ibv_pd *pd;
    struct ibv_mr *mr;
    struct ibv_cq *cq;
    struct ibv_qp *qp;
    char buf[4096];

    /* 1. 디바이스 열기 */
    dev_list = ibv_get_device_list(NULL);
    ctx = ibv_open_device(dev_list[0]);

    /* 2. PD, MR, CQ 생성 */
    pd = ibv_alloc_pd(ctx);
    mr = ibv_reg_mr(pd, buf, sizeof(buf),
                    IBV_ACCESS_LOCAL_WRITE |
                    IBV_ACCESS_REMOTE_WRITE |
                    IBV_ACCESS_REMOTE_READ);
    cq = ibv_create_cq(ctx, 16, NULL, NULL, 0);

    /* 3. QP 생성 */
    struct ibv_qp_init_attr qp_init = {
        .send_cq = cq,
        .recv_cq = cq,
        .cap = { .max_send_wr = 16, .max_recv_wr = 16,
                 .max_send_sge = 1, .max_recv_sge = 1 },
        .qp_type = IBV_QPT_RC,
    };
    qp = ibv_create_qp(pd, &qp_init);

    /* 4. QP 상태 전이: RESET → INIT → RTR → RTS */
    struct ibv_qp_attr attr;
    memset(&attr, 0, sizeof(attr));

    /* INIT */
    attr.qp_state        = IBV_QPS_INIT;
    attr.pkey_index      = 0;
    attr.port_num        = 1;
    attr.qp_access_flags = IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ;
    ibv_modify_qp(qp, &attr,
        IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS);

    /* RTR — 원격 QPN, LID, GID, PSN 필요 (out-of-band 교환) */
    attr.qp_state              = IBV_QPS_RTR;
    attr.path_mtu              = IBV_MTU_4096;
    attr.dest_qp_num           = remote_qpn;   /* 상대방 QPN */
    attr.rq_psn                = 0;
    attr.max_dest_rd_atomic    = 1;
    attr.min_rnr_timer         = 12;
    attr.ah_attr.dlid          = remote_lid;
    attr.ah_attr.port_num      = 1;
    ibv_modify_qp(qp, &attr,
        IBV_QP_STATE | IBV_QP_AV | IBV_QP_PATH_MTU |
        IBV_QP_DEST_QPN | IBV_QP_RQ_PSN |
        IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_MIN_RNR_TIMER);

    /* RTS */
    attr.qp_state      = IBV_QPS_RTS;
    attr.sq_psn         = 0;
    attr.timeout        = 14;
    attr.retry_cnt      = 7;
    attr.rnr_retry      = 7;
    attr.max_rd_atomic  = 1;
    ibv_modify_qp(qp, &attr,
        IBV_QP_STATE | IBV_QP_SQ_PSN | IBV_QP_TIMEOUT |
        IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC);

    /* 5. Receive WR 게시 (수신 측) */
    struct ibv_sge recv_sge = { .addr = (uintptr_t)buf,
                                .length = sizeof(buf), .lkey = mr->lkey };
    struct ibv_recv_wr recv_wr = { .sg_list = &recv_sge, .num_sge = 1 };
    struct ibv_recv_wr *bad_recv;
    ibv_post_recv(qp, &recv_wr, &bad_recv);

    /* 6. Send WR 게시 (송신 측) */
    memcpy(buf, "Hello RDMA", 10);
    struct ibv_sge send_sge = { .addr = (uintptr_t)buf,
                                .length = 10, .lkey = mr->lkey };
    struct ibv_send_wr send_wr = {
        .opcode     = IBV_WR_SEND,
        .send_flags = IBV_SEND_SIGNALED,
        .sg_list    = &send_sge,
        .num_sge    = 1,
    };
    struct ibv_send_wr *bad_send;
    ibv_post_send(qp, &send_wr, &bad_send);

    /* 7. 완료 폴링 */
    struct ibv_wc wc;
    while (ibv_poll_cq(cq, 1, &wc) == 0)
        ;  /* busy-wait */

    /* 8. 정리 */
    ibv_destroy_qp(qp);
    ibv_destroy_cq(cq);
    ibv_dereg_mr(mr);
    ibv_dealloc_pd(pd);
    ibv_close_device(ctx);
    ibv_free_device_list(dev_list);
    return 0;
}

코드 예제 — RDMA Write with librdmacm

/* librdmacm RDMA Write 예제 (클라이언트 측, 간략화) */
#include <rdma/rdma_cma.h>
#include <infiniband/verbs.h>

int main(void)
{
    struct rdma_cm_id *id;
    struct rdma_event_channel *ec;
    struct rdma_conn_param conn_param = {};
    struct ibv_pd *pd;
    struct ibv_mr *mr;
    struct ibv_cq *cq;
    char buf[4096];

    /* 1. RDMA CM ID 생성 및 주소 해석 */
    ec = rdma_create_event_channel();
    rdma_create_id(ec, &id, NULL, RDMA_PS_TCP);

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port   = htons(20000),
    };
    inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);
    rdma_resolve_addr(id, NULL, (struct sockaddr *)&addr, 2000);
    /* ... rdma_resolve_route() ... */

    /* 2. 리소스 생성 (rdmacm이 QP 상태 전이 자동 처리) */
    pd = ibv_alloc_pd(id->verbs);
    cq = ibv_create_cq(id->verbs, 16, NULL, NULL, 0);
    mr = ibv_reg_mr(pd, buf, sizeof(buf),
                    IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);

    struct ibv_qp_init_attr qp_attr = {
        .send_cq = cq, .recv_cq = cq,
        .cap = { .max_send_wr = 16, .max_recv_wr = 16,
                 .max_send_sge = 1, .max_recv_sge = 1 },
        .qp_type = IBV_QPT_RC,
    };
    rdma_create_qp(id, pd, &qp_attr);

    /* 3. 연결 (QP 자동으로 RTS 전이) */
    rdma_connect(id, &conn_param);

    /* 4. RDMA Write (원격 rkey/addr은 out-of-band로 수신) */
    memcpy(buf, "RDMA Write Data", 15);
    struct ibv_sge sge = {
        .addr = (uintptr_t)buf, .length = 15, .lkey = mr->lkey
    };
    struct ibv_send_wr wr = {
        .opcode     = IBV_WR_RDMA_WRITE,
        .send_flags = IBV_SEND_SIGNALED,
        .sg_list    = &sge,
        .num_sge    = 1,
        .wr.rdma    = { .remote_addr = remote_addr, .rkey = remote_rkey },
    };
    struct ibv_send_wr *bad;
    ibv_post_send(id->qp, &wr, &bad);

    /* 5. 완료 대기 및 정리 */
    struct ibv_wc wc;
    while (ibv_poll_cq(cq, 1, &wc) == 0);

    rdma_disconnect(id);
    rdma_destroy_qp(id);
    ibv_dereg_mr(mr);
    ibv_destroy_cq(cq);
    ibv_dealloc_pd(pd);
    rdma_destroy_id(id);
    rdma_destroy_event_channel(ec);
    return 0;
}

IPoIB (IP over InfiniBand)

IPoIB는 InfiniBand 패브릭 위에서 기존 IP/TCP/UDP 프로토콜을 투명하게 사용할 수 있게 합니다. 커널의 drivers/infiniband/ulp/ipoib/에 구현되어 있으며, ib0, ib1 등의 네트워크 인터페이스로 나타납니다.

Connected Mode vs Datagram Mode

MTU 설정 및 커널 구성

# IPoIB Connected Mode 활성화
echo connected > /sys/class/net/ib0/mode

# Connected Mode MTU 설정 (최대 65520)
ip link set ib0 mtu 65520

# IPoIB 인터페이스에 IP 주소 할당
ip addr add 10.0.0.1/24 dev ib0
ip link set ib0 up

# 커널 구성 옵션
CONFIG_INFINIBAND_IPOIB=m
CONFIG_INFINIBAND_IPOIB_CM=y       # Connected Mode 지원
CONFIG_INFINIBAND_IPOIB_DEBUG=y    # 디버그 메시지 (선택)
IPoIB Connected Mode MTU: Connected Mode에서 MTU를 65520으로 설정하면 IP 계층 프래그먼테이션 없이 대용량 전송이 가능하여 TCP/IP 성능이 크게 향상됩니다. 다만, ARP/DHCP 등 브로드캐스트가 필요한 프로토콜은 여전히 UD(Datagram) QP를 사용합니다. ip link show ib0에서 MASTER 플래그와 함께 connected mode 여부를 확인할 수 있습니다.

상위 프로토콜 (ULP)

ULP프로토콜 계층용도커널 모듈특징
SRP SCSI over RDMA 블록 스토리지 ib_srp / ib_srpt SCSI 명령 + 데이터를 RDMA로 전송, iSCSI 대비 저지연
iSER iSCSI Extensions for RDMA 블록 스토리지 ib_iser / ib_isert 기존 iSCSI 프로토콜을 RDMA로 가속, iSCSI 호환
NVMe-oF/RDMA NVMe over Fabrics NVMe 스토리지 nvme-rdma / nvmet-rdma NVMe 큐 모델을 RDMA에 직접 매핑, 초저지연 스토리지
NFS/RDMA NFS over RDMA (RPCRDMA) 파일 시스템 xprtrdma / svcrdma RPC 데이터를 RDMA Write/Read로 전송

NVMe-oF/RDMA 설정 예제

### NVMe-oF/RDMA Target 설정 ###

# 커널 모듈 로드
modprobe nvmet
modprobe nvmet-rdma

# NVMe-oF 서브시스템 생성
mkdir -p /sys/kernel/config/nvmet/subsystems/nqn.2024-01.io.example:nvme-rdma
cd /sys/kernel/config/nvmet/subsystems/nqn.2024-01.io.example:nvme-rdma
echo 1 > attr_allow_any_host

# 네임스페이스에 NVMe 디바이스 연결
mkdir namespaces/1
echo /dev/nvme0n1 > namespaces/1/device_path
echo 1 > namespaces/1/enable

# RDMA 포트 생성 및 서브시스템 바인드
mkdir -p /sys/kernel/config/nvmet/ports/1
echo rdma > /sys/kernel/config/nvmet/ports/1/addr_trtype
echo 10.0.0.1 > /sys/kernel/config/nvmet/ports/1/addr_traddr
echo 4420 > /sys/kernel/config/nvmet/ports/1/addr_trsvcid
echo ipv4 > /sys/kernel/config/nvmet/ports/1/addr_adrfam
ln -s /sys/kernel/config/nvmet/subsystems/nqn.2024-01.io.example:nvme-rdma \
      /sys/kernel/config/nvmet/ports/1/subsystems/

### NVMe-oF/RDMA Initiator (클라이언트) 설정 ###

# 커널 모듈 로드
modprobe nvme-rdma

# Target 디스커버리
nvme discover -t rdma -a 10.0.0.1 -s 4420

# Target 연결
nvme connect -t rdma -n nqn.2024-01.io.example:nvme-rdma \
             -a 10.0.0.1 -s 4420

# 연결된 NVMe 디바이스 확인
nvme list
lsblk
NVMe-oF/RDMA 성능: NVMe-oF/RDMA는 NVMe의 멀티큐 모델을 RDMA Send/RDMA Write에 직접 매핑하여 원격 NVMe SSD에 로컬과 유사한 지연시간(~10μs 추가)으로 접근할 수 있습니다. iSCSI 대비 CPU 사용률이 크게 낮고, 4KB random read에서 수백만 IOPS를 달성할 수 있습니다. Block I/O 서브시스템 페이지의 blk-mq 섹션도 참고하세요.

관리 및 진단 도구

opensm (Subnet Manager)

opensm은 Linux에서 가장 많이 사용되는 오픈소스 Subnet Manager입니다. 서브넷 토폴로지를 탐색하고 LID를 할당하며, 라우팅 테이블을 계산하여 스위치에 배포합니다.

# opensm 시작 (기본 설정)
opensm

# 포그라운드 + 디버그 레벨 지정
opensm -F -D 0x02

# 특정 포트 GUID로 SM 바인딩
opensm -g 0x0002c90300000001

# 라우팅 알고리즘 지정 (minhop, updn, ftree, dor 등)
opensm --routing_engine ftree

진단 도구

# HCA 상태 확인
ibstat
## 출력 예시:
## CA 'mlx5_0'
##   CA type: MT4123
##   Number of ports: 1
##   Port 1:
##     State: Active
##     Physical state: LinkUp
##     Rate: 100 (EDR)
##     Base lid: 1
##     SM lid: 1

# 디바이스 상세 정보
ibv_devinfo

# 디바이스 속성 (최대 QP 수, MR 크기 등)
ibv_devinfo -v

# 서브넷 노드 목록
ibnetdiscover

# 특정 노드 경로 추적
ibtracert <src_lid> <dst_lid>

# 포트 카운터 확인
perfquery

# 포트 에러 카운터 초기화
perfquery -x -r <lid> <port>

# 핑 (GRH 없이 LID 기반)
ibping -S   # 서버 모드
ibping <lid>   # 클라이언트

성능 도구

# RDMA Write 대역폭 측정
ib_write_bw -d mlx5_0      # 서버
ib_write_bw -d mlx5_0 <server_ip>  # 클라이언트

# RDMA Write 지연시간 측정
ib_write_lat -d mlx5_0      # 서버
ib_write_lat -d mlx5_0 <server_ip>  # 클라이언트

# Send/Recv 대역폭/지연
ib_send_bw, ib_send_lat

# RDMA Read 대역폭/지연
ib_read_bw, ib_read_lat

# Atomic 연산 지연
ib_atomic_lat -d mlx5_0 <server_ip>

rdma (iproute2 rdma tool)

# RDMA 디바이스 목록
rdma dev

# RDMA 링크 상태
rdma link

# RDMA 통계 (상세)
rdma statistic show

# RDMA 리소스 (QP, CQ, MR 등) 목록
rdma resource show

# 특정 디바이스의 QP 목록
rdma resource show qp -d mlx5_0

# RoCE GID 테이블 확인
rdma link show mlx5_0/1

# netns에서 RDMA 디바이스 사용 설정
rdma system set netns shared

성능 최적화

Adaptive Routing / SR-IOV

메모리 핀닝/등록 최적화

Completion Channel vs Polling

GPUDirect RDMA

NVIDIA GPUDirect RDMA는 GPU 메모리를 RDMA MR로 직접 등록하여, CPU 메모리를 거치지 않고 GPU ↔ HCA 간 직접 P2P DMA 전송을 수행합니다. AI/ML 분산 학습에서 GPU 간 gradient 교환 성능을 극적으로 향상시킵니다.

DMA 심화 페이지의 P2P DMA / GPUDirect 섹션도 참고하세요.

메모리 핀닝 주의사항: RDMA MR 등록은 물리 페이지를 핀하여 스왑 아웃을 방지합니다. 대량의 MR을 등록하면 시스템 가용 메모리가 급격히 줄어들어 OOM이 발생할 수 있습니다. ulimit -l로 프로세스별 메모리 잠금 제한을 확인하고, /etc/security/limits.conf에서 RDMA 사용자의 memlock 값을 적절히 설정해야 합니다. 컨테이너 환경에서는 cgroups의 memory.max도 함께 고려해야 합니다.

트러블슈팅 및 주의사항

RoCE v2 Lossless 네트워크 요구사항: RoCE v2는 UDP 기반이므로 패킷 손실 시 성능이 급격히 저하됩니다. 프로덕션 환경에서는 반드시 다음을 구성해야 합니다:
  • PFC (Priority Flow Control): IEEE 802.1Qbb, RDMA 트래픽이 사용하는 우선순위에 대해 무손실 흐름 제어 활성화
  • ECN (Explicit Congestion Notification): 스위치에서 혼잡 시 ECN 마킹, HCA에서 DCQCN 알고리즘으로 전송 속도 조절
  • DSCP/PCP 매핑: RDMA 트래픽을 특정 우선순위 큐에 매핑하여 일반 트래픽과 격리
  • 스위치 버퍼 튜닝: 무손실 큐에 충분한 공유/헤드룸 버퍼 할당
네이티브 InfiniBand는 Credit-based Flow Control이 내장되어 있어 이러한 추가 구성이 불필요합니다.

일반적인 트러블슈팅 순서:

  1. ibstat으로 HCA 포트 상태 확인 (Active/LinkUp)
  2. ibv_devinfo로 디바이스 속성 및 GID 테이블 확인
  3. rdma link으로 RDMA 링크 상태 확인
  4. ibping으로 기본 연결성 테스트
  5. perfquery로 포트 에러 카운터 확인 (Symbol Error, Link Error Recovery 등)
  6. dmesg | grep -i rdma로 커널 메시지 확인
  7. ib_write_bw로 기본 성능 확인

커널 모듈 관련 이슈는 네트워크 스택 페이지를, PCIe/SR-IOV 관련 문제는 PCI/PCIe 서브시스템 페이지를 참고하세요.