X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=VNFs%2FDPPD-PROX%2Fhandle_gen.c;h=4fd2c39970b117a7213f42ae9214067660a1ea3a;hb=438a3dcafd3bd943b4f7cadf0520fd9a2e74b079;hp=89dbe9e4acba153a8c9a68acf03b0fccdb68cc63;hpb=5feebc8726530d07c29c651b8c54d37c57cb8e8f;p=samplevnf.git diff --git a/VNFs/DPPD-PROX/handle_gen.c b/VNFs/DPPD-PROX/handle_gen.c index 89dbe9e4..4fd2c399 100644 --- a/VNFs/DPPD-PROX/handle_gen.c +++ b/VNFs/DPPD-PROX/handle_gen.c @@ -13,7 +13,6 @@ // See the License for the specific language governing permissions and // limitations under the License. */ - #include #include #include @@ -52,15 +51,13 @@ struct pkt_template { uint16_t len; uint16_t l2_len; uint16_t l3_len; - uint8_t buf[ETHER_MAX_LEN]; + uint8_t *buf; }; -#define MAX_TEMPLATE_INDEX 65536 -#define TEMPLATE_INDEX_MASK (MAX_TEMPLATE_INDEX - 1) -#define MBUF_ARP MAX_TEMPLATE_INDEX - #define IP4(x) x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >> 24 +#define TASK_OVERWRITE_SRC_MAC_WITH_PORT_MAC 1 + static void pkt_template_init_mbuf(struct pkt_template *pkt_template, struct rte_mbuf *mbuf, uint8_t *pkt) { const uint32_t pkt_size = pkt_template->len; @@ -86,7 +83,6 @@ struct task_gen_pcap { struct task_gen { struct task_base base; uint64_t hz; - uint64_t link_speed; struct token_time token_time; struct local_mbuf local_mbuf; struct pkt_template *pkt_template; /* packet templates used at runtime */ @@ -97,6 +93,7 @@ struct task_gen { uint32_t n_pkts; /* number of packets in pcap */ uint32_t pkt_idx; /* current packet from pcap */ uint32_t pkt_count; /* how many pakets to generate */ + uint32_t max_frame_size; uint32_t runtime_flags; uint16_t lat_pos; uint16_t packet_id_pos; @@ -116,13 +113,14 @@ struct task_gen { uint16_t rand_offset; /* each random has an offset*/ uint8_t rand_len; /* # bytes to take from random (no bias introduced) */ } rand[64]; - uint64_t accur[64]; + uint64_t accur[ACCURACY_WINDOW]; uint64_t pkt_tsc_offset[64]; struct pkt_template *pkt_template_orig; /* packet templates (from inline or from pcap) */ struct ether_addr src_mac; uint8_t flags; uint8_t cksum_offload; struct prox_port_cfg *port; + uint64_t *bytes_to_tsc; } __rte_cache_aligned; static inline uint8_t ipv4_get_hdr_len(struct ipv4_hdr *ip) @@ -261,15 +259,9 @@ static int handle_gen_pcap_bulk(struct task_base *tbase, struct rte_mbuf **mbuf, return task->base.tx_pkt(&task->base, new_pkts, send_bulk, NULL); } -static uint64_t bytes_to_tsc(struct task_gen *task, uint32_t bytes) +static inline uint64_t bytes_to_tsc(struct task_gen *task, uint32_t bytes) { - const uint64_t hz = task->hz; - const uint64_t bytes_per_hz = task->link_speed; - - if (bytes_per_hz == UINT64_MAX) - return 0; - - return hz * bytes / bytes_per_hz; + return task->bytes_to_tsc[bytes]; } static uint32_t task_gen_next_pkt_idx(const struct task_gen *task, uint32_t pkt_idx) @@ -359,9 +351,10 @@ static void task_gen_apply_accur_pos(struct task_gen *task, uint8_t *pkt_hdr, ui *(uint32_t *)(pkt_hdr + task->accur_pos) = accuracy; } -static void task_gen_apply_sig(struct task_gen *task, uint8_t *pkt_hdr) +static void task_gen_apply_sig(struct task_gen *task, struct pkt_template *dst) { - *(uint32_t *)(pkt_hdr + task->sig_pos) = task->sig; + if (task->sig_pos) + *(uint32_t *)(dst->buf + task->sig_pos) = task->sig; } static void task_gen_apply_all_accur_pos(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) @@ -369,26 +362,12 @@ static void task_gen_apply_all_accur_pos(struct task_gen *task, struct rte_mbuf if (!task->accur_pos) return; - /* The accuracy of task->pkt_queue_index - 64 is stored in - packet task->pkt_queue_index. The ID modulo 64 is the + /* The accuracy of task->pkt_queue_index - ACCURACY_WINDOW is stored in + packet task->pkt_queue_index. The ID modulo ACCURACY_WINDOW is the same. */ for (uint16_t j = 0; j < count; ++j) { - if ((mbufs[j]->udata64 & MBUF_ARP) == 0) { - uint32_t accuracy = task->accur[(task->pkt_queue_index + j) & 63]; - task_gen_apply_accur_pos(task, pkt_hdr[j], accuracy); - } - } -} - -static void task_gen_apply_all_sig(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) -{ - if (!task->sig_pos) - return; - - for (uint16_t j = 0; j < count; ++j) { - if ((mbufs[j]->udata64 & MBUF_ARP) == 0) { - task_gen_apply_sig(task, pkt_hdr[j]); - } + uint32_t accuracy = task->accur[(task->pkt_queue_index + j) & (ACCURACY_WINDOW - 1)]; + task_gen_apply_accur_pos(task, pkt_hdr[j], accuracy); } } @@ -405,11 +384,9 @@ static void task_gen_apply_all_unique_id(struct task_gen *task, struct rte_mbuf return; for (uint16_t i = 0; i < count; ++i) { - if ((mbufs[i]->udata64 & MBUF_ARP) == 0) { - struct unique_id id; - unique_id_init(&id, task->generator_id, task->pkt_queue_index++); - task_gen_apply_unique_id(task, pkt_hdr[i], &id); - } + struct unique_id id; + unique_id_init(&id, task->generator_id, task->pkt_queue_index++); + task_gen_apply_unique_id(task, pkt_hdr[i], &id); } } @@ -423,11 +400,9 @@ static void task_gen_checksum_packets(struct task_gen *task, struct rte_mbuf **m uint32_t pkt_idx = task_gen_offset_pkt_idx(task, - count); for (uint16_t i = 0; i < count; ++i) { - if ((mbufs[i]->udata64 & MBUF_ARP) == 0) { - struct pkt_template *pkt_template = &task->pkt_template[pkt_idx]; - checksum_packet(pkt_hdr[i], mbufs[i], pkt_template, task->cksum_offload); - pkt_idx = task_gen_next_pkt_idx(task, pkt_idx); - } + struct pkt_template *pkt_template = &task->pkt_template[pkt_idx]; + checksum_packet(pkt_hdr[i], mbufs[i], pkt_template, task->cksum_offload); + pkt_idx = task_gen_next_pkt_idx(task, pkt_idx); } } @@ -447,8 +422,12 @@ static uint64_t task_gen_calc_bulk_duration(struct task_gen *task, uint32_t coun uint32_t pkt_idx = task_gen_offset_pkt_idx(task, - 1); struct pkt_template *last_pkt_template = &task->pkt_template[pkt_idx]; uint32_t last_pkt_len = pkt_len_to_wire_size(last_pkt_template->len); +#ifdef NO_EXTRAPOLATION + uint64_t bulk_duration = task->pkt_tsc_offset[count - 1]; +#else uint64_t last_pkt_duration = bytes_to_tsc(task, last_pkt_len); uint64_t bulk_duration = task->pkt_tsc_offset[count - 1] + last_pkt_duration; +#endif return bulk_duration; } @@ -483,6 +462,14 @@ static uint64_t task_gen_write_latency(struct task_gen *task, uint8_t **pkt_hdr, simply sleeping until delta_t is zero would leave a period of silence on the line. The error has been introduced earlier, but the packets have already been sent. */ + + /* This happens typically if previous bulk was delayed + by an interrupt e.g. (with Time in nsec) + Time x: sleep 4 microsec + Time x+4000: send 64 packets (64 packets as 4000 nsec, w/ 10Gbps 64 bytes) + Time x+5000: send 16 packets (16 packets as 1000 nsec) + When we send the 16 packets, the 64 ealier packets are not yet + fully sent */ if (tx_tsc < task->earliest_tsc_next_pkt) delta_t = task->earliest_tsc_next_pkt - tx_tsc; else @@ -491,12 +478,10 @@ static uint64_t task_gen_write_latency(struct task_gen *task, uint8_t **pkt_hdr, for (uint16_t i = 0; i < count; ++i) { uint32_t *pos = (uint32_t *)(pkt_hdr[i] + task->lat_pos); const uint64_t pkt_tsc = tx_tsc + delta_t + task->pkt_tsc_offset[i]; - *pos = pkt_tsc >> LATENCY_ACCURACY; } uint64_t bulk_duration = task_gen_calc_bulk_duration(task, count); - task->earliest_tsc_next_pkt = tx_tsc + delta_t + bulk_duration; write_tsc_after = rte_rdtsc(); task->write_duration_estimate = write_tsc_after - write_tsc_before; @@ -506,6 +491,7 @@ static uint64_t task_gen_write_latency(struct task_gen *task, uint8_t **pkt_hdr, do { tsc_before_tx = rte_rdtsc(); } while (tsc_before_tx < tx_tsc); + return tsc_before_tx; } @@ -518,7 +504,7 @@ static void task_gen_store_accuracy(struct task_gen *task, uint32_t count, uint6 uint64_t first_accuracy_idx = task->pkt_queue_index - count; for (uint32_t i = 0; i < count; ++i) { - uint32_t accuracy_idx = (first_accuracy_idx + i) & 63; + uint32_t accuracy_idx = (first_accuracy_idx + i) & (ACCURACY_WINDOW - 1); task->accur[accuracy_idx] = accur; } @@ -542,10 +528,13 @@ static void task_gen_build_packets(struct task_gen *task, struct rte_mbuf **mbuf struct pkt_template *pktpl = &task->pkt_template[task->pkt_idx]; struct pkt_template *pkt_template = &task->pkt_template[task->pkt_idx]; pkt_template_init_mbuf(pkt_template, mbufs[i], pkt_hdr[i]); - mbufs[i]->udata64 = task->pkt_idx & TEMPLATE_INDEX_MASK; struct ether_hdr *hdr = (struct ether_hdr *)pkt_hdr[i]; if (task->lat_enabled) { +#ifdef NO_EXTRAPOLATION + task->pkt_tsc_offset[i] = 0; +#else task->pkt_tsc_offset[i] = bytes_to_tsc(task, will_send_bytes); +#endif will_send_bytes += pkt_len_to_wire_size(pkt_template->len); } task->pkt_idx = task_gen_next_pkt_idx(task, task->pkt_idx); @@ -641,16 +630,6 @@ static int handle_gen_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uin int i, j; - // If link is down, link_speed is 0 - if (unlikely(task->link_speed == 0)) { - if (task->port && task->port->link_speed != 0) { - task->link_speed = task->port->link_speed * 125000L; - plog_info("\tPort %u: link speed is %ld Mbps\n", - (uint8_t)(task->port - prox_port_cfg), 8 * task->link_speed / 1000000); - } else - return 0; - } - task_gen_update_config(task); if (task->pkt_count == 0) { @@ -679,7 +658,6 @@ static int handle_gen_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uin task_gen_build_packets(task, new_pkts, pkt_hdr, send_bulk); task_gen_apply_all_random_fields(task, pkt_hdr, send_bulk); task_gen_apply_all_accur_pos(task, new_pkts, pkt_hdr, send_bulk); - task_gen_apply_all_sig(task, new_pkts, pkt_hdr, send_bulk); task_gen_apply_all_unique_id(task, new_pkts, pkt_hdr, send_bulk); uint64_t tsc_before_tx; @@ -688,6 +666,20 @@ static int handle_gen_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uin task_gen_checksum_packets(task, new_pkts, pkt_hdr, send_bulk); ret = task->base.tx_pkt(&task->base, new_pkts, send_bulk, out); task_gen_store_accuracy(task, send_bulk, tsc_before_tx); + + // If we failed to send some packets, we need to do some clean-up: + + if (unlikely(ret)) { + // We need re-use the packets indexes not being sent + // Hence non-sent packets will not be considered as lost by the receiver when it looks at + // packet ids. This should also increase the percentage of packets used for latency measurements + task->pkt_queue_index -= ret; + + // In case of failures, the estimate about when we can send next packet (earliest_tsc_next_pkt) is wrong + // This would result in under-estimated latency (up to 0 or negative) + uint64_t bulk_duration = task_gen_calc_bulk_duration(task, ret); + task->earliest_tsc_next_pkt -= bulk_duration; + } return ret; } @@ -697,14 +689,17 @@ static void init_task_gen_seeds(struct task_gen *task) random_init_seed(&task->rand[i].state); } -static uint32_t pcap_count_pkts(pcap_t *handle) +static uint32_t pcap_count_pkts(pcap_t *handle, uint32_t *max_frame_size) { struct pcap_pkthdr header; const uint8_t *buf; uint32_t ret = 0; + *max_frame_size = 0; long pkt1_fpos = ftell(pcap_file(handle)); while ((buf = pcap_next(handle, &header))) { + if (header.len > *max_frame_size) + *max_frame_size = header.len; ret++; } int ret2 = fseek(pcap_file(handle), pkt1_fpos, SEEK_SET); @@ -721,7 +716,7 @@ static uint64_t avg_time_stamp(uint64_t *time_stamp, uint32_t n) return (tot_inter_pkt + n / 2)/n; } -static int pcap_read_pkts(pcap_t *handle, const char *file_name, uint32_t n_pkts, struct pkt_template *proto, uint64_t *time_stamp) +static int pcap_read_pkts(pcap_t *handle, const char *file_name, uint32_t n_pkts, struct pkt_template *proto, uint64_t *time_stamp, uint32_t max_frame_size) { struct pcap_pkthdr header; const uint8_t *buf; @@ -732,7 +727,7 @@ static int pcap_read_pkts(pcap_t *handle, const char *file_name, uint32_t n_pkts PROX_PANIC(buf == NULL, "Failed to read packet %d from pcap %s\n", i, file_name); proto[i].len = header.len; - len = RTE_MIN(header.len, sizeof(proto[i].buf)); + len = RTE_MIN(header.len, max_frame_size); if (header.len > len) plogx_warn("Packet truncated from %u to %zu bytes\n", header.len, len); @@ -767,7 +762,7 @@ static int pcap_read_pkts(pcap_t *handle, const char *file_name, uint32_t n_pkts static int check_pkt_size(struct task_gen *task, uint32_t pkt_size, int do_panic) { const uint16_t min_len = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr); - const uint16_t max_len = ETHER_MAX_LEN - 4; + const uint16_t max_len = task->max_frame_size; if (do_panic) { PROX_PANIC(pkt_size == 0, "Invalid packet size length (no packet defined?)\n"); @@ -913,7 +908,8 @@ static void task_gen_reset_pkt_templates_content(struct task_gen *task) for (size_t i = 0; i < task->n_pkts; ++i) { src = &task->pkt_template_orig[i]; dst = &task->pkt_template[i]; - memcpy(dst->buf, src->buf, dst->len); + memcpy(dst->buf, src->buf, RTE_MAX(src->len, dst->len)); + task_gen_apply_sig(task, dst); } } @@ -928,8 +924,6 @@ static void task_init_gen_load_pkt_inline(struct task_gen *task, struct task_arg { const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); - if (targ->pkt_size > sizeof(task->pkt_template[0].buf)) - targ->pkt_size = sizeof(task->pkt_template[0].buf); task->n_pkts = 1; size_t mem_size = task->n_pkts * sizeof(*task->pkt_template); @@ -938,7 +932,17 @@ static void task_init_gen_load_pkt_inline(struct task_gen *task, struct task_arg PROX_PANIC(task->pkt_template == NULL || task->pkt_template_orig == NULL, - "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size); + "Failed to allocate %lu bytes (in huge pages) for packet template\n", mem_size); + + task->pkt_template->buf = prox_zmalloc(task->max_frame_size, socket_id); + task->pkt_template_orig->buf = prox_zmalloc(task->max_frame_size, socket_id); + PROX_PANIC(task->pkt_template->buf == NULL || + task->pkt_template_orig->buf == NULL, + "Failed to allocate %u bytes (in huge pages) for packet\n", task->max_frame_size); + + PROX_PANIC(targ->pkt_size > task->max_frame_size, + targ->pkt_size > ETHER_MAX_LEN + 2 * PROX_VLAN_TAG_SIZE - 4 ? + "pkt_size too high and jumbo frames disabled" : "pkt_size > mtu"); rte_memcpy(task->pkt_template_orig[0].buf, targ->pkt_inline, targ->pkt_size); task->pkt_template_orig[0].len = targ->pkt_size; @@ -951,15 +955,18 @@ static void task_init_gen_load_pcap(struct task_gen *task, struct task_args *tar { const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); char err[PCAP_ERRBUF_SIZE]; + uint32_t max_frame_size; pcap_t *handle = pcap_open_offline(targ->pcap_file, err); PROX_PANIC(handle == NULL, "Failed to open PCAP file: %s\n", err); - task->n_pkts = pcap_count_pkts(handle); - plogx_info("%u packets in pcap file '%s'\n", task->n_pkts, targ->pcap_file); + task->n_pkts = pcap_count_pkts(handle, &max_frame_size); + plogx_info("%u packets in pcap file '%s'; max frame size=%d\n", task->n_pkts, targ->pcap_file, max_frame_size); + PROX_PANIC(max_frame_size > task->max_frame_size, + max_frame_size > ETHER_MAX_LEN + 2 * PROX_VLAN_TAG_SIZE -4 ? + "pkt_size too high and jumbo frames disabled" : "pkt_size > mtu"); if (targ->n_pkts) task->n_pkts = RTE_MIN(task->n_pkts, targ->n_pkts); - PROX_PANIC(task->n_pkts > MAX_TEMPLATE_INDEX, "Too many packets specified in pcap - increase MAX_TEMPLATE_INDEX\n"); plogx_info("Loading %u packets from pcap\n", task->n_pkts); size_t mem_size = task->n_pkts * sizeof(*task->pkt_template); task->pkt_template = prox_zmalloc(mem_size, socket_id); @@ -968,24 +975,41 @@ static void task_init_gen_load_pcap(struct task_gen *task, struct task_args *tar task->pkt_template_orig == NULL, "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size); - pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->pkt_template_orig, NULL); + for (uint i = 0; i < task->n_pkts; i++) { + task->pkt_template[i].buf = prox_zmalloc(task->max_frame_size, socket_id); + task->pkt_template_orig[i].buf = prox_zmalloc(task->max_frame_size, socket_id); + + PROX_PANIC(task->pkt_template->buf == NULL || + task->pkt_template_orig->buf == NULL, + "Failed to allocate %u bytes (in huge pages) for pcap file\n", task->max_frame_size); + } + + pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->pkt_template_orig, NULL, max_frame_size); pcap_close(handle); task_gen_reset_pkt_templates(task); } -static struct rte_mempool *task_gen_create_mempool(struct task_args *targ) +static struct rte_mempool *task_gen_create_mempool(struct task_args *targ, uint16_t max_frame_size) { static char name[] = "gen_pool"; struct rte_mempool *ret; const int sock_id = rte_lcore_to_socket_id(targ->lconf->id); name[0]++; - ret = rte_mempool_create(name, targ->nb_mbuf - 1, MBUF_SIZE, + uint32_t mbuf_size = TX_MBUF_SIZE; + if (max_frame_size + (unsigned)sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM > mbuf_size) + mbuf_size = max_frame_size + (unsigned)sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM; + plog_info("\t\tCreating mempool with name '%s'\n", name); + ret = rte_mempool_create(name, targ->nb_mbuf - 1, mbuf_size, targ->nb_cache_mbuf, sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, 0, sock_id, 0); PROX_PANIC(ret == NULL, "Failed to allocate dummy memory pool on socket %u with %u elements\n", sock_id, targ->nb_mbuf - 1); + + plog_info("\t\tMempool %p size = %u * %u cache %u, socket %d\n", ret, + targ->nb_mbuf - 1, mbuf_size, targ->nb_cache_mbuf, sock_id); + return ret; } @@ -1001,12 +1025,16 @@ int task_gen_set_pkt_size(struct task_base *tbase, uint32_t pkt_size) struct task_gen *task = (struct task_gen *)tbase; int rc; - if ((rc = check_pkt_size(task, pkt_size, 0)) != 0) - return rc; - if ((rc = check_fields_in_bounds(task, pkt_size, 0)) != 0) - return rc; - task->pkt_template[0].len = pkt_size; - return rc; + for (size_t i = 0; i < task->n_pkts; ++i) { + if ((rc = check_pkt_size(task, pkt_size, 0)) != 0) + return rc; + if ((rc = check_fields_in_bounds(task, pkt_size, 0)) != 0) + return rc; + } + for (size_t i = 0; i < task->n_pkts; ++i) { + task->pkt_template[i].len = pkt_size; + } + return 0; } void task_gen_set_rate(struct task_base *tbase, uint64_t bps) @@ -1032,6 +1060,8 @@ int task_gen_set_value(struct task_base *tbase, uint32_t value, uint32_t offset, { struct task_gen *task = (struct task_gen *)tbase; + if (offset + len > task->max_frame_size) + return -1; for (size_t i = 0; i < task->n_pkts; ++i) { uint32_t to_write = rte_cpu_to_be_32(value) >> ((4 - len) * 8); uint8_t *dst = task->pkt_template[i].buf; @@ -1049,6 +1079,11 @@ void task_gen_reset_values(struct task_base *tbase) struct task_gen *task = (struct task_gen *)tbase; task_gen_reset_pkt_templates_content(task); + if (task->flags & TASK_OVERWRITE_SRC_MAC_WITH_PORT_MAC) { + for (uint32_t i = 0; i < task->n_pkts; ++i) { + rte_memcpy(&task->pkt_template[i].buf[sizeof(struct ether_addr)], &task->src_mac, sizeof(struct ether_addr)); + } + } } uint32_t task_gen_get_n_randoms(struct task_base *tbase) @@ -1062,29 +1097,28 @@ static void init_task_gen_pcap(struct task_base *tbase, struct task_args *targ) { struct task_gen_pcap *task = (struct task_gen_pcap *)tbase; const uint32_t sockid = rte_lcore_to_socket_id(targ->lconf->id); + uint32_t max_frame_size; task->loop = targ->loop; task->pkt_idx = 0; task->hz = rte_get_tsc_hz(); - task->local_mbuf.mempool = task_gen_create_mempool(targ); - - PROX_PANIC(!strcmp(targ->pcap_file, ""), "No pcap file defined\n"); - char err[PCAP_ERRBUF_SIZE]; pcap_t *handle = pcap_open_offline(targ->pcap_file, err); PROX_PANIC(handle == NULL, "Failed to open PCAP file: %s\n", err); - task->n_pkts = pcap_count_pkts(handle); + task->n_pkts = pcap_count_pkts(handle, &max_frame_size); plogx_info("%u packets in pcap file '%s'\n", task->n_pkts, targ->pcap_file); + task->local_mbuf.mempool = task_gen_create_mempool(targ, max_frame_size); + + PROX_PANIC(!strcmp(targ->pcap_file, ""), "No pcap file defined\n"); + if (targ->n_pkts) { plogx_info("Configured to load %u packets\n", targ->n_pkts); if (task->n_pkts > targ->n_pkts) task->n_pkts = targ->n_pkts; } - PROX_PANIC(task->n_pkts > MAX_TEMPLATE_INDEX, "Too many packets specified in pcap - increase MAX_TEMPLATE_INDEX\n"); - plogx_info("Loading %u packets from pcap\n", task->n_pkts); size_t mem_size = task->n_pkts * (sizeof(*task->proto) + sizeof(*task->proto_tsc)); @@ -1094,7 +1128,12 @@ static void init_task_gen_pcap(struct task_base *tbase, struct task_args *targ) task->proto = (struct pkt_template *) mem; task->proto_tsc = (uint64_t *)(mem + task->n_pkts * sizeof(*task->proto)); - pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->proto, task->proto_tsc); + for (uint i = 0; i < targ->n_pkts; i++) { + task->proto[i].buf = prox_zmalloc(max_frame_size, sockid); + PROX_PANIC(task->proto[i].buf == NULL, "Failed to allocate %u bytes (in huge pages) for pcap file\n", max_frame_size); + } + + pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->proto, task->proto_tsc, max_frame_size); pcap_close(handle); } @@ -1155,18 +1194,7 @@ static void start(struct task_base *tbase) if (tbase->l3.tmaster) { register_all_ip_to_ctrl_plane(task); } - if (task->port) { - // task->port->link_speed reports the link speed in Mbps e.g. 40k for a 40 Gbps NIC. - // task->link_speed reports link speed in Bytes per sec. - // It can be 0 if link is down, and must hence be updated in fast path. - task->link_speed = task->port->link_speed * 125000L; - if (task->link_speed) - plog_info("\tPort %u: link speed is %ld Mbps\n", - (uint8_t)(task->port - prox_port_cfg), 8 * task->link_speed / 1000000); - else - plog_info("\tPort %u: link speed is %ld Mbps - link might be down\n", - (uint8_t)(task->port - prox_port_cfg), 8 * task->link_speed / 1000000); - } + /* TODO Handle the case when two tasks transmit to the same port and one of them is stopped. In that case ARP (requests or replies) @@ -1204,7 +1232,17 @@ static void init_task_gen(struct task_base *tbase, struct task_args *targ) task->packet_id_pos = targ->packet_id_pos; - task->local_mbuf.mempool = task_gen_create_mempool(targ); + struct prox_port_cfg *port = find_reachable_port(targ); + // TODO: check that all reachable ports have the same mtu... + if (port) { + task->cksum_offload = port->requested_tx_offload & (DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM); + task->port = port; + task->max_frame_size = port->mtu + ETHER_HDR_LEN + 2 * PROX_VLAN_TAG_SIZE; + } else { + // Not generating to any port... + task->max_frame_size = ETHER_MAX_LEN; + } + task->local_mbuf.mempool = task_gen_create_mempool(targ, task->max_frame_size); PROX_PANIC(task->local_mbuf.mempool == NULL, "Failed to create mempool\n"); task->pkt_idx = 0; task->hz = rte_get_tsc_hz(); @@ -1242,7 +1280,29 @@ static void init_task_gen(struct task_base *tbase, struct task_args *targ) task->generator_id = targ->generator_id; plog_info("\tGenerator id = %d\n", task->generator_id); - task->link_speed = UINT64_MAX; + + // Allocate array holding bytes to tsc for supported frame sizes + task->bytes_to_tsc = prox_zmalloc(task->max_frame_size * MAX_PKT_BURST * sizeof(task->bytes_to_tsc[0]), rte_lcore_to_socket_id(targ->lconf->id)); + PROX_PANIC(task->bytes_to_tsc == NULL, + "Failed to allocate %u bytes (in huge pages) for bytes_to_tsc\n", task->max_frame_size); + + // task->port->max_link_speed reports the maximum, non negotiated ink speed in Mbps e.g. 40k for a 40 Gbps NIC. + // It can be UINT32_MAX (virtual devices or not supported by DPDK < 16.04) + uint64_t bytes_per_hz = UINT64_MAX; + if ((task->port) && (task->port->max_link_speed != UINT32_MAX)) { + bytes_per_hz = task->port->max_link_speed * 125000L; + plog_info("\tPort %u: max link speed is %ld Mbps\n", + (uint8_t)(task->port - prox_port_cfg), 8 * bytes_per_hz / 1000000); + } + // There are cases where hz estimate might be slighly over-estimated + // This results in too much extrapolation + // Only account for 99% of extrapolation to handle cases with up to 1% error clocks + for (unsigned int i = 0; i < task->max_frame_size * MAX_PKT_BURST ; i++) { + if (bytes_per_hz == UINT64_MAX) + task->bytes_to_tsc[i] = 0; + else + task->bytes_to_tsc[i] = (task->hz * i * 0.99) / bytes_per_hz; + } if (!strcmp(targ->pcap_file, "")) { plog_info("\tUsing inline definition of a packet\n"); @@ -1252,23 +1312,18 @@ static void init_task_gen(struct task_base *tbase, struct task_args *targ) task_init_gen_load_pcap(task, targ); } - if ((targ->flags & DSF_KEEP_SRC_MAC) == 0 && (targ->nb_txrings || targ->nb_txports)) { - uint8_t *src_addr = prox_port_cfg[tbase->tx_params_hw.tx_port_queue->port].eth_addr.addr_bytes; + PROX_PANIC(((targ->nb_txrings == 0) && (targ->nb_txports == 0)), "Gen mode requires a tx ring or a tx port"); + if ((targ->flags & DSF_KEEP_SRC_MAC) == 0) { + task->flags |= TASK_OVERWRITE_SRC_MAC_WITH_PORT_MAC; + memcpy(&task->src_mac, &prox_port_cfg[task->base.tx_params_hw.tx_port_queue->port].eth_addr, sizeof(struct ether_addr)); for (uint32_t i = 0; i < task->n_pkts; ++i) { - rte_memcpy(&task->pkt_template[i].buf[6], src_addr, 6); + rte_memcpy(&task->pkt_template[i].buf[sizeof(struct ether_addr)], &task->src_mac, sizeof(struct ether_addr)); } } - memcpy(&task->src_mac, &prox_port_cfg[task->base.tx_params_hw.tx_port_queue->port].eth_addr, sizeof(struct ether_addr)); for (uint32_t i = 0; i < targ->n_rand_str; ++i) { PROX_PANIC(task_gen_add_rand(tbase, targ->rand_str[i], targ->rand_offset[i], UINT32_MAX), "Failed to add random\n"); } - - struct prox_port_cfg *port = find_reachable_port(targ); - if (port) { - task->cksum_offload = port->capabilities.tx_offload_cksum; - task->port = port; - } } static struct task_init task_init_gen = { @@ -1280,7 +1335,7 @@ static struct task_init task_init_gen = { #ifdef SOFT_CRC // For SOFT_CRC, no offload is needed. If both NOOFFLOADS and NOMULTSEGS flags are set the // vector mode is used by DPDK, resulting (theoretically) in higher performance. - .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, #else .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX, #endif @@ -1297,13 +1352,14 @@ static struct task_init task_init_gen_l3 = { #ifdef SOFT_CRC // For SOFT_CRC, no offload is needed. If both NOOFFLOADS and NOMULTSEGS flags are set the // vector mode is used by DPDK, resulting (theoretically) in higher performance. - .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, #else .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX, #endif .size = sizeof(struct task_gen) }; +/* This mode uses time stamps in the pcap file */ static struct task_init task_init_gen_pcap = { .mode_str = "gen", .sub_mode_str = "pcap", @@ -1312,7 +1368,7 @@ static struct task_init task_init_gen_pcap = { .start = start_pcap, .early_init = init_task_gen_early, #ifdef SOFT_CRC - .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, #else .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX, #endif