#include <linux/config.h>
#include <linux/module.h>
+ #include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>");
MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver");
+ MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
static int debug = -1;
- MODULE_PARM (debug, "i");
+ module_param(debug, int, 0);
MODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number");
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
The RTL chips use a 64 element hash table based on the Ethernet CRC. */
static int multicast_filter_limit = 32;
- MODULE_PARM (multicast_filter_limit, "i");
+ module_param(multicast_filter_limit, int, 0);
MODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses");
#define PFX DRV_NAME ": "
RingEnd = (1 << 30), /* End of descriptor ring */
FirstFrag = (1 << 29), /* First segment of a packet */
LastFrag = (1 << 28), /* Final segment of a packet */
+ LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */
+ MSSShift = 16, /* MSS value position */
+ MSSMask = 0xfff, /* MSS value: 11 bits */
TxError = (1 << 23), /* Tx error summary */
RxError = (1 << 20), /* Rx error summary */
IPCS = (1 << 18), /* Calculate IP checksum */
struct ring_info {
struct sk_buff *skb;
dma_addr_t mapping;
- unsigned frag;
+ u32 len;
};
struct cp_dma_stats {
static void __cp_set_rx_mode (struct net_device *dev);
static void cp_tx (struct cp_private *cp);
static void cp_clean_rings (struct cp_private *cp);
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ static void cp_poll_controller(struct net_device *dev);
+ #endif
static struct pci_device_id cp_pci_tbl[] = {
{ PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139,
return IRQ_HANDLED;
}
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ /*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+ static void cp_poll_controller(struct net_device *dev)
+ {
+ disable_irq(dev->irq);
+ cp_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+ }
+ #endif
+
static void cp_tx (struct cp_private *cp)
{
unsigned tx_head = cp->tx_head;
BUG();
pci_unmap_single(cp->pdev, cp->tx_skb[tx_tail].mapping,
- skb->len, PCI_DMA_TODEVICE);
+ cp->tx_skb[tx_tail].len, PCI_DMA_TODEVICE);
if (status & LastFrag) {
if (status & (TxError | TxFIFOUnder)) {
{
struct cp_private *cp = netdev_priv(dev);
unsigned entry;
- u32 eor;
+ u32 eor, flags;
#if CP_VLAN_TAG_USED
u32 vlan_tag = 0;
#endif
+ int mss = 0;
spin_lock_irq(&cp->lock);
entry = cp->tx_head;
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
+ if (dev->features & NETIF_F_TSO)
+ mss = skb_shinfo(skb)->tso_size;
+
if (skb_shinfo(skb)->nr_frags == 0) {
struct cp_desc *txd = &cp->tx_ring[entry];
u32 len;
txd->addr = cpu_to_le64(mapping);
wmb();
- if (skb->ip_summed == CHECKSUM_HW) {
+ flags = eor | len | DescOwn | FirstFrag | LastFrag;
+
+ if (mss)
+ flags |= LargeSend | ((mss & MSSMask) << MSSShift);
+ else if (skb->ip_summed == CHECKSUM_HW) {
const struct iphdr *ip = skb->nh.iph;
if (ip->protocol == IPPROTO_TCP)
- txd->opts1 = cpu_to_le32(eor | len | DescOwn |
- FirstFrag | LastFrag |
- IPCS | TCPCS);
+ flags |= IPCS | TCPCS;
else if (ip->protocol == IPPROTO_UDP)
- txd->opts1 = cpu_to_le32(eor | len | DescOwn |
- FirstFrag | LastFrag |
- IPCS | UDPCS);
+ flags |= IPCS | UDPCS;
else
- BUG();
- } else
- txd->opts1 = cpu_to_le32(eor | len | DescOwn |
- FirstFrag | LastFrag);
+ WARN_ON(1); /* we need a WARN() */
+ }
+
+ txd->opts1 = cpu_to_le32(flags);
wmb();
cp->tx_skb[entry].skb = skb;
cp->tx_skb[entry].mapping = mapping;
- cp->tx_skb[entry].frag = 0;
+ cp->tx_skb[entry].len = len;
entry = NEXT_TX(entry);
} else {
struct cp_desc *txd;
first_len, PCI_DMA_TODEVICE);
cp->tx_skb[entry].skb = skb;
cp->tx_skb[entry].mapping = first_mapping;
- cp->tx_skb[entry].frag = 1;
+ cp->tx_skb[entry].len = first_len;
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
len, PCI_DMA_TODEVICE);
eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
- if (skb->ip_summed == CHECKSUM_HW) {
- ctrl = eor | len | DescOwn | IPCS;
+ ctrl = eor | len | DescOwn;
+
+ if (mss)
+ ctrl |= LargeSend |
+ ((mss & MSSMask) << MSSShift);
+ else if (skb->ip_summed == CHECKSUM_HW) {
if (ip->protocol == IPPROTO_TCP)
- ctrl |= TCPCS;
+ ctrl |= IPCS | TCPCS;
else if (ip->protocol == IPPROTO_UDP)
- ctrl |= UDPCS;
+ ctrl |= IPCS | UDPCS;
else
BUG();
- } else
- ctrl = eor | len | DescOwn;
+ }
if (frag == skb_shinfo(skb)->nr_frags - 1)
ctrl |= LastFrag;
cp->tx_skb[entry].skb = skb;
cp->tx_skb[entry].mapping = mapping;
- cp->tx_skb[entry].frag = frag + 2;
+ cp->tx_skb[entry].len = len;
entry = NEXT_TX(entry);
}
cp->rx_skb[i].mapping = pci_map_single(cp->pdev,
skb->tail, cp->rx_buf_sz, PCI_DMA_FROMDEVICE);
cp->rx_skb[i].skb = skb;
- cp->rx_skb[i].frag = 0;
cp->rx_ring[i].opts2 = 0;
cp->rx_ring[i].addr = cpu_to_le64(cp->rx_skb[i].mapping);
{
unsigned i;
- memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE);
- memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE);
-
for (i = 0; i < CP_RX_RING_SIZE; i++) {
if (cp->rx_skb[i].skb) {
pci_unmap_single(cp->pdev, cp->rx_skb[i].mapping,
for (i = 0; i < CP_TX_RING_SIZE; i++) {
if (cp->tx_skb[i].skb) {
struct sk_buff *skb = cp->tx_skb[i].skb;
+
pci_unmap_single(cp->pdev, cp->tx_skb[i].mapping,
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb(skb);
+ cp->tx_skb[i].len, PCI_DMA_TODEVICE);
+ if (le32_to_cpu(cp->tx_ring[i].opts1) & LastFrag)
+ dev_kfree_skb(skb);
cp->net_stats.tx_dropped++;
}
}
+ memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE);
+ memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE);
+
memset(&cp->rx_skb, 0, sizeof(struct ring_info) * CP_RX_RING_SIZE);
memset(&cp->tx_skb, 0, sizeof(struct ring_info) * CP_TX_RING_SIZE);
}
.set_tx_csum = ethtool_op_set_tx_csum, /* local! */
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
.get_regs = cp_get_regs,
.get_wol = cp_get_wol,
.set_wol = cp_set_wol,
/* Configure DMA attributes. */
if ((sizeof(dma_addr_t) > 4) &&
- !pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL) &&
- !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+ !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) &&
+ !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
pci_using_dac = 1;
} else {
pci_using_dac = 0;
- rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (rc) {
printk(KERN_ERR PFX "No usable DMA configuration, "
"aborting.\n");
goto err_out_res;
}
- rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+ rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
if (rc) {
printk(KERN_ERR PFX "No usable consistent DMA configuration, "
"aborting.\n");
dev->get_stats = cp_get_stats;
dev->do_ioctl = cp_ioctl;
dev->poll = cp_rx_poll;
+ #ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = cp_poll_controller;
+ #endif
dev->weight = 16; /* arbitrary? from NAPI_HOWTO.txt. */
#ifdef BROKEN
dev->change_mtu = cp_change_mtu;
if (pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
+ #if 0 /* disabled by default until verified */
+ dev->features |= NETIF_F_TSO;
+ #endif
+
dev->irq = pdev->irq;
rc = register_netdev(dev);
#else
#define DRIVERNAPI "-NAPI"
#endif
- #define DRV_VERSION "1.0.90-k2"DRIVERNAPI
-char ixgb_driver_version[] = "1.0.95-k2"DRIVERNAPI;
++
++#define DRV_VERSION "1.0.95-k2"DRIVERNAPI
+char ixgb_driver_version[] = DRV_VERSION;
char ixgb_copyright[] = "Copyright (c) 1999-2005 Intel Corporation.";
/* ixgb_pci_tbl - PCI Device ID Table
static int ixgb_set_mac(struct net_device *netdev, void *p);
static irqreturn_t ixgb_intr(int irq, void *data, struct pt_regs *regs);
static boolean_t ixgb_clean_tx_irq(struct ixgb_adapter *adapter);
+
#ifdef CONFIG_IXGB_NAPI
static int ixgb_clean(struct net_device *netdev, int *budget);
static boolean_t ixgb_clean_rx_irq(struct ixgb_adapter *adapter,
static void ixgb_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
static void ixgb_restore_vlan(struct ixgb_adapter *adapter);
- static int ixgb_notify_reboot(struct notifier_block *, unsigned long event,
- void *ptr);
- static int ixgb_suspend(struct pci_dev *pdev, uint32_t state);
-
#ifdef CONFIG_NET_POLL_CONTROLLER
/* for netdump / net console */
static void ixgb_netpoll(struct net_device *dev);
#endif
- struct notifier_block ixgb_notifier_reboot = {
- .notifier_call = ixgb_notify_reboot,
- .next = NULL,
- .priority = 0
- };
-
/* Exported from other modules */
extern void ixgb_check_options(struct ixgb_adapter *adapter);
static struct pci_driver ixgb_driver = {
- .name = ixgb_driver_name,
+ .name = ixgb_driver_name,
.id_table = ixgb_pci_tbl,
- .probe = ixgb_probe,
- .remove = __devexit_p(ixgb_remove),
- /* Power Managment Hooks */
- .suspend = NULL,
- .resume = NULL
+ .probe = ixgb_probe,
+ .remove = __devexit_p(ixgb_remove),
};
MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
MODULE_DESCRIPTION("Intel(R) PRO/10GbE Network Driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
/* some defines for controlling descriptor fetches in h/w */
#define RXDCTL_PTHRESH_DEFAULT 128 /* chip considers prefech below this */
static int __init
ixgb_init_module(void)
{
- int ret;
printk(KERN_INFO "%s - version %s\n",
ixgb_driver_string, ixgb_driver_version);
printk(KERN_INFO "%s\n", ixgb_copyright);
- ret = pci_module_init(&ixgb_driver);
- if(ret >= 0) {
- register_reboot_notifier(&ixgb_notifier_reboot);
- }
- return ret;
+ return pci_module_init(&ixgb_driver);
}
module_init(ixgb_init_module);
static void __exit
ixgb_exit_module(void)
{
- unregister_reboot_notifier(&ixgb_notifier_reboot);
pci_unregister_driver(&ixgb_driver);
}
{
if(atomic_dec_and_test(&adapter->irq_sem)) {
IXGB_WRITE_REG(&adapter->hw, IMS,
- IXGB_INT_RXT0 | IXGB_INT_RXDMT0 | IXGB_INT_TXDW |
- IXGB_INT_RXO | IXGB_INT_LSC);
+ IXGB_INT_RXT0 | IXGB_INT_RXDMT0 | IXGB_INT_TXDW |
+ IXGB_INT_LSC);
IXGB_WRITE_FLUSH(&adapter->hw);
}
}
| IXGB_CONTEXT_DESC_CMD_TSE
| IXGB_CONTEXT_DESC_CMD_IP
| IXGB_CONTEXT_DESC_CMD_TCP
- | IXGB_CONTEXT_DESC_CMD_RS
| IXGB_CONTEXT_DESC_CMD_IDE
| (skb->len - (hdr_len)));
+
if(++i == adapter->tx_ring.count) i = 0;
adapter->tx_ring.next_to_use = i;
context_desc->mss = 0;
context_desc->cmd_type_len =
cpu_to_le32(IXGB_CONTEXT_DESC_TYPE
- | IXGB_TX_DESC_CMD_RS
- | IXGB_TX_DESC_CMD_IDE);
+ | IXGB_TX_DESC_CMD_IDE);
if(++i == adapter->tx_ring.count) i = 0;
adapter->tx_ring.next_to_use = i;
unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
unsigned int f;
+
len -= skb->data_len;
i = tx_ring->next_to_use;
void
ixgb_update_stats(struct ixgb_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
+
+ if((netdev->flags & IFF_PROMISC) || (netdev->flags & IFF_ALLMULTI) ||
+ (netdev->mc_count > IXGB_MAX_NUM_MULTICAST_ADDRESSES)) {
+ u64 multi = IXGB_READ_REG(&adapter->hw, MPRCL);
+ u32 bcast_l = IXGB_READ_REG(&adapter->hw, BPRCL);
+ u32 bcast_h = IXGB_READ_REG(&adapter->hw, BPRCH);
+ u64 bcast = ((u64)bcast_h << 32) | bcast_l;
+
+ multi |= ((u64)IXGB_READ_REG(&adapter->hw, MPRCH) << 32);
+ /* fix up multicast stats by removing broadcasts */
+ multi -= bcast;
+
+ adapter->stats.mprcl += (multi & 0xFFFFFFFF);
+ adapter->stats.mprch += (multi >> 32);
+ adapter->stats.bprcl += bcast_l;
+ adapter->stats.bprch += bcast_h;
+ } else {
+ adapter->stats.mprcl += IXGB_READ_REG(&adapter->hw, MPRCL);
+ adapter->stats.mprch += IXGB_READ_REG(&adapter->hw, MPRCH);
+ adapter->stats.bprcl += IXGB_READ_REG(&adapter->hw, BPRCL);
+ adapter->stats.bprch += IXGB_READ_REG(&adapter->hw, BPRCH);
+ }
adapter->stats.tprl += IXGB_READ_REG(&adapter->hw, TPRL);
adapter->stats.tprh += IXGB_READ_REG(&adapter->hw, TPRH);
adapter->stats.gprcl += IXGB_READ_REG(&adapter->hw, GPRCL);
adapter->stats.gprch += IXGB_READ_REG(&adapter->hw, GPRCH);
- adapter->stats.bprcl += IXGB_READ_REG(&adapter->hw, BPRCL);
- adapter->stats.bprch += IXGB_READ_REG(&adapter->hw, BPRCH);
- adapter->stats.mprcl += IXGB_READ_REG(&adapter->hw, MPRCL);
- adapter->stats.mprch += IXGB_READ_REG(&adapter->hw, MPRCH);
adapter->stats.uprcl += IXGB_READ_REG(&adapter->hw, UPRCL);
adapter->stats.uprch += IXGB_READ_REG(&adapter->hw, UPRCH);
adapter->stats.vprcl += IXGB_READ_REG(&adapter->hw, VPRCL);
struct pci_dev *pdev = adapter->pdev;
struct ixgb_rx_desc *rx_desc, *next_rxd;
struct ixgb_buffer *buffer_info, *next_buffer, *next2_buffer;
- struct sk_buff *skb, *next_skb;
uint32_t length;
unsigned int i, j;
boolean_t cleaned = FALSE;
buffer_info = &rx_ring->buffer_info[i];
while(rx_desc->status & IXGB_RX_DESC_STATUS_DD) {
+ struct sk_buff *skb, *next_skb;
+ u8 status;
#ifdef CONFIG_IXGB_NAPI
if(*work_done >= work_to_do)
(*work_done)++;
#endif
+ status = rx_desc->status;
skb = buffer_info->skb;
+
prefetch(skb->data);
if(++i == rx_ring->count) i = 0;
next_skb = next_buffer->skb;
prefetch(next_skb);
-
cleaned = TRUE;
pci_unmap_single(pdev,
length = le16_to_cpu(rx_desc->length);
- if(unlikely(!(rx_desc->status & IXGB_RX_DESC_STATUS_EOP))) {
+ if(unlikely(!(status & IXGB_RX_DESC_STATUS_EOP))) {
/* All receives must fit into a single buffer */
"length<%x>\n", length);
dev_kfree_skb_irq(skb);
- rx_desc->status = 0;
- buffer_info->skb = NULL;
-
- rx_desc = next_rxd;
- buffer_info = next_buffer;
- continue;
+ goto rxdesc_done;
}
if (unlikely(rx_desc->errors
IXGB_RX_DESC_ERRORS_RXE))) {
dev_kfree_skb_irq(skb);
- rx_desc->status = 0;
- buffer_info->skb = NULL;
-
- rx_desc = next_rxd;
- buffer_info = next_buffer;
- continue;
+ goto rxdesc_done;
}
/* Good Receive */
skb->protocol = eth_type_trans(skb, netdev);
#ifdef CONFIG_IXGB_NAPI
- if(adapter->vlgrp && (rx_desc->status & IXGB_RX_DESC_STATUS_VP)) {
+ if(adapter->vlgrp && (status & IXGB_RX_DESC_STATUS_VP)) {
vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
le16_to_cpu(rx_desc->special) &
IXGB_RX_DESC_SPECIAL_VLAN_MASK);
netif_receive_skb(skb);
}
#else /* CONFIG_IXGB_NAPI */
- if(adapter->vlgrp && (rx_desc->status & IXGB_RX_DESC_STATUS_VP)) {
+ if(adapter->vlgrp && (status & IXGB_RX_DESC_STATUS_VP)) {
vlan_hwaccel_rx(skb, adapter->vlgrp,
le16_to_cpu(rx_desc->special) &
IXGB_RX_DESC_SPECIAL_VLAN_MASK);
#endif /* CONFIG_IXGB_NAPI */
netdev->last_rx = jiffies;
+ rxdesc_done:
+ /* clean up descriptor, might be written over by hw */
rx_desc->status = 0;
buffer_info->skb = NULL;
+ /* use prefetched values */
rx_desc = next_rxd;
buffer_info = next_buffer;
}
num_group_tail_writes = IXGB_RX_BUFFER_WRITE;
- /* leave one descriptor unused */
- while(--cleancount > 0) {
+ /* leave three descriptors unused */
+ while(--cleancount > 2) {
rx_desc = IXGB_RX_DESC(*rx_ring, i);
skb = dev_alloc_skb(adapter->rx_buffer_len + NET_IP_ALIGN);
PCI_DMA_FROMDEVICE);
rx_desc->buff_addr = cpu_to_le64(buffer_info->dma);
+ /* guarantee DD bit not set now before h/w gets descriptor
+ * this is the rest of the workaround for h/w double
+ * writeback. */
+ rx_desc->status = 0;
if((i & ~(num_group_tail_writes- 1)) == i) {
/* Force memory writes to complete before letting h/w
}
}
- /**
- * ixgb_notify_reboot - handles OS notification of reboot event.
- * @param nb notifier block, unused
- * @param event Event being passed to driver to act upon
- * @param p A pointer to our net device
- **/
- static int
- ixgb_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
- {
- struct pci_dev *pdev = NULL;
-
- switch(event) {
- case SYS_DOWN:
- case SYS_HALT:
- case SYS_POWER_OFF:
- while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
- if (pci_dev_driver(pdev) == &ixgb_driver)
- ixgb_suspend(pdev, 3);
- }
- }
- return NOTIFY_DONE;
- }
-
- /**
- * ixgb_suspend - driver suspend function called from notify.
- * @param pdev pci driver structure used for passing to
- * @param state power state to enter
- **/
- static int
- ixgb_suspend(struct pci_dev *pdev, uint32_t state)
- {
- struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgb_adapter *adapter = netdev->priv;
-
- netif_device_detach(netdev);
-
- if(netif_running(netdev))
- ixgb_down(adapter, TRUE);
-
- pci_save_state(pdev);
-
- state = (state > 0) ? 3 : 0;
- pci_set_power_state(pdev, state);
- msec_delay(200);
-
- return 0;
- }
-
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
* Polling 'interrupt' - used by things like netconsole to send skbs
static void ixgb_netpoll(struct net_device *dev)
{
struct ixgb_adapter *adapter = dev->priv;
+
disable_irq(adapter->pdev->irq);
ixgb_intr(adapter->pdev->irq, dev, NULL);
enable_irq(adapter->pdev->irq);