/* * XXQIO * * Copyright 2019 VMS Software, Inc. * * Sends packets through a specified device to a specified MAC address. * If xxqio is running on the target (whether a remote device or a local * device on the the system), it receives each packet and either discards * it or loops it back to the sender. It also does a data compare on the * received data, if requested. * * Multiple initiators can be directed at a single target instance. * * To get help: * * xxqio -h * * Packet data format: * * 31 ... 0 * +---------------------------------+ * | DA | 0 * +----------------+ --+ * | | | 4 * +-- +----------------+ * | SA | 8 * +----------------+----------------+ * | PTY | 12 * +----------------+----------------+ * | | 14 * +-- Sequence Number --+ * | | * +---------------------------------+ * : Packet Data : 22 * : (8-bit counting pattern) : * +---------------------------------+ * * To build: * cc xxqio+xx_support+sys$library:sys$lib_c.tlb/lib * link xxqio */ #include "xx_support.h" /* Packet and transmit defaults */ #define PKT_MIN 46 #define PKT_MAX 9000 #define HDRPLUSCRC (14+4) /* XXQIO compare status */ #define XX_CMP_ERR 1 #define XX_SEQ_ERR 2 /* XXQIO user data buffer */ typedef struct xx_buffer { uint64_t seq_num; uint8_t pk_data[PKT_MAX - 8]; /* PKT_MAX less sequence number */ } XX_BUFFER; /* Statistics */ uint64_t stat_xpk = 0, stat_xby = 0, stat_rpk = 0, stat_rby = 0; uint64_t stat_out = 0, stat_lost = 0, stat_cmp_err = 0, stat_seq_err = 0; uint64_t last_stat_xpk = 0, last_stat_xby = 0, last_stat_rpk = 0, last_stat_rby = 0; uint64_t last_stat_lost = 0, last_stat_cmp_err = 0, last_stat_seq_err = 0; uint64_t interval_stat_xpk, interval_stat_xby, interval_stat_rpk, interval_stat_rby; uint64_t interval_stat_lost, interval_stat_cmp_err, interval_stat_seq_err; double bwx, bwr, bwpx, bwpr, bwxt, bwrt, bwpxt, bwprt; /* Send and receive context */ XX_BUFFER xmt_buffer; XX_BUFFER rcv_buffer; XX_BUFFER exp_buffer; uint32_t xmt_hdr_checksum; int pkt_len = 1000, pkt_max, pkt_mod; uint64_t xmt_seq = 0; /* Sequence number of last packet sent (initiator only) */ uint64_t rcv_seq = 0; /* Sequence number of last packet received (initiator only) */ uint64_t req_transmits = 0; int pipeline = 0; int maxxmt = 0; /* User information */ uint8_t da[6], sa[6], macda[6], macsa[6], pha[6], misc[8]; uint8_t pty[2] = {0x60, 6}; /* 60-06 = user protocol */ uint8_t cur_mac[6]; char cur_mac_str[18]; int got_pha = FALSE; #pragma nomember_alignment struct parm_eth { short pcli_bfn; /* Number of buffers */ int bfn_value; short pcli_bus; /* Maximum buffer size */ int bus_value; short pcli_pad; /* Padding */ int pad_value; short pcli_fmt; /* Packet format */ int fmt_value; short pcli_pty; /* Ethernet Protocol type */ int pty_value; short pcli_mlt; /* All multicast mode */ int mlt_value; short pcli_prm; /* Promiscuous mode */ int prm_value; short pcli_mca; /* Multicast address list */ short mca_len; short mca_func; char mca[6*3]; short pcli_pha; /* Physical address (12 extra bytes) */ short pha_len; short pha_func; char pha[6]; } setparm_eth = {NMA$C_PCLI_BFN, 16, /* Buffer count */ NMA$C_PCLI_BUS, PKT_MAX, /* User buffer size (9018 - Eth header (14) - Pad field (2) - CRC (4) */ NMA$C_PCLI_PAD, NMA$C_STATE_OFF, /* Padded protocol = no */ NMA$C_PCLI_FMT, NMA$C_LINFM_ETH, /* Select format */ NMA$C_PCLI_PTY, 0x0560, /* Protocol xx-xx */ NMA$C_PCLI_MLT, NMA$C_STATE_OFF, /* All multicast mode */ NMA$C_PCLI_PRM, NMA$C_STATE_OFF, /* Promiscuous mode */ NMA$C_PCLI_MCA, 2+18, NMA$C_LINMC_SET, /* One multicast address */ 0xAB, 0, 0, 2, 0, 0, /* AB-00-00-02-00-00 = Target */ 0xAB, 0, 0, 2, 0, 2, /* AB-00-00-02-00-02 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* FF-FF-FF-FF-FF-FF */ NMA$C_PCLI_PHA, 2+6, NMA$C_LINMC_SET, /* One multicast address */ 0xAA, 0, 4, 2, 2, 2}; /* AB-00-00-02-00-00 = Target */ typedef struct setparmdsc { int parm_len; void *parm_buffer; } SETPARMDSC; #define $QIO$STATIC_DSC(name,strname,length) struct dsc$descriptor_s name = { \ length, \ DSC$K_DTYPE_T, \ DSC$K_CLASS_S, \ (char *)strname} typedef struct p5_param { char da[6]; char sa[6]; char misc[20]; } P5_PARAM; P5_PARAM rcv_param; P5_PARAM xmt_param; SETPARMDSC qio_setparmdsc; int qio_chan; /* Channel assigned */ char qio_name[16] = "EWA0"; /* Device under test name */ $QIO$STATIC_DSC(qio_name_dsc,&qio_name,16); /* Device under test name descriptor */ IOSB qio_iosb = {0,0,0,0}; /* QIO status */ /* pty_setup_initiator (client) - Start the user */ void pty_setup_initiator () { int status; /* Initialize startup parameters */ setparm_eth.pty_value = pty[0] + (pty[1] << 8); qio_setparmdsc.parm_len = sizeof(setparm_eth) - ((got_pha) ? 0 : 12); qio_setparmdsc.parm_buffer = &setparm_eth; memcpy(&xmt_param.da[0], (void *)&da[0], 6); memcpy(&xmt_param.sa[0], (void *)&pty[0], 2); /* Assign a channel to the device specified and start up the user */ qio_chan = 0; qio_name_dsc.dsc$a_pointer = (void *)qio_name; qio_name_dsc.dsc$w_length = strlen(qio_name); status = sys$assign(&qio_name_dsc,(unsigned short int *)&qio_chan,0,0); if (status == SS$_NOSUCHDEV) exit(status); status = sys$qiow(0,(unsigned short int)qio_chan, IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP, (struct _iosb *)&qio_iosb,0,0,0, (__int64)&qio_setparmdsc,0,0,0,0); /* Parameter buffer */ if ($SUCCESS(status)) status = qio_iosb.w_err; if ($FAIL(status)) exit(status); } /* pty_setup_target (server) - Start the user */ void pty_setup_target () { int status; /* Initialize startup parameters */ setparm_eth.pty_value = pty[0] + (pty[1] << 8); qio_setparmdsc.parm_len = sizeof(setparm_eth) - ((got_pha) ? 0 : 12); qio_setparmdsc.parm_buffer = &setparm_eth; memcpy(&xmt_param.da[0], (void *)&da[0], 6); memcpy(&xmt_param.sa[0], (void *)&pty[0], 2); /* Assign a channel to the device specified and start up the user */ qio_chan = 0; qio_name_dsc.dsc$a_pointer = (void *)qio_name; qio_name_dsc.dsc$w_length = strlen(qio_name); status = sys$assign(&qio_name_dsc,(unsigned short int *)&qio_chan,0,0); if (status == SS$_NOSUCHDEV) exit(status); status = sys$qiow(0,(unsigned short int)qio_chan, IO$_SETMODE | IO$M_CTRL | IO$M_STARTUP, (struct _iosb *)&qio_iosb,0,0,0, (__int64)&qio_setparmdsc,0,0,0,0); /* Parameter buffer */ if ($SUCCESS(status)) status = qio_iosb.w_err; if ($FAIL(status)) exit(status); } /* pk_echo - Echo packet data for the length specified */ void pk_echo (char *what, XX_BUFFER *buf, int pkt_len) { int echo_len = (pkt_len > echo_size) ? echo_size : pkt_len; uint32_t *pk = (uint32_t *)buf; struct timeval echo_time; int eth = ((misc[0] << 8) | misc[1]) > 1500; int pid = (eth == 0) && (misc[2] == 0xAA) && (misc[3] == 0xAA) && (misc[4] == 0x03); echo_num--; /* Print packet context */ gettimeofday(&echo_time, NULL); if (eth) { if ((misc[0] == pty[0]) && (misc[1] == pty[1])) sprintf(pline, "%lld.%06lld %s, DA %02X-%02X-%02X-%02X-%02X-%02X, SA %02X-%02X-%02X-%02X-%02X-%02X, " "PTY %02X-%02X, Len %d, Seq %llu", echo_time.tv_sec, echo_time.tv_usec, what, macda[0], macda[1], macda[2], macda[3], macda[4], macda[5], macsa[0], macsa[1], macsa[2], macsa[3], macsa[4], macsa[5], misc[0], misc[1], pkt_len, (long long unsigned int)buf->seq_num); else sprintf(pline, "%lld.%06lld %s, DA %02X-%02X-%02X-%02X-%02X-%02X, SA %02X-%02X-%02X-%02X-%02X-%02X, " "PTY %02X-%02X, Len %d", echo_time.tv_sec, echo_time.tv_usec, what, macda[0], macda[1], macda[2], macda[3], macda[4], macda[5], macsa[0], macsa[1], macsa[2], macsa[3], macsa[4], macsa[5], misc[0], misc[1], pkt_len); } else if (pid) sprintf(pline, "%lld.%06lld %s, DA %02X-%02X-%02X-%02X-%02X-%02X, SA %02X-%02X-%02X-%02X-%02X-%02X, " "Len %02X-%02X, PID %02X-%02X-%02X-%02X-%02X, Len %d", echo_time.tv_sec, echo_time.tv_usec, what, macda[0], macda[1], macda[2], macda[3], macda[4], macda[5], macsa[0], macsa[1], macsa[2], macsa[3], macsa[4], macsa[5], misc[0], misc[1], misc[2], misc[3], misc[4], misc[5], misc[6], pkt_len); else sprintf(pline, "%lld.%06lld %s, DA %02X-%02X-%02X-%02X-%02X-%02X, SA %02X-%02X-%02X-%02X-%02X-%02X, " "Len %02X-%02X, DSAP %02X, SSAP %02X, CTL %02X-%02X, Len %d", echo_time.tv_sec, echo_time.tv_usec, what, macda[0], macda[1], macda[2], macda[3], macda[4], macda[5], macsa[0], macsa[1], macsa[2], macsa[3], macsa[4], macsa[5], misc[0], misc[1], misc[2], misc[3], misc[4], misc[5], pkt_len); print_line(pline, pline); /* Print packet contents if specified */ pk_dump_packet(pk, echo_len); } /* pk_comp - Do data compare including sequence number check */ int pk_comp (char *what, XX_BUFFER *buf, int pkt_len, uint64 exp_seq) { uint8_t *pk_exp = (uint8_t *)&exp_buffer; uint8_t *pk_data = (uint8_t *)buf; int err = 0; /* Check for sequence error */ if (exp_seq > buf->seq_num) { sprintf(pline, "%s: Sequence error, expected %llu, actual %llu", what, exp_seq, buf->seq_num); print_line(pline, pline); err |= XX_SEQ_ERR; } exp_buffer.seq_num = buf->seq_num; #if DEBUG_CERR /* Check for debug forcing a compare error */ if (force_cerr_flag && (stat_rpk == force_cerr_flag)) { pk_data[33] ^= 1; force_cerr_flag = 0; } #endif /* Do data compare */ if (memcmp((void *)pk_data, (void *)pk_exp, pkt_len)) { int err_offset; for (err_offset=0; err_offsetseq_num); print_line(pline, pline); err |= XX_CMP_ERR; } if (err) { pk_dump("Actual:", (uint32_t *)pk_data, pkt_len); pk_dump("Expected:", (uint32_t *)pk_exp, pkt_len); } return err; } /* Get mac address */ void get_mac_addr () { int status; int user_desc[2]; /* QIO buffer descriptor */ unsigned char user_buffer[512]; /* Generic IO buffer */ memset(cur_mac, 0, sizeof(cur_mac)); /* Do sensemode to find the MAC address in use */ user_desc[0] = sizeof(user_buffer); user_desc[1] = (int)&user_buffer[0]; status = sys$qiow(0, (unsigned short int)qio_chan, IO$_SENSEMODE|IO$M_CTRL|IO$M_SENSE_MAC, (struct _iosb *)&qio_iosb, 0, 0, 0, (__int64)&user_desc, 0, 0, 0, 0); if ($SUCCESS(status)) { char *buf = (char *)&user_buffer[0]; /* Pointer to next parameter */ int blen = qio_iosb.w_xfer_size; /* Length of remainder of buffer */ int param; /* Parameter in the buffer */ int plen; /* Size of the parameter value */ while (blen > 0) { /* Scan the buffer */ param = *(UNALIGNED_SHORTP)buf; /* Get next parameter in buffer */ buf += 2; /* Skip past the parameter code */ blen -= 2; /* Determine the length of the value of the parameter in the buffer. * Bit 12 tells us if it's a string parameter (bit 12 set) or not * (bit 12 clear). The size of non-string parameters is 4 bytes. * The size of string parameters is in the buffer in the word * following the parameter. */ if ((param & 0x0FFF) == param) plen = 4; else { plen = *(UNALIGNED_SHORTP)buf; buf += 2; blen -= 2; } if ((param & 0xFFF) == NMA$C_PCLI_PHA) { memcpy(cur_mac, buf, 6); break; } buf += plen; blen -= plen; } } sprintf(cur_mac_str, "%02X-%02X-%02X-%02X-%02X-%02X", cur_mac[0], cur_mac[1], cur_mac[2], cur_mac[3], cur_mac[4], cur_mac[5]); } /* Print_banner */ void print_banner (char *test) { uint8 *mca; if (stats_interval == 1000000000) return; /* Wait for start of next second */ cur_time = time(0); while (cur_time == time(0)); /* Get times and set up time for periodic display */ cur_time = time(0); stat_time = cur_time + 1; end_time = cur_time + test_duration; if (end_time < cur_time) end_time = 0xFFFFFFFF; gettimeofday(&begin_stats_time, NULL); last_stats_time = begin_stats_time; cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; strcpy(pline, "------------------------------------------------------------"); print_line(pline, pline); print_line(test, test); print_line(pline, pline); get_mac_addr(); if (is_target) { if (got_pha) { sprintf(pline, "Setting PHA to %02X-%02X-%02X-%02X-%02X-%02X", pha[0], pha[1], pha[2], pha[3], pha[4], pha[5]); print_line(pline, pline); } sprintf(pline, "Receiving on device %s (%s)", qio_name, cur_mac_str); print_line(pline, pline); } else { char req_transmits_str[32]; if (got_pha) { sprintf(pline, "Setting PHA to %02X-%02X-%02X-%02X-%02X-%02X", pha[0], pha[1], pha[2], pha[3], pha[4], pha[5]); print_line(pline, pline); } sprintf(pline, "Sending from device %s (%s) to %02X-%02X-%02X-%02X-%02X-%02X", qio_name, cur_mac_str, da[0], da[1], da[2], da[3], da[4], da[5]); print_line(pline, pline); if (rand_flag) sprintf(pline, "Packet size random from %d to %d bytes (excl MAC header and CRC bytes)", PKT_MIN, pkt_max); else sprintf(pline, "Packet size %d bytes (excl MAC header and CRC bytes)", pkt_len); print_line(pline, pline); if (req_transmits == 0x7FFFFFFFFFFFFFFF) strcpy(req_transmits_str, "gobs"); else sprintf(req_transmits_str, "%llu", req_transmits); if (maxxmt) sprintf(pline, "Packets to send %s, pipeline %d packets (%d pps)", req_transmits_str, pipeline, maxxmt); else sprintf(pline, "Packets to send %s, pipeline %d packets", req_transmits_str, pipeline); print_line(pline, pline); } sprintf(pline, "Protocol type %02X-%02X", pty[0], pty[1]); print_line(pline, pline); mca = (uint8 *)&setparm_eth.mca[0]; sprintf(pline, "MCA %02X-%02X-%02X-%02X-%02X-%02X, %02X-%02X-%02X-%02X-%02X-%02X, " "%02X-%02X-%02X-%02X-%02X-%02X", mca[0], mca[1], mca[2], mca[3], mca[4], mca[5], mca[6], mca[7], mca[8], mca[9], mca[10], mca[11], mca[12], mca[13], mca[14], mca[15], mca[16], mca[17]); print_line(pline, pline); sprintf(pline, "Promiscous mode %s, all-multicast mode %s", (setparm_eth.prm_value == NMA$C_STATE_ON) ? "On" : "Off", (setparm_eth.mlt_value == NMA$C_STATE_ON) ? "On" : "Off"); print_line(pline, pline); if (echo_num && echo_size) { sprintf(pline, "Echo %d bytes of the next %d packets", echo_size, echo_num); print_line(pline, pline); } else if (echo_num) { sprintf(pline, "Echo packet context of the next %d packets", echo_num); print_line(pline, pline); } sprintf(pline, "Dumped packet data is %s", (swap_flag) ? "left-to-right" : "right-to-left"); print_line(pline, pline); sprintf(pline, "Data compare %s", (compare_flag) ? "enabled" : "disabled"); print_line(pline, pline); if (discard_flag) { sprintf(pline, "Receives discarded, not looped back"); print_line(pline, pline); } if (is_initiator) { sprintf(pline, "Bursting %d%s packets", burst_num, rand_burst_flag ? "(random)" : ""); print_line(pline, pline); } if (test_duration != 0x7FFFFFFF) { sprintf(pline, "Test duration %d seconds", test_duration); print_line(pline, pline); } sprintf(pline, "Writing results to %s", log_name); print_line(pline, pline); } /* Calculate stats for printing */ void calc_stats () { /* Get current time and calculate elapsed usec */ cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; gettimeofday(&cur_stats_time, NULL); interval_usec = get_delta(&cur_stats_time, &last_stats_time); total_usec = get_delta(&cur_stats_time, &begin_stats_time); /* Calculate outstanding and lost */ stat_out = xmt_seq - rcv_seq; stat_lost = stat_xpk - stat_rpk - stat_out; /* Calculate interval stats */ interval_stat_xpk = stat_xpk - last_stat_xpk; interval_stat_xby = stat_xby - last_stat_xby; interval_stat_rpk = stat_rpk - last_stat_rpk; interval_stat_rby = stat_rby - last_stat_rby; interval_stat_lost = (stat_lost > last_stat_lost) ? stat_lost - last_stat_lost : 0; interval_stat_cmp_err = stat_cmp_err - last_stat_cmp_err; interval_stat_seq_err = stat_seq_err - last_stat_seq_err; /* Calculate per second values */ bwx = (interval_stat_xby + interval_stat_xpk * HDRPLUSCRC) * 8.0 / interval_usec; bwr = (interval_stat_rby + interval_stat_rpk * HDRPLUSCRC) * 8.0 / interval_usec; bwpx = interval_stat_xpk * 1000000.0 / interval_usec; bwpr = interval_stat_rpk * 1000000.0 / interval_usec; if (((nprint + 1) % HDR_INTERVAL) == 0) { bwxt = (stat_xby + stat_xpk * HDRPLUSCRC) * 8.0 / total_usec; bwrt = (stat_rby + stat_rpk * HDRPLUSCRC) * 8.0 / total_usec; bwpxt = stat_xpk * 1000000.0 / total_usec; bwprt = stat_rpk * 1000000.0 / total_usec; } /* Update last interval values */ last_stat_xpk = stat_xpk; last_stat_xby = stat_xby; last_stat_rpk = stat_rpk; last_stat_rby = stat_rby; last_stat_lost = stat_lost; last_stat_cmp_err = stat_cmp_err; last_stat_seq_err = stat_seq_err; last_stats_time = cur_stats_time; } /* Calculate final stats for printing */ void calc_final_stats () { /* Get current time and calculate elapsed usec */ cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; gettimeofday(&cur_stats_time, NULL); total_usec = get_delta(&cur_stats_time, &begin_stats_time); /* Calculate outstanding and lost */ stat_out = xmt_seq - rcv_seq; stat_lost = stat_xpk - stat_rpk - stat_out; /* Calculate per second values */ bwxt = (stat_xby + stat_xpk * HDRPLUSCRC) * 8.0 / total_usec; bwrt = (stat_rby + stat_rpk * HDRPLUSCRC) * 8.0 / total_usec; bwpxt = stat_xpk * 1000000.0 / total_usec; bwprt = stat_rpk * 1000000.0 / total_usec; } /* Check for ok to transmit, according to pk/sec max (1 if ok) */ int oktoxmt () { double xmtpersec; /* Get current time and calculate elapsed usec */ gettimeofday(&cur_stats_time, NULL); total_usec = get_delta(&cur_stats_time, &begin_stats_time); xmtpersec = stat_xpk / (total_usec / 1000000.0); return xmtpersec < maxxmt; } /* Print stats for the Initiator Test and Initiator/Target Test */ void print_initiator_header (uint64_t total_usec) { if (stats_interval == 1000000000) return; if (total_usec) { total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(""); print_vline(pline); } if (compare_flag) sprintf(pline, " xpk xby rpk rby out lost cerr serr xbs rbs mbs xps rps pps"); else sprintf(pline, " xpk xby rpk rby out lost xbs rbs mbs xps rps pps"); print_vline(""); print_vline(pline); } void print_initiator_stats () { char line[240]; if (stats_interval == 1000000000) return; if ((stat_xpk == last_stat_xpk) && (stat_rpk == last_stat_rpk)) return; calc_stats(); if (nprint == 0) print_initiator_header(0); cvt_value(interval_stat_xpk, &line[0]); cvt_value(interval_stat_xby, &line[8]); cvt_value(interval_stat_rpk, &line[16]); cvt_value(interval_stat_rby, &line[24]); cvt_value(stat_out, &line[32]); cvt_value(interval_stat_lost, &line[40]); if (compare_flag) { cvt_value(interval_stat_cmp_err, &line[48]); cvt_value(interval_stat_seq_err, &line[56]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwx, bwr, bwx+bwr, bwpx, bwpr, bwpx+bwpr); if (verbose_flag) print_line(pline, pline); else { sprintf(line, "xxxxxxxx BytesSnt, xxxxxxxx BytesRcv, %8.0f Total MBits/Sec", bwx+bwr); cvt_value(stat_xby, &line[0]); line[8] = ' '; cvt_value(stat_rby, &line[19]); line[27] = ' '; print_line(line, pline); } if ((((nprint + 1) % HDR_INTERVAL) == 0) && nprint) { print_initiator_header(get_delta(&cur_stats_time, &begin_stats_time)); if (nprint) { cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); cvt_value(stat_out, &line[32]); cvt_value(stat_lost, &line[40]); if (compare_flag) { cvt_value(stat_cmp_err, &line[48]); cvt_value(stat_seq_err, &line[56]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_initiator_header(0); } } nprint++; } /* Print last stats for the Initiator Test */ void print_final_initiator_stats () { char line[240]; if (stats_interval == 1000000000) return; calc_final_stats(); print_initiator_header(0); cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); cvt_value(stat_out, &line[32]); cvt_value(stat_lost, &line[40]); if (compare_flag) { cvt_value(stat_cmp_err, &line[48]); cvt_value(stat_seq_err, &line[56]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_vline(""); total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(pline); if ((pipeline == 1) && is_initiator && (bwpxt || bwprt)) { sprintf(pline, "Average latency (one direction) %8.2f usec", 1000000.0 / (bwpxt+bwprt)); print_vline(pline); } } /* Print stats for the Target Test */ void print_target_header (uint64_t total_usec) { if (stats_interval == 1000000000) return; if (total_usec) { total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(""); print_vline(pline); } if (compare_flag) sprintf(pline, " xpk xby rpk rby cerr xbs rbs mbs xps rps pps"); else sprintf(pline, " xpk xby rpk rby xbs rbs mbs xps rps pps"); print_vline(""); print_vline(pline); } void print_target_stats () { char line[240]; if (stats_interval == 1000000000) return; if ((stat_xpk == last_stat_xpk) && (stat_rpk == last_stat_rpk)) return; calc_stats(); if (nprint == 0) print_target_header(0); cvt_value(interval_stat_xpk, &line[0]); cvt_value(interval_stat_xby, &line[8]); cvt_value(interval_stat_rpk, &line[16]); cvt_value(interval_stat_rby, &line[24]); if (compare_flag) cvt_value(interval_stat_cmp_err, &line[32]); sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwx, bwr, bwx+bwr, bwpx, bwpr, bwpx+bwpr); if (verbose_flag) print_line(pline, pline); else { sprintf(line, "xxxxxxxx BytesSnt, xxxxxxxx BytesRcv, %8.0f Total MBits/Sec", bwx+bwr); cvt_value(stat_xby, &line[0]); line[8] = ' '; cvt_value(stat_rby, &line[19]); line[27] = ' '; print_line(line, pline); } if ((((nprint + 1) % HDR_INTERVAL) == 0) && nprint) { print_target_header(get_delta(&cur_stats_time, &begin_stats_time)); if (nprint) { cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); if (compare_flag) cvt_value(stat_cmp_err, &line[32]); sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_target_header(0); } } nprint++; } /* Print last stats for the Target Test */ void print_final_target_stats () { char line[240]; if (stats_interval == 1000000000) return; calc_final_stats(); print_target_header(0); cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); if (compare_flag) cvt_value(stat_cmp_err, &line[32]); sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_vline(""); total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(pline); } /* do_target_test: * * while (1) * - Receive packet * - Data compare if requested * - Loop back if requested * - Print stats periodically */ void do_target_test () { int status, rx_len, tx_len, addr_len, err; /* Set up target device */ pty_setup_target(); /* Display test parameters */ print_banner("Target Test"); /* Loop forever, receiving */ while (1) { rx_len = 0; status = sys$qiow(0,(unsigned short int)qio_chan, IO$_READVBLK | IO$M_NOW, (struct _iosb *)&qio_iosb,0,0, &rcv_buffer, /* Receive buffer */ PKT_MAX,0,0, /* Receive length */ (__int64)&rcv_param,0); /* Receive header */ if ($SUCCESS(status)) status = qio_iosb.w_err; if ($SUCCESS(status)) rx_len = qio_iosb.w_xfer_size; /* If data received */ if (rx_len > 0) { if (echo_num) { memcpy(&macda[0], &rcv_param.da[0], 6); memcpy(&macsa[0], &rcv_param.sa[0], 6); memcpy(&misc[0], &rcv_param.misc[0], 7); pk_echo("Rcv", &rcv_buffer, rx_len); } if (compare_flag && (err = pk_comp("Rcv", &rcv_buffer, rx_len, 0))) { if (err & XX_CMP_ERR) stat_cmp_err++; } stat_rpk++; stat_rby += rx_len; req_transmits--; xmt_seq = rcv_seq = 0; /* Target test doesn't check sequence numbers */ /* Loop back the packet if enabled */ if (discard_flag == FALSE) { memcpy(&xmt_param.da[0], &rcv_param.sa[0], 6); memcpy(&xmt_param.sa[0], (void *)&pty[0], 2); status = sys$qiow(0, (unsigned short int)qio_chan, IO$_WRITEVBLK, (struct _iosb *)&qio_iosb, 0, 0, &rcv_buffer, /* Transmit buffer */ rx_len, 0, 0, /* Transmit length */ (__int64)&xmt_param, 0); /* Transmit header */ if ($SUCCESS(status)) status = qio_iosb.w_err; /* If successful send */ if ($SUCCESS(status)) { if (echo_num) { memcpy(&macda[0], &xmt_param.da[0], 6); memcpy(&macsa[0], &cur_mac[0], 6); misc[0] = pty[0]; misc[1] = pty[1]; pk_echo("Xmt", &rcv_buffer, rx_len); } stat_xpk++; stat_xby += rx_len; /* Error */ } else break; } } /* Print periodic stats */ cur_time = time(0); if ((req_transmits == 0) || (end_time <= cur_time)) goto done; if ((stat_time <= cur_time) || override_interval_flag) { override_interval_flag = 0; print_target_stats(); stat_time = cur_time + stats_interval; } } done: /* Do final stats */ print_target_stats(); print_final_target_stats(); } /* do_initiator_test: * * while (1) * - Send packet on initiator device to target address * - While receives available: * - Receive packet on initiator device * - Data compare if requested * - Print stats periodically */ void do_initiator_test () { int status, rx_len, tx_len, addr_len, err, burst; /* Set up initiator device */ pty_setup_initiator(); /* Display test parameters */ print_banner("Initiator Test"); /* Loop till no more packets to send */ while (req_transmits) { if ((maxxmt == 0) || oktoxmt()) { GEN_BURST; for (burst=0; burst PKT_MAX) pkt_len = PKT_MAX; if (pkt_len < PKT_MIN) pkt_len = PKT_MIN; break; case 'p': { uint32_t p1, p2; sscanf(optarg, "%02X-%02X", &p1, &p2); pty[0] = p1; pty[1] = p2; break; } case 't': { uint32_t d0, d1, d2, d3, d4, d5; if (sscanf(optarg, "%02X-%02X-%02X-%02X-%02X-%02X", &d0, &d1, &d2, &d3, &d4, &d5) < 6) if (sscanf(optarg, "%02X:%02X:%02X:%02X:%02X:%02X", &d0, &d1, &d2, &d3, &d4, &d5) < 6) { d0 = 0xAB; d1 = d2 = d4 = d5 = 0; d3 = 2; } da[0] = d0; da[1] = d1; da[2] = d2; da[3] = d3; da[4] = d4; da[5] = d5; is_initiator = TRUE; is_target = FALSE; break; } case 'x': setparm_eth.mlt_value = NMA$C_STATE_ON; break; case 'y': setparm_eth.prm_value = NMA$C_STATE_ON; break; } } /* Set maximum packet size for random setting */ pkt_max = pkt_len; pkt_mod = 1; while (pkt_mod < (pkt_max - PKT_MIN)) pkt_mod <<= 1; pkt_mod--; /* Set maximum burst number for random setting */ burst_max = burst_num; burst_mod = 1; while (burst_mod < burst_max) burst_mod <<= 1; burst_mod--; /* Check if we should run forever */ if (req_transmits == 0) req_transmits = 0x7FFFFFFFFFFFFFFF; /* Initialize buffer for data compares */ for (i=0; i