| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- mbuf
- dpdk
- nic
- rte_eth_dev
- DPDK 초기화
- pmd
- Mempool
- DPDK EAL
- SR-IOV
- HugePage
- kni
- Descriptor Ring
- Kernel Bypass
- polling
- DPDK Architecture
- lcore
- Today
- Total
우당탕탕 개발일지
[DPDK] KNI 본문
[DPDK] DPDK 패킷 흐름
[DPDK] DPDK 아키텍처[DPDK] NIC의 개념과 역할네트워크 환경에서 높은 처리 성능을 얻기 위해 DPDK와 같은 고성능 패킷 처리 기술이 사용된다. 이러한 기술을 이해하려면, 먼저 네트워크 패킷이 서버
uj0791.tistory.com

DPDK는 커널을 우회하여 NIC을 user space에서 직접 제어하며, 커널은 해당 NIC를 인식하지 못한다. 따라서 ip addr, ping, arp와 같은 커널 기반 네트워크 기능이나 ARP, ICMP와 같은 control plane 패킷 처리가 불가능해지는 문제가 발생한다.
이러한 한계를 보완하기 위해 KNI(Kernel Network Interface)가 사용된다. KNI는 DPDK 애플리케이션과 리눅스 커널 네트워크 스택 사이에서 패킷을 교환할 수 있도록 해주는 가상 네트워크 인터페이스이다.
※ KNI는 커널 의존성과 유지보수 문제로 인해 최신 DPDK에서는 더 이상 권장되지 않는다. 실제로 DPDK 23.11 버전부터는 KNI 라이브러리와 커널 모듈이 제거되었으며, 이를 대체하기 위해 virtio_user 기반의 exception path 방식이 권장되고 있다.
KNI (Kernel Network Interface)

KNI는 공유 메모리 기반의 FIFO 구조를 사용하여 user space와 kernel space 간 패킷을 교환한다. DPDK 애플리케이션에서 rte_kni_alloc()를 호출하면 /dev/kni 장치 파일을 open하고, ioctl()을 통해 다음 정보를 커널에 전달한다.
- 공유 메모리 주소
- ring 정보
- 인터페이스 설정 정보
커널은 이 정보를 기반으로 net_device를 생성하고, 전달받은 공유 메모리 영역을 커널 주소 공간에 매핑하여 이를 RX/TX 큐로 사용한다. 이후 DPDK 애플리케이션은 해당 인터페이스와 연결된 KNI 구조체를 통해 큐에 접근하여 패킷을 주고받을 수 있다.

KNI를 생성하기 위해서는 먼저 rte_kni_conf 구조체에 필요한 설정값을 채워야 한다.
struct rte_kni *
rte_kni_alloc(
struct rte_mempool *pktmbuf_pool, // mbuf_pool
const struct rte_kni_conf *conf,
struct rte_kni_ops *ops
)
이 구조체에는 커널에 생성될 net_device의 속성이 정의되어 있으며, 이후 rte_kni_alloc() 함수에 전달되어 실제 KNI 인터페이스가 생성된다.
conf.name 필드는 커널에서 보이는 네트워크 인터페이스의 이름을 의미한다. 예를 들어 애플리케이션에서 conf.name을 dpdk_sip로 설정하면, 커널에는 dpdk_sip라는 이름의 네트워크 인터페이스가 생성된다.

KNI는 패킷 데이터 뿐만 아니라 커널에서 일반 네트워크 인터페이스를 다룰 때 발생하는 각종 제어 요청도 함께 처리해야 한다. 예를 들어 사용자가 ifconfig, ip link, ethtool 같은 명령을 통해 인터페이스 설정을 변경하면, 커널은 해당 KNI 인터페이스에 대해 MTU 변경, MAC 주소 변경, 인터페이스 up/down 과 같은 제어 요청을 발생시킨다.
이러한 요청을 처리하기 위해 KNI 생성 시 rte_kni_alloc() 함수에 rte_kni_ops 구조체를 함께 전달한다.

이 구조체에는 커널로부터 제어 요청이 들어왔을 때 실행할 콜백 함수 포인터들이 등록된다. 즉, 애플리케이션은 이 구조체를 통해 “MTU 변경 요청이 오면 어떤 함수를 실행할지”, “MAC 주소 변경 요청이 오면 어떤 함수를 실행할지”를 미리 정의해 둔다.
이러한 제어 요청이 커널에서 발생했다고 해서 DPDK 애플리케이션 내부에서 자동으로 즉시 처리되지 않는다. DPDK 애플리케이션은 기본적으로 Busy Polling 기반으로 동작하므로, 패킷 수신뿐 아니라 커널의 제어 요청 역시 애플리케이션이 직접 주기적으로 확인해야 한다.
이때 사용하는 함수가 rte_kni_handle_request() 이다.

rte_kni_handle_request()는 해당 KNI 인터페이스에 대해 커널로부터 들어온 제어 요청이 있는지 확인한다. 그리고 요청이 존재하면, KNI 생성 시 ops에 등록해 둔 콜백 함수를 호출하여 그 요청을 처리한다.
KNI 생성이 완료되면, 리눅스에서 ip link show 명령을 통해 해당 인터페이스를 확인할 수 있으며, 일반 네트워크 인터페이스와 동일하게 관리 및 설정이 가능하다.
KNI 패킷 구조
KNI는 커널과 DPDK 애플리케이션 사이에서 패킷을 전달할 때, 실제 패킷 데이터를 시스템 콜을 통해 복사하는 방식이 아니라 공유 메모리 기반의 FIFO 큐를 사용한다. KNI는 다음 네 가지 ring을 사용한다.
- rx_q → Kernel 방향 전송
- tx_q → User Space 방향 전송
- alloc_q
- free_q

여기서 alloc_q 는 커널이 사용할 mbuf를 공급받는 큐다. 커널은 DPDK의 mempool을 직접 사용할 수 없다. 그래서 mbuf가 필요할 때 User Space에 요청해야 한다. 반대로 mbuf 반납도 free_q 를 통해 돌려주어야 한다. 이외에 rx_q는 DPDK에서 커널로 전달되는 패킷을 위한 큐이며, tx_q는 커널에서 DPDK로 전달되는 패킷을 위한 큐 이다.
이때 패킷 자체를 복사하는 것이 아니라, mbuf와 같은 패킷 버퍼에 대한 포인터를 enqueue 및 dequeue 하는 방식으로 데이터를 교환한다.
DPDK → Kernel

DPDK에서 커널로 패킷이 전달되는 흐름을 보면, 먼저 DPDK 애플리케이션이 rte_eth_rx_burst()를 통해 NIC로부터 패킷을 수신하면, 해당 패킷은 mbuf 형태로 존재하게 된다. 이후 이 mbuf는 rte_kni_tx_burst()를 통해 rx_q에 enqueue된다.
커널 측에서는 이 큐에서 mbuf를 dequeue한 뒤, 이를 커널 네트워크 스택에서 사용하는 sk_buff 구조로 변환한다. 변환된 패킷은 내부적으로 netif_rx()를 통해 커널 네트워크 스택으로 전달되어 일반적인 네트워크 처리 과정을 거치게 된다. 이후 패킷 처리가 끝나면 사용된 mbuf는 free_q로 반환되며, DPDK는 이를 회수하여 rte_pktmbuf_free()를 통해 mempool로 되돌린다.
Kernel → DPDK

반대로 커널에서 DPDK로 패킷이 전달되는 흐름을 보면, 커널은 먼저 alloc_q에서 mbuf를 하나 가져온 뒤, 자신이 관리하는 sk_buff 데이터를 mbuf 형태로 변환한다. 이후 변환된 mbuf는 tx_q에 enqueue되며, DPDK 애플리케이션은 rte_kni_rx_burst()를 통해 이를 dequeue한다. 이후 rte_eth_tx_burst()를 호출하여 NIC로 패킷을 전송하고, 전송이 완료된 mbuf는 다시 mempool로 반환된다.
'Network' 카테고리의 다른 글
| [DPDK] DPDK 패킷 흐름 (0) | 2026.04.01 |
|---|---|
| [DPDK] DPDK 초기화 과정 (0) | 2026.03.31 |
| [DPDK] DPDK EAL (0) | 2026.03.30 |
| [DPDK] DPDK 아키텍처 (0) | 2026.03.22 |
| [DPDK] NIC의 개념과 역할 (0) | 2026.03.22 |