X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fpcap%2Frte_eth_pcap.c;h=aa7ef6fdbc2a0d33fb3016ef40c6f89ca09c257c;hb=a76552d48f17a990662592ff5e13a6e83b62025c;hp=e8810a171a563d73698e8deb7e87877934560501;hpb=d1c3ab220a367085451f595d94e481320e091a77;p=dpdk.git diff --git a/drivers/net/pcap/rte_eth_pcap.c b/drivers/net/pcap/rte_eth_pcap.c index e8810a171a..aa7ef6fdbc 100644 --- a/drivers/net/pcap/rte_eth_pcap.c +++ b/drivers/net/pcap/rte_eth_pcap.c @@ -7,6 +7,14 @@ #include #include +#include +#include +#include + +#if defined(RTE_EXEC_ENV_FREEBSD) +#include +#include +#endif #include @@ -17,9 +25,10 @@ #include #include #include +#include #define RTE_ETH_PCAP_SNAPSHOT_LEN 65535 -#define RTE_ETH_PCAP_SNAPLEN ETHER_MAX_JUMBO_FRAME_LEN +#define RTE_ETH_PCAP_SNAPLEN RTE_ETHER_MAX_JUMBO_FRAME_LEN #define RTE_ETH_PCAP_PROMISC 1 #define RTE_ETH_PCAP_TIMEOUT -1 @@ -29,16 +38,18 @@ #define ETH_PCAP_RX_IFACE_IN_ARG "rx_iface_in" #define ETH_PCAP_TX_IFACE_ARG "tx_iface" #define ETH_PCAP_IFACE_ARG "iface" +#define ETH_PCAP_PHY_MAC_ARG "phy_mac" +#define ETH_PCAP_INFINITE_RX_ARG "infinite_rx" #define ETH_PCAP_ARG_MAXLEN 64 #define RTE_PMD_PCAP_MAX_QUEUES 16 static char errbuf[PCAP_ERRBUF_SIZE]; -static unsigned char tx_pcap_data[RTE_ETH_PCAP_SNAPLEN]; static struct timeval start_time; static uint64_t start_cycles; static uint64_t hz; +static uint8_t iface_idx; struct queue_stat { volatile unsigned long pkts; @@ -47,17 +58,20 @@ struct queue_stat { }; struct pcap_rx_queue { - pcap_t *pcap; - uint16_t in_port; + uint16_t port_id; + uint16_t queue_id; struct rte_mempool *mb_pool; struct queue_stat rx_stat; char name[PATH_MAX]; char type[ETH_PCAP_ARG_MAXLEN]; + + /* Contains pre-generated packets to be looped through */ + struct rte_ring *pkts; }; struct pcap_tx_queue { - pcap_dumper_t *dumper; - pcap_t *pcap; + uint16_t port_id; + uint16_t queue_id; struct queue_stat tx_stat; char name[PATH_MAX]; char type[ETH_PCAP_ARG_MAXLEN]; @@ -66,8 +80,18 @@ struct pcap_tx_queue { struct pmd_internals { struct pcap_rx_queue rx_queue[RTE_PMD_PCAP_MAX_QUEUES]; struct pcap_tx_queue tx_queue[RTE_PMD_PCAP_MAX_QUEUES]; + char devargs[ETH_PCAP_ARG_MAXLEN]; + struct rte_ether_addr eth_addr; int if_index; int single_iface; + int phy_mac; + unsigned int infinite_rx; +}; + +struct pmd_process_private { + pcap_t *rx_pcap[RTE_PMD_PCAP_MAX_QUEUES]; + pcap_t *tx_pcap[RTE_PMD_PCAP_MAX_QUEUES]; + pcap_dumper_t *tx_dumper[RTE_PMD_PCAP_MAX_QUEUES]; }; struct pmd_devargs { @@ -78,6 +102,18 @@ struct pmd_devargs { const char *name; const char *type; } queue[RTE_PMD_PCAP_MAX_QUEUES]; + int phy_mac; +}; + +struct pmd_devargs_all { + struct pmd_devargs rx_queues; + struct pmd_devargs tx_queues; + int single_iface; + unsigned int is_tx_pcap; + unsigned int is_tx_iface; + unsigned int is_rx_pcap; + unsigned int is_rx_iface; + unsigned int infinite_rx; }; static const char *valid_arguments[] = { @@ -87,13 +123,11 @@ static const char *valid_arguments[] = { ETH_PCAP_RX_IFACE_IN_ARG, ETH_PCAP_TX_IFACE_ARG, ETH_PCAP_IFACE_ARG, + ETH_PCAP_PHY_MAC_ARG, + ETH_PCAP_INFINITE_RX_ARG, NULL }; -static struct ether_addr eth_addr = { - .addr_bytes = { 0, 0, 0, 0x1, 0x2, 0x3 } -}; - static struct rte_eth_link pmd_link = { .link_speed = ETH_SPEED_NUM_10G, .link_duplex = ETH_LINK_FULL_DUPLEX, @@ -145,19 +179,41 @@ eth_pcap_rx_jumbo(struct rte_mempool *mb_pool, struct rte_mbuf *mbuf, return mbuf->nb_segs; } -/* Copy data from mbuf chain to a buffer suitable for writing to a PCAP file. */ -static void -eth_pcap_gather_data(unsigned char *data, struct rte_mbuf *mbuf) +static uint16_t +eth_pcap_rx_infinite(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) { - uint16_t data_len = 0; + int i; + struct pcap_rx_queue *pcap_q = queue; + uint32_t rx_bytes = 0; - while (mbuf) { - rte_memcpy(data + data_len, rte_pktmbuf_mtod(mbuf, void *), - mbuf->data_len); + if (unlikely(nb_pkts == 0)) + return 0; - data_len += mbuf->data_len; - mbuf = mbuf->next; + if (rte_pktmbuf_alloc_bulk(pcap_q->mb_pool, bufs, nb_pkts) != 0) + return 0; + + for (i = 0; i < nb_pkts; i++) { + struct rte_mbuf *pcap_buf; + int err = rte_ring_dequeue(pcap_q->pkts, (void **)&pcap_buf); + if (err) + return i; + + rte_memcpy(rte_pktmbuf_mtod(bufs[i], void *), + rte_pktmbuf_mtod(pcap_buf, void *), + pcap_buf->data_len); + bufs[i]->data_len = pcap_buf->data_len; + bufs[i]->pkt_len = pcap_buf->pkt_len; + bufs[i]->port = pcap_q->port_id; + rx_bytes += pcap_buf->data_len; + + /* Enqueue packet back on ring to allow infinite rx. */ + rte_ring_enqueue(pcap_q->pkts, pcap_buf); } + + pcap_q->rx_stat.pkts += i; + pcap_q->rx_stat.bytes += rx_bytes; + + return i; } static uint16_t @@ -165,14 +221,18 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) { unsigned int i; struct pcap_pkthdr header; + struct pmd_process_private *pp; const u_char *packet; struct rte_mbuf *mbuf; struct pcap_rx_queue *pcap_q = queue; uint16_t num_rx = 0; - uint16_t buf_size; uint32_t rx_bytes = 0; + pcap_t *pcap; + + pp = rte_eth_devices[pcap_q->port_id].process_private; + pcap = pp->rx_pcap[pcap_q->queue_id]; - if (unlikely(pcap_q->pcap == NULL || nb_pkts == 0)) + if (unlikely(pcap == NULL || nb_pkts == 0)) return 0; /* Reads the given number of packets from the pcap file one by one @@ -180,7 +240,7 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) */ for (i = 0; i < nb_pkts; i++) { /* Get the next PCAP packet */ - packet = pcap_next(pcap_q->pcap, &header); + packet = pcap_next(pcap, &header); if (unlikely(packet == NULL)) break; @@ -188,11 +248,7 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) if (unlikely(mbuf == NULL)) break; - /* Now get the space available for data in the mbuf */ - buf_size = rte_pktmbuf_data_room_size(pcap_q->mb_pool) - - RTE_PKTMBUF_HEADROOM; - - if (header.caplen <= buf_size) { + if (header.caplen <= rte_pktmbuf_tailroom(mbuf)) { /* pcap packet will fit in the mbuf, can copy it */ rte_memcpy(rte_pktmbuf_mtod(mbuf, void *), packet, header.caplen); @@ -209,7 +265,10 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) } mbuf->pkt_len = (uint16_t)header.caplen; - mbuf->port = pcap_q->in_port; + mbuf->timestamp = (uint64_t)header.ts.tv_sec * 1000000 + + header.ts.tv_usec; + mbuf->ol_flags |= PKT_RX_TIMESTAMP; + mbuf->port = pcap_q->port_id; bufs[num_rx] = mbuf; num_rx++; rx_bytes += header.caplen; @@ -220,6 +279,14 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) return num_rx; } +static uint16_t +eth_null_rx(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + static inline void calculate_timestamp(struct timeval *ts) { uint64_t cycles; @@ -239,43 +306,47 @@ eth_pcap_tx_dumper(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) { unsigned int i; struct rte_mbuf *mbuf; + struct pmd_process_private *pp; struct pcap_tx_queue *dumper_q = queue; uint16_t num_tx = 0; uint32_t tx_bytes = 0; struct pcap_pkthdr header; + pcap_dumper_t *dumper; + unsigned char temp_data[RTE_ETH_PCAP_SNAPLEN]; + size_t len; - if (dumper_q->dumper == NULL || nb_pkts == 0) + pp = rte_eth_devices[dumper_q->port_id].process_private; + dumper = pp->tx_dumper[dumper_q->queue_id]; + + if (dumper == NULL || nb_pkts == 0) return 0; /* writes the nb_pkts packets to the previously opened pcap file * dumper */ for (i = 0; i < nb_pkts; i++) { mbuf = bufs[i]; + len = rte_pktmbuf_pkt_len(mbuf); + if (unlikely(!rte_pktmbuf_is_contiguous(mbuf) && + len > sizeof(temp_data))) { + PMD_LOG(ERR, + "Dropping multi segment PCAP packet. Size (%zd) > max size (%zd).", + len, sizeof(temp_data)); + rte_pktmbuf_free(mbuf); + continue; + } + calculate_timestamp(&header.ts); - header.len = mbuf->pkt_len; + header.len = len; header.caplen = header.len; - - if (likely(mbuf->nb_segs == 1)) { - pcap_dump((u_char *)dumper_q->dumper, &header, - rte_pktmbuf_mtod(mbuf, void*)); - } else { - if (mbuf->pkt_len <= ETHER_MAX_JUMBO_FRAME_LEN) { - eth_pcap_gather_data(tx_pcap_data, mbuf); - pcap_dump((u_char *)dumper_q->dumper, &header, - tx_pcap_data); - } else { - PMD_LOG(ERR, - "Dropping PCAP packet. Size (%d) > max jumbo size (%d).", - mbuf->pkt_len, - ETHER_MAX_JUMBO_FRAME_LEN); - - rte_pktmbuf_free(mbuf); - break; - } - } + /* rte_pktmbuf_read() returns a pointer to the data directly + * in the mbuf (when the mbuf is contiguous) or, otherwise, + * a pointer to temp_data after copying into it. + */ + pcap_dump((u_char *)dumper, &header, + rte_pktmbuf_read(mbuf, 0, len, temp_data)); num_tx++; - tx_bytes += mbuf->pkt_len; + tx_bytes += len; rte_pktmbuf_free(mbuf); } @@ -284,12 +355,36 @@ eth_pcap_tx_dumper(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) * process stops and to make sure the pcap file is actually written, * we flush the pcap dumper within each burst. */ - pcap_dump_flush(dumper_q->dumper); + pcap_dump_flush(dumper); dumper_q->tx_stat.pkts += num_tx; dumper_q->tx_stat.bytes += tx_bytes; dumper_q->tx_stat.err_pkts += nb_pkts - num_tx; - return num_tx; + return nb_pkts; +} + +/* + * Callback to handle dropping packets in the infinite rx case. + */ +static uint16_t +eth_tx_drop(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + unsigned int i; + uint32_t tx_bytes = 0; + struct pcap_tx_queue *tx_queue = queue; + + if (unlikely(nb_pkts == 0)) + return 0; + + for (i = 0; i < nb_pkts; i++) { + tx_bytes += bufs[i]->data_len; + rte_pktmbuf_free(bufs[i]); + } + + tx_queue->tx_stat.pkts += nb_pkts; + tx_queue->tx_stat.bytes += tx_bytes; + + return i; } /* @@ -301,48 +396,50 @@ eth_pcap_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) unsigned int i; int ret; struct rte_mbuf *mbuf; + struct pmd_process_private *pp; struct pcap_tx_queue *tx_queue = queue; uint16_t num_tx = 0; uint32_t tx_bytes = 0; + pcap_t *pcap; + unsigned char temp_data[RTE_ETH_PCAP_SNAPLEN]; + size_t len; + + pp = rte_eth_devices[tx_queue->port_id].process_private; + pcap = pp->tx_pcap[tx_queue->queue_id]; - if (unlikely(nb_pkts == 0 || tx_queue->pcap == NULL)) + if (unlikely(nb_pkts == 0 || pcap == NULL)) return 0; for (i = 0; i < nb_pkts; i++) { mbuf = bufs[i]; - - if (likely(mbuf->nb_segs == 1)) { - ret = pcap_sendpacket(tx_queue->pcap, - rte_pktmbuf_mtod(mbuf, u_char *), - mbuf->pkt_len); - } else { - if (mbuf->pkt_len <= ETHER_MAX_JUMBO_FRAME_LEN) { - eth_pcap_gather_data(tx_pcap_data, mbuf); - ret = pcap_sendpacket(tx_queue->pcap, - tx_pcap_data, mbuf->pkt_len); - } else { - PMD_LOG(ERR, - "Dropping PCAP packet. Size (%d) > max jumbo size (%d).", - mbuf->pkt_len, - ETHER_MAX_JUMBO_FRAME_LEN); - - rte_pktmbuf_free(mbuf); - break; - } + len = rte_pktmbuf_pkt_len(mbuf); + if (unlikely(!rte_pktmbuf_is_contiguous(mbuf) && + len > sizeof(temp_data))) { + PMD_LOG(ERR, + "Dropping multi segment PCAP packet. Size (%zd) > max size (%zd).", + len, sizeof(temp_data)); + rte_pktmbuf_free(mbuf); + continue; } + /* rte_pktmbuf_read() returns a pointer to the data directly + * in the mbuf (when the mbuf is contiguous) or, otherwise, + * a pointer to temp_data after copying into it. + */ + ret = pcap_sendpacket(pcap, + rte_pktmbuf_read(mbuf, 0, len, temp_data), len); if (unlikely(ret != 0)) break; num_tx++; - tx_bytes += mbuf->pkt_len; + tx_bytes += len; rte_pktmbuf_free(mbuf); } tx_queue->tx_stat.pkts += num_tx; tx_queue->tx_stat.bytes += tx_bytes; - tx_queue->tx_stat.err_pkts += nb_pkts - num_tx; + tx_queue->tx_stat.err_pkts += i - num_tx; - return num_tx; + return i; } /* @@ -414,11 +511,30 @@ open_single_rx_pcap(const char *pcap_filename, pcap_t **pcap) return 0; } +static uint64_t +count_packets_in_pcap(pcap_t **pcap, struct pcap_rx_queue *pcap_q) +{ + const u_char *packet; + struct pcap_pkthdr header; + uint64_t pcap_pkt_count = 0; + + while ((packet = pcap_next(*pcap, &header))) + pcap_pkt_count++; + + /* The pcap is reopened so it can be used as normal later. */ + pcap_close(*pcap); + *pcap = NULL; + open_single_rx_pcap(pcap_q->name, pcap); + + return pcap_pkt_count; +} + static int eth_dev_start(struct rte_eth_dev *dev) { unsigned int i; struct pmd_internals *internals = dev->data->dev_private; + struct pmd_process_private *pp = dev->process_private; struct pcap_tx_queue *tx; struct pcap_rx_queue *rx; @@ -427,10 +543,11 @@ eth_dev_start(struct rte_eth_dev *dev) tx = &internals->tx_queue[0]; rx = &internals->rx_queue[0]; - if (!tx->pcap && strcmp(tx->type, ETH_PCAP_IFACE_ARG) == 0) { - if (open_single_iface(tx->name, &tx->pcap) < 0) + if (!pp->tx_pcap[0] && + strcmp(tx->type, ETH_PCAP_IFACE_ARG) == 0) { + if (open_single_iface(tx->name, &pp->tx_pcap[0]) < 0) return -1; - rx->pcap = tx->pcap; + pp->rx_pcap[0] = pp->tx_pcap[0]; } goto status_up; @@ -440,13 +557,14 @@ eth_dev_start(struct rte_eth_dev *dev) for (i = 0; i < dev->data->nb_tx_queues; i++) { tx = &internals->tx_queue[i]; - if (!tx->dumper && + if (!pp->tx_dumper[i] && strcmp(tx->type, ETH_PCAP_TX_PCAP_ARG) == 0) { - if (open_single_tx_pcap(tx->name, &tx->dumper) < 0) + if (open_single_tx_pcap(tx->name, + &pp->tx_dumper[i]) < 0) return -1; - } else if (!tx->pcap && + } else if (!pp->tx_pcap[i] && strcmp(tx->type, ETH_PCAP_TX_IFACE_ARG) == 0) { - if (open_single_iface(tx->name, &tx->pcap) < 0) + if (open_single_iface(tx->name, &pp->tx_pcap[i]) < 0) return -1; } } @@ -455,14 +573,14 @@ eth_dev_start(struct rte_eth_dev *dev) for (i = 0; i < dev->data->nb_rx_queues; i++) { rx = &internals->rx_queue[i]; - if (rx->pcap != NULL) + if (pp->rx_pcap[i] != NULL) continue; if (strcmp(rx->type, ETH_PCAP_RX_PCAP_ARG) == 0) { - if (open_single_rx_pcap(rx->name, &rx->pcap) < 0) + if (open_single_rx_pcap(rx->name, &pp->rx_pcap[i]) < 0) return -1; } else if (strcmp(rx->type, ETH_PCAP_RX_IFACE_ARG) == 0) { - if (open_single_iface(rx->name, &rx->pcap) < 0) + if (open_single_iface(rx->name, &pp->rx_pcap[i]) < 0) return -1; } } @@ -489,39 +607,32 @@ eth_dev_stop(struct rte_eth_dev *dev) { unsigned int i; struct pmd_internals *internals = dev->data->dev_private; - struct pcap_tx_queue *tx; - struct pcap_rx_queue *rx; + struct pmd_process_private *pp = dev->process_private; /* Special iface case. Single pcap is open and shared between tx/rx. */ if (internals->single_iface) { - tx = &internals->tx_queue[0]; - rx = &internals->rx_queue[0]; - pcap_close(tx->pcap); - tx->pcap = NULL; - rx->pcap = NULL; + pcap_close(pp->tx_pcap[0]); + pp->tx_pcap[0] = NULL; + pp->rx_pcap[0] = NULL; goto status_down; } for (i = 0; i < dev->data->nb_tx_queues; i++) { - tx = &internals->tx_queue[i]; - - if (tx->dumper != NULL) { - pcap_dump_close(tx->dumper); - tx->dumper = NULL; + if (pp->tx_dumper[i] != NULL) { + pcap_dump_close(pp->tx_dumper[i]); + pp->tx_dumper[i] = NULL; } - if (tx->pcap != NULL) { - pcap_close(tx->pcap); - tx->pcap = NULL; + if (pp->tx_pcap[i] != NULL) { + pcap_close(pp->tx_pcap[i]); + pp->tx_pcap[i] = NULL; } } for (i = 0; i < dev->data->nb_rx_queues; i++) { - rx = &internals->rx_queue[i]; - - if (rx->pcap != NULL) { - pcap_close(rx->pcap); - rx->pcap = NULL; + if (pp->rx_pcap[i] != NULL) { + pcap_close(pp->rx_pcap[i]); + pp->rx_pcap[i] = NULL; } } @@ -541,7 +652,7 @@ eth_dev_configure(struct rte_eth_dev *dev __rte_unused) return 0; } -static void +static int eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) { @@ -553,7 +664,8 @@ eth_dev_info(struct rte_eth_dev *dev, dev_info->max_rx_queues = dev->data->nb_rx_queues; dev_info->max_tx_queues = dev->data->nb_tx_queues; dev_info->min_rx_bufsize = 0; - dev_info->rx_offload_capa = DEV_RX_OFFLOAD_CRC_STRIP; + + return 0; } static int @@ -577,10 +689,9 @@ eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) i < dev->data->nb_tx_queues; i++) { stats->q_opackets[i] = internal->tx_queue[i].tx_stat.pkts; stats->q_obytes[i] = internal->tx_queue[i].tx_stat.bytes; - stats->q_errors[i] = internal->tx_queue[i].tx_stat.err_pkts; tx_packets_total += stats->q_opackets[i]; tx_bytes_total += stats->q_obytes[i]; - tx_packets_err_total += stats->q_errors[i]; + tx_packets_err_total += internal->tx_queue[i].tx_stat.err_pkts; } stats->ipackets = rx_packets_total; @@ -592,7 +703,7 @@ eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) return 0; } -static void +static int eth_stats_reset(struct rte_eth_dev *dev) { unsigned int i; @@ -608,11 +719,30 @@ eth_stats_reset(struct rte_eth_dev *dev) internal->tx_queue[i].tx_stat.bytes = 0; internal->tx_queue[i].tx_stat.err_pkts = 0; } + + return 0; } static void -eth_dev_close(struct rte_eth_dev *dev __rte_unused) +eth_dev_close(struct rte_eth_dev *dev) { + unsigned int i; + struct pmd_internals *internals = dev->data->dev_private; + + /* Device wide flag, but cleanup must be performed per queue. */ + if (internals->infinite_rx) { + for (i = 0; i < dev->data->nb_rx_queues; i++) { + struct pcap_rx_queue *pcap_q = &internals->rx_queue[i]; + struct rte_mbuf *pcap_buf; + + while (!rte_ring_dequeue(pcap_q->pkts, + (void **)&pcap_buf)) + rte_pktmbuf_free(pcap_buf); + + rte_ring_free(pcap_q->pkts); + } + } + } static void @@ -639,8 +769,62 @@ eth_rx_queue_setup(struct rte_eth_dev *dev, struct pcap_rx_queue *pcap_q = &internals->rx_queue[rx_queue_id]; pcap_q->mb_pool = mb_pool; + pcap_q->port_id = dev->data->port_id; + pcap_q->queue_id = rx_queue_id; dev->data->rx_queues[rx_queue_id] = pcap_q; - pcap_q->in_port = dev->data->port_id; + + if (internals->infinite_rx) { + struct pmd_process_private *pp; + char ring_name[NAME_MAX]; + static uint32_t ring_number; + uint64_t pcap_pkt_count = 0; + struct rte_mbuf *bufs[1]; + pcap_t **pcap; + + pp = rte_eth_devices[pcap_q->port_id].process_private; + pcap = &pp->rx_pcap[pcap_q->queue_id]; + + if (unlikely(*pcap == NULL)) + return -ENOENT; + + pcap_pkt_count = count_packets_in_pcap(pcap, pcap_q); + + snprintf(ring_name, sizeof(ring_name), "PCAP_RING%" PRIu16, + ring_number); + + pcap_q->pkts = rte_ring_create(ring_name, + rte_align64pow2(pcap_pkt_count + 1), 0, + RING_F_SP_ENQ | RING_F_SC_DEQ); + ring_number++; + if (!pcap_q->pkts) + return -ENOENT; + + /* Fill ring with packets from PCAP file one by one. */ + while (eth_pcap_rx(pcap_q, bufs, 1)) { + /* Check for multiseg mbufs. */ + if (bufs[0]->nb_segs != 1) { + rte_pktmbuf_free(*bufs); + + while (!rte_ring_dequeue(pcap_q->pkts, + (void **)bufs)) + rte_pktmbuf_free(*bufs); + + rte_ring_free(pcap_q->pkts); + PMD_LOG(ERR, "Multiseg mbufs are not supported in infinite_rx " + "mode."); + return -EINVAL; + } + + rte_ring_enqueue_bulk(pcap_q->pkts, + (void * const *)bufs, 1, NULL); + } + /* + * Reset the stats for this queue since eth_pcap_rx calls above + * didn't result in the application receiving packets. + */ + pcap_q->rx_stat.pkts = 0; + pcap_q->rx_stat.bytes = 0; + } return 0; } @@ -653,8 +837,11 @@ eth_tx_queue_setup(struct rte_eth_dev *dev, const struct rte_eth_txconf *tx_conf __rte_unused) { struct pmd_internals *internals = dev->data->dev_private; + struct pcap_tx_queue *pcap_q = &internals->tx_queue[tx_queue_id]; - dev->data->tx_queues[tx_queue_id] = &internals->tx_queue[tx_queue_id]; + pcap_q->port_id = dev->data->port_id; + pcap_q->queue_id = tx_queue_id; + dev->data->tx_queues[tx_queue_id] = pcap_q; return 0; } @@ -862,7 +1049,33 @@ open_tx_iface(const char *key, const char *value, void *extra_args) return open_iface(key, value, extra_args); } -static struct rte_vdev_driver pmd_pcap_drv; +static int +select_phy_mac(const char *key __rte_unused, const char *value, + void *extra_args) +{ + if (extra_args) { + const int phy_mac = atoi(value); + int *enable_phy_mac = extra_args; + + if (phy_mac) + *enable_phy_mac = 1; + } + return 0; +} + +static int +get_infinite_rx_arg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (extra_args) { + const int infinite_rx = atoi(value); + int *enable_infinite_rx = extra_args; + + if (infinite_rx > 0) + *enable_infinite_rx = 1; + } + return 0; +} static int pmd_init_internals(struct rte_vdev_device *vdev, @@ -872,16 +1085,29 @@ pmd_init_internals(struct rte_vdev_device *vdev, struct rte_eth_dev **eth_dev) { struct rte_eth_dev_data *data; + struct pmd_process_private *pp; unsigned int numa_node = vdev->device.numa_node; PMD_LOG(INFO, "Creating pcap-backed ethdev on numa socket %d", numa_node); + pp = (struct pmd_process_private *) + rte_zmalloc(NULL, sizeof(struct pmd_process_private), + RTE_CACHE_LINE_SIZE); + + if (pp == NULL) { + PMD_LOG(ERR, + "Failed to allocate memory for process private"); + return -1; + } + /* reserve an ethdev entry */ *eth_dev = rte_eth_vdev_allocate(vdev, sizeof(**internals)); - if (!(*eth_dev)) + if (!(*eth_dev)) { + rte_free(pp); return -1; - + } + (*eth_dev)->process_private = pp; /* now put it all together * - store queue data in internals, * - store numa_node info in eth_dev @@ -889,11 +1115,22 @@ pmd_init_internals(struct rte_vdev_device *vdev, * - and point eth_dev structure to new eth_dev_data structure */ *internals = (*eth_dev)->data->dev_private; + /* + * Interface MAC = 02:70:63:61:70: + * derived from: 'locally administered':'p':'c':'a':'p':'iface_idx' + * where the middle 4 characters are converted to hex. + */ + (*internals)->eth_addr = (struct rte_ether_addr) { + .addr_bytes = { 0x02, 0x70, 0x63, 0x61, 0x70, iface_idx++ } + }; + (*internals)->phy_mac = 0; data = (*eth_dev)->data; data->nb_rx_queues = (uint16_t)nb_rx_queues; data->nb_tx_queues = (uint16_t)nb_tx_queues; data->dev_link = pmd_link; - data->mac_addrs = ð_addr; + data->mac_addrs = &(*internals)->eth_addr; + data->promiscuous = 1; + data->all_multicast = 1; /* * NOTE: we'll replace the data element, of originally allocated @@ -901,76 +1138,147 @@ pmd_init_internals(struct rte_vdev_device *vdev, */ (*eth_dev)->dev_ops = &ops; + strlcpy((*internals)->devargs, rte_vdev_device_args(vdev), + ETH_PCAP_ARG_MAXLEN); + return 0; } static int -eth_from_pcaps_common(struct rte_vdev_device *vdev, - struct pmd_devargs *rx_queues, const unsigned int nb_rx_queues, - struct pmd_devargs *tx_queues, const unsigned int nb_tx_queues, - struct rte_kvargs *kvlist, struct pmd_internals **internals, - struct rte_eth_dev **eth_dev) +eth_pcap_update_mac(const char *if_name, struct rte_eth_dev *eth_dev, + const unsigned int numa_node) { - struct rte_kvargs_pair *pair = NULL; - unsigned int k_idx; - unsigned int i; +#if defined(RTE_EXEC_ENV_LINUX) + void *mac_addrs; + struct ifreq ifr; + int if_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (if_fd == -1) + return -1; + + rte_strscpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); + if (ioctl(if_fd, SIOCGIFHWADDR, &ifr)) { + close(if_fd); + return -1; + } + + mac_addrs = rte_zmalloc_socket(NULL, RTE_ETHER_ADDR_LEN, 0, numa_node); + if (!mac_addrs) { + close(if_fd); + return -1; + } + + PMD_LOG(INFO, "Setting phy MAC for %s", if_name); + eth_dev->data->mac_addrs = mac_addrs; + rte_memcpy(eth_dev->data->mac_addrs[0].addr_bytes, + ifr.ifr_hwaddr.sa_data, RTE_ETHER_ADDR_LEN); - /* do some parameter checking */ - if (rx_queues == NULL && nb_rx_queues > 0) + close(if_fd); + + return 0; + +#elif defined(RTE_EXEC_ENV_FREEBSD) + void *mac_addrs; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + int mib[6]; + size_t len = 0; + char *buf; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + mib[5] = if_nametoindex(if_name); + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + + if (len == 0) + return -1; + + buf = rte_malloc(NULL, len, 0); + if (!buf) return -1; - if (tx_queues == NULL && nb_tx_queues > 0) + + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + rte_free(buf); + return -1; + } + ifm = (struct if_msghdr *)buf; + sdl = (struct sockaddr_dl *)(ifm + 1); + + mac_addrs = rte_zmalloc_socket(NULL, RTE_ETHER_ADDR_LEN, 0, numa_node); + if (!mac_addrs) { + rte_free(buf); return -1; + } + + PMD_LOG(INFO, "Setting phy MAC for %s", if_name); + eth_dev->data->mac_addrs = mac_addrs; + rte_memcpy(eth_dev->data->mac_addrs[0].addr_bytes, + LLADDR(sdl), RTE_ETHER_ADDR_LEN); + + rte_free(buf); + + return 0; +#else + return -1; +#endif +} + +static int +eth_from_pcaps_common(struct rte_vdev_device *vdev, + struct pmd_devargs_all *devargs_all, + struct pmd_internals **internals, struct rte_eth_dev **eth_dev) +{ + struct pmd_process_private *pp; + struct pmd_devargs *rx_queues = &devargs_all->rx_queues; + struct pmd_devargs *tx_queues = &devargs_all->tx_queues; + const unsigned int nb_rx_queues = rx_queues->num_of_queue; + const unsigned int nb_tx_queues = tx_queues->num_of_queue; + unsigned int i; if (pmd_init_internals(vdev, nb_rx_queues, nb_tx_queues, internals, eth_dev) < 0) return -1; + pp = (*eth_dev)->process_private; for (i = 0; i < nb_rx_queues; i++) { struct pcap_rx_queue *rx = &(*internals)->rx_queue[i]; struct devargs_queue *queue = &rx_queues->queue[i]; - rx->pcap = queue->pcap; - snprintf(rx->name, sizeof(rx->name), "%s", queue->name); - snprintf(rx->type, sizeof(rx->type), "%s", queue->type); + pp->rx_pcap[i] = queue->pcap; + strlcpy(rx->name, queue->name, sizeof(rx->name)); + strlcpy(rx->type, queue->type, sizeof(rx->type)); } for (i = 0; i < nb_tx_queues; i++) { struct pcap_tx_queue *tx = &(*internals)->tx_queue[i]; struct devargs_queue *queue = &tx_queues->queue[i]; - tx->dumper = queue->dumper; - tx->pcap = queue->pcap; - snprintf(tx->name, sizeof(tx->name), "%s", queue->name); - snprintf(tx->type, sizeof(tx->type), "%s", queue->type); - } - - for (k_idx = 0; k_idx < kvlist->count; k_idx++) { - pair = &kvlist->pairs[k_idx]; - if (strstr(pair->key, ETH_PCAP_IFACE_ARG) != NULL) - break; + pp->tx_dumper[i] = queue->dumper; + pp->tx_pcap[i] = queue->pcap; + strlcpy(tx->name, queue->name, sizeof(tx->name)); + strlcpy(tx->type, queue->type, sizeof(tx->type)); } - if (pair == NULL) - (*internals)->if_index = 0; - else - (*internals)->if_index = if_nametoindex(pair->value); - return 0; } static int eth_from_pcaps(struct rte_vdev_device *vdev, - struct pmd_devargs *rx_queues, const unsigned int nb_rx_queues, - struct pmd_devargs *tx_queues, const unsigned int nb_tx_queues, - struct rte_kvargs *kvlist, int single_iface, - unsigned int using_dumpers) + struct pmd_devargs_all *devargs_all) { struct pmd_internals *internals = NULL; struct rte_eth_dev *eth_dev = NULL; + struct pmd_devargs *rx_queues = &devargs_all->rx_queues; + int single_iface = devargs_all->single_iface; + unsigned int infinite_rx = devargs_all->infinite_rx; int ret; - ret = eth_from_pcaps_common(vdev, rx_queues, nb_rx_queues, - tx_queues, nb_tx_queues, kvlist, &internals, ð_dev); + ret = eth_from_pcaps_common(vdev, devargs_all, &internals, ð_dev); if (ret < 0) return ret; @@ -978,12 +1286,35 @@ eth_from_pcaps(struct rte_vdev_device *vdev, /* store weather we are using a single interface for rx/tx or not */ internals->single_iface = single_iface; - eth_dev->rx_pkt_burst = eth_pcap_rx; + if (single_iface) { + internals->if_index = if_nametoindex(rx_queues->queue[0].name); - if (using_dumpers) - eth_dev->tx_pkt_burst = eth_pcap_tx_dumper; + /* phy_mac arg is applied only only if "iface" devarg is provided */ + if (rx_queues->phy_mac) { + int ret = eth_pcap_update_mac(rx_queues->queue[0].name, + eth_dev, vdev->device.numa_node); + if (ret == 0) + internals->phy_mac = 1; + } + } + + internals->infinite_rx = infinite_rx; + /* Assign rx ops. */ + if (infinite_rx) + eth_dev->rx_pkt_burst = eth_pcap_rx_infinite; + else if (devargs_all->is_rx_pcap || devargs_all->is_rx_iface || + single_iface) + eth_dev->rx_pkt_burst = eth_pcap_rx; else + eth_dev->rx_pkt_burst = eth_null_rx; + + /* Assign tx ops. */ + if (devargs_all->is_tx_pcap) + eth_dev->tx_pkt_burst = eth_pcap_tx_dumper; + else if (devargs_all->is_tx_iface || single_iface) eth_dev->tx_pkt_burst = eth_pcap_tx; + else + eth_dev->tx_pkt_burst = eth_tx_drop; rte_eth_dev_probing_finish(eth_dev); return 0; @@ -993,13 +1324,19 @@ static int pmd_pcap_probe(struct rte_vdev_device *dev) { const char *name; - unsigned int is_rx_pcap = 0, is_tx_pcap = 0; struct rte_kvargs *kvlist; struct pmd_devargs pcaps = {0}; struct pmd_devargs dumpers = {0}; - struct rte_eth_dev *eth_dev; - int single_iface = 0; - int ret; + struct rte_eth_dev *eth_dev = NULL; + struct pmd_internals *internal; + int ret = 0; + + struct pmd_devargs_all devargs_all = { + .single_iface = 0, + .is_tx_pcap = 0, + .is_tx_iface = 0, + .infinite_rx = 0, + }; name = rte_vdev_device_name(dev); PMD_LOG(INFO, "Initializing pmd_pcap for %s", name); @@ -1008,23 +1345,24 @@ pmd_pcap_probe(struct rte_vdev_device *dev) start_cycles = rte_get_timer_cycles(); hz = rte_get_timer_hz(); - if (rte_eal_process_type() == RTE_PROC_SECONDARY && - strlen(rte_vdev_device_args(dev)) == 0) { + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { eth_dev = rte_eth_dev_attach_secondary(name); if (!eth_dev) { PMD_LOG(ERR, "Failed to probe %s", name); return -1; } - /* TODO: request info from primary to set up Rx and Tx */ - eth_dev->dev_ops = &ops; - eth_dev->device = &dev->device; - rte_eth_dev_probing_finish(eth_dev); - return 0; - } - kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_arguments); - if (kvlist == NULL) - return -1; + internal = eth_dev->data->dev_private; + + kvlist = rte_kvargs_parse(internal->devargs, valid_arguments); + if (kvlist == NULL) + return -1; + } else { + kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), + valid_arguments); + if (kvlist == NULL) + return -1; + } /* * If iface argument is passed we open the NICs and use them for @@ -1034,13 +1372,19 @@ pmd_pcap_probe(struct rte_vdev_device *dev) ret = rte_kvargs_process(kvlist, ETH_PCAP_IFACE_ARG, &open_rx_tx_iface, &pcaps); - if (ret < 0) goto free_kvlist; dumpers.queue[0] = pcaps.queue[0]; - single_iface = 1; + ret = rte_kvargs_process(kvlist, ETH_PCAP_PHY_MAC_ARG, + &select_phy_mac, &pcaps.phy_mac); + if (ret < 0) + goto free_kvlist; + + dumpers.phy_mac = pcaps.phy_mac; + + devargs_all.single_iface = 1; pcaps.num_of_queue = 1; dumpers.num_of_queue = 1; @@ -1048,43 +1392,142 @@ pmd_pcap_probe(struct rte_vdev_device *dev) } /* - * We check whether we want to open a RX stream from a real NIC or a - * pcap file + * We check whether we want to open a RX stream from a real NIC, a + * pcap file or open a dummy RX stream */ - is_rx_pcap = rte_kvargs_count(kvlist, ETH_PCAP_RX_PCAP_ARG) ? 1 : 0; + devargs_all.is_rx_pcap = + rte_kvargs_count(kvlist, ETH_PCAP_RX_PCAP_ARG) ? 1 : 0; + devargs_all.is_rx_iface = + rte_kvargs_count(kvlist, ETH_PCAP_RX_IFACE_ARG) ? 1 : 0; pcaps.num_of_queue = 0; - if (is_rx_pcap) { + devargs_all.is_tx_pcap = + rte_kvargs_count(kvlist, ETH_PCAP_TX_PCAP_ARG) ? 1 : 0; + devargs_all.is_tx_iface = + rte_kvargs_count(kvlist, ETH_PCAP_TX_IFACE_ARG) ? 1 : 0; + dumpers.num_of_queue = 0; + + if (devargs_all.is_rx_pcap) { + /* + * We check whether we want to infinitely rx the pcap file. + */ + unsigned int infinite_rx_arg_cnt = rte_kvargs_count(kvlist, + ETH_PCAP_INFINITE_RX_ARG); + + if (infinite_rx_arg_cnt == 1) { + ret = rte_kvargs_process(kvlist, + ETH_PCAP_INFINITE_RX_ARG, + &get_infinite_rx_arg, + &devargs_all.infinite_rx); + if (ret < 0) + goto free_kvlist; + PMD_LOG(INFO, "infinite_rx has been %s for %s", + devargs_all.infinite_rx ? "enabled" : "disabled", + name); + + } else if (infinite_rx_arg_cnt > 1) { + PMD_LOG(WARNING, "infinite_rx has not been enabled since the " + "argument has been provided more than once " + "for %s", name); + } + ret = rte_kvargs_process(kvlist, ETH_PCAP_RX_PCAP_ARG, &open_rx_pcap, &pcaps); - } else { + } else if (devargs_all.is_rx_iface) { ret = rte_kvargs_process(kvlist, NULL, &rx_iface_args_process, &pcaps); + } else if (devargs_all.is_tx_iface || devargs_all.is_tx_pcap) { + unsigned int i; + + /* Count number of tx queue args passed before dummy rx queue + * creation so a dummy rx queue can be created for each tx queue + */ + unsigned int num_tx_queues = + (rte_kvargs_count(kvlist, ETH_PCAP_TX_PCAP_ARG) + + rte_kvargs_count(kvlist, ETH_PCAP_TX_IFACE_ARG)); + + PMD_LOG(INFO, "Creating null rx queue since no rx queues were provided."); + + /* Creating a dummy rx queue for each tx queue passed */ + for (i = 0; i < num_tx_queues; i++) + ret = add_queue(&pcaps, "dummy_rx", "rx_null", NULL, + NULL); + } else { + PMD_LOG(ERR, "Error - No rx or tx queues provided"); + ret = -ENOENT; } - if (ret < 0) goto free_kvlist; /* - * We check whether we want to open a TX stream to a real NIC or a - * pcap file + * We check whether we want to open a TX stream to a real NIC, + * a pcap file, or drop packets on tx */ - is_tx_pcap = rte_kvargs_count(kvlist, ETH_PCAP_TX_PCAP_ARG) ? 1 : 0; - dumpers.num_of_queue = 0; - - if (is_tx_pcap) + if (devargs_all.is_tx_pcap) { ret = rte_kvargs_process(kvlist, ETH_PCAP_TX_PCAP_ARG, &open_tx_pcap, &dumpers); - else + } else if (devargs_all.is_tx_iface) { ret = rte_kvargs_process(kvlist, ETH_PCAP_TX_IFACE_ARG, &open_tx_iface, &dumpers); + } else { + unsigned int i; + + PMD_LOG(INFO, "Dropping packets on tx since no tx queues were provided."); + + /* Add 1 dummy queue per rxq which counts and drops packets. */ + for (i = 0; i < pcaps.num_of_queue; i++) + ret = add_queue(&dumpers, "dummy_tx", "tx_drop", NULL, + NULL); + } if (ret < 0) goto free_kvlist; create_eth: - ret = eth_from_pcaps(dev, &pcaps, pcaps.num_of_queue, &dumpers, - dumpers.num_of_queue, kvlist, single_iface, is_tx_pcap); + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { + struct pmd_process_private *pp; + unsigned int i; + + internal = eth_dev->data->dev_private; + pp = (struct pmd_process_private *) + rte_zmalloc(NULL, + sizeof(struct pmd_process_private), + RTE_CACHE_LINE_SIZE); + + if (pp == NULL) { + PMD_LOG(ERR, + "Failed to allocate memory for process private"); + ret = -1; + goto free_kvlist; + } + + eth_dev->dev_ops = &ops; + eth_dev->device = &dev->device; + + /* setup process private */ + for (i = 0; i < pcaps.num_of_queue; i++) + pp->rx_pcap[i] = pcaps.queue[i].pcap; + + for (i = 0; i < dumpers.num_of_queue; i++) { + pp->tx_dumper[i] = dumpers.queue[i].dumper; + pp->tx_pcap[i] = dumpers.queue[i].pcap; + } + + eth_dev->process_private = pp; + eth_dev->rx_pkt_burst = eth_pcap_rx; + if (devargs_all.is_tx_pcap) + eth_dev->tx_pkt_burst = eth_pcap_tx_dumper; + else + eth_dev->tx_pkt_burst = eth_pcap_tx; + + rte_eth_dev_probing_finish(eth_dev); + goto free_kvlist; + } + + devargs_all.rx_queues = pcaps; + devargs_all.tx_queues = dumpers; + + ret = eth_from_pcaps(dev, &devargs_all); free_kvlist: rte_kvargs_free(kvlist); @@ -1095,6 +1538,7 @@ free_kvlist: static int pmd_pcap_remove(struct rte_vdev_device *dev) { + struct pmd_internals *internals = NULL; struct rte_eth_dev *eth_dev = NULL; PMD_LOG(INFO, "Closing pcap ethdev on numa socket %d", @@ -1108,8 +1552,16 @@ pmd_pcap_remove(struct rte_vdev_device *dev) if (eth_dev == NULL) return -1; - rte_free(eth_dev->data->dev_private); + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { + internals = eth_dev->data->dev_private; + if (internals != NULL && internals->phy_mac == 0) + /* not dynamically allocated, must not be freed */ + eth_dev->data->mac_addrs = NULL; + } + + eth_dev_close(eth_dev); + rte_free(eth_dev->process_private); rte_eth_dev_release_port(eth_dev); return 0; @@ -1128,7 +1580,9 @@ RTE_PMD_REGISTER_PARAM_STRING(net_pcap, ETH_PCAP_RX_IFACE_ARG "= " ETH_PCAP_RX_IFACE_IN_ARG "= " ETH_PCAP_TX_IFACE_ARG "= " - ETH_PCAP_IFACE_ARG "="); + ETH_PCAP_IFACE_ARG "= " + ETH_PCAP_PHY_MAC_ARG "=" + ETH_PCAP_INFINITE_RX_ARG "=<0|1>"); RTE_INIT(eth_pcap_init_log) {