Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Mon, 15 Jul 2002 09:41:16 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Mon, 15 Jul 2002 09:41:15 -0400 Received: from mta9n.bluewin.ch ([195.186.1.215]:31379 "EHLO mta9n.bluewin.ch") by vger.kernel.org with ESMTP id ; Mon, 15 Jul 2002 09:41:11 -0400 Date: Mon, 15 Jul 2002 15:36:15 +0200 From: Roger Luethi To: linux-kernel@vger.kernel.org Cc: Urban Widmark , Jeff Garzik Subject: [PATCH] #8 VIA Rhine (stalls, stats, backoff, clean up) Message-ID: <20020715133615.GA5948@k3.hellgate.ch> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="KsGdsel6WgEHnImy" Content-Disposition: inline User-Agent: Mutt/1.3.27i X-Operating-System: Linux 2.4.19-pre10-ac2 on i686 Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 13266 Lines: 341 --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/