netdev
[Top] [All Lists]

[prepatch] 3c59x.c

To: "David S. Miller" <davem@xxxxxxxxxx>, Alexey Kuznetsov <kuznet@xxxxxxxxxxxxx>
Subject: [prepatch] 3c59x.c
From: Andrew Morton <andrewm@xxxxxxxxxx>
Date: Sat, 27 Jan 2001 01:18:22 +1100
Cc: "netdev@xxxxxxxxxxx" <netdev@xxxxxxxxxxx>
Sender: owner-netdev@xxxxxxxxxxx
OK, here's the rolled-up patch against 2.4.1-pre10 vanilla.

It compiles and works just fine with and without the zc patch.
The idea is that if this patch is applied to the Linus tree,
the 3c59x part of the zerocopy diff can be removed.  Once
the zerocopy patch is merged to Linus' tree, it'll "just work".

Non-ZC notes:

- Added `medialock' stuff for NICs which have multiple physical
  interfaces.  This is because the driver was wandering off and
  trying 10base2 and AUI interfaces if 10baseT is unplugged.
  It was getting stuck there and a driver reload was needed to recover.

- Misc fixlets from Arnaldo Carvalho de Melo

- Added and used EEPROM_NORESET to make PM resumes a little
  more succesful with 3c556B's.

- Fixed a memory leak: missing pci_free_consistent()

ZC notes

- Added the HAS_HWCKSM device flag.  If this flag
  is present, we use hardware tx checksums.

- All devices which are marked IS_CYCLONE or IS_TORNADO
  have also been marked HAS_HWCKSM.  This means that all these
  devices will use hw checksums, as was the case in the zerocopy
  patch.

- Added the `hw_checksums' module parameter.

  This means that if hw checksums cause problems, the user can
  disable it with `hw_checksums=0'.

  It also means that if the user thinks their NIC _does_ support
  hw checksums, and we haven't enabled this, they can use
  `hw_checksums=1' to override the device table setting.

I've tested it with:

3c590:                      Doesn't do SG or checksums
3c905:                      Does SG, no checksums
3c905B, 3c905C, 3CCFE575CT  These do both.

Please let me know if this all looks sane and I'll punt
it over to Linus.  Or we could roll them together if
you're confident that zerocopy is going in.  I'd like
to get the unrelated fixes into 2.4.1....

Thanks.



--- linux-2.4.1-pre10/drivers/net/3c59x.c       Tue Jan 23 19:28:15 2001
+++ linux-akpm/drivers/net/3c59x.c      Sat Jan 27 00:53:26 2001
@@ -118,7 +118,7 @@
    LK1.1.11 13 Nov 2000 andrewm
     - Dump MOD_INC/DEC_USE_COUNT, use SET_MODULE_OWNER
 
-   LK1.1.12 1 Jan 2001 andrewm
+   LK1.1.12 1 Jan 2001 andrewm (2.4.0-pre1)
     - Call pci_enable_device before we request our IRQ (Tobias Ringstrom)
     - Add 3c590 PCI latency timer hack to vortex_probe1 (from 0.99Ra)
     - Added extended wait_for_completion for the 3c905CX.
@@ -126,6 +126,16 @@
     - Add HAS_NWAY to 3cSOHO100-TX (Brett Frankenberger)
     - Don't free skbs we don't own on oom path in vortex_open().
 
+   LK1.1.13 27 Jan 2001
+    - Added explicit `medialock' flag so we can truly
+      lock the media type down with `options'.
+    - "check ioremap return and some tidbits" (Arnaldo Carvalho de Melo 
<acme@xxxxxxxxxxxxxxxx>)
+    - Added and used EEPROM_NORESET for 3c556B PM resumes.
+    - Fixed leakage of vp->rx_ring.
+    - Break out separate HAS_HWCKSM device capability flag.
+    - Kill vp->tx_full (ANK)
+    - Merge zerocopy fragment handling (ANK?)
+
     - See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.3 for more details.
     - Also see Documentation/networking/vortex.txt
 */
@@ -154,7 +164,7 @@
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 32;
 /* Tx timeout interval (millisecs) */
