diff options
| -rw-r--r-- | Documentation/networking/dl2k.txt | 44 | ||||
| -rw-r--r-- | drivers/net/8139cp.c | 143 | ||||
| -rw-r--r-- | drivers/net/dl2k.c | 26 | ||||
| -rw-r--r-- | drivers/net/dl2k.h | 15 | ||||
| -rw-r--r-- | include/linux/ethtool.h | 11 |
5 files changed, 199 insertions, 40 deletions
diff --git a/Documentation/networking/dl2k.txt b/Documentation/networking/dl2k.txt index dbcf694c6a0c..29d6b35efffe 100644 --- a/Documentation/networking/dl2k.txt +++ b/Documentation/networking/dl2k.txt @@ -1,7 +1,7 @@ D-Link DL2000-based Gigabit Ethernet Adapter Installation for Linux - Jan 29, 2002 + May 23, 2002 Contents ======== @@ -202,7 +202,7 @@ media=media_type - Specifies the media type the NIC operates at. 1000mbps_fd and 1000mbps_hd types are only available for fiber adapter. -vlan=[0|1] - Specifies the VLAN ID. If vlan=0, the +vlan=n - Specifies the VLAN ID. If vlan=0, the Virtual Local Area Network (VLAN) function is disable. @@ -211,24 +211,34 @@ jumbo=[0|1] - Specifies the jumbo frame support. If jumbo=1, function is disabled. Jumbo frame usually improve the performance int gigabit. + This feature need jumbo frame compatible + remote. -rx_coalesce=n - Rx frame count each interrupt. -rx_timeout=n - Rx DMA wait time for an interrupt. Proper - values of rx_coalesce and rx_timeout bring - a conspicuous performance in the fast machine. - Ex. rx_coalesce=5 and rx_timeout=750 - -tx_coalesce=n - Tx transmit count each TxComp interrupt. - Setting value larger than 1 will improve - performance, but this is possible to lower - stability in slow UP machines. By default, - tx_coalesce=1. (dl2k) +rx_coalesce=m - Number of rx frame handled each interrupt. +rx_timeout=n - Rx DMA wait time for an interrupt. + If set rx_coalesce > 0, hardware only assert + an interrupt for m frames. Hardware won't + assert rx interrupt until m frames received or + reach timeout of n * 640 nano seconds. + Set proper rx_coalesce and rx_timeout can + reduce congestion collapse and overload which + has been a bottlenect for high speed network. -tx_flow=[1|0] - Specifies the Tx flow control. If tx_flow=1, - the Tx flow control enable. + For example, rx_coalesce=10 rx_timeout=800. + that is, hardware assert only 1 interrupt + for 10 frames received or timeout of 512 us. + +tx_coalesce=n - Number of tx frame handled each interrupt. + Set n > 1 can reduce the interrupts + congestion usually lower performance of + high speed network card. Default is 16. -rx_flow=[1|0] - Specifies the Rx flow control. If rx_flow=1, - the Rx flow control enable. +tx_flow=[1|0] - Specifies the Tx flow control. If tx_flow=0, + the Tx flow control disable else driver + autodetect. +rx_flow=[1|0] - Specifies the Rx flow control. If rx_flow=0, + the Rx flow control enable else driver + autodetect. Configuration Script Sample diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index f26a591369aa..d591b962b06b 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -100,13 +100,16 @@ MODULE_PARM_DESC (multicast_filter_limit, "8139cp maximum number of filtered mul #define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ NETIF_MSG_LINK) +#define CP_NUM_STATS 14 /* struct cp_dma_stats, plus one */ +#define CP_STATS_SIZE 64 /* size in bytes of DMA stats block */ #define CP_REGS_SIZE (0xff + 1) #define CP_REGS_VER 1 /* version 1 */ #define CP_RX_RING_SIZE 64 #define CP_TX_RING_SIZE 64 #define CP_RING_BYTES \ ((sizeof(struct cp_desc) * CP_RX_RING_SIZE) + \ - (sizeof(struct cp_desc) * CP_TX_RING_SIZE)) + (sizeof(struct cp_desc) * CP_TX_RING_SIZE) + \ + CP_STATS_SIZE) #define NEXT_TX(N) (((N) + 1) & (CP_TX_RING_SIZE - 1)) #define NEXT_RX(N) (((N) + 1) & (CP_RX_RING_SIZE - 1)) #define TX_BUFFS_AVAIL(CP) \ @@ -135,6 +138,7 @@ enum { /* NIC register offsets */ MAC0 = 0x00, /* Ethernet hardware address. */ MAR0 = 0x08, /* Multicast filter. */ + StatsAddr = 0x10, /* 64-bit start addr of 64-byte DMA stats blk */ TxRingAddr = 0x20, /* 64-bit start addr of Tx ring */ HiTxRingAddr = 0x28, /* 64-bit start addr of high priority Tx ring */ Cmd = 0x37, /* Command register */ @@ -197,6 +201,9 @@ enum { RxErrLong = (1 << 21), /* Rx error, packet > 4096 bytes */ RxErrFIFO = (1 << 22), /* Rx error, FIFO overflowed, pkt bad */ + /* StatsAddr register */ + DumpStats = (1 << 3), /* Begin stats dump */ + /* RxConfig register */ RxCfgFIFOShift = 13, /* Shift, to get Rx FIFO thresh value */ RxCfgDMAShift = 8, /* Shift, to get Rx Max DMA value */ @@ -284,6 +291,22 @@ struct ring_info { unsigned frag; }; +struct cp_dma_stats { + u64 tx_ok; + u64 rx_ok; + u64 tx_err; + u32 rx_err; + u16 rx_fifo; + u16 frame_align; + u32 tx_ok_1col; + u32 tx_ok_mcol; + u64 rx_ok_phys; + u64 rx_ok_bcast; + u32 rx_ok_mcast; + u16 tx_abort; + u16 tx_underrun; +} __attribute__((packed)); + struct cp_extra_stats { unsigned long rx_frags; }; @@ -312,6 +335,8 @@ struct cp_private { struct net_device_stats net_stats; struct cp_extra_stats cp_stats; + struct cp_dma_stats *nic_stats; + dma_addr_t nic_stats_dma; struct pci_dev *pdev; u32 rx_config; @@ -373,6 +398,26 @@ static struct pci_device_id cp_pci_tbl[] __devinitdata = { }; MODULE_DEVICE_TABLE(pci, cp_pci_tbl); +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "tx_ok" }, + { "rx_ok" }, + { "tx_err" }, + { "rx_err" }, + { "rx_fifo" }, + { "frame_align" }, + { "tx_ok_1col" }, + { "tx_ok_mcol" }, + { "rx_ok_phys" }, + { "rx_ok_bcast" }, + { "rx_ok_mcast" }, + { "tx_abort" }, + { "tx_underrun" }, + { "rx_frags" }, +}; + + static inline void cp_set_rxbufsize (struct cp_private *cp) { unsigned int mtu = cp->dev->mtu; @@ -949,9 +994,9 @@ static void cp_init_hw (struct cp_private *cp) cpw32_f(HiTxRingAddr + 4, 0); cpw32_f(RxRingAddr, cp->ring_dma); - cpw32_f(RxRingAddr + 4, 0); + cpw32_f(RxRingAddr + 4, 0); /* FIXME: 64-bit PCI */ cpw32_f(TxRingAddr, cp->ring_dma + (sizeof(struct cp_desc) * CP_RX_RING_SIZE)); - cpw32_f(TxRingAddr + 4, 0); + cpw32_f(TxRingAddr + 4, 0); /* FIXME: 64-bit PCI */ cpw16(MultiIntr, 0); @@ -1010,10 +1055,19 @@ static int cp_init_rings (struct cp_private *cp) static int cp_alloc_rings (struct cp_private *cp) { - cp->rx_ring = pci_alloc_consistent(cp->pdev, CP_RING_BYTES, &cp->ring_dma); - if (!cp->rx_ring) + void *mem; + + mem = pci_alloc_consistent(cp->pdev, CP_RING_BYTES, &cp->ring_dma); + if (!mem) return -ENOMEM; + + cp->rx_ring = mem; cp->tx_ring = &cp->rx_ring[CP_RX_RING_SIZE]; + + mem += (CP_RING_BYTES - CP_STATS_SIZE); + cp->nic_stats = mem; + cp->nic_stats_dma = cp->ring_dma + (CP_RING_BYTES - CP_STATS_SIZE); + return cp_init_rings(cp); } @@ -1052,6 +1106,7 @@ static void cp_free_rings (struct cp_private *cp) pci_free_consistent(cp->pdev, CP_RING_BYTES, cp->rx_ring, cp->ring_dma); cp->rx_ring = NULL; cp->tx_ring = NULL; + cp->nic_stats = NULL; } static int cp_open (struct net_device *dev) @@ -1181,6 +1236,7 @@ static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) strcpy (info.version, DRV_VERSION); strcpy (info.bus_info, cp->pdev->slot_name); info.regdump_len = CP_REGS_SIZE; + info.n_stats = CP_NUM_STATS; if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; @@ -1359,6 +1415,83 @@ err_out_gregs: return 0; } + /* get string list(s) */ + case ETHTOOL_GSTRINGS: { + struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; + + if (copy_from_user(&estr, useraddr, sizeof(estr))) + return -EFAULT; + if (estr.string_set != ETH_SS_STATS) + return -EINVAL; + + estr.len = CP_NUM_STATS; + if (copy_to_user(useraddr, &estr, sizeof(estr))) + return -EFAULT; + if (copy_to_user(useraddr + sizeof(estr), + ðtool_stats_keys, + sizeof(ethtool_stats_keys))) + return -EFAULT; + return 0; + } + + /* get NIC-specific statistics */ + case ETHTOOL_GSTATS: { + struct ethtool_stats estats = { ETHTOOL_GSTATS }; + u64 *tmp_stats; + unsigned int work = 100; + const unsigned int sz = sizeof(u64) * CP_NUM_STATS; + int i; + + /* begin NIC statistics dump */ + cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ + cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); + cpr32(StatsAddr); + + estats.n_stats = CP_NUM_STATS; + if (copy_to_user(useraddr, &estats, sizeof(estats))) + return -EFAULT; + + while (work-- > 0) { + if ((cpr32(StatsAddr) & DumpStats) == 0) + break; + cpu_relax(); + } + + if (cpr32(StatsAddr) & DumpStats) + return -EIO; + + tmp_stats = kmalloc(sz, GFP_KERNEL); + if (!tmp_stats) + return -ENOMEM; + memset(tmp_stats, 0, sz); + + i = 0; + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun); + tmp_stats[i++] = cp->cp_stats.rx_frags; + if (i != CP_NUM_STATS) + BUG(); + + i = copy_to_user(useraddr + sizeof(estats), + tmp_stats, sz); + kfree(tmp_stats); + + if (i) + return -EFAULT; + return 0; + } + default: break; } diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 76d6a4260b3f..aa6e13a6788c 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -1,6 +1,6 @@ /* D-Link DL2000-based Gigabit Ethernet Adapter Linux driver */ /* - Copyright (c) 2001,2002 by D-Link Corporation + Copyright (c) 2001, 2002 by D-Link Corporation Written by Edward Peng.<edward_peng@dlink.com.tw> Created 03-May-2001, base on Linux' sundance.c. @@ -33,12 +33,13 @@ 1.11 2002/05/23 Added ISR schedule scheme. Fixed miscount of rx frame error for DGE-550SX. Fixed VLAN bug. + 1.12 2002/06/13 Lock tx_coalesce=1 on 10/100Mbps mode. */ #include "dl2k.h" static char version[] __devinitdata = - KERN_INFO "D-Link DL2000-based linux driver v1.11 2002/05/23\n"; + KERN_INFO "D-Link DL2000-based linux driver v1.12 2002/06/13\n"; #define MAX_UNITS 8 static int mtu[MAX_UNITS]; @@ -138,15 +139,15 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) } SET_MODULE_OWNER (dev); -#ifdef USE_IO_OPS - ioaddr = pci_resource_start (pdev, 0); -#else +#ifdef MEM_MAPPING ioaddr = pci_resource_start (pdev, 1); ioaddr = (long) ioremap (ioaddr, RIO_IO_SIZE); if (!ioaddr) { err = -ENOMEM; goto err_out_dev; } +#else + ioaddr = pci_resource_start (pdev, 0); #endif dev->base_addr = ioaddr; dev->irq = irq; @@ -158,6 +159,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) /* Parse manual configuration */ np->an_enable = 1; + np->tx_coalesce = 1; if (card_idx < MAX_UNITS) { if (media[card_idx] != NULL) { np->an_enable = 0; @@ -213,7 +215,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) if (tx_coalesce < 1) tx_coalesce = 1; - if (tx_coalesce > TX_RING_SIZE-1) + else if (tx_coalesce > TX_RING_SIZE-1) tx_coalesce = TX_RING_SIZE - 1; } dev->open = &rio_open; @@ -303,7 +305,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) err_out_unmap_tx: pci_free_consistent (pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma); err_out_iounmap: -#ifndef USE_IO_OPS +#ifdef MEM_MAPPING iounmap ((void *) ioaddr); err_out_dev: @@ -361,7 +363,7 @@ parse_eeprom (struct net_device *dev) } /* Check CRC */ - crc = ~ether_crc_le(256 - 4, sromdata); + crc = ~ether_crc_le (256 - 4, sromdata); if (psrom->crc != crc) { printk (KERN_ERR "%s: EEPROM data CRC error.\n", dev->name); return -1; @@ -636,7 +638,7 @@ start_xmit (struct sk_buff *skb, struct net_device *dev) /* DL2K bug: DMA fails to get next descriptor ptr in 10Mbps mode * Work around: Always use 1 descriptor in 10Mbps mode */ - if (entry % tx_coalesce == 0 || np->speed == 10) + if (entry % np->tx_coalesce == 0 || np->speed == 10) txdesc->status = cpu_to_le64 (entry | tfc_vlan_tag | WordAlignDisable | TxDMAIndicate | @@ -936,6 +938,10 @@ rio_error (struct net_device *dev, int int_status) mii_get_media_pcs (dev); else mii_get_media (dev); + if (np->speed == 1000) + np->tx_coalesce = tx_coalesce; + else + np->tx_coalesce = 1; macctrl = 0; macctrl |= (np->vlan) ? AutoVLANuntagging : 0; macctrl |= (np->full_duplex) ? DuplexSelect : 0; @@ -1671,7 +1677,7 @@ rio_remove1 (struct pci_dev *pdev) np->rx_ring_dma); pci_free_consistent (pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma); -#ifndef USE_IO_OPS +#ifdef MEM_MAPPING iounmap ((char *) (dev->base_addr)); #endif kfree (dev); diff --git a/drivers/net/dl2k.h b/drivers/net/dl2k.h index a3e3b30fc207..3108493bd048 100644 --- a/drivers/net/dl2k.h +++ b/drivers/net/dl2k.h @@ -33,15 +33,15 @@ #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/time.h> -#define TX_RING_SIZE 128 -#define TX_QUEUE_LEN 120 /* Limit ring entries actually used. */ -#define RX_RING_SIZE 128 +#define TX_RING_SIZE 256 +#define TX_QUEUE_LEN (TX_RING_SIZE - 1) /* Limit ring entries actually used.*/ +#define RX_RING_SIZE 256 #define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct netdev_desc) #define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct netdev_desc) /* This driver was written to use PCI memory space, however x86-oriented hardware often uses I/O space accesses. */ -#ifdef USE_IO_OPS +#ifndef MEM_MAPPING #undef readb #undef readw #undef readl @@ -658,6 +658,7 @@ struct netdev_private { unsigned int chip_id; /* PCI table chip id */ unsigned int rx_coalesce; /* Maximum frames each RxDMAComplete intr */ unsigned int rx_timeout; /* Wait time between RxDMAComplete intr */ + unsigned int tx_coalesce; /* Maximum frames each tx interrupt */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int an_enable:2; /* Auto-Negotiated Enable */ unsigned int jumbo:1; /* Jumbo frame enable */ @@ -681,10 +682,10 @@ struct netdev_private { }; /* The station address location in the EEPROM. */ -#ifdef USE_IO_OPS -#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0) -#else +#ifdef MEM_MAPPING #define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0) #endif /* The struct pci_device_id consist of: vendor, device Vendor and device ID to match (or PCI_ANY_ID) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index a4af66c7128a..6165f292f5ac 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -39,7 +39,8 @@ struct ethtool_drvinfo { char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ /* For PCI devices, use pci_dev->slot_name. */ char reserved1[32]; - char reserved2[20]; + char reserved2[16]; + u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ u32 testinfo_len; u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ @@ -242,6 +243,13 @@ struct ethtool_test { u64 data[0]; }; +/* for dumping NIC-specific statistics */ +struct ethtool_stats { + u32 cmd; /* ETHTOOL_GSTATS */ + u32 n_stats; /* number of u64's being returned */ + u64 data[0]; +}; + /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* Get settings. */ #define ETHTOOL_SSET 0x00000002 /* Set settings, privileged. */ @@ -272,6 +280,7 @@ struct ethtool_test { #define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ #define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET |
