PDP11, VAX: Add logic to receive buffer descriptor processing to avoid list overrun.

If a driver sets up a receive buffer descriptor list as a circular ring, the
potential exists for a burst of arriving packets to wrap around the receive
buffer ring in a single round of input processing.  This is avoided by
stopping list processing when a full circle has been observed.

Additionally, debug output has been added to display issues while
processing both the receive buffer descriptor list and the transmit buffer
descriptor list.  The debug bit names are RBDL and XBDL respectively.
This commit is contained in:
Mark Pizzolato 2016-01-20 11:13:28 -08:00
parent 3a88a1d812
commit 2459a4c697
2 changed files with 46 additions and 19 deletions

View file

@ -521,6 +521,8 @@ DEBTAB xq_debug[] = {
{"CSR", DBG_CSR, "watch CSR"},
{"VAR", DBG_VAR, "watch VAR"},
{"WARN", DBG_WRN, "display warnings"},
{"RBDL", DBG_RBL, "display RBDL warnings"},
{"XBDL", DBG_XBL, "display XBDL warnings"},
{"SETUP", DBG_SET, "display setup info"},
{"SANITY", DBG_SAN, "display sanity timer info"},
{"REG", DBG_REG, "trace read/write registers"},
@ -724,6 +726,7 @@ t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc)
fprintf(st, fmt, "SW Reset:", xq->var->stats.reset);
fprintf(st, fmt, "Setup:", xq->var->stats.setup);
fprintf(st, fmt, "Loopback:", xq->var->stats.loop);
fprintf(st, fmt, "Recv Overrun:",xq->var->stats.recv_overrun);
fprintf(st, fmt, "ReadQ count:", xq->var->ReadQ.count);
fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high);
eth_show_dev(st, xq->var->etherface);
@ -1090,7 +1093,8 @@ t_stat xq_process_rbdl(CTLR* xq)
{
int32 rstatus, wstatus;
uint16 b_length, w_length, rbl;
uint32 address;
uint32 address, start_rbdl_ba;
int dcount;
ETH_ITEM* item;
uint8* rbuf;
@ -1102,6 +1106,9 @@ t_stat xq_process_rbdl(CTLR* xq)
if (xq->var->csr & XQ_CSR_RL)
return SCPE_OK;
start_rbdl_ba = xq->var->rbdl_ba;
dcount = 0;
/* process buffer descriptors */
while(1) {
@ -1112,6 +1119,15 @@ t_stat xq_process_rbdl(CTLR* xq)
/* DEQNA stops processing if nothing in read queue */
if ((xq->var->type == XQ_T_DEQNA) && (!xq->var->ReadQ.count)) break;
/* if all descriptors have been processed, avoid overrun and stop now */
/* this only happens if the receive descriptors are setup in a circular loop */
if (dcount && (xq->var->rbdl_ba == start_rbdl_ba)) {
++xq->var->stats.recv_overrun;
sim_debug(DBG_RBL, xq->dev, "RBDL Processed all %d descriptors, avoiding overrun\n", dcount);
break;
}
++dcount;
/* set descriptor processed flag */
xq->var->rbdl_buf[0] = 0xFFFF;
wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]);
@ -1154,6 +1170,13 @@ t_stat xq_process_rbdl(CTLR* xq)
sim_debug(DBG_TRC, xq->dev, "Using receive descriptor=0x%X, flags=0x%04X, bits=0x%04X, addr=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X\n",
xq->var->rbdl_ba, xq->var->rbdl_buf[0], xq->var->rbdl_buf[1] & 0xFFC0, address, b_length, xq->var->rbdl_buf[4], xq->var->rbdl_buf[5]);
/* Examine the descriptor to try and determine if any prior contents haven't been 'digested' yet */
if (((xq->var->rbdl_buf[4] & 0xC000) != 0x8000) ||
((xq->var->rbdl_buf[5] & 0xFF) == (((xq->var->rbdl_buf[5] >> 8) & 0xFF)))) {
sim_debug(DBG_TRC, xq->dev, "Undigested receive descriptor=0x%X, flags=0x%04X, bits=0x%04X, addr=0x%X, len=0x%X, st1=0x%04X, st2=0x%04X\n",
xq->var->rbdl_ba, xq->var->rbdl_buf[0], xq->var->rbdl_buf[1] & 0xFFC0, address, b_length, xq->var->rbdl_buf[4], xq->var->rbdl_buf[5]);
}
item = &xq->var->ReadQ.item[xq->var->ReadQ.head];
rbl = (uint16)item->packet.len;
rbuf = item->packet.msg;
@ -1172,7 +1195,7 @@ t_stat xq_process_rbdl(CTLR* xq)
packets sent by the host diagnostics (OR short setup packets) */
if ((item->type == ETH_ITM_NORMAL) && (rbl < ETH_MIN_PACKET)) {
xq->var->stats.runt += 1;
sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl);
sim_debug(DBG_RBL, xq->dev, "Runt detected, size = %d\n", rbl);
/* pad runts with zeros up to minimum size - this allows "legal" (size - 60)
processing of those weird short ARP packets that seem to occur occasionally */
memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl);
@ -1182,7 +1205,7 @@ t_stat xq_process_rbdl(CTLR* xq)
/* adjust oversized non-loopback packets */
if ((item->type != ETH_ITM_LOOPBACK) && (rbl > ETH_FRAME_SIZE)) {
xq->var->stats.giant += 1;
sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl);
sim_debug(DBG_RBL, xq->dev, "Giant detected, size=%d\n", rbl);
/* trim giants down to maximum size - no documentation on how to handle the data loss */
if (rbl > XQ_MAX_RCV_PACKET) {
item->packet.len = XQ_MAX_RCV_PACKET;
@ -1238,7 +1261,7 @@ t_stat xq_process_rbdl(CTLR* xq)
xq->var->rbdl_buf[4] |= XQ_RST_LASTNOT; /* not last segment */
xq->var->rbdl_buf[5] = ((rbl & 0x00FF) << 8) | (rbl & 0x00FF);
if (xq->var->ReadQ.loss) {
sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n");
sim_debug(DBG_RBL, xq->dev, "ReadQ overflow!\n");
xq->var->rbdl_buf[4] |= XQ_RST_OVERFLOW; /* set overflow bit */
xq->var->stats.dropped += xq->var->ReadQ.loss;
xq->var->ReadQ.loss = 0; /* reset loss counter */
@ -1485,14 +1508,14 @@ t_stat xq_process_xbdl(CTLR* xq)
/* explicit chain buffer? */
if (xq->var->xbdl_buf[1] & XQ_DSC_C) {
xq->var->xbdl_ba = address;
sim_debug(DBG_WRN, xq->dev, "XBDL chaining to buffer descriptor at: 0x%X\n", address);
sim_debug(DBG_XBL, xq->dev, "Chaining to buffer descriptor at: 0x%X\n", address);
continue;
}
/* invalid buffer? */
if (~xq->var->xbdl_buf[1] & XQ_DSC_V) {
xq_csr_set_clr(xq, XQ_CSR_XL, 0);
sim_debug(DBG_WRN, xq->dev, "XBDL List empty\n");
sim_debug(DBG_XBL, xq->dev, "List empty\n");
return SCPE_OK;
}
@ -1560,13 +1583,13 @@ t_stat xq_process_xbdl(CTLR* xq)
if (xq->var->coalesce_latency == 0)
xq_svc(&xq->unit[0]); /* service any received data */
}
sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n");
sim_debug(DBG_XBL, xq->dev, "completed processing write\n");
} /* loopback/non-loopback */
} else { /* not at end-of-message */
sim_debug(DBG_WRN, xq->dev, "XBDL implicitly chaining to buffer descriptor at: 0x%X\n", xq->var->xbdl_ba+12);
sim_debug(DBG_XBL, xq->dev, "implicitly chaining to buffer descriptor at: 0x%X\n", xq->var->xbdl_ba+12);
/* update bdl status words */
wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, (uint16*) implicit_chain_status);
if(wstatus) return xq_nxm_error(xq);
@ -1744,7 +1767,7 @@ t_stat xq_process_turbo_rbdl(CTLR* xq)
/* adjust non loopback runt packets */
if ((item->type != ETH_ITM_LOOPBACK) && (rbl < ETH_MIN_PACKET)) {
xq->var->stats.runt += 1;
sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl);
sim_debug(DBG_RBL, xq->dev, "Runt detected, size = %d\n", rbl);
/* pad runts with zeros up to minimum size - this allows "legal" (size - 60)
processing of those weird short ARP packets that seem to occur occasionally */
memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl);
@ -1754,7 +1777,7 @@ t_stat xq_process_turbo_rbdl(CTLR* xq)
/* adjust oversized non-loopback packets */
if ((item->type != ETH_ITM_LOOPBACK) && (rbl > ETH_FRAME_SIZE)) {
xq->var->stats.giant += 1;
sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl);
sim_debug(DBG_RBL, xq->dev, "Giant detected, size=%d\n", rbl);
/* trim giants down to maximum size - no documentation on how to handle the data loss */
item->packet.len = ETH_MAX_PACKET;
rbl = ETH_FRAME_SIZE;
@ -1783,7 +1806,7 @@ t_stat xq_process_turbo_rbdl(CTLR* xq)
if (xq->var->ReadQ.loss) {
xq->var->rring[i].rmd2 |= XQ_RMD2_MIS;
sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n");
sim_debug(DBG_RBL, xq->dev, "ReadQ overflow!\n");
xq->var->stats.dropped += xq->var->ReadQ.loss;
xq->var->ReadQ.loss = 0; /* reset loss counter */
}
@ -1807,7 +1830,7 @@ t_stat xq_process_turbo_rbdl(CTLR* xq)
} while (0 == (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN));
if (xq->var->rring[xq->var->rbindx].rmd3 & XQ_RMD3_OWN) {
sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_rbdl() - receive ring full\n");
sim_debug(DBG_RBL, xq->dev, "xq_process_turbo_rbdl() - receive ring full\n");
}
if (descriptors_consumed)
@ -1894,7 +1917,7 @@ t_stat xq_process_turbo_xbdl(CTLR* xq)
xq->var->xring[i].tmd0 = 0;
xq->var->xring[i].tmd1 = (uint16)(100 + xq->var->write_buffer.len * 8); /* arbitrary value */
}
sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n");
sim_debug(DBG_XBL, xq->dev, "completed processing write\n");
/* clear transmit buffer */
xq->var->write_buffer.len = 0;
xq->var->xring[i].tmd2 = XQ_TMD2_RON | XQ_TMD2_TON;
@ -1931,7 +1954,7 @@ t_stat xq_process_turbo_xbdl(CTLR* xq)
and finding nothing to do. We ignore this and the next write the ARQR will
properly cause the packet transmission.
*/
sim_debug(DBG_WRN, xq->dev, "xq_process_turbo_xbdl() - Nothing to Transmit\n");
sim_debug(DBG_XBL, xq->dev, "xq_process_turbo_xbdl() - Nothing to Transmit\n");
}
return status;
@ -2475,7 +2498,6 @@ t_stat xq_wr(int32 ldata, int32 PA, int32 access)
break;
case 3: /* receive bdl high bits */
xq->var->rbdl[1] = data;
xq_csr_set_clr(xq, 0, XQ_CSR_RL);
xq_dispatch_rbdl(xq); /* start receive operation */
break;
case 4: /* transmit bdl low bits */
@ -3245,6 +3267,8 @@ const char helpString[] =
"++TRACE Shows detailed routine calls.\n"
"++CSR Shows activities affecting the CSR.\n"
"++VAR Shows activities affecting the VAR.\n"
"++RBL Shows receive list warnings.\n"
"++XBL Shows transmit list warnings.\n"
"++WARN Shows warnings.\n"
"++SETUP Shows setup info.\n"
"++SANITY Shows sanity timer info.\n"

View file

@ -211,6 +211,7 @@ struct xq_stats {
int giant; /* oversize packets */
int setup; /* setup packets */
int loop; /* loopback packets */
int recv_overrun; /* receiver overruns */
};
#pragma pack(2)
@ -434,10 +435,12 @@ typedef struct xq_controller CTLR;
#define DBG_CSR 0x0004 /* watch CSR */
#define DBG_VAR 0x0008 /* watch VAR */
#define DBG_WRN 0x0010 /* display warnings */
#define DBG_SAN 0x0020 /* display sanity timer info */
#define DBG_SET 0x0040 /* display setup info */
#define DBG_PCK 0x0080 /* display packet headers */
#define DBG_DAT 0x0100 /* display packet data */
#define DBG_RBL 0x0020 /* RBDL issues */
#define DBG_XBL 0x0040 /* XBDL issues */
#define DBG_SAN 0x0080 /* display sanity timer info */
#define DBG_SET 0x0100 /* display setup info */
#define DBG_PCK 0x0200 /* display packet headers */
#define DBG_DAT 0x0400 /* display packet data */
#define DBG_ETH 0x8000 /* debug ethernet device */
#endif /* _PDP11_XQ_H */