-static int watchdog = 400;
+static int watchdog = 5000;
 
 /* Allow aggregation of Tx interrupts.  Saves CPU load at the cost
  * of possible Tx stalls if the system is blocking interrupts
@@ -174,10 +184,6 @@
 static int vortex_debug = 1;
 #endif
 
-/* Some values here only for performance evaluation and path-coverage
-   debugging. */
-static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
-
 #ifndef __OPTIMIZE__
 #error You must compile this file with the correct options!
 #error See the last lines of the source file.
@@ -211,13 +217,14 @@
 #include <linux/delay.h>
 
 static char version[] __devinitdata =
-"3c59x.c:LK1.1.12 06 Jan 2000  Donald Becker and others. 
http://www.scyld.com/network/vortex.html " "$Revision: 1.102.2.46 $\n";
+"3c59x.c:LK1.1.13 27 Jan 2001  Donald Becker and others. 
http://www.scyld.com/network/vortex.html\n";;
 
 MODULE_AUTHOR("Donald Becker <becker@xxxxxxxxx>");
 MODULE_DESCRIPTION("3Com 3c59x/3c90x/3c575 series Vortex/Boomerang/Cyclone 
driver");
 MODULE_PARM(debug, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(hw_checksums, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(flow_ctrl, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(max_interrupt_work, "i");
@@ -332,7 +339,7 @@
        EEPROM_8BIT=0x10,       /* AKPM: Uses 0x230 as the base bitmaps for 
EEPROM reads */
        HAS_PWR_CTRL=0x20, HAS_MII=0x40, HAS_NWAY=0x80, HAS_CB_FNS=0x100,
        INVERT_MII_PWR=0x200, INVERT_LED_PWR=0x400, MAX_COLLISION_RESET=0x800,
-       EEPROM_OFFSET=0x1000 };
+       EEPROM_OFFSET=0x1000, EEPROM_NORESET=0x2000, HAS_HWCKSM=0x4000 };
 
 enum vortex_chips {
        CH_3C590 = 0,
@@ -405,58 +412,65 @@
        {"3c900 Boomerang 10Mbps Combo",
         PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, },
        {"3c900 Cyclone 10Mbps TPO",                                            
/* AKPM: from Don's 0.99M */
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
        {"3c900 Cyclone 10Mbps Combo",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
 
        {"3c900 Cyclone 10Mbps TPC",                                            
/* AKPM: from Don's 0.99M */
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
        {"3c900B-FL Cyclone 10base-FL",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
        {"3c905 Boomerang 100baseTx",
         PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
        {"3c905 Boomerang 100baseT4",
         PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, },
        {"3c905B Cyclone 100baseTx",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
 
        {"3c905B Cyclone 10/100/BNC",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
        {"3c905B-FX Cyclone 100baseFx",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
        {"3c905C Tornado",
-        PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
        {"3c980 Cyclone",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
        {"3c980 10/100 Base-TX NIC(Python-T)",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
 
        {"3cSOHO100-TX Hurricane",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
        {"3c555 Laptop Hurricane",
-        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|EEPROM_8BIT, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|EEPROM_8BIT|HAS_HWCKSM, 128, },
        {"3c556 Laptop Tornado",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|EEPROM_8BIT|HAS_CB_FNS|INVERT_MII_PWR, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|EEPROM_8BIT|HAS_CB_FNS|INVERT_MII_PWR|
+                                                                       
HAS_HWCKSM, 128, },
        {"3c556B Laptop Hurricane",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR|
+                                                                       
EEPROM_NORESET|HAS_HWCKSM, 128, },
        {"3c575 [Megahertz] 10/100 LAN  CardBus",
-        PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
+       PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
 
        {"3c575 Boomerang CardBus",
         PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
        {"3CCFE575BT Cyclone CardBus",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_LED_PWR, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|
+                                                                       
INVERT_LED_PWR|HAS_HWCKSM, 128, },
        {"3CCFE575CT Tornado CardBus",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|MAX_COLLISION_RESET, 
128, },
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+                                                                       
MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
        {"3CCFE656 Cyclone CardBus",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|INVERT_LED_PWR, 128, 
},
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+                                                                       
INVERT_LED_PWR|HAS_HWCKSM, 128, },
        {"3CCFEM656B Cyclone+Winmodem CardBus",
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|INVERT_LED_PWR, 128, 
},
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+                                                                       
INVERT_LED_PWR|HAS_HWCKSM, 128, },
 
        {"3CXFEM656C Tornado+Winmodem CardBus",                 /* From 
pcmcia-cs-3.1.5 */
-        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|MAX_COLLISION_RESET, 
128, },
+        PCI_USES_IO|PCI_USES_MASTER, 
IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+                                                                       
MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
        {"3c450 HomePNA Tornado",                                               
/* AKPM: from Don's 0.99Q */
-        PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY, 128, },
+        PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
        {0,}, /* 0 terminated list. */
 };
 
@@ -631,11 +645,24 @@
        IPChksumValid=1<<29, TCPChksumValid=1<<30, UDPChksumValid=1<<31,
 };
 
+#ifdef MAX_SKB_FRAGS
+#define DO_ZEROCOPY 1
+#else
+#define DO_ZEROCOPY 0
+#endif
+
 struct boom_tx_desc {
        u32 next;                                       /* Last entry points to 
0.   */
        s32 status;                                     /* bits 0:12 length, 
others see below.  */
-       u32 addr;
-       s32 length;
+#if DO_ZEROCOPY
+       struct {
+               u32 addr;
+               s32 length;
+       } frag[1+MAX_SKB_FRAGS];
+#else
+               u32 addr;
+               s32 length;
+#endif
 };
 
 /* Values for the Tx status entry. */
@@ -668,6 +695,10 @@
        struct pci_dev *pdev;
        char *cb_fn_base;                                       /* CardBus 
function status addr space. */
 
+       /* Some values here only for performance evaluation and path-coverage */
+       int rx_nocopy, rx_copy, queued_packet, rx_csumhits;
+       int card_idx;
+
        /* The remainder are related to chip state, mostly media selection. */
        struct timer_list timer;                        /* Media selection 
timer. */
        struct timer_list rx_oom_timer;         /* Rx skb allocation retry 
timer */
@@ -682,6 +713,7 @@
                tx_full:1,
                has_nway:1,
                open:1,
+               medialock:1,
                must_free_region:1;                             /* Flag: if 
zero, Cardbus owns the I/O region */
        int drv_flags;
        u16 status_enable;
@@ -755,6 +787,7 @@
 #define MAX_UNITS 8
 static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,};
 static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int hw_checksums[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 static int flow_ctrl[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 
 /* #define dev_alloc_skb dev_alloc_skb_debug */
@@ -889,9 +922,9 @@
        }
 
        dev = init_etherdev(NULL, sizeof(*vp));
+       retval = -ENOMEM;
        if (!dev) {
                printk (KERN_ERR PFX "unable to allocate etherdev, aborting\n");
-               retval = -ENOMEM;
                goto out;
        }
        SET_MODULE_OWNER(dev);
@@ -909,6 +942,7 @@
        vp->drv_flags = vci->drv_flags;
        vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
        vp->io_size = vci->io_size;
+       vp->card_idx = card_idx;
 
        /* module list only for EISA devices */
        if (pdev == NULL) {
@@ -953,10 +987,9 @@
        vp->rx_ring = pci_alloc_consistent(pdev, sizeof(struct boom_rx_desc) * 
RX_RING_SIZE
                                           + sizeof(struct boom_tx_desc) * 
TX_RING_SIZE,
                                           &vp->rx_ring_dma);
-       if (vp->rx_ring == 0) {
-               retval = -ENOMEM;
+       retval = -ENOMEM;
+       if (vp->rx_ring == 0)
                goto free_region;
-       }
 
        vp->tx_ring = (struct boom_tx_desc *)(vp->rx_ring + RX_RING_SIZE);
        vp->tx_ring_dma = vp->rx_ring_dma + sizeof(struct boom_rx_desc) * 
RX_RING_SIZE;
@@ -982,6 +1015,8 @@
        vp->media_override = 7;
        if (option >= 0) {
                vp->media_override = ((option & 7) == 2)  ?  0  :  option & 15;
+               if (vp->media_override != 7)
+                       vp->medialock = 1;
                vp->full_duplex = (option & 0x200) ? 1 : 0;
                vp->bus_master = (option & 16) ? 1 : 0;
        }
@@ -1049,7 +1084,7 @@
 
        EL3WINDOW(4);
        step = (inb(ioaddr + Wn4_NetDiag) & 0x1e) >> 1;
-       printk(KERN_INFO "  product code '%c%c' rev %02x.%d date %02d-"
+       printk(KERN_INFO "  product code %02x%02x rev %02x.%d date %02d-"
                   "%02d-%02d\n", eeprom[6]&0xff, eeprom[6]>>8, eeprom[0x14],
                   step, (eeprom[4]>>5) & 15, eeprom[4] & 31, eeprom[4]>>9);
 
@@ -1059,8 +1094,12 @@
                unsigned short n;
 
                fn_st_addr = pci_resource_start (pdev, 2);
-               if (fn_st_addr)
+               if (fn_st_addr) {
                        vp->cb_fn_base = ioremap(fn_st_addr, 128);
+                       retval = -ENOMEM;
+                       if (!vp->cb_fn_base)
+                               goto free_ring;
+               }
                printk(KERN_INFO "%s: CardBus functions mapped %8.8lx->%p\n",
                           dev->name, fn_st_addr, vp->cb_fn_base);
                EL3WINDOW(2);
@@ -1167,21 +1206,43 @@
 
        /* The 3c59x-specific entries in the device structure. */
        dev->open = vortex_open;
-       dev->hard_start_xmit = vp->full_bus_master_tx ?
-                                       boomerang_start_xmit : 
vortex_start_xmit;
+       if (vp->full_bus_master_tx) {
+               dev->hard_start_xmit = boomerang_start_xmit;
+#ifndef CONFIG_HIGHMEM
+               /* Actually, it still should work with iommu. */
+               dev->features |= NETIF_F_SG;
+#endif
+               if (((hw_checksums[card_idx] == -1) && (vp->drv_flags & 
HAS_HWCKSM)) ||
+                                       (hw_checksums[card_idx] == 1))
+                               dev->features |= NETIF_F_IP_CSUM;
+       } else {
+               dev->hard_start_xmit = vortex_start_xmit;
+       }
+
+       if (vortex_debug > 0) {
+               printk(KERN_INFO "%s: scatter/gather %sabled. h/w checksums 
%sabled\n",
+                               dev->name,
+                               (dev->features & NETIF_F_SG) ? "en":"dis",
+                               (dev->features & NETIF_F_IP_CSUM) ? "en":"dis");
+       }
+
        dev->stop = vortex_close;
        dev->get_stats = vortex_get_stats;
        dev->do_ioctl = vortex_ioctl;
        dev->set_multicast_list = set_rx_mode;
        dev->tx_timeout = vortex_tx_timeout;
        dev->watchdog_timeo = (watchdog * HZ) / 1000;
-//     publish_netdev(dev);
        return 0;
 
+free_ring:
+       pci_free_consistent(pdev,
+                                               sizeof(struct boom_rx_desc) * 
RX_RING_SIZE
+                                                       + sizeof(struct 
boom_tx_desc) * TX_RING_SIZE,
+                                               vp->rx_ring,
+                                               vp->rx_ring_dma);
 free_region:
        if (vp->must_free_region)
                release_region(ioaddr, vci->io_size);
-//     withdraw_netdev(dev);
        unregister_netdev(dev);
        kfree (dev);
        printk(KERN_ERR PFX "vortex_probe1 fails.  Returns %d\n", retval);
@@ -1202,7 +1263,8 @@
        /* OK, that didn't work.  Do it the slow way.  One second */
        for (i = 0; i < 100000; i++) {
                if (!(inw(dev->base_addr + EL3_STATUS) & CmdInProgress)) {
-                       printk(KERN_INFO "%s: command 0x%04x took %d usecs! 
Please tell andrewm@xxxxxxxxxx\n",
+                       if (vortex_debug > 1)
+                               printk(KERN_INFO "%s: command 0x%04x took %d 
usecs\n",
                                           dev->name, cmd, i * 10);
                        return;
                }
@@ -1478,6 +1540,8 @@
                printk(KERN_DEBUG "dev->watchdog_timeo=%d\n", 
dev->watchdog_timeo);
        }
 
+       if (vp->medialock)
+               goto leave_media_alone;
        disable_irq(dev->irq);
        old_window = inw(ioaddr + EL3_CMD) >> 13;
        EL3WINDOW(4);
@@ -1567,6 +1631,7 @@
        EL3WINDOW(old_window);
        enable_irq(dev->irq);
 
+leave_media_alone:
        if (vortex_debug > 2)
          printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
                         dev->name, media_tbl[dev->if_port].name);
@@ -1619,18 +1684,12 @@
 
        vp->stats.tx_errors++;
        if (vp->full_bus_master_tx) {
-               if (vortex_debug > 0)
-                       printk(KERN_DEBUG "%s: Resetting the Tx ring 
pointer.\n",
-                                  dev->name);
+               printk(KERN_DEBUG "%s: Resetting the Tx ring pointer.\n", 
dev->name);
                if (vp->cur_tx - vp->dirty_tx > 0  &&  inl(ioaddr + 
DownListPtr) == 0)
                        outl(vp->tx_ring_dma + (vp->dirty_tx % TX_RING_SIZE) * 
sizeof(struct boom_tx_desc),
                                 ioaddr + DownListPtr);
-               if (vp->tx_full && (vp->cur_tx - vp->dirty_tx <= TX_RING_SIZE - 
1)) {
-                       vp->tx_full = 0;
+               if (vp->cur_tx - vp->dirty_tx < TX_RING_SIZE)
                        netif_wake_queue (dev);
-               }
-               if (vp->tx_full)
-                       netif_stop_queue (dev);
                if (vp->drv_flags & IS_BOOMERANG)
                        outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
                outw(DownUnstall, ioaddr + EL3_CMD);
@@ -1820,17 +1879,57 @@
                                   dev->name, vp->cur_tx);
        }
 
-       if (vp->tx_full) {
+       if (vp->cur_tx - vp->dirty_tx >= TX_RING_SIZE) {
                if (vortex_debug > 0)
-                       printk(KERN_WARNING "%s: Tx Ring full, refusing to send 
buffer.\n",
+                       printk(KERN_WARNING "%s: BUG! Tx Ring full, refusing to 
send buffer.\n",
                                   dev->name);
+               netif_stop_queue(dev);
                return 1;
        }
+
        vp->tx_skbuff[entry] = skb;
+
        vp->tx_ring[entry].next = 0;
+#if DO_ZEROCOPY
+       if (skb->ip_summed != CHECKSUM_HW)
+                       vp->tx_ring[entry].status = cpu_to_le32(skb->len | 
TxIntrUploaded);
+       else {
+                       vp->tx_ring[entry].status = cpu_to_le32(skb->len | 
TxIntrUploaded | AddTCPChksum);
+                       vp->stats.rx_compressed++;
+       }
+
+       if (!skb_shinfo(skb)->nr_frags) {
+               vp->tx_ring[entry].frag[0].addr = 
cpu_to_le32(pci_map_single(vp->pdev, skb->data,
+                                                                               
skb->len, PCI_DMA_TODEVICE));
+               vp->tx_ring[entry].frag[0].length = cpu_to_le32(skb->len | 
LAST_FRAG);
+       } else {
+               int i;
+
+               vp->stats.tx_compressed++;
+
+               vp->tx_ring[entry].frag[0].addr = 
cpu_to_le32(pci_map_single(vp->pdev, skb->data,
+                                                                               
skb->len-skb->data_len, PCI_DMA_TODEVICE));
+               vp->tx_ring[entry].frag[0].length = 
cpu_to_le32(skb->len-skb->data_len);
+
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                       vp->tx_ring[entry].frag[i+1].addr =
+                                       cpu_to_le32(pci_map_single(vp->pdev,
+                                                                               
           (void*)page_address(frag->page) + frag->page_offset,
+                                                                               
           frag->size, PCI_DMA_TODEVICE));
+
+                       if (i == skb_shinfo(skb)->nr_frags-1)
+                                       vp->tx_ring[entry].frag[i+1].length = 
cpu_to_le32(frag->size|LAST_FRAG);
+                       else
+                                       vp->tx_ring[entry].frag[i+1].length = 
cpu_to_le32(frag->size);
+               }
+       }
+#else
        vp->tx_ring[entry].addr = cpu_to_le32(pci_map_single(vp->pdev, 
skb->data, skb->len, PCI_DMA_TODEVICE));
        vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
        vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
+#endif
 
        spin_lock_irqsave(&vp->lock, flags);
        /* Wait for the stall to complete. */
@@ -1838,18 +1937,19 @@
        prev_entry->next = cpu_to_le32(vp->tx_ring_dma + entry * sizeof(struct 
boom_tx_desc));
        if (inl(ioaddr + DownListPtr) == 0) {
                outl(vp->tx_ring_dma + entry * sizeof(struct boom_tx_desc), 
ioaddr + DownListPtr);
-               queued_packet++;
+               vp->queued_packet++;
        }
 
        vp->cur_tx++;
        if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) {
-               vp->tx_full = 1;
                netif_stop_queue (dev);
        } else {                                        /* Clear previous 
interrupt enable. */
 #if defined(tx_interrupt_mitigation)
+               /* Dubious. If in boomeang_interrupt "faster" cyclone ifdef
+                * were selected, this would corrupt DN_COMPLETE. No?
+                */
                prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
 #endif
-               /* netif_start_queue (dev); */          /* AKPM: redundant? */
        }
        outw(DownUnstall, ioaddr + EL3_CMD);
        spin_unlock_irqrestore(&vp->lock, flags);
@@ -2032,9 +2132,17 @@
                                        
                                if (vp->tx_skbuff[entry]) {
                                        struct sk_buff *skb = 
vp->tx_skbuff[entry];
-                                       
+#if DO_ZEROCOPY                                        
+                                       int i;
+                                       for (i=0; i<=skb_shinfo(skb)->nr_frags; 
i++)
+                                                       
pci_unmap_single(vp->pdev,
+                                                                               
         le32_to_cpu(vp->tx_ring[entry].frag[i].addr),
+                                                                               
         le32_to_cpu(vp->tx_ring[entry].frag[i].length)&0xFFF,
+                                                                               
         PCI_DMA_TODEVICE);
+#else
                                        pci_unmap_single(vp->pdev,
                                                
le32_to_cpu(vp->tx_ring[entry].addr), skb->len, PCI_DMA_TODEVICE);
+#endif
                                        dev_kfree_skb_irq(skb);
                                        vp->tx_skbuff[entry] = 0;
                                } else {
@@ -2044,10 +2152,9 @@
                                dirty_tx++;
                        }
                        vp->dirty_tx = dirty_tx;
-                       if (vp->tx_full && (vp->cur_tx - dirty_tx <= 
TX_RING_SIZE - 1)) {
+                       if (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1) {
                                if (vortex_debug > 6)
                                        printk(KERN_DEBUG "boomerang_interrupt: 
clearing tx_full\n");
-                               vp->tx_full = 0;
                                netif_wake_queue (dev);
                        }
                }
@@ -2199,14 +2306,14 @@
                                memcpy(skb_put(skb, pkt_len),
                                           vp->rx_skbuff[entry]->tail,
                                           pkt_len);
-                               rx_copy++;
+                               vp->rx_copy++;
                        } else {
                                /* Pass up the skbuff already on the Rx ring. */
                                skb = vp->rx_skbuff[entry];
                                vp->rx_skbuff[entry] = NULL;
                                skb_put(skb, pkt_len);
                                pci_unmap_single(vp->pdev, dma, PKT_BUF_SZ, 
PCI_DMA_FROMDEVICE);
-                               rx_nocopy++;
+                               vp->rx_nocopy++;
                        }
                        skb->protocol = eth_type_trans(skb, dev);
                        {                                       /* Use hardware 
checksum info. */
@@ -2215,7 +2322,7 @@
                                        (csum_bits == (IPChksumValid | 
TCPChksumValid) ||
                                         csum_bits == (IPChksumValid | 
UDPChksumValid))) {
                                        skb->ip_summed = CHECKSUM_UNNECESSARY;
-                                       rx_csumhits++;
+                                       vp->rx_csumhits++;
                                }
                        }
                        netif_rx(skb);
@@ -2320,9 +2427,18 @@
                           dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + 
TxStatus));
                printk(KERN_DEBUG "%s: vortex close stats: rx_nocopy %d rx_copy 
%d"
                           " tx_queued %d Rx pre-checksummed %d.\n",
-                          dev->name, rx_nocopy, rx_copy, queued_packet, 
rx_csumhits);
+                          dev->name, vp->rx_nocopy, vp->rx_copy, 
vp->queued_packet, vp->rx_csumhits);
        }
 
+#if DO_ZEROCOPY
+       if (    vp->rx_csumhits &&
+                       ((vp->drv_flags & HAS_HWCKSM) == 0) &&
+                       (hw_checksums[vp->card_idx] == -1)) {
+               printk(KERN_WARNING "%s supports hardware checksums, and we're 
not using them!\n", dev->name);
+               printk(KERN_WARNING "Please see 
http://www.uow.edu.au/~andrewm/zerocopy.html\n";);
+       }
+#endif
+               
        free_irq(dev->irq, dev);
 
        if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. 
*/
@@ -2335,14 +2451,24 @@
                        }
        }
        if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. 
*/
-               for (i = 0; i < TX_RING_SIZE; i++)
+               for (i = 0; i < TX_RING_SIZE; i++) {
                        if (vp->tx_skbuff[i]) {
                                struct sk_buff *skb = vp->tx_skbuff[i];
+#if DO_ZEROCOPY
+                               int k;
 
+                               for (k=0; k<=skb_shinfo(skb)->nr_frags; k++)
+                                               pci_unmap_single(vp->pdev,
+                                                                               
 le32_to_cpu(vp->tx_ring[i].frag[k].addr),
+                                                                               
 le32_to_cpu(vp->tx_ring[i].frag[k].length)&0xFFF,
+                                                                               
 PCI_DMA_TODEVICE);
+#else
                                pci_unmap_single(vp->pdev, 
le32_to_cpu(vp->tx_ring[i].addr), skb->len, PCI_DMA_TODEVICE);
+#endif
                                dev_kfree_skb(skb);
                                vp->tx_skbuff[i] = 0;
                        }
+               }
        }
 
        vp->open = 0;
@@ -2360,7 +2486,6 @@
                        int i;
                        int stalled = inl(ioaddr + PktStatus) & 0x04;   /* 
Possible racy. But it's only debug stuff */
 
-                       wait_for_completion(dev, DownStall);
                        printk(KERN_ERR "  Flags; bus-master %d, full %d; dirty 
%d(%d) "
                                        "current %d(%d).\n",
                                        vp->full_bus_master_tx, vp->tx_full,
@@ -2369,10 +2494,15 @@
                        printk(KERN_ERR "  Transmit list %8.8x vs. %p.\n",
                                   inl(ioaddr + DownListPtr),
                                   &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
+                       wait_for_completion(dev, DownStall);
                        for (i = 0; i < TX_RING_SIZE; i++) {
                                printk(KERN_ERR "  %d: @%p  length %8.8x status 
%8.8x\n", i,
                                           &vp->tx_ring[i],
+#if DO_ZEROCOPY
+                                          
le32_to_cpu(vp->tx_ring[i].frag[0].length),
+#else
                                           le32_to_cpu(vp->tx_ring[i].length),
+#endif
                                           le32_to_cpu(vp->tx_ring[i].status));
                        }
                        if (!stalled)
@@ -2634,7 +2764,13 @@
         * here
         */
        unregister_netdev(dev);
-       outw(TotalReset, dev->base_addr + EL3_CMD);
+       /* Should really use wait_for_completion() here */
+       outw((vp->drv_flags & EEPROM_NORESET) ? (TotalReset|0x10) : TotalReset, 
dev->base_addr + EL3_CMD);
+       pci_free_consistent(pdev,
+                                               sizeof(struct boom_rx_desc) * 
RX_RING_SIZE
+                                                       + sizeof(struct 
boom_tx_desc) * TX_RING_SIZE,
+                                               vp->rx_ring,
+                                               vp->rx_ring_dma);
        if (vp->must_free_region)
                release_region(dev->base_addr, vp->io_size);
        kfree(dev);
@@ -2707,7 +2843,6 @@
 
 module_init(vortex_init);
 module_exit(vortex_cleanup);
-
 
 
 /*
--- linux-2.4.1-pre10/Documentation/networking/vortex.txt       Sun Oct 15 
01:27:35 2000
+++ linux-akpm/Documentation/networking/vortex.txt      Sat Jan 27 00:11:11 2001
@@ -116,7 +116,11 @@
 full_duplex=N1,N2,N3...
 
   Similar to bit 9 of 'options'.  Forces the corresponding card into
-  full-duplex mode.
+  full-duplex mode.  Please use this in preference to the `options'
+  parameter.
+
+  In fact, please don't use this at all! You're better off getting
+  autonegotiation working properly.
 
 flow_ctrl=N1,N2,N3...
 
@@ -156,6 +160,27 @@
   is exceeded the interrupt service routine gives up and generates a
   warning message "eth0: Too much work in interrupt".
 
+hw_checksums=N1,N2,N3,...
+
+  Recent 3com NICs are able to generate IPv4, TCP and UDP checksums
+  in hardware.  Linux has used the Rx checksumming for a long time. 
+  The "zero copy" patch which is planned for kernel 2.4.1 allows you to
+  make use of the transmit checksumming as well.
+
+  The driver is set up so that, when the zerocopy patch is applied,
+  all Tornado and Cyclone devices will use Tx checksums.
+
+  This module parameter has been provided so you can override this
+  decision.  If you think that Tx checksums are causing a problem, you
+  may disable the feature with `hw_checksums=0'.
+
+  If you think your NIC should be performing Tx checksumming and the
+  driver isn't enabling it, you can force the use of hardware Tx
+  checksumming with `hw_checksums=1'.
+
+  The driver drops a message in the logfiles to indicate whether or
+  not it is using hardware scatter/gather and hardware Tx checksums.
+
 compaq_ioaddr=N
 compaq_irq=N
 compaq_device_id=N
@@ -168,7 +193,29 @@
   decides that the transmitter has become stuck and needs to be reset. 
   This is mainly for debugging purposes, although it may be advantageous
   to increase this value on LANs which have very high collision rates.
-  The default value is 400 (0.4 seconds).
+  The default value is 5000 (5.0 seconds).
+
+
+Media selection
+---------------
+
+A number of the older NICs such as the 3c590 and 3c900 series have
+10base2 and AUI interfaces.
+
+Prior to January, 2001 this driver would autoeselect the 10base2 or AUI
+port if it didn't detect activity on the 10baseT port.  It would then
+get stuck on the 10base2 port and a driver reload was necessary to
+switch back to 10baseT.  This behaviour could not be prevented with a
+module option override.
+
+Later (current) versions of the driver _do_ support locking of the
+media type.  So if you load the driver module with
+
+       modprobe 3c59x options=0
+
+it will permanently select the 10baseT port.  Automatic selection of
+other media types does not occur.
+
 
 Additional resources
 --------------------
@@ -193,7 +240,6 @@
 NIC's Media Independent Interface subsystem:
 
      http://www.scyld.com/diag/#mii-diag
-     http://cesdis.gsfc.nasa.gov/linux/diag/#mii-diag
 
 3Com's documentation for many NICs, including the ones supported by
 this driver is available at 
@@ -222,6 +268,21 @@
   tree parameter for the port the machine is plugged into to 'portfast'
   mode.  Otherwise, the negotiation fails.  This has been an issue
   we've noticed for a while but haven't had the time to track down.
+
+  Cisco switches    (Jeff Busch <jbusch@xxxxxxxx>)
+
+    My "standard config" for ports to which PC's/servers connect directly:
+
+        interface FastEthernet0/N
+        description machinename
+        load-interval 30
+        spanning-tree portfast
+
+    If autonegotiation is a problem, you may need to specify "speed
+    100" and "duplex full" as well (or "speed 10" and "duplex half").
+
+    WARNING: DO NOT hook up hubs/switches/bridges to these
+    specially-configured ports! The switch will become very confused.
 
 
 Reporting and diagnosing problems

<Prev in Thread] Current Thread [Next in Thread>