Commit eb68db45 authored by Ong Boon Leong's avatar Ong Boon Leong Committed by Alexei Starovoitov

samples/bpf: xdpsock: Add timestamp for Tx-only operation

It may be useful to add timestamp for Tx packets for continuous or cyclic
transmit operation. The timestamp and sequence ID of a Tx packet are
stored according to pktgen header format. To enable per-packet timestamp,
use -y|--tstamp option. If timestamp is off, pktgen header is not
included in the UDP payload. This means receiving side can use the magic
number for pktgen for differentiation.

The implementation supports both VLAN tagged and untagged option. By
default, the minimum packet size is set at 64B. However, if VLAN tagged
is on (-V), the minimum packet size is increased to 66B just so to fit
the pktgen_hdr size.

Added hex_dump() into the code path just for future cross-checking.
As before, simply change to "#define DEBUG_HEXDUMP 1" to inspect the
accuracy of TX packet.
Signed-off-by: default avatarOng Boon Leong <boon.leong.ong@intel.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211230035447.523177-8-boon.leong.ong@intel.com
parent 8121e789
...@@ -111,6 +111,7 @@ static bool opt_app_stats; ...@@ -111,6 +111,7 @@ static bool opt_app_stats;
static const char *opt_irq_str = ""; static const char *opt_irq_str = "";
static u32 irq_no; static u32 irq_no;
static int irqs_at_init = -1; static int irqs_at_init = -1;
static u32 sequence;
static int opt_poll; static int opt_poll;
static int opt_interval = 1; static int opt_interval = 1;
static int opt_retries = 3; static int opt_retries = 3;
...@@ -129,6 +130,7 @@ static clockid_t opt_clock = CLOCK_MONOTONIC; ...@@ -129,6 +130,7 @@ static clockid_t opt_clock = CLOCK_MONOTONIC;
static unsigned long opt_tx_cycle_ns; static unsigned long opt_tx_cycle_ns;
static int opt_schpolicy = SCHED_OTHER; static int opt_schpolicy = SCHED_OTHER;
static int opt_schprio = SCHED_PRI__DEFAULT; static int opt_schprio = SCHED_PRI__DEFAULT;
static bool opt_tstamp;
struct vlan_ethhdr { struct vlan_ethhdr {
unsigned char h_dest[6]; unsigned char h_dest[6];
...@@ -138,6 +140,14 @@ struct vlan_ethhdr { ...@@ -138,6 +140,14 @@ struct vlan_ethhdr {
__be16 h_vlan_encapsulated_proto; __be16 h_vlan_encapsulated_proto;
}; };
#define PKTGEN_MAGIC 0xbe9be955
struct pktgen_hdr {
__be32 pgh_magic;
__be32 seq_num;
__be32 tv_sec;
__be32 tv_usec;
};
struct xsk_ring_stats { struct xsk_ring_stats {
unsigned long rx_npkts; unsigned long rx_npkts;
unsigned long tx_npkts; unsigned long tx_npkts;
...@@ -836,18 +846,25 @@ static inline u16 udp_csum(u32 saddr, u32 daddr, u32 len, ...@@ -836,18 +846,25 @@ static inline u16 udp_csum(u32 saddr, u32 daddr, u32 len,
#define ETH_HDR_SIZE (opt_vlan_tag ? sizeof(struct vlan_ethhdr) : \ #define ETH_HDR_SIZE (opt_vlan_tag ? sizeof(struct vlan_ethhdr) : \
sizeof(struct ethhdr)) sizeof(struct ethhdr))
#define PKTGEN_HDR_SIZE (opt_tstamp ? sizeof(struct pktgen_hdr) : 0)
#define PKT_HDR_SIZE (ETH_HDR_SIZE + sizeof(struct iphdr) + \ #define PKT_HDR_SIZE (ETH_HDR_SIZE + sizeof(struct iphdr) + \
sizeof(struct udphdr)) sizeof(struct udphdr) + PKTGEN_HDR_SIZE)
#define PKTGEN_HDR_OFFSET (ETH_HDR_SIZE + sizeof(struct iphdr) + \
sizeof(struct udphdr))
#define PKTGEN_SIZE_MIN (PKTGEN_HDR_OFFSET + sizeof(struct pktgen_hdr) + \
ETH_FCS_SIZE)
#define PKT_SIZE (opt_pkt_size - ETH_FCS_SIZE) #define PKT_SIZE (opt_pkt_size - ETH_FCS_SIZE)
#define IP_PKT_SIZE (PKT_SIZE - ETH_HDR_SIZE) #define IP_PKT_SIZE (PKT_SIZE - ETH_HDR_SIZE)
#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr)) #define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr))
#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - sizeof(struct udphdr)) #define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - \
(sizeof(struct udphdr) + PKTGEN_HDR_SIZE))
static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE]; static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE];
static void gen_eth_hdr_data(void) static void gen_eth_hdr_data(void)
{ {
struct pktgen_hdr *pktgen_hdr;
struct udphdr *udp_hdr; struct udphdr *udp_hdr;
struct iphdr *ip_hdr; struct iphdr *ip_hdr;
...@@ -860,7 +877,10 @@ static void gen_eth_hdr_data(void) ...@@ -860,7 +877,10 @@ static void gen_eth_hdr_data(void)
sizeof(struct iphdr)); sizeof(struct iphdr));
ip_hdr = (struct iphdr *)(pkt_data + ip_hdr = (struct iphdr *)(pkt_data +
sizeof(struct vlan_ethhdr)); sizeof(struct vlan_ethhdr));
pktgen_hdr = (struct pktgen_hdr *)(pkt_data +
sizeof(struct vlan_ethhdr) +
sizeof(struct iphdr) +
sizeof(struct udphdr));
/* ethernet & VLAN header */ /* ethernet & VLAN header */
memcpy(veth_hdr->h_dest, &opt_txdmac, ETH_ALEN); memcpy(veth_hdr->h_dest, &opt_txdmac, ETH_ALEN);
memcpy(veth_hdr->h_source, &opt_txsmac, ETH_ALEN); memcpy(veth_hdr->h_source, &opt_txsmac, ETH_ALEN);
...@@ -877,7 +897,10 @@ static void gen_eth_hdr_data(void) ...@@ -877,7 +897,10 @@ static void gen_eth_hdr_data(void)
sizeof(struct iphdr)); sizeof(struct iphdr));
ip_hdr = (struct iphdr *)(pkt_data + ip_hdr = (struct iphdr *)(pkt_data +
sizeof(struct ethhdr)); sizeof(struct ethhdr));
pktgen_hdr = (struct pktgen_hdr *)(pkt_data +
sizeof(struct ethhdr) +
sizeof(struct iphdr) +
sizeof(struct udphdr));
/* ethernet header */ /* ethernet header */
memcpy(eth_hdr->h_dest, &opt_txdmac, ETH_ALEN); memcpy(eth_hdr->h_dest, &opt_txdmac, ETH_ALEN);
memcpy(eth_hdr->h_source, &opt_txsmac, ETH_ALEN); memcpy(eth_hdr->h_source, &opt_txsmac, ETH_ALEN);
...@@ -906,6 +929,9 @@ static void gen_eth_hdr_data(void) ...@@ -906,6 +929,9 @@ static void gen_eth_hdr_data(void)
udp_hdr->dest = htons(0x1000); udp_hdr->dest = htons(0x1000);
udp_hdr->len = htons(UDP_PKT_SIZE); udp_hdr->len = htons(UDP_PKT_SIZE);
if (opt_tstamp)
pktgen_hdr->pgh_magic = htonl(PKTGEN_MAGIC);
/* UDP data */ /* UDP data */
memset32_htonl(pkt_data + PKT_HDR_SIZE, opt_pkt_fill_pattern, memset32_htonl(pkt_data + PKT_HDR_SIZE, opt_pkt_fill_pattern,
UDP_PKT_DATA_SIZE); UDP_PKT_DATA_SIZE);
...@@ -1049,6 +1075,7 @@ static struct option long_options[] = { ...@@ -1049,6 +1075,7 @@ static struct option long_options[] = {
{"tx-dmac", required_argument, 0, 'G'}, {"tx-dmac", required_argument, 0, 'G'},
{"tx-smac", required_argument, 0, 'H'}, {"tx-smac", required_argument, 0, 'H'},
{"tx-cycle", required_argument, 0, 'T'}, {"tx-cycle", required_argument, 0, 'T'},
{"tstamp", no_argument, 0, 'y'},
{"policy", required_argument, 0, 'W'}, {"policy", required_argument, 0, 'W'},
{"schpri", required_argument, 0, 'U'}, {"schpri", required_argument, 0, 'U'},
{"extra-stats", no_argument, 0, 'x'}, {"extra-stats", no_argument, 0, 'x'},
...@@ -1099,6 +1126,7 @@ static void usage(const char *prog) ...@@ -1099,6 +1126,7 @@ static void usage(const char *prog)
" -G, --tx-dmac=<MAC> Dest MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n" " -G, --tx-dmac=<MAC> Dest MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n"
" -H, --tx-smac=<MAC> Src MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n" " -H, --tx-smac=<MAC> Src MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n"
" -T, --tx-cycle=n Tx cycle time in micro-seconds (For -t|--txonly).\n" " -T, --tx-cycle=n Tx cycle time in micro-seconds (For -t|--txonly).\n"
" -y, --tstamp Add time-stamp to packet (For -t|--txonly).\n"
" -W, --policy=POLICY Schedule policy. Default: SCHED_OTHER\n" " -W, --policy=POLICY Schedule policy. Default: SCHED_OTHER\n"
" -U, --schpri=n Schedule priority. Default: %d\n" " -U, --schpri=n Schedule priority. Default: %d\n"
" -x, --extra-stats Display extra statistics.\n" " -x, --extra-stats Display extra statistics.\n"
...@@ -1125,7 +1153,7 @@ static void parse_command_line(int argc, char **argv) ...@@ -1125,7 +1153,7 @@ static void parse_command_line(int argc, char **argv)
for (;;) { for (;;) {
c = getopt_long(argc, argv, c = getopt_long(argc, argv,
"Frtli:q:pSNn:w:O:czf:muMd:b:C:s:P:VJ:K:G:H:T:W:U:xQaI:BR", "Frtli:q:pSNn:w:O:czf:muMd:b:C:s:P:VJ:K:G:H:T:yW:U:xQaI:BR",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
...@@ -1246,6 +1274,9 @@ static void parse_command_line(int argc, char **argv) ...@@ -1246,6 +1274,9 @@ static void parse_command_line(int argc, char **argv)
opt_tx_cycle_ns = atoi(optarg); opt_tx_cycle_ns = atoi(optarg);
opt_tx_cycle_ns *= NSEC_PER_USEC; opt_tx_cycle_ns *= NSEC_PER_USEC;
break; break;
case 'y':
opt_tstamp = 1;
break;
case 'W': case 'W':
if (get_schpolicy(&opt_schpolicy, optarg)) { if (get_schpolicy(&opt_schpolicy, optarg)) {
fprintf(stderr, fprintf(stderr,
...@@ -1462,9 +1493,10 @@ static void rx_drop_all(void) ...@@ -1462,9 +1493,10 @@ static void rx_drop_all(void)
} }
} }
static int tx_only(struct xsk_socket_info *xsk, u32 *frame_nb, int batch_size) static int tx_only(struct xsk_socket_info *xsk, u32 *frame_nb,
int batch_size, unsigned long tx_ns)
{ {
u32 idx; u32 idx, tv_sec, tv_usec;
unsigned int i; unsigned int i;
while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) <
...@@ -1474,11 +1506,31 @@ static int tx_only(struct xsk_socket_info *xsk, u32 *frame_nb, int batch_size) ...@@ -1474,11 +1506,31 @@ static int tx_only(struct xsk_socket_info *xsk, u32 *frame_nb, int batch_size)
return 0; return 0;
} }
if (opt_tstamp) {
tv_sec = (u32)(tx_ns / NSEC_PER_SEC);
tv_usec = (u32)((tx_ns % NSEC_PER_SEC) / 1000);
}
for (i = 0; i < batch_size; i++) { for (i = 0; i < batch_size; i++) {
struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx,
idx + i); idx + i);
tx_desc->addr = (*frame_nb + i) * opt_xsk_frame_size; tx_desc->addr = (*frame_nb + i) * opt_xsk_frame_size;
tx_desc->len = PKT_SIZE; tx_desc->len = PKT_SIZE;
if (opt_tstamp) {
struct pktgen_hdr *pktgen_hdr;
u64 addr = tx_desc->addr;
char *pkt;
pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
pktgen_hdr = (struct pktgen_hdr *)(pkt + PKTGEN_HDR_OFFSET);
pktgen_hdr->seq_num = htonl(sequence++);
pktgen_hdr->tv_sec = htonl(tv_sec);
pktgen_hdr->tv_usec = htonl(tv_usec);
hex_dump(pkt, PKT_SIZE, addr);
}
} }
xsk_ring_prod__submit(&xsk->tx, batch_size); xsk_ring_prod__submit(&xsk->tx, batch_size);
...@@ -1552,6 +1604,7 @@ static void tx_only_all(void) ...@@ -1552,6 +1604,7 @@ static void tx_only_all(void)
while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) { while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
int batch_size = get_batch_size(pkt_cnt); int batch_size = get_batch_size(pkt_cnt);
unsigned long tx_ns = 0;
struct timespec next; struct timespec next;
int tx_cnt = 0; int tx_cnt = 0;
long diff; long diff;
...@@ -1581,7 +1634,8 @@ static void tx_only_all(void) ...@@ -1581,7 +1634,8 @@ static void tx_only_all(void)
} }
/* Measure periodic Tx scheduling variance */ /* Measure periodic Tx scheduling variance */
diff = get_nsecs() - next_tx_ns; tx_ns = get_nsecs();
diff = tx_ns - next_tx_ns;
if (diff < tx_cycle_diff_min) if (diff < tx_cycle_diff_min)
tx_cycle_diff_min = diff; tx_cycle_diff_min = diff;
...@@ -1590,10 +1644,12 @@ static void tx_only_all(void) ...@@ -1590,10 +1644,12 @@ static void tx_only_all(void)
tx_cycle_diff_ave += (double)diff; tx_cycle_diff_ave += (double)diff;
tx_cycle_cnt++; tx_cycle_cnt++;
} else if (opt_tstamp) {
tx_ns = get_nsecs();
} }
for (i = 0; i < num_socks; i++) for (i = 0; i < num_socks; i++)
tx_cnt += tx_only(xsks[i], &frame_nb[i], batch_size); tx_cnt += tx_only(xsks[i], &frame_nb[i], batch_size, tx_ns);
pkt_cnt += tx_cnt; pkt_cnt += tx_cnt;
...@@ -1895,6 +1951,9 @@ int main(int argc, char **argv) ...@@ -1895,6 +1951,9 @@ int main(int argc, char **argv)
apply_setsockopt(xsks[i]); apply_setsockopt(xsks[i]);
if (opt_bench == BENCH_TXONLY) { if (opt_bench == BENCH_TXONLY) {
if (opt_tstamp && opt_pkt_size < PKTGEN_SIZE_MIN)
opt_pkt_size = PKTGEN_SIZE_MIN;
gen_eth_hdr_data(); gen_eth_hdr_data();
for (i = 0; i < NUM_FRAMES; i++) for (i = 0; i < NUM_FRAMES; i++)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment