[PATCH] #8 VIA Rhine (stalls, stats, backoff, clean up)

Roger Luethi (rl@hellgate.ch)
Mon, 15 Jul 2002 15:36:15 +0200


--KsGdsel6WgEHnImy
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

This patch is a more comprehensive and cleaned up version of earlier VIA
Rhine patches I posted in May. It should be self-explanatory. The change
summary reads:

- show confused chip where to continue after Tx error; this is known to fix
at least some (hopefully all) of the infamous Rhine stalls under load

- location of collision counter is chip specific (underflow counter, too)

- allow selecting backoff algorithm -- added new module parameter;
this is a trade-off; higher performance typically means many aborts due
to excessive collisions and performance degradation for other users.
Default comes from EEPROM and depends on the card.

- cosmetic cleanups

Note: Testing on several cards seems to indicate that waiting for the chip
before restarting the Tx engine is pointless; in the rare case where the
flag is not down by the time the driver is ready to restart, it will stay
up forever, but restarting the Tx engine immediately still works.

Beware: It seems that in certain cases, the interrupt status the driver
relies on for error handling is wrong. In those cases, only the respective
buffer descriptor carries the correct error information. Should this turn
out to be a problem in real life, the error handling handling can be moved
into via_rhine_rx(), or the correct error code can be passed to
via_rhine_error() alternatively.

Some changes in the statistics section are tentative. They are coded
according to the specs, and they assume that the old code worked for the
VT86C100A, and that all chips but the VT86C100A work like the VT6102, for
which the changes have been tested. FWIW, some counters work with the
VT6102 for the first time with this patch (it's not as if anybody cared, or
the broken counters wouldn't have gone unnoticed for such a long time).

The patch is against the latest version in Jeff's public tree. It is most
definitely an improvement for many Rhine users and should not create any
problems. Unless somebody finds bugs, I don't intend to release a new
version anytime soon.

Roger

--KsGdsel6WgEHnImy
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="via-rhine.c.8.patch"

--- drivers/net/bk_11Jul.c Mon Jul 15 14:53:27 2002
+++ drivers/net/via-rhine.c Mon Jul 15 14:53:53 2002
@@ -93,7 +93,10 @@
- transmit frame queue message is off by one - fixed
- adds IntrNormalSummary to "Something Wicked" exclusion list
so normal interrupts will not trigger the message (src: Donald Becker)
- (Roger Lahti)
+ (Roger Luethi)
+ - show confused chip where to continue after Tx error
+ - location of collision counter is chip specific
+ - allow selecting backoff algorithm (module parameter)
- cosmetic cleanups, remove 3 unused members of struct netdev_private

*/
@@ -113,6 +116,9 @@ static int max_interrupt_work = 20;
Setting to > 1518 effectively disables this feature. */
static int rx_copybreak;

+/* Select a backoff algorithm (Ethernet capture effect) */
+static int backoff;
+
/* Used to pass the media type, etc.
Both 'options[]' and 'full_duplex[]' should exist for driver
interoperability.
@@ -215,11 +221,13 @@ MODULE_LICENSE("GPL");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(backoff, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM_DESC(max_interrupt_work, "VIA Rhine maximum events handled per interrupt");
MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)");
MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(backoff, "VIA Rhine: Bits 0-3: backoff algorithm");
MODULE_PARM_DESC(options, "VIA Rhine: Bits 0-3: media type, bit 17: full duplex");
MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)");

@@ -236,7 +244,8 @@ II. Board-specific settings
Boards with this chip are functional only in a bus-master PCI slot.

Many operational settings are loaded from the EEPROM to the Config word at
-offset 0x78. This driver assumes that they are correct.
+offset 0x78. For most of these settings, this driver assumes that they are
+correct.
If this driver is compiled to use PCI memory space operations the EEPROM
must be configured to enable memory ops.

@@ -388,9 +397,10 @@ enum register_offsets {
StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC,
};

-/* Bits in ConfigD (select backoff algorithm (Ethernet capture effect)) */
+/* Bits in ConfigD */
enum backoff_bits {
- BackOpt=0x01, BackAMD=0x02, BackDEC=0x04, BackRandom=0x08
+ BackOptional=0x01, BackModify=0x02,
+ BackCaptureEffect=0x04, BackRandom=0x08
};

#ifdef USE_MEM
@@ -404,7 +414,7 @@ int mmio_verify_registers[] = {
/* Bits in the interrupt status/mask registers. */
enum intr_status_bits {
IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
- IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010,
+ IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0010,
IntrPCIErr=0x0040,
IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200,
IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
@@ -430,24 +440,27 @@ enum mii_status_bits {
/* The Rx and Tx buffer descriptors. */
struct rx_desc {
s32 rx_status;
- u32 desc_length;
+ u32 desc_length; /* Chain flag, Buffer/frame length */
u32 addr;
u32 next_desc;
};
struct tx_desc {
s32 tx_status;
- u32 desc_length;
+ u32 desc_length; /* Chain flag, Tx Config, Frame length */
u32 addr;
u32 next_desc;
};

+/* Initial value for tx_desc.desc_length, Buffer size goes to bits 0-10 */
+#define TXDESC 0x00e08000
+
enum rx_status_bits {
RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
};

-/* Bits in *_desc.status */
+/* Bits in *_desc.*_status */
enum desc_status_bits {
- DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,
+ DescOwn=0x80000000
};

/* Bits in ChipCmd. */
@@ -519,6 +532,7 @@ static struct net_device_stats *via_rhin
static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int via_rhine_close(struct net_device *dev);
static inline void clear_tally_counters(long ioaddr);
+static inline void via_restart_tx(struct net_device *dev);

static void wait_for_reset(struct net_device *dev, int chip_id, char *name)
{
@@ -705,6 +719,11 @@ static int __devinit via_rhine_init_one
writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA);
}

+ /* Select backoff algorithm */
+ if (backoff)
+ writeb(readb(ioaddr + ConfigD) & (0xF0 | backoff),
+ ioaddr + ConfigD);
+
dev->irq = pdev->irq;

np = dev->priv;
@@ -939,7 +958,7 @@ static void alloc_tbufs(struct net_devic
for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_skbuff[i] = 0;
np->tx_ring[i].tx_status = 0;
- np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+ np->tx_ring[i].desc_length = cpu_to_le32(TXDESC);
next += sizeof(struct tx_desc);
np->tx_ring[i].next_desc = cpu_to_le32(next);
np->tx_buf[i] = &np->tx_bufs[i * PKT_BUF_SZ];
@@ -955,7 +974,7 @@ static void free_tbufs(struct net_device

for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_ring[i].tx_status = 0;
- np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+ np->tx_ring[i].desc_length = cpu_to_le32(TXDESC);
np->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
if (np->tx_skbuff[i]) {
if (np->tx_skbuff_dma[i]) {
@@ -980,7 +999,7 @@ static void init_registers(struct net_de
writeb(dev->dev_addr[i], ioaddr + StationAddr + i);

/* Initialize other registers. */
- writew(0x0006, ioaddr + PCIBusConfig); /* Store & forward */
+ writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */
/* Configure initial FIFO thresholds. */
writeb(0x20, ioaddr + TxConfig);
np->tx_thresh = 0x20;
@@ -995,8 +1014,9 @@ static void init_registers(struct net_de
via_rhine_set_rx_mode(dev);

/* Enable interrupts by setting the interrupt mask. */
- writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped|
- IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow |
+ IntrRxDropped | IntrRxNoBuf | IntrTxAborted |
+ IntrTxDone | IntrTxError | IntrTxUnderrun |
IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange,
ioaddr + IntrEnable);

@@ -1239,7 +1259,7 @@ static int via_rhine_start_tx(struct sk_
}

np->tx_ring[entry].desc_length =
- cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+ cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));

/* lock eth irq */
spin_lock_irq (&np->lock);
@@ -1291,13 +1311,14 @@ static void via_rhine_interrupt(int irq,
IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
via_rhine_rx(dev);

- if (intr_status & (IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ if (intr_status & (IntrTxDone | IntrTxError | IntrTxUnderrun |
IntrTxAborted))
via_rhine_tx(dev);

/* Abnormal error summary/uncommon events handlers. */
if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
- IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
+ IntrStatsMax | IntrTxError | IntrTxAborted |
+ IntrTxUnderrun))
via_rhine_error(dev, intr_status);

if (--boguscnt < 0) {
@@ -1309,7 +1330,7 @@ static void via_rhine_interrupt(int irq,
}

if (debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%4.4x.\n",
dev->name, readw(ioaddr + IntrStatus));
}

@@ -1325,11 +1346,11 @@ static void via_rhine_tx(struct net_devi
/* find and cleanup dirty tx descriptors */
while (np->dirty_tx != np->cur_tx) {
txstatus = le32_to_cpu(np->tx_ring[entry].tx_status);
- if (txstatus & DescOwn)
- break;
if (debug > 6)
printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n",
entry, txstatus);
+ if (txstatus & DescOwn)
+ break;
if (txstatus & 0x8000) {
if (debug > 1)
printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
@@ -1339,10 +1360,22 @@ static void via_rhine_tx(struct net_devi
if (txstatus & 0x0200) np->stats.tx_window_errors++;
if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
- if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
+ if (((np->chip_id == VT86C100A) && txstatus & 0x0002) ||
+ (txstatus & 0x0800) || (txstatus & 0x1000)) {
+ np->stats.tx_fifo_errors++;
+ np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn);
+ break; /* Keep the skb - we try again */
+ }
/* Transmitter restarted in 'abnormal' handler. */
} else {
- np->stats.collisions += (txstatus >> 3) & 15;
+ if (np->chip_id == VT86C100A)
+ np->stats.collisions += (txstatus >> 3) & 0x0F;
+ else
+ np->stats.collisions += txstatus & 0x0F;
+ if (debug > 6)
+ printk(KERN_DEBUG "collisions: %1.1x:%1.1x\n",
+ (txstatus >> 3) & 0xF,
+ txstatus & 0xF);
np->stats.tx_bytes += np->tx_skbuff[entry]->len;
np->stats.tx_packets++;
}
@@ -1478,6 +1511,17 @@ static void via_rhine_rx(struct net_devi
writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
}

+static inline void via_restart_tx(struct net_device *dev) {
+ struct netdev_private *np = dev->priv;
+ int entry = np->dirty_tx % TX_RING_SIZE;
+
+ /* We know better than the chip where it should continue */
+ writel(np->tx_ring_dma + entry * sizeof(struct tx_desc),
+ dev->base_addr + TxRingPtr);
+
+ writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+}
+
static void via_rhine_error(struct net_device *dev, int intr_status)
{
struct netdev_private *np = dev->priv;
@@ -1503,19 +1547,23 @@ static void via_rhine_error(struct net_d
np->stats.rx_missed_errors += readw(ioaddr + RxMissed);
clear_tally_counters(ioaddr);
}
- if (intr_status & IntrTxAbort) {
- /* Stats counted in Tx-done handler, just restart Tx. */
- writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+ if (intr_status & IntrTxError) {
+ if (debug > 1)
+ printk(KERN_INFO "%s: Abort %4.4x, frame dropped.\n",
+ dev->name, intr_status);
+ via_restart_tx(dev);
}
if (intr_status & IntrTxUnderrun) {
if (np->tx_thresh < 0xE0)
writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
if (debug > 1)
- printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
- "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
+ printk(KERN_INFO "%s: Transmitter underrun, Tx "
+ "threshold now %2.2x.\n",
+ dev->name, np->tx_thresh);
+ via_restart_tx(dev);
}
if (intr_status & ~( IntrLinkChange | IntrStatsMax |
- IntrTxAbort | IntrTxAborted | IntrNormalSummary)) {
+ IntrTxError | IntrTxAborted | IntrNormalSummary)) {
if (debug > 1)
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status);

--KsGdsel6WgEHnImy--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/