相关文章推荐
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

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
        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:

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 ()

Please provide a minimal reproducible example and replace the picture with the output as text. From How do I ask a good question?: "DO NOT post images of code, data, error messages, etc. - copy or type the text into the question" – Ted Lyngmo Aug 16, 2021 at 20:26 @stackinside, thanks for the hint. You are indeed right that rte_pktmbuf_alloc() returns a NULL pointer. The next step is to understand why is this happening. and how to avoid this. – Resting Platypus Aug 16, 2021 at 20:49

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;
        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]);
            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:

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

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

     
    推荐文章