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 비교
| 특성 | InfiniBand | Ethernet (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 네트워크 호환, 높은 지연 |
InfiniBand 서브넷 아키텍처
서브넷 구성 요소
InfiniBand 서브넷은 다음 구성 요소로 이루어집니다:
- HCA (Host Channel Adapter): 호스트에 장착되는 InfiniBand NIC. 각 HCA는 1~2개의 물리 포트를 가지며, 각 포트가 서브넷에 연결됩니다. RDMA 엔진, DMA 엔진, QP/CQ 하드웨어 큐를 내장합니다.
- Switch: IB 패킷을 LID(Local ID) 기반으로 포워딩하는 L2 스위치. 서브넷 내부 통신을 담당하며, 포워딩 테이블은 Subnet Manager가 설정합니다.
- Router: 서로 다른 서브넷 간 GID 기반 L3 라우팅을 수행합니다.
- Subnet Manager (SM): 서브넷의 중앙 관리자. 토폴로지 탐색, LID 할당, 경로 계산, 포워딩 테이블 배포, QoS 설정 등을 수행합니다. 보통 전용 노드 또는 스위치 내장 SM을 사용하며, standby SM으로 HA를 구성합니다.
물리 계층 및 속도
| 세대 | 신호 속도 | 1x 대역폭 | 4x 대역폭 | 12x 대역폭 | 인코딩 |
|---|---|---|---|---|---|
| SDR | 2.5 Gbaud | 2 Gbps | 8 Gbps | 24 Gbps | 8b/10b |
| DDR | 5 Gbaud | 4 Gbps | 16 Gbps | 48 Gbps | 8b/10b |
| QDR | 10 Gbaud | 8 Gbps | 32 Gbps | 96 Gbps | 8b/10b |
| FDR | 14.0625 Gbaud | 13.64 Gbps | 54.54 Gbps | 163.64 Gbps | 64b/66b |
| EDR | 25 Gbaud | 25 Gbps | 100 Gbps | 300 Gbps | 64b/66b |
| HDR | 50 Gbaud | 50 Gbps | 200 Gbps | 600 Gbps | 64b/66b + PAM4 |
| NDR | 100 Gbaud | 100 Gbps | 400 Gbps | 1.2 Tbps | 64b/66b + PAM4 |
| XDR | 200 Gbaud | 200 Gbps | 800 Gbps | 2.4 Tbps | 64b/66b + PAM4 |
주소 체계
- LID (Local Identifier): 16비트, 서브넷 내 고유 주소. Subnet Manager가 할당합니다. 유니캐스트 LID 범위: 0x0001~0xBFFF, 멀티캐스트: 0xC000~0xFFFE.
- GID (Global Identifier): 128비트, IPv6 형식의 글로벌 주소. 서브넷 간 라우팅에 사용됩니다.
Subnet Prefix (64bit) + GUID (64bit)로 구성됩니다. - GUID (Globally Unique Identifier): 64비트, 제조사가 부여하는 고유 식별자. Node GUID, Port GUID, System Image GUID가 있습니다.
- P_Key (Partition Key): 16비트, IB 서브넷 내 논리적 파티션 구분. 이더넷의 VLAN과 유사한 개념으로, 같은 P_Key를 가진 노드끼리만 통신할 수 있습니다.
핵심 자료구조 및 개념
Queue Pair (QP)
QP는 RDMA 통신의 기본 단위로, Send Queue (SQ)와 Receive Queue (RQ) 한 쌍으로 구성됩니다. 각 QP는 고유한 QP Number (QPN)를 가지며, 원격 QP와 1:1(RC), 1:N(UD) 등의 연결 관계를 맺습니다.
- SQ (Send Queue): 송신 측 Work Request(WR)를 큐잉하는 하드웨어 큐. RDMA Write, RDMA Read, Send, Atomic 등의 WR을 제출합니다.
- RQ (Receive Queue): 수신 측 Receive WR을 큐잉합니다. Send/Recv 모델에서 수신 버퍼를 미리 게시(post)해야 합니다.
- SRQ (Shared Receive Queue): 여러 QP가 공유하는 수신 큐. 수신 버퍼를 효율적으로 관리할 수 있습니다.
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로 직접 접근할 수 있는 가상→물리 주소 변환 테이블이 생성됩니다.
- lkey (Local Key): 로컬 QP가 이 MR에 접근할 때 사용하는 키.
- rkey (Remote Key): 원격 QP가 RDMA Write/Read로 이 MR에 접근할 때 사용하는 키. rkey를 원격 측에 전달해야 RDMA 동작이 가능합니다.
Protection Domain (PD) / Address Handle (AH)
- PD (Protection Domain): QP, MR, CQ 등의 리소스를 논리적으로 격리하는 보호 도메인. 같은 PD 내의 리소스만 상호 접근 가능합니다.
- AH (Address Handle): UD QP에서 목적지 주소(LID/GID)를 캡슐화하는 객체. RC/UC QP는 연결 시 주소를 설정하므로 AH가 불필요합니다.
Work Request (WR) / Work Completion (WC)
- WR (Work Request): QP의 SQ 또는 RQ에 제출하는 작업 요청. Send, Recv, RDMA Write, RDMA Read, Atomic CAS/FAA 등의 유형이 있습니다. 각 WR은 하나 이상의 SGE (Scatter/Gather Element)로 데이터 버퍼를 지정합니다.
- WC (Work Completion): CQ에서 poll하여 얻는 완료 결과. 성공/실패 상태, 전송 바이트 수, 완료된 WR의 ID(
wr_id) 등을 포함합니다.
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을 반환합니다.
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)
- Compare and Swap (CAS): 원격 메모리의 64비트 값을 비교하여 일치하면 새 값으로 교체합니다. 분산 락 구현에 활용됩니다.
- Fetch and Add (FAA): 원격 메모리의 64비트 값에 지정한 값을 더하고 이전 값을 반환합니다. 분산 카운터에 활용됩니다.
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 | 다수 프로세스 간 효율적 통신 |
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
- ib_uverbs:
/dev/infiniband/uverbsN캐릭터 디바이스를 통해 사용자 공간에서 Verbs API를 사용할 수 있게 합니다. libibverbs가 이 인터페이스를 사용합니다. - ib_umad:
/dev/infiniband/umadN을 통해 사용자 공간에서 MAD(Management Datagram)를 송수신할 수 있게 합니다. opensm이 이 인터페이스를 사용합니다.
주요 커널 구조체
/* 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 수 등) 조회 | |
| PD | ib_alloc_pd() | Protection Domain 생성 |
ib_dealloc_pd() | PD 해제 | |
| QP | ib_create_qp() | QP 생성 (타입, CQ, SRQ 지정) |
ib_modify_qp() | QP 상태 전이 (RESET→INIT→RTR→RTS) | |
ib_destroy_qp() | QP 파괴 | |
| CQ | ib_alloc_cq() / ib_create_cq() | CQ 생성 |
ib_poll_cq() | CQ에서 완료 이벤트 폴링 | |
| MR | ib_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 함수:
ibv_get_device_list(): RDMA 디바이스 목록 조회ibv_open_device(): 디바이스 컨텍스트 열기ibv_alloc_pd(): Protection Domain 생성ibv_reg_mr(): Memory Region 등록ibv_create_cq()/ibv_create_cq_ex(): CQ 생성ibv_create_qp(): QP 생성ibv_modify_qp(): QP 상태 전이ibv_post_send()/ibv_post_recv(): WR 게시 (커널 바이패스)ibv_poll_cq(): CQ 폴링 (커널 바이패스)
librdmacm (rdma_* API)
librdmacm은 RDMA Connection Manager 라이브러리로, TCP 소켓과 유사한 연결 설정/해제 인터페이스를 제공합니다.
QP 상태 전이를 자동으로 처리하므로 개발이 훨씬 간편합니다.
rdma_create_id(): RDMA CM ID 생성 (소켓에 해당)rdma_resolve_addr(): IP → GID 주소 해석rdma_resolve_route(): 라우팅 경로 해석rdma_connect()/rdma_accept(): 연결 설정rdma_disconnect(): 연결 해제
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
- Datagram Mode (기본): UD QP를 사용합니다. MTU는 IB MTU (보통 2044 바이트, IB 헤더 포함)로 제한됩니다. 브로드캐스트/멀티캐스트를 지원합니다.
- Connected Mode: RC QP를 사용하여 더 큰 MTU(최대 65520 바이트)를 지원합니다. 대용량 IP 트래픽에 적합하지만, 각 peer마다 RC QP가 필요하므로 리소스 소비가 큽니다.
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 # 디버그 메시지 (선택)
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
관리 및 진단 도구
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
- Adaptive Routing (AR): HDR 이상의 IB 스위치가 실시간 네트워크 혼잡도를 감지하여 패킷 경로를 동적으로 변경합니다. 특히 All-to-All 통신 패턴(AI/ML 학습)에서 효과적입니다.
- SR-IOV: 하나의 물리 HCA를 여러 Virtual Function(VF)으로 분할하여 각 VM/컨테이너에 독립적인 RDMA 디바이스를 제공합니다. PCI/PCIe 서브시스템과 가상화 (KVM) 페이지도 참고하세요.
메모리 핀닝/등록 최적화
- 대용량 페이지(Huge Pages): 2MB/1GB 페이지를 사용하면 MR 등록 시 페이지 테이블 워크가 줄어들고, TLB 미스도 감소합니다.
- ODP (On-Demand Paging): 메모리를 미리 핀하지 않고 HCA 페이지 폴트 시 동적으로 매핑합니다. 등록 시간은 단축되지만 첫 접근 시 오버헤드가 있습니다.
- MR 풀링: 자주 사용하는 MR을 미리 등록하고 재사용합니다.
ib_mr_pool커널 API 또는 사용자 공간에서 직접 구현합니다. - NUMA 배치: HCA와 같은 NUMA 노드의 메모리를 사용하면 DMA 전송 성능이 향상됩니다. 메모리 심화 및 NUMA 페이지를 참고하세요.
Completion Channel vs Polling
- Polling (ibv_poll_cq): CQ를 busy-wait으로 폴링합니다. 최저 지연이 필요할 때 사용하지만, CPU 코어를 100% 점유합니다.
- Completion Channel:
ibv_get_cq_event()로 이벤트를 대기합니다. CPU를 절약하지만 이벤트 전달 지연이 추가됩니다. - 하이브리드: 평소에는 polling으로 처리하다가 유휴 시 completion channel로 전환하는 적응형 방식이 실무에서 많이 사용됩니다.
GPUDirect RDMA
NVIDIA GPUDirect RDMA는 GPU 메모리를 RDMA MR로 직접 등록하여, CPU 메모리를 거치지 않고 GPU ↔ HCA 간 직접 P2P DMA 전송을 수행합니다. AI/ML 분산 학습에서 GPU 간 gradient 교환 성능을 극적으로 향상시킵니다.
nvidia_p2p_get_pages(): GPU 메모리 페이지를 핀ib_reg_mr(): 핀된 GPU 페이지를 MR로 등록- PCIe P2P 경로로 GPU ↔ HCA 직접 전송
DMA 심화 페이지의 P2P DMA / GPUDirect 섹션도 참고하세요.
ulimit -l로 프로세스별 메모리 잠금 제한을 확인하고, /etc/security/limits.conf에서
RDMA 사용자의 memlock 값을 적절히 설정해야 합니다.
컨테이너 환경에서는 cgroups의 memory.max도 함께 고려해야 합니다.
트러블슈팅 및 주의사항
- PFC (Priority Flow Control): IEEE 802.1Qbb, RDMA 트래픽이 사용하는 우선순위에 대해 무손실 흐름 제어 활성화
- ECN (Explicit Congestion Notification): 스위치에서 혼잡 시 ECN 마킹, HCA에서 DCQCN 알고리즘으로 전송 속도 조절
- DSCP/PCP 매핑: RDMA 트래픽을 특정 우선순위 큐에 매핑하여 일반 트래픽과 격리
- 스위치 버퍼 튜닝: 무손실 큐에 충분한 공유/헤드룸 버퍼 할당
일반적인 트러블슈팅 순서:
ibstat으로 HCA 포트 상태 확인 (Active/LinkUp)ibv_devinfo로 디바이스 속성 및 GID 테이블 확인rdma link으로 RDMA 링크 상태 확인ibping으로 기본 연결성 테스트perfquery로 포트 에러 카운터 확인 (Symbol Error, Link Error Recovery 등)dmesg | grep -i rdma로 커널 메시지 확인ib_write_bw로 기본 성능 확인
커널 모듈 관련 이슈는 네트워크 스택 페이지를, PCIe/SR-IOV 관련 문제는 PCI/PCIe 서브시스템 페이지를 참고하세요.