KNI가 buffer를 free 하는 방법

Page content

DPDK to KNI RX

KNI는 rx_q로부터 mbuf를 수신한 후 data_len 크기의 skb를 할당하여 데이터를 복사한 후 netif_rx를 호출한다. 그러므로 mbuf는 KNI kernel module까지만 사용되고, 커널 networking stack에서는 사용되지는 않는다.

kni_net.ckni_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_qfree_q에는 이전 DPDK application에서 KNI에 전달했던 mbuf정보이다. 해당 버퍼 영역은 더 이상 유효하지 않고, rx_q, free_q 조차도 유효하지 않다. 그러므로 새로 실행된 DPDK application이 rx_q, free_q에 접근하여 queue에 존재하는 버퍼를 처리하는 것 조차 문제될 수 있다(확실한가???)

2. KNI -> DPDK

tx_qalloc_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;
};