KNI가 buffer를 free 하는 방법
DPDK to KNI RX
KNI는 rx_q
로부터 mbuf를 수신한 후 data_len
크기의 skb를 할당하여 데이터를 복사한 후 netif_rx
를 호출한다.
그러므로 mbuf는 KNI kernel module까지만 사용되고, 커널 networking stack에서는 사용되지는 않는다.
kni_net.c
의 kni_net_rx_normal()
함수가 DPDK application으로부터 mbuf를 받아 커널에 전달하는 함수인데 실제 함수는 batch processing을 위해 한번에 여러 개의 패킷을 rx_q
로부터 읽어 처리하도록 구현되어 있다.
아래는 하나의 패킷에 대해 수행되는 코드를 간략화 한 것이다(예외 처리 부분도 제외)
num_rx = kni_fifo_get(kni->rx_q, (void **)va, num_rx);
kva = (void *)va[i] - kni->mbuf_va + kni->mbuf_kva;
len = kva->data_len;
data_kva = kva->buf_addr + kva->data_off - kni->mbuf_va + kni->mbuf_kva;
skb = dev_alloc_skb(len + 2);
/* Align IP on 16B boundary */
skb_reserve(skb, 2);
memcpy(skb_put(skb, len), data_kva, len);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
/* Call netif interface */
netif_rx(skb);
/* Update statistics */
kni->stats.rx_bytes += len;
kni->stats.rx_packets++;
/* Burst enqueue mbufs into free_q */
ret = kni_fifo_put(kni->free_q, (void **)va, num_rx);
In case DPDK application restarted
만일 DPPK application이 비정상적으로 종료되어 다시 실행하는 경우 버퍼를 초기화 과정에서 문제가 발생할 수 있다.
1. DPDK -> KNI
rx_q
나 free_q
에는 이전 DPDK application에서 KNI에 전달했던 mbuf정보이다. 해당 버퍼 영역은 더 이상 유효하지 않고, rx_q
, free_q
조차도 유효하지 않다. 그러므로 새로 실행된 DPDK application이 rx_q
, free_q
에 접근하여 queue에 존재하는 버퍼를 처리하는 것 조차 문제될 수 있다(확실한가???)
2. KNI -> DPDK
tx_q
나 alloc_q
역시 rx_q
, free_q
와 동일한 이슈를 갖는다. Tx를 위해 사용할 mbuf를 미리 할당해 놓은 alloc_q
에 존재하는 mbuf는 이전 DPDK application이 할당한 것이고, tx_q
에 존재하는 mbuf 역시 동일하다.
struct rte_kni_mbuf
는 DPDK와 KNI kernel module간 통신할 때 사용되는 구조체로 rte_mbuf
중 KNI에게 필요한 정보만 모은 것이다.
/*
* The kernel image of the rte_mbuf struct, with only the relevant fields.
* Padding is necessary to assure the offsets of these fields
*/
struct rte_kni_mbuf {
void *buf_addr __attribute__((__aligned__(RTE_CACHE_LINE_SIZE)));
char pad0[10];
uint16_t data_off; /**< Start address of data in segment buffer. */
char pad1[4];
uint64_t ol_flags; /**< Offload features. */
char pad2[4];
uint32_t pkt_len; /**< Total pkt len: sum of all segment data_len. */
uint16_t data_len; /**< Amount of data in segment buffer. */
/* fields on second cache line */
char pad3[8] __attribute__((__aligned__(RTE_CACHE_LINE_SIZE)));
void *pool;
void *next;
};