New PROX commit ID and bug fix
[samplevnf.git] / VNFs / DPPD-PROX / handle_gen.c
index 0e5164b..4fd2c39 100644 (file)
@@ -13,7 +13,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 */
-
 #include <rte_mbuf.h>
 #include <pcap.h>
 #include <string.h>
@@ -55,12 +54,10 @@ struct pkt_template {
        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 */
@@ -117,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)
@@ -262,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)
@@ -360,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)
@@ -370,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);
        }
 }
 
@@ -406,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);
        }
 }
 
@@ -424,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);
        }
 }
 
@@ -448,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;
 }
@@ -484,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
@@ -492,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;
@@ -507,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;
 }
 
@@ -519,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;
        }
@@ -543,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);
@@ -642,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) {
@@ -680,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;
@@ -689,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;
 }
 
@@ -725,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;
@@ -736,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);
 
@@ -917,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);
        }
 }
 
@@ -968,14 +960,13 @@ static void task_init_gen_load_pcap(struct task_gen *task, struct task_args *tar
        PROX_PANIC(handle == NULL, "Failed to open PCAP file: %s\n", err);
 
        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);
+       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);
@@ -985,15 +976,15 @@ static void task_init_gen_load_pcap(struct task_gen *task, struct task_args *tar
                   "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size);
 
        for (uint i = 0; i < task->n_pkts; i++) {
-               task->pkt_template[i].buf = prox_zmalloc(max_frame_size, socket_id);
-               task->pkt_template_orig[i].buf = prox_zmalloc(max_frame_size, socket_id);
+               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);
+       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);
 }
@@ -1005,15 +996,20 @@ static struct rte_mempool *task_gen_create_mempool(struct task_args *targ, uint1
        const int sock_id = rte_lcore_to_socket_id(targ->lconf->id);
 
        name[0]++;
-       uint32_t mbuf_size = 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;
 }
 
@@ -1029,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)
@@ -1060,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;
@@ -1077,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)
@@ -1112,8 +1119,6 @@ static void init_task_gen_pcap(struct task_base *tbase, struct task_args *targ)
                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));
@@ -1128,7 +1133,7 @@ static void init_task_gen_pcap(struct task_base *tbase, struct task_args *targ)
                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);
+       pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->proto, task->proto_tsc, max_frame_size);
        pcap_close(handle);
 }
 
@@ -1189,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)
@@ -1241,7 +1235,7 @@ static void init_task_gen(struct task_base *tbase, struct task_args *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->capabilities.tx_offload_cksum;
+               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 {
@@ -1286,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");
@@ -1296,13 +1312,14 @@ 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");
@@ -1318,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
@@ -1335,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",
@@ -1350,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