diff options
Diffstat (limited to 'drivers/net/myri10ge/myri10ge.c')
-rw-r--r-- | drivers/net/myri10ge/myri10ge.c | 268 |
1 files changed, 183 insertions, 85 deletions
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index bf84849600c..1d2247554a3 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -1,7 +1,7 @@ /************************************************************************* * myri10ge.c: Myricom Myri-10G Ethernet driver. * - * Copyright (C) 2005 - 2009 Myricom, Inc. + * Copyright (C) 2005 - 2011 Myricom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,7 +79,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.5.2-1.459" +#define MYRI10GE_VERSION_STR "1.5.3-1.534" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -193,6 +193,7 @@ struct myri10ge_slice_state { int watchdog_tx_done; int watchdog_tx_req; int watchdog_rx_done; + int stuck; #ifdef CONFIG_MYRI10GE_DCA int cached_dca_tag; int cpu; @@ -210,7 +211,6 @@ struct myri10ge_priv { int big_bytes; int max_intr_slots; struct net_device *dev; - spinlock_t stats_lock; u8 __iomem *sram; int sram_size; unsigned long board_span; @@ -377,7 +377,8 @@ static inline void put_be32(__be32 val, __be32 __iomem * p) __raw_writel((__force __u32) val, (__force void __iomem *)p); } -static struct net_device_stats *myri10ge_get_stats(struct net_device *dev); +static struct rtnl_link_stats64 *myri10ge_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats); static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated) { @@ -1013,7 +1014,7 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) cmd.data2 = i; status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA, &cmd, 0); - }; + } status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0); @@ -1080,11 +1081,14 @@ static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on) int ret, cap, err; u16 ctl; - cap = pci_find_capability(pdev, PCI_CAP_ID_EXP); + cap = pci_pcie_cap(pdev); if (!cap) return 0; err = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl); + if (err) + return 0; + ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4; if (ret != on) { ctl &= ~PCI_EXP_DEVCTL_RELAX_EN; @@ -1139,20 +1143,19 @@ static void myri10ge_setup_dca(struct myri10ge_priv *mgp) mgp->ss[i].cpu = -1; mgp->ss[i].cached_dca_tag = -1; myri10ge_update_dca(&mgp->ss[i]); - } + } } static void myri10ge_teardown_dca(struct myri10ge_priv *mgp) { struct pci_dev *pdev = mgp->pdev; - int err; if (!mgp->dca_enabled) return; mgp->dca_enabled = 0; if (mgp->relaxed_order) myri10ge_toggle_relaxed(pdev, 1); - err = dca_remove_requester(&pdev->dev); + dca_remove_requester(&pdev->dev); } static int myri10ge_notify_dca_device(struct device *dev, void *data) @@ -1313,7 +1316,7 @@ myri10ge_unmap_rx_page(struct pci_dev *pdev, static inline int myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum, - int lro_enabled) + bool lro_enabled) { struct myri10ge_priv *mgp = ss->mgp; struct sk_buff *skb; @@ -1461,7 +1464,8 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index) /* start the queue if we've stopped it */ if (netif_tx_queue_stopped(dev_queue) && - tx->req - tx->done < (tx->mask >> 1)) { + tx->req - tx->done < (tx->mask >> 1) && + ss->mgp->running == MYRI10GE_ETH_RUNNING) { tx->wake_queue++; netif_tx_wake_queue(dev_queue); } @@ -1472,11 +1476,9 @@ myri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget) { struct myri10ge_rx_done *rx_done = &ss->rx_done; struct myri10ge_priv *mgp = ss->mgp; - unsigned long rx_bytes = 0; unsigned long rx_packets = 0; unsigned long rx_ok; - int idx = rx_done->idx; int cnt = rx_done->cnt; int work_done = 0; @@ -1529,16 +1531,14 @@ static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp) mgp->link_state = link_up; if (mgp->link_state == MXGEFW_LINK_UP) { - if (netif_msg_link(mgp)) - netdev_info(mgp->dev, "link up\n"); + netif_info(mgp, link, mgp->dev, "link up\n"); netif_carrier_on(mgp->dev); mgp->link_changes++; } else { - if (netif_msg_link(mgp)) - netdev_info(mgp->dev, "link %s\n", - link_up == MXGEFW_LINK_MYRINET ? + netif_info(mgp, link, mgp->dev, "link %s\n", + (link_up == MXGEFW_LINK_MYRINET ? "mismatch (Myrinet detected)" : - "down"); + "down")); netif_carrier_off(mgp->dev); mgp->link_changes++; } @@ -1619,7 +1619,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg) if (send_done_count != tx->pkt_done) myri10ge_tx_done(ss, (int)send_done_count); if (unlikely(i > myri10ge_max_irq_loops)) { - netdev_err(mgp->dev, "irq stuck?\n"); + netdev_warn(mgp->dev, "irq stuck?\n"); stats->valid = 0; schedule_work(&mgp->watchdog_work); } @@ -1783,9 +1783,8 @@ static const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = { "----------- slice ---------", "tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt", - "wake_queue", "stop_queue", "tx_linearized", "LRO aggregated", - "LRO flushed", - "LRO avg aggr", "LRO no_desc" + "wake_queue", "stop_queue", "tx_linearized", + "LRO aggregated", "LRO flushed", "LRO avg aggr", "LRO no_desc", }; #define MYRI10GE_NET_STATS_LEN 21 @@ -1831,13 +1830,15 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, { struct myri10ge_priv *mgp = netdev_priv(netdev); struct myri10ge_slice_state *ss; + struct rtnl_link_stats64 link_stats; int slice; int i; /* force stats update */ - (void)myri10ge_get_stats(netdev); + memset(&link_stats, 0, sizeof(link_stats)); + (void)myri10ge_get_stats(netdev, &link_stats); for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++) - data[i] = ((unsigned long *)&netdev->stats)[i]; + data[i] = ((u64 *)&link_stats)[i]; data[i++] = (unsigned int)mgp->tx_boundary; data[i++] = (unsigned int)mgp->wc_enabled; @@ -1907,6 +1908,60 @@ static u32 myri10ge_get_msglevel(struct net_device *netdev) return mgp->msg_enable; } +/* + * Use a low-level command to change the LED behavior. Rather than + * blinking (which is the normal case), when identify is used, the + * yellow LED turns solid. + */ +static int myri10ge_led(struct myri10ge_priv *mgp, int on) +{ + struct mcp_gen_header *hdr; + struct device *dev = &mgp->pdev->dev; + size_t hdr_off, pattern_off, hdr_len; + u32 pattern = 0xfffffffe; + + /* find running firmware header */ + hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)); + if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) { + dev_err(dev, "Running firmware has bad header offset (%d)\n", + (int)hdr_off); + return -EIO; + } + hdr_len = swab32(readl(mgp->sram + hdr_off + + offsetof(struct mcp_gen_header, header_length))); + pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern); + if (pattern_off >= (hdr_len + hdr_off)) { + dev_info(dev, "Firmware does not support LED identification\n"); + return -EINVAL; + } + if (!on) + pattern = swab32(readl(mgp->sram + pattern_off + 4)); + writel(htonl(pattern), mgp->sram + pattern_off); + return 0; +} + +static int +myri10ge_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) +{ + struct myri10ge_priv *mgp = netdev_priv(netdev); + int rc; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + rc = myri10ge_led(mgp, 1); + break; + + case ETHTOOL_ID_INACTIVE: + rc = myri10ge_led(mgp, 0); + break; + + default: + rc = -EINVAL; + } + + return rc; +} + static const struct ethtool_ops myri10ge_ethtool_ops = { .get_settings = myri10ge_get_settings, .get_drvinfo = myri10ge_get_drvinfo, @@ -1921,6 +1976,7 @@ static const struct ethtool_ops myri10ge_ethtool_ops = { .get_ethtool_stats = myri10ge_get_ethtool_stats, .set_msglevel = myri10ge_set_msglevel, .get_msglevel = myri10ge_get_msglevel, + .set_phys_id = myri10ge_phys_id, }; static int myri10ge_allocate_rings(struct myri10ge_slice_state *ss) @@ -2000,8 +2056,12 @@ static int myri10ge_allocate_rings(struct myri10ge_slice_state *ss) ss->rx_big.page_offset = MYRI10GE_ALLOC_SIZE; ss->rx_small.watchdog_needed = 0; ss->rx_big.watchdog_needed = 0; - myri10ge_alloc_rx_pages(mgp, &ss->rx_small, - mgp->small_bytes + MXGEFW_PAD, 0); + if (mgp->small_bytes == 0) { + ss->rx_small.fill_cnt = ss->rx_small.mask + 1; + } else { + myri10ge_alloc_rx_pages(mgp, &ss->rx_small, + mgp->small_bytes + MXGEFW_PAD, 0); + } if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) { netdev_err(dev, "slice-%d: alloced only %d small bufs\n", @@ -2027,6 +2087,8 @@ abort_with_rx_big_ring: } abort_with_rx_small_ring: + if (mgp->small_bytes == 0) + ss->rx_small.fill_cnt = ss->rx_small.cnt; for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) { int idx = i & ss->rx_small.mask; myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx], @@ -2077,6 +2139,8 @@ static void myri10ge_free_rings(struct myri10ge_slice_state *ss) put_page(ss->rx_big.info[idx].page); } + if (mgp->small_bytes == 0) + ss->rx_small.fill_cnt = ss->rx_small.cnt; for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) { idx = i & ss->rx_small.mask; if (i == ss->rx_small.fill_cnt - 1) @@ -2255,7 +2319,7 @@ myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr, *ip_hdr = iph; if (iph->protocol != IPPROTO_TCP) return -1; - if (iph->frag_off & htons(IP_MF | IP_OFFSET)) + if (ip_is_fragment(iph)) return -1; *hdr_flags |= LRO_TCP; *tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2); @@ -2414,7 +2478,7 @@ static int myri10ge_open(struct net_device *dev) mgp->small_bytes = VLAN_ETH_FRAME_LEN; /* Override the small buffer size? */ - if (myri10ge_small_bytes > 0) + if (myri10ge_small_bytes >= 0) mgp->small_bytes = myri10ge_small_bytes; /* Firmware needs the big buff size as a power of 2. Lie and @@ -2976,15 +3040,13 @@ drop: return NETDEV_TX_OK; } -static struct net_device_stats *myri10ge_get_stats(struct net_device *dev) +static struct rtnl_link_stats64 *myri10ge_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) { - struct myri10ge_priv *mgp = netdev_priv(dev); - struct myri10ge_slice_netstats *slice_stats; - struct net_device_stats *stats = &dev->stats; + const struct myri10ge_priv *mgp = netdev_priv(dev); + const struct myri10ge_slice_netstats *slice_stats; int i; - spin_lock(&mgp->stats_lock); - memset(stats, 0, sizeof(*stats)); for (i = 0; i < mgp->num_slices; i++) { slice_stats = &mgp->ss[i].stats; stats->rx_packets += slice_stats->rx_packets; @@ -2994,7 +3056,6 @@ static struct net_device_stats *myri10ge_get_stats(struct net_device *dev) stats->rx_dropped += slice_stats->rx_dropped; stats->tx_dropped += slice_stats->tx_dropped; } - spin_unlock(&mgp->stats_lock); return stats; } @@ -3127,7 +3188,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) { struct pci_dev *bridge = mgp->pdev->bus->self; struct device *dev = &mgp->pdev->dev; - unsigned cap; + int cap; unsigned err_cap; u16 val; u8 ext_type; @@ -3137,7 +3198,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) return; /* check that the bridge is a root port */ - cap = pci_find_capability(bridge, PCI_CAP_ID_EXP); + cap = pci_pcie_cap(bridge); pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val); ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4; if (ext_type != PCI_EXP_TYPE_ROOT_PORT) { @@ -3155,8 +3216,7 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) " to force ECRC\n"); return; } - cap = - pci_find_capability(bridge, PCI_CAP_ID_EXP); + cap = pci_pcie_cap(bridge); pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val); ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4; @@ -3266,7 +3326,6 @@ abort: /* fall back to using the unaligned firmware */ mgp->tx_boundary = 2048; set_fw_name(mgp, myri10ge_fw_unaligned, false); - } static void myri10ge_select_firmware(struct myri10ge_priv *mgp) @@ -3277,7 +3336,7 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) int link_width, exp_cap; u16 lnk; - exp_cap = pci_find_capability(mgp->pdev, PCI_CAP_ID_EXP); + exp_cap = pci_pcie_cap(mgp->pdev); pci_read_config_word(mgp->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk); link_width = (lnk >> 4) & 0x3f; @@ -3327,6 +3386,26 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) mgp->fw_name); } +static void myri10ge_mask_surprise_down(struct pci_dev *pdev) +{ + struct pci_dev *bridge = pdev->bus->self; + int cap; + u32 mask; + + if (bridge == NULL) + return; + + cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR); + if (cap) { + /* a sram parity error can cause a surprise link + * down; since we expect and can recover from sram + * parity errors, mask surprise link down events */ + pci_read_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, &mask); + mask |= 0x20; + pci_write_config_dword(bridge, cap + PCI_ERR_UNCOR_MASK, mask); + } +} + #ifdef CONFIG_PM static int myri10ge_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -3422,6 +3501,42 @@ static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp) return reboot; } +static void +myri10ge_check_slice(struct myri10ge_slice_state *ss, int *reset_needed, + int *busy_slice_cnt, u32 rx_pause_cnt) +{ + struct myri10ge_priv *mgp = ss->mgp; + int slice = ss - mgp->ss; + + if (ss->tx.req != ss->tx.done && + ss->tx.done == ss->watchdog_tx_done && + ss->watchdog_tx_req != ss->watchdog_tx_done) { + /* nic seems like it might be stuck.. */ + if (rx_pause_cnt != mgp->watchdog_pause) { + if (net_ratelimit()) + netdev_warn(mgp->dev, "slice %d: TX paused, " + "check link partner\n", slice); + } else { + netdev_warn(mgp->dev, + "slice %d: TX stuck %d %d %d %d %d %d\n", + slice, ss->tx.queue_active, ss->tx.req, + ss->tx.done, ss->tx.pkt_start, + ss->tx.pkt_done, + (int)ntohl(mgp->ss[slice].fw_stats-> + send_done_count)); + *reset_needed = 1; + ss->stuck = 1; + } + } + if (ss->watchdog_tx_done != ss->tx.done || + ss->watchdog_rx_done != ss->rx_done.cnt) { + *busy_slice_cnt += 1; + } + ss->watchdog_tx_done = ss->tx.done; + ss->watchdog_tx_req = ss->tx.req; + ss->watchdog_rx_done = ss->rx_done.cnt; +} + /* * This watchdog is used to check whether the board has suffered * from a parity error and needs to be recovered. @@ -3430,10 +3545,12 @@ static void myri10ge_watchdog(struct work_struct *work) { struct myri10ge_priv *mgp = container_of(work, struct myri10ge_priv, watchdog_work); - struct myri10ge_tx_buf *tx; - u32 reboot; + struct myri10ge_slice_state *ss; + u32 reboot, rx_pause_cnt; int status, rebooted; int i; + int reset_needed = 0; + int busy_slice_cnt = 0; u16 cmd, vendor; mgp->watchdog_resets++; @@ -3445,8 +3562,7 @@ static void myri10ge_watchdog(struct work_struct *work) * For now, just report it */ reboot = myri10ge_read_reboot(mgp); netdev_err(mgp->dev, "NIC rebooted (0x%x),%s resetting\n", - reboot, - myri10ge_reset_recover ? "" : " not"); + reboot, myri10ge_reset_recover ? "" : " not"); if (myri10ge_reset_recover == 0) return; rtnl_lock(); @@ -3478,23 +3594,24 @@ static void myri10ge_watchdog(struct work_struct *work) return; } } - /* Perhaps it is a software error. Try to reset */ - - netdev_err(mgp->dev, "device timeout, resetting\n"); + /* Perhaps it is a software error. See if stuck slice + * has recovered, reset if not */ + rx_pause_cnt = ntohl(mgp->ss[0].fw_stats->dropped_pause); for (i = 0; i < mgp->num_slices; i++) { - tx = &mgp->ss[i].tx; - netdev_err(mgp->dev, "(%d): %d %d %d %d %d %d\n", - i, tx->queue_active, tx->req, - tx->done, tx->pkt_start, tx->pkt_done, - (int)ntohl(mgp->ss[i].fw_stats-> - send_done_count)); - msleep(2000); - netdev_info(mgp->dev, "(%d): %d %d %d %d %d %d\n", - i, tx->queue_active, tx->req, - tx->done, tx->pkt_start, tx->pkt_done, - (int)ntohl(mgp->ss[i].fw_stats-> - send_done_count)); + ss = mgp->ss; + if (ss->stuck) { + myri10ge_check_slice(ss, &reset_needed, + &busy_slice_cnt, + rx_pause_cnt); + ss->stuck = 0; + } } + if (!reset_needed) { + netdev_dbg(mgp->dev, "not resetting\n"); + return; + } + + netdev_err(mgp->dev, "device timeout, resetting\n"); } if (!rebooted) { @@ -3547,27 +3664,8 @@ static void myri10ge_watchdog_timer(unsigned long arg) myri10ge_fill_thresh) ss->rx_big.watchdog_needed = 0; } - - if (ss->tx.req != ss->tx.done && - ss->tx.done == ss->watchdog_tx_done && - ss->watchdog_tx_req != ss->watchdog_tx_done) { - /* nic seems like it might be stuck.. */ - if (rx_pause_cnt != mgp->watchdog_pause) { - if (net_ratelimit()) - netdev_err(mgp->dev, "slice %d: TX paused, check link partner\n", - i); - } else { - netdev_warn(mgp->dev, "slice %d stuck:", i); - reset_needed = 1; - } - } - if (ss->watchdog_tx_done != ss->tx.done || - ss->watchdog_rx_done != ss->rx_done.cnt) { - busy_slice_cnt++; - } - ss->watchdog_tx_done = ss->tx.done; - ss->watchdog_tx_req = ss->tx.req; - ss->watchdog_rx_done = ss->rx_done.cnt; + myri10ge_check_slice(ss, &reset_needed, &busy_slice_cnt, + rx_pause_cnt); } /* if we've sent or received no traffic, poll the NIC to * ensure it is still there. Otherwise, we risk not noticing @@ -3613,8 +3711,8 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp) dma_free_coherent(&pdev->dev, bytes, ss->fw_stats, ss->fw_stats_bus); ss->fw_stats = NULL; - netif_napi_del(&ss->napi); } + netif_napi_del(&ss->napi); } kfree(mgp->ss); mgp->ss = NULL; @@ -3790,7 +3888,7 @@ static const struct net_device_ops myri10ge_netdev_ops = { .ndo_open = myri10ge_open, .ndo_stop = myri10ge_close, .ndo_start_xmit = myri10ge_xmit, - .ndo_get_stats = myri10ge_get_stats, + .ndo_get_stats64 = myri10ge_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = myri10ge_change_mtu, .ndo_fix_features = myri10ge_fix_features, @@ -3845,6 +3943,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto abort_with_enabled; } + myri10ge_mask_surprise_down(pdev); pci_set_master(pdev); dac_enabled = 1; status = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); @@ -3964,7 +4063,6 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer, (unsigned long)mgp); - spin_lock_init(&mgp->stats_lock); SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops); INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog); status = register_netdev(netdev); |