인피니밴드 Verbs API 심층 해부
초록
본 논문은 OFED 패키지의 기본 API인 libibverbs를 이용한 가장 단순한 InfiniBand 예제인 ibv_rc_pingpong 코드를 상세히 분석한다. 800줄에 달하는 소스의 구조와 동작 흐름을 단계별로 분해해, 리소스 초기화, 큐 페어(QP) 설정, 메모리 등록, 데이터 전송 및 완료 처리 등 Verbs API 사용 시 반드시 이해해야 할 핵심 개념을 명확히 제시한다. 이를 통해 초보 개발자가 InfiniBand 프로그래밍을 빠르게 습득하고, 자체 애플리케이션에 적용할 수 있는 토대를 제공한다.
상세 분석
ibv_rc_pingpong 은 InfiniBand의 Reliable Connection(RC) 전송 모드를 사용해 두 노드 간에 ping‑pong 형태의 메시지를 주고받는 가장 기본적인 예제이다. 전체 흐름은 크게 네 단계로 나뉜다. 첫 번째는 리소스 초기화 단계로, ibv_get_device_list 로 시스템에 연결된 HCA(Host Channel Adapter) 목록을 얻고, ibv_open_device 로 원하는 디바이스를 연다. 그 뒤 ibv_alloc_pd 로 보호 도메인(Protection Domain, PD)을 할당하고, ibv_reg_mr 로 전송 버퍼를 메모리 등록한다. 메모리 등록은 물리 주소와 가상 주소를 매핑하고, 해당 영역에 대한 접근 권한(READ, WRITE, REMOTE_WRITE 등)을 지정한다는 점이 핵심이다.
두 번째 단계는 큐 페어(QP)와 컴플리션 큐(CQ) 설정이다. ibv_create_cq 로 송수신 각각에 대한 CQ를 만든 뒤, ibv_create_qp 로 QP를 생성한다. QP는 INIT → RTR(Rotate To Ready) → RTS(Ready To Send) 세 단계의 상태 전이(state transition)를 거쳐야 한다. 각 단계에서 ibv_modify_qp 를 호출해 QP 속성을 설정한다. 예를 들어 INIT 단계에서는 QP의 포트 번호와 접근 권한을 지정하고, RTR 단계에서는 원격 QP 번호와 LID(로컬 ID), GID(전역 ID) 등을 설정한다. 마지막 RTS 단계에서는 전송 키(PSN)와 타임아웃, 재전송 제한 등을 정의한다.
세 번째는 연결 설정 및 데이터 전송이다. 두 노드가 서로의 QP 정보를 교환하기 위해 out‑of‑band 방식(예: 파일, 표준 입출력)으로 QP 번호, LID, GID, PSN 등을 주고받는다. 교환이 끝나면 각 노드는 QP를 RTS 상태로 전환하고, ibv_post_send 로 SEND 워크 요청을 큐에 삽입한다. 워크 요청은 ibv_sge 구조체에 버퍼 주소와 길이, 로컬 키(lkey)를 지정한다. 반대편에서는 ibv_post_recv 로 RECV 워크 요청을 미리 큐에 넣어 두어야 한다.
마지막 단계는 완료 처리와 정리이다. ibv_get_cq_event 로 CQ에서 발생한 이벤트를 받아 ibv_ack_cq_events 로 확인하고, ibv_poll_cq 로 워크 완료 상태를 확인한다. SEND가 성공하면 RECV 워크가 완료된 뒤, 동일한 버퍼를 다시 SEND 하여 ping‑pong 루프를 구현한다. 프로그램 종료 시에는 ibv_destroy_qp, ibv_destroy_cq, ibv_dereg_mr, ibv_dealloc_pd, ibv_close_device 순으로 리소스를 해제한다.
핵심 인사이트는 Verbs API가 저수준이기 때문에 모든 상태 전이와 메모리 보호를 명시적으로 관리해야 한다는 점이다. 특히 QP 상태 전이 과정에서 포트 번호, MTU, PSN, SL(Service Level) 등 세부 파라미터를 정확히 맞추지 않으면 연결이 성립되지 않는다. 또한 메모리 등록은 성능에 직결되므로, 대용량 전송 시에는 페이지 정렬과 MR 캐시 활용을 고려해야 한다. ibv_rc_pingpong 은 이러한 복잡성을 최소화한 샘플이지만, 실제 애플리케이션에서는 멀티스레드, 비동기 이벤트, 다중 QP 관리 등 추가적인 설계가 필요하다.
댓글 및 학술 토론
Loading comments...
의견 남기기