VPP 패킷 버퍼 구조 (vlib_buffer_t)

VPP vlib_buffer_t 구조체 상세: 2-캐시라인 레이아웃, 메타데이터 영역, current_data/current_length, chained buffer, 메모리 할당 패턴을 다룹니다.

선행 문서: 이 페이지는 기초와 아키텍처의 벡터 패킷 처리·그래프 노드 개념을 전제로 합니다. 내부 구현 카테고리의 다른 페이지와 함께 읽어 주세요.

vlib_buffer_t 메모리 레이아웃과 캐시라인

vlib_buffer_t는 VPP에서 패킷 데이터를 관리하는 핵심 구조체로, 커널의 sk_buff에 대응합니다. 정확히 2개의 캐시라인(128바이트)으로 설계되어 캐시 효율을 극대화합니다.

vlib_buffer_t 메모리 레이아웃 (2 캐시라인) Cache Line 0 (오프셋 0x00 ~ 0x3F, 64바이트) — 핫 필드 current_data (i16) current_length (u16) flags (u32) flow_id (u32) next_buffer (u32) current_config_index error (u16) n_add_refs (u8) buffer_pool_index opaque[10] (u32×10, 40바이트) ← 플러그인별 메타데이터 (ip, tcp 등) Cache Line 1 (오프셋 0x40 ~ 0x7F, 64바이트) — 콜드 필드 opaque2[16] (u32×16, 64바이트) ← 추가 메타데이터 (vnet, trace 등) trace_handle (u32) ← 패킷 트레이싱 연결 데이터 영역 (오프셋 0x80+) pre_data → [ headroom ] → data[0..current_length] → 구조체 크기: 128바이트 (2 캐시라인) + 패킷 데이터 (기본 2048바이트)
필드오프셋(Offset)크기용도
current_data0x00i16data[0]부터 현재 패킷 시작 위치까지의 오프셋
current_length0x02u16현재 버퍼의 유효 데이터 길이
flags0x04u32VLIB_BUFFER_TOTAL_LENGTH_VALID, IS_TRACED 등 플래그
flow_id0x08u32NIC RSS 해시(Hash) 또는 플로우 분류 ID
next_buffer0x0Cu32체인 버퍼의 다음 버퍼 인덱스 (점보 프레임)
current_config_index0x10u32Feature arc 설정 인덱스
error0x14u16노드별 에러 코드 인덱스
n_add_refs0x16u8추가 참조 카운트(Reference Count) (복제 시)
buffer_pool_index0x17u8버퍼가 속한 풀 인덱스
opaque[10]0x1840B노드별 메타데이터 (ip4_header_t *, adjacency index 등)
opaque2[16]0x4064B확장 메타데이터 (vnet 계층, 트레이스 정보)
trace_handleu32show trace 결과와 연결되는 핸들

핵심 접근 API

/* vlib_buffer_t 핵심 접근 API */

/* 버퍼 인덱스(u32)로 버퍼 포인터 획득 */
vlib_buffer_t *b = vlib_get_buffer(vm, buffer_index);

/* 현재 데이터 시작 포인터 */
void *data = vlib_buffer_get_current(b);

/* 헤더 추가/제거 (current_data 이동) */
vlib_buffer_advance(b, -sizeof(ethernet_header_t));  /* 헤더 추가 */
vlib_buffer_advance(b, sizeof(ip4_header_t));       /* 헤더 제거 */

/* 체인 버퍼의 전체 길이 */
u32 total = vlib_buffer_length_in_chain(vm, b);

/* 버퍼 복제 (멀티캐스트 등) */
u32 clone_bi;
vlib_buffer_clone(vm, buffer_index, &clone_bi, 1, CLIB_CACHE_LINE_BYTES);
코드 설명
  • 3행 vlib_get_buffer()는 32비트 인덱스를 버퍼 포인터로 변환합니다. 버퍼 풀의 시작 주소에 인덱스를 더하는 단순 산술 연산으로, O(1) 시간에 완료됩니다.
  • 6행 vlib_buffer_get_current()b->data + b->current_data를 반환합니다. 각 노드가 헤더를 파싱한 후 current_data 오프셋을 전진시키므로, 다음 노드는 자신의 헤더 시작 위치를 바로 얻습니다.
  • 9~10행 vlib_buffer_advance()는 음수 값이면 헤더를 추가(포인터 후퇴), 양수이면 헤더를 소비(포인터 전진)합니다. 데이터 복사 없이 오프셋만 조정하여 제로 카피 처리를 가능하게 합니다.
  • 16~17행 vlib_buffer_clone()은 멀티캐스트나 미러링 시 사용됩니다. 원본 데이터는 공유하고 메타데이터만 복제하여 메모리 사용과 복사 비용을 최소화합니다.

opaque 영역과 버퍼 풀 운영

/* opaque 영역 사용 예: ip4 노드가 저장하는 메타데이터 */
typedef struct {
    ip4_header_t *ip_header;
    u32 adj_index;
    u32 flow_hash;
    u32 fib_index;
} ip4_buffer_opaque_t;

/* opaque 접근 매크로 */
#define vnet_buffer(b) ((vnet_buffer_opaque_t *)(b)->opaque)
#define vnet_buffer2(b) ((vnet_buffer_opaque2_t *)(b)->opaque2)
버퍼 풀 고갈 증상: show buffers에서 free 카운트가 0에 가까워지면, 패킷이 error-drop 노드로 전달되며 show errorsno-buffer 에러가 증가합니다. buffers-per-numa 값을 늘리거나 hugepage를 추가 할당하세요.