Resting Platypus
Resting Platypus

Reputation: 107

Segmentation fault DPDK

I've written a sender application using dpdk libraries.

struct my_message{
    struct rte_ether_hdr eth_hdr; // the ethernet header
    char payload[10];
};

*/ Sender Application */
void my_send(struct rte_mempool *mbuf_pool, uint16_t port, uint64_t max_packets)
{
    int retval;
    struct rte_mbuf *bufs[BURST_SIZE];
    struct rte_ether_addr src_mac_addr;
    retval = rte_eth_macaddr_get(0, &src_mac_addr); // gets MAC address of Port 0
    struct rte_ether_addr dst_mac_addr = {{0xaa,0xbb,0xcc,0xdd,0xee,0xff}}; 
    struct my_message *my_pkt;

    int j=0;
    uint16_t sent_packets = BURST_SIZE; //BURST_SIZE is 256
    do{
        for(int i = 0; i < sent_packets; i ++)
        {
            bufs[i] = rte_pktmbuf_alloc(mbuf_pool); //allocates a new mbuff from mempool
            my_pkt = rte_pktmbuf_mtod(bufs[i], struct my_message*); //points (pointer is casted to struct my_message*) to start of data in mbuf
            *my_pkt->payload = 'Hello2021';    
            int pkt_size = sizeof(struct my_message);
            bufs[i]->pkt_len = bufs[i]->data_len = pkt_size;
            rte_ether_addr_copy(&src_mac_addr, &my_pkt->eth_hdr.s_addr);
            rte_ether_addr_copy(&dst_mac_addr, &my_pkt->eth_hdr.d_addr);
            my_pkt->eth_hdr.ether_type = htons(PTP_PROTOCOL);
        }

        const uint16_t sent_packets = rte_eth_tx_burst(port, 0, bufs, BURST_SIZE);
        printf("Number of packets tx %" PRIu16 "\n", sent_packets);

        j = j + sent_packets;
    }
    while(j < max_packets);
        /* Free any unsent packets. */
    if (unlikely(sent_packets < BURST_SIZE)) {
            uint16_t buf;
            for (buf = sent_packets; buf < BURST_SIZE; buf++)
                    rte_pktmbuf_free(bufs[buf]);
        }        

}

I call this function as

int main(int argc, char *argv[])
-- 
EAL initialization and port initialization
--
struct rte_mempool *mbuf_pool;
my_send(mbuf_pool, 0, 3000000);

The program compiles with no errors, however, upon running this program, I'm getting a segmentation fault. Following is the output:

enter image description here

I ran gdb but couldn't debug from the gdb output

and got the following message

Thread 1 "app_tx_v2" received signal SIGSEGV, Segmentation fault. 0x0000555555555567 in my_send ()

Upvotes: 0

Views: 1118

Answers (1)

user10304260
user10304260

Reputation:

As per the provided source code and debug printouts, every time rte_eth_tx_burst() fails to send the whole batch of 256 mbufs, your program leaks unsent packets. The loop reiterates thus overwriting mbufs. The leak subsequently grows, and the mempool runs out of available objects. At some point rte_pktmbuf_alloc() returns NULL. Your program does not check the return value and thus the subsequent access to the mbuf data causes the observed segmentation fault.

As for debug information, I trust you already know that one needs to specify -g argument on gcc invocation in order to have it. Also, please make sure to specify -Wall key.

As for the program itself, it's hard to read and thus it hides the said mbuf leak. Consider re-implementing it the following way:

static void
fill_out_mbuf(struct rte_mbuf *m) {
    /* Get mtod */
    /* Set the data / packet size */
    /* Fill out the header and payload */
    /* ... */
}

static uint16_t
send_mbufs(uint16_t          port,
           struct rte_mbuf **mbufs,
           uint16_t          nb)
{
    uint16_t ret;

    ret = rte_eth_tx_prepare(port, 0, mbufs, nb);
    if (ret == 0)
        return 0;

    return rte_eth_tx_burst(port, 0, mbufs, ret);
}

#define NB_RETRIES_MAX (1000)

static void
my_send(struct rte_mempool *mbuf_pool,
        uint16_t            port,
        uint64_t            max_packets)
{
    struct rte_mbuf *mbufs[BURST_SIZE];
    unsigned int     nb_retries;
    uint16_t         nb_mbufs;
    uint16_t         nb_done;
    uint16_t         i;

    if (max_packets == 0)
        return;

    do {
        memset(mbufs, 0, sizeof(mbufs));

        nb_mbufs = RTE_MIN(max_packets, RTE_DIM(mbufs));
        nb_retries = 0;
        nb_done = 0;

        if (rte_pktmbuf_alloc_bulk(mbuf_pool, mbufs, nb_mbufs) != 0)
            break;

        for (i = 0; i < nb_mbufs; ++i)
            fill_out_mbuf(mbufs[i]);

        do {
            nb_done += send_mbufs(port, mbufs + nb_done, nb_mbufs - nb_done);
            ++nb_retries;
        } while (nb_done < nb_mbufs && nb_retries < NB_RETRIES_MAX);

        max_packets -= nb_done;
    } while (max_packets > 0 && nb_retries < NB_RETRIES_MAX);

    for (i = nb_done; i < nb_mbufs; ++i)
        rte_pktmbuf_free(mbufs[i]);
}

The key points here are as follows:

  1. Factor out functions like fill_out_mbuf() for the sake of code clarity;
  2. Invoke rte_eth_tx_prepare() before rte_eth_tx_burst() as required by the RTE API contract;
  3. Use rte_pktmbuf_alloc_bulk() for improved efficacy;
  4. If the mbuf batch has been sent only partially, don't start from scratch; instead, attempt to send remaining mbufs;
  5. Do not retry sending infinitely; limit the number of retries.

Upvotes: 2

Related Questions