diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_devlink.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_int.c | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qede/qede_main.c | 21 | ||||
-rw-r--r-- | drivers/net/usb/lan78xx.c | 1060 | ||||
-rw-r--r-- | drivers/net/xen-netfront.c | 272 |
6 files changed, 1040 insertions, 333 deletions
diff --git a/drivers/net/ethernet/qlogic/qed/qed_devlink.c b/drivers/net/ethernet/qlogic/qed/qed_devlink.c index 4c7501b9c284..78070682f2df 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_devlink.c +++ b/drivers/net/ethernet/qlogic/qed/qed_devlink.c @@ -93,7 +93,7 @@ static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = { .dump = qed_fw_fatal_reporter_dump, }; -#define QED_REPORTER_FW_GRACEFUL_PERIOD 1200000 +#define QED_REPORTER_FW_GRACEFUL_PERIOD 0 void qed_fw_reporters_create(struct devlink *devlink) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index ab6d4f737316..f78e6055f654 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -351,6 +351,9 @@ static int qed_fw_assertion(struct qed_hwfn *p_hwfn) qed_hw_err_notify(p_hwfn, p_hwfn->p_dpc_ptt, QED_HW_ERR_FW_ASSERT, "FW assertion!\n"); + /* Clear assert indications */ + qed_wr(p_hwfn, p_hwfn->p_dpc_ptt, MISC_REG_AEU_GENERAL_ATTN_32, 0); + return -EINVAL; } @@ -953,6 +956,13 @@ qed_int_deassertion_aeu_bit(struct qed_hwfn *p_hwfn, DP_INFO(p_hwfn, "`%s' - Disabled future attentions\n", p_bit_name); + /* Re-enable FW aassertion (Gen 32) interrupts */ + val = qed_rd(p_hwfn, p_hwfn->p_dpc_ptt, + MISC_REG_AEU_ENABLE4_IGU_OUT_0); + val |= MISC_REG_AEU_ENABLE4_IGU_OUT_0_GENERAL_ATTN32; + qed_wr(p_hwfn, p_hwfn->p_dpc_ptt, + MISC_REG_AEU_ENABLE4_IGU_OUT_0, val); + out: return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index 9db22be42476..da1b7fdcbda7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -504,6 +504,8 @@ 0x180824UL #define MISC_REG_AEU_GENERAL_ATTN_0 \ 0x008400UL +#define MISC_REG_AEU_GENERAL_ATTN_32 \ + 0x008480UL #define MISC_REG_AEU_GENERAL_ATTN_35 \ 0x00848cUL #define CAU_REG_SB_ADDR_MEMORY \ @@ -518,6 +520,12 @@ 0x180804UL #define MISC_REG_AEU_ENABLE1_IGU_OUT_0 \ 0x00849cUL +#define MISC_REG_AEU_ENABLE4_IGU_OUT_0 \ + 0x0084a8UL +#define MISC_REG_AEU_ENABLE4_IGU_OUT_0_GENERAL_ATTN32 \ + (0x1UL << 0) +#define MISC_REG_AEU_ENABLE4_IGU_OUT_0_GENERAL_ATTN32_SHIFT \ + 0 #define MISC_REG_AEU_AFTER_INVERT_1_IGU \ 0x0087b4UL #define MISC_REG_AEU_MASK_ATTN_IGU \ diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index d400e9b235bf..4877cb88c31a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1898,6 +1898,12 @@ static int qede_req_msix_irqs(struct qede_dev *edev) &edev->fp_array[i]); if (rc) { DP_ERR(edev, "Request fp %d irq failed\n", i); +#ifdef CONFIG_RFS_ACCEL + if (edev->ndev->rx_cpu_rmap) + free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); + + edev->ndev->rx_cpu_rmap = NULL; +#endif qede_sync_free_irqs(edev); return rc; } @@ -2290,6 +2296,15 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, rc = qede_stop_queues(edev); if (rc) { +#ifdef CONFIG_RFS_ACCEL + if (edev->dev_info.common.b_arfs_capable) { + qede_poll_for_freeing_arfs_filters(edev); + if (edev->ndev->rx_cpu_rmap) + free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); + + edev->ndev->rx_cpu_rmap = NULL; + } +#endif qede_sync_free_irqs(edev); goto out; } @@ -2620,8 +2635,10 @@ static void qede_generic_hw_err_handler(struct qede_dev *edev) "Generic sleepable HW error handling started - err_flags 0x%lx\n", edev->err_flags); - if (edev->devlink) + if (edev->devlink) { + DP_NOTICE(edev, "Reporting fatal error to devlink\n"); edev->ops->common->report_fatal_error(edev->devlink, edev->last_err_type); + } clear_bit(QEDE_ERR_IS_HANDLED, &edev->err_flags); @@ -2643,6 +2660,8 @@ static void qede_set_hw_err_flags(struct qede_dev *edev, case QED_HW_ERR_FW_ASSERT: set_bit(QEDE_ERR_ATTN_CLR_EN, &err_flags); set_bit(QEDE_ERR_GET_DBG_INFO, &err_flags); + /* make this error as recoverable and start recovery*/ + set_bit(QEDE_ERR_IS_RECOVERABLE, &err_flags); break; default: diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 4e8d3c28f73e..793f8fbe0069 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -46,6 +46,19 @@ #define MAX_RX_FIFO_SIZE (12 * 1024) #define MAX_TX_FIFO_SIZE (12 * 1024) + +#define FLOW_THRESHOLD(n) ((((n) + 511) / 512) & 0x7F) +#define FLOW_CTRL_THRESHOLD(on, off) ((FLOW_THRESHOLD(on) << 0) | \ + (FLOW_THRESHOLD(off) << 8)) + +/* Flow control turned on when Rx FIFO level rises above this level (bytes) */ +#define FLOW_ON_SS 9216 +#define FLOW_ON_HS 8704 + +/* Flow control turned off when Rx FIFO level falls below this level (bytes) */ +#define FLOW_OFF_SS 4096 +#define FLOW_OFF_HS 1024 + #define DEFAULT_BURST_CAP_SIZE (MAX_TX_FIFO_SIZE) #define DEFAULT_BULK_IN_DELAY (0x0800) #define MAX_SINGLE_PACKET_SIZE (9000) @@ -87,6 +100,12 @@ /* statistic update interval (mSec) */ #define STAT_UPDATE_TIMER (1 * 1000) +/* time to wait for MAC or FCT to stop (jiffies) */ +#define HW_DISABLE_TIMEOUT (HZ / 10) + +/* time to wait between polling MAC or FCT state (ms) */ +#define HW_DISABLE_DELAY_MS 1 + /* defines interrupts from interrupt EP */ #define MAX_INT_EP (32) #define INT_EP_INTEP (31) @@ -341,6 +360,7 @@ struct usb_context { #define EVENT_DEV_ASLEEP 7 #define EVENT_DEV_OPEN 8 #define EVENT_STAT_UPDATE 9 +#define EVENT_DEV_DISCONNECT 10 struct statstage { struct mutex access_lock; /* for stats access */ @@ -370,7 +390,6 @@ struct lan78xx_net { struct sk_buff_head rxq; struct sk_buff_head txq; struct sk_buff_head done; - struct sk_buff_head rxq_pause; struct sk_buff_head txq_pend; struct tasklet_struct bh; @@ -381,8 +400,9 @@ struct lan78xx_net { struct urb *urb_intr; struct usb_anchor deferred; + struct mutex dev_mutex; /* serialise open/stop wrt suspend/resume */ struct mutex phy_mutex; /* for phy access */ - unsigned pipe_in, pipe_out, pipe_intr; + unsigned int pipe_in, pipe_out, pipe_intr; u32 hard_mtu; /* count any extra framing */ size_t rx_urb_size; /* size for rx urbs */ @@ -392,8 +412,7 @@ struct lan78xx_net { wait_queue_head_t *wait; unsigned char suspend_count; - unsigned maxpacket; - struct timer_list delay; + unsigned int maxpacket; struct timer_list stat_monitor; unsigned long data[5]; @@ -426,9 +445,13 @@ MODULE_PARM_DESC(msg_level, "Override default message level"); static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) { - u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL); + u32 *buf; int ret; + if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags)) + return -ENODEV; + + buf = kmalloc(sizeof(u32), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -439,7 +462,7 @@ static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) if (likely(ret >= 0)) { le32_to_cpus(buf); *data = *buf; - } else { + } else if (net_ratelimit()) { netdev_warn(dev->net, "Failed to read register index 0x%08x. ret = %d", index, ret); @@ -452,9 +475,13 @@ static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) static int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data) { - u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL); + u32 *buf; int ret; + if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags)) + return -ENODEV; + + buf = kmalloc(sizeof(u32), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -465,7 +492,8 @@ static int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data) USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, buf, 4, USB_CTRL_SET_TIMEOUT); - if (unlikely(ret < 0)) { + if (unlikely(ret < 0) && + net_ratelimit()) { netdev_warn(dev->net, "Failed to write register index 0x%08x. ret = %d", index, ret); @@ -476,6 +504,26 @@ static int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data) return ret; } +static int lan78xx_update_reg(struct lan78xx_net *dev, u32 reg, u32 mask, + u32 data) +{ + int ret; + u32 buf; + + ret = lan78xx_read_reg(dev, reg, &buf); + if (ret < 0) + return ret; + + buf &= ~mask; + buf |= (mask & data); + + ret = lan78xx_write_reg(dev, reg, buf); + if (ret < 0) + return ret; + + return 0; +} + static int lan78xx_read_stats(struct lan78xx_net *dev, struct lan78xx_statstage *data) { @@ -501,7 +549,7 @@ static int lan78xx_read_stats(struct lan78xx_net *dev, if (likely(ret >= 0)) { src = (u32 *)stats; dst = (u32 *)data; - for (i = 0; i < sizeof(*stats)/sizeof(u32); i++) { + for (i = 0; i < sizeof(*stats) / sizeof(u32); i++) { le32_to_cpus(&src[i]); dst[i] = src[i]; } @@ -515,10 +563,11 @@ static int lan78xx_read_stats(struct lan78xx_net *dev, return ret; } -#define check_counter_rollover(struct1, dev_stats, member) { \ - if (struct1->member < dev_stats.saved.member) \ - dev_stats.rollover_count.member++; \ - } +#define check_counter_rollover(struct1, dev_stats, member) \ + do { \ + if ((struct1)->member < (dev_stats).saved.member) \ + (dev_stats).rollover_count.member++; \ + } while (0) static void lan78xx_check_stat_rollover(struct lan78xx_net *dev, struct lan78xx_statstage *stats) @@ -844,9 +893,9 @@ static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset, for (i = 0; i < length; i++) { lan78xx_write_reg(dev, OTP_ADDR1, - ((offset + i) >> 8) & OTP_ADDR1_15_11); + ((offset + i) >> 8) & OTP_ADDR1_15_11); lan78xx_write_reg(dev, OTP_ADDR2, - ((offset + i) & OTP_ADDR2_10_3)); + ((offset + i) & OTP_ADDR2_10_3)); lan78xx_write_reg(dev, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_); lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_); @@ -900,9 +949,9 @@ static int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset, for (i = 0; i < length; i++) { lan78xx_write_reg(dev, OTP_ADDR1, - ((offset + i) >> 8) & OTP_ADDR1_15_11); + ((offset + i) >> 8) & OTP_ADDR1_15_11); lan78xx_write_reg(dev, OTP_ADDR2, - ((offset + i) & OTP_ADDR2_10_3)); + ((offset + i) & OTP_ADDR2_10_3)); lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]); lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_); lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_); @@ -959,7 +1008,7 @@ static int lan78xx_dataport_wait_not_busy(struct lan78xx_net *dev) usleep_range(40, 100); } - netdev_warn(dev->net, "lan78xx_dataport_wait_not_busy timed out"); + netdev_warn(dev->net, "%s timed out", __func__); return -EIO; } @@ -972,7 +1021,7 @@ static int lan78xx_dataport_write(struct lan78xx_net *dev, u32 ram_select, int i, ret; if (usb_autopm_get_interface(dev->intf) < 0) - return 0; + return 0; mutex_lock(&pdata->dataport_mutex); @@ -1045,9 +1094,9 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param) for (i = 1; i < NUM_OF_MAF; i++) { lan78xx_write_reg(dev, MAF_HI(i), 0); lan78xx_write_reg(dev, MAF_LO(i), - pdata->pfilter_table[i][1]); + pdata->pfilter_table[i][1]); lan78xx_write_reg(dev, MAF_HI(i), - pdata->pfilter_table[i][0]); + pdata->pfilter_table[i][0]); } lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); @@ -1066,11 +1115,12 @@ static void lan78xx_set_multicast(struct net_device *netdev) RFE_CTL_DA_PERFECT_ | RFE_CTL_MCAST_HASH_); for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) - pdata->mchash_table[i] = 0; + pdata->mchash_table[i] = 0; + /* pfilter_table[0] has own HW address */ for (i = 1; i < NUM_OF_MAF; i++) { - pdata->pfilter_table[i][0] = - pdata->pfilter_table[i][1] = 0; + pdata->pfilter_table[i][0] = 0; + pdata->pfilter_table[i][1] = 0; } pdata->rfe_ctl |= RFE_CTL_BCAST_EN_; @@ -1134,9 +1184,9 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, flow |= FLOW_CR_RX_FCEN_; if (dev->udev->speed == USB_SPEED_SUPER) - fct_flow = 0x817; + fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_SS, FLOW_OFF_SS); else if (dev->udev->speed == USB_SPEED_HIGH) - fct_flow = 0x211; + fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_HS, FLOW_OFF_HS); netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s", (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), @@ -1150,6 +1200,52 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, return 0; } +static int lan78xx_mac_reset(struct lan78xx_net *dev) +{ + unsigned long start_time = jiffies; + u32 val; + int ret; + + mutex_lock(&dev->phy_mutex); + + /* Resetting the device while there is activity on the MDIO + * bus can result in the MAC interface locking up and not + * completing register access transactions. + */ + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + + ret = lan78xx_read_reg(dev, MAC_CR, &val); + if (ret < 0) + goto done; + + val |= MAC_CR_RST_; + ret = lan78xx_write_reg(dev, MAC_CR, val); + if (ret < 0) + goto done; + + /* Wait for the reset to complete before allowing any further + * MAC register accesses otherwise the MAC may lock up. + */ + do { + ret = lan78xx_read_reg(dev, MAC_CR, &val); + if (ret < 0) + goto done; + + if (!(val & MAC_CR_RST_)) { + ret = 0; + goto done; + } + } while (!time_after(jiffies, start_time + HZ)); + + ret = -ETIMEDOUT; +done: + mutex_unlock(&dev->phy_mutex); + + return ret; +} + static int lan78xx_link_reset(struct lan78xx_net *dev) { struct phy_device *phydev = dev->net->phydev; @@ -1160,7 +1256,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) /* clear LAN78xx interrupt status */ ret = lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_); if (unlikely(ret < 0)) - return -EIO; + return ret; mutex_lock(&phydev->lock); phy_read_status(phydev); @@ -1171,13 +1267,9 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) dev->link_on = false; /* reset MAC */ - ret = lan78xx_read_reg(dev, MAC_CR, &buf); - if (unlikely(ret < 0)) - return -EIO; - buf |= MAC_CR_RST_; - ret = lan78xx_write_reg(dev, MAC_CR, buf); - if (unlikely(ret < 0)) - return -EIO; + ret = lan78xx_mac_reset(dev); + if (ret < 0) + return ret; del_timer(&dev->stat_monitor); } else if (link && !dev->link_on) { @@ -1189,18 +1281,30 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) if (ecmd.base.speed == 1000) { /* disable U2 */ ret = lan78xx_read_reg(dev, USB_CFG1, &buf); + if (ret < 0) + return ret; buf &= ~USB_CFG1_DEV_U2_INIT_EN_; ret = lan78xx_write_reg(dev, USB_CFG1, buf); + if (ret < 0) + return ret; /* enable U1 */ ret = lan78xx_read_reg(dev, USB_CFG1, &buf); + if (ret < 0) + return ret; buf |= USB_CFG1_DEV_U1_INIT_EN_; ret = lan78xx_write_reg(dev, USB_CFG1, buf); + if (ret < 0) + return ret; } else { /* enable U1 & U2 */ ret = lan78xx_read_reg(dev, USB_CFG1, &buf); + if (ret < 0) + return ret; buf |= USB_CFG1_DEV_U2_INIT_EN_; buf |= USB_CFG1_DEV_U1_INIT_EN_; ret = lan78xx_write_reg(dev, USB_CFG1, buf); + if (ret < 0) + return ret; } } @@ -1218,6 +1322,8 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv, radv); + if (ret < 0) + return ret; if (!timer_pending(&dev->stat_monitor)) { dev->delta = 1; @@ -1228,7 +1334,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) tasklet_schedule(&dev->bh); } - return ret; + return 0; } /* some work can't be done in tasklets, so we use keventd @@ -1264,9 +1370,10 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb) generic_handle_irq(dev->domain_data.phyirq); local_irq_enable(); } - } else + } else { netdev_warn(dev->net, "unexpected interrupt: 0x%08x\n", intdata); + } } static int lan78xx_ethtool_get_eeprom_len(struct net_device *netdev) @@ -1355,7 +1462,7 @@ static void lan78xx_get_wol(struct net_device *netdev, struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); if (usb_autopm_get_interface(dev->intf) < 0) - return; + return; ret = lan78xx_read_reg(dev, USB_CFG0, &buf); if (unlikely(ret < 0)) { @@ -2003,7 +2110,7 @@ static int lan8835_fixup(struct phy_device *phydev) /* RGMII MAC TXC Delay Enable */ lan78xx_write_reg(dev, MAC_RGMII_ID, - MAC_RGMII_ID_TXC_DELAY_EN_); + MAC_RGMII_ID_TXC_DELAY_EN_); /* RGMII TX DLL Tune Adjust */ lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00); @@ -2267,11 +2374,16 @@ static int lan78xx_change_mtu(struct net_device *netdev, int new_mtu) int ll_mtu = new_mtu + netdev->hard_header_len; int old_hard_mtu = dev->hard_mtu; int old_rx_urb_size = dev->rx_urb_size; + int ret; /* no second zero-length packet read wanted after mtu-sized packets */ if ((ll_mtu % dev->maxpacket) == 0) return -EDOM; + ret = usb_autopm_get_interface(dev->intf); + if (ret < 0) + return ret; + lan78xx_set_rx_max_frame_length(dev, new_mtu + VLAN_ETH_HLEN); netdev->mtu = new_mtu; @@ -2287,6 +2399,8 @@ static int lan78xx_change_mtu(struct net_device *netdev, int new_mtu) } } + usb_autopm_put_interface(dev->intf); + return 0; } @@ -2443,26 +2557,186 @@ static void lan78xx_init_ltm(struct lan78xx_net *dev) lan78xx_write_reg(dev, LTM_INACTIVE1, regs[5]); } +static int lan78xx_start_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enable) +{ + return lan78xx_update_reg(dev, reg, hw_enable, hw_enable); +} + +static int lan78xx_stop_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enabled, + u32 hw_disabled) +{ + unsigned long timeout; + bool stopped = true; + int ret; + u32 buf; + + /* Stop the h/w block (if not already stopped) */ + + ret = lan78xx_read_reg(dev, reg, &buf); + if (ret < 0) + return ret; + + if (buf & hw_enabled) { + buf &= ~hw_enabled; + + ret = lan78xx_write_reg(dev, reg, buf); + if (ret < 0) + return ret; + + stopped = false; + timeout = jiffies + HW_DISABLE_TIMEOUT; + do { + ret = lan78xx_read_reg(dev, reg, &buf); + if (ret < 0) + return ret; + + if (buf & hw_disabled) + stopped = true; + else + msleep(HW_DISABLE_DELAY_MS); + } while (!stopped && !time_after(jiffies, timeout)); + } + + ret = stopped ? 0 : -ETIME; + + return ret; +} + +static int lan78xx_flush_fifo(struct lan78xx_net *dev, u32 reg, u32 fifo_flush) +{ + return lan78xx_update_reg(dev, reg, fifo_flush, fifo_flush); +} + +static int lan78xx_start_tx_path(struct lan78xx_net *dev) +{ + int ret; + + netif_dbg(dev, drv, dev->net, "start tx path"); + + /* Start the MAC transmitter */ + + ret = lan78xx_start_hw(dev, MAC_TX, MAC_TX_TXEN_); + if (ret < 0) + return ret; + + /* Start the Tx FIFO */ + + ret = lan78xx_start_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_); + if (ret < 0) + return ret; + + return 0; +} + +static int lan78xx_stop_tx_path(struct lan78xx_net *dev) +{ + int ret; + + netif_dbg(dev, drv, dev->net, "stop tx path"); + + /* Stop the Tx FIFO */ + + ret = lan78xx_stop_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_, FCT_TX_CTL_DIS_); + if (ret < 0) + return ret; + + /* Stop the MAC transmitter */ + + ret = lan78xx_stop_hw(dev, MAC_TX, MAC_TX_TXEN_, MAC_TX_TXD_); + if (ret < 0) + return ret; + + return 0; +} + +/* The caller must ensure the Tx path is stopped before calling + * lan78xx_flush_tx_fifo(). + */ +static int lan78xx_flush_tx_fifo(struct lan78xx_net *dev) +{ + return lan78xx_flush_fifo(dev, FCT_TX_CTL, FCT_TX_CTL_RST_); +} + +static int lan78xx_start_rx_path(struct lan78xx_net *dev) +{ + int ret; + + netif_dbg(dev, drv, dev->net, "start rx path"); + + /* Start the Rx FIFO */ + + ret = lan78xx_start_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_); + if (ret < 0) + return ret; + + /* Start the MAC receiver*/ + + ret = lan78xx_start_hw(dev, MAC_RX, MAC_RX_RXEN_); + if (ret < 0) + return ret; + + return 0; +} + +static int lan78xx_stop_rx_path(struct lan78xx_net *dev) +{ + int ret; + + netif_dbg(dev, drv, dev->net, "stop rx path"); + + /* Stop the MAC receiver */ + + ret = lan78xx_stop_hw(dev, MAC_RX, MAC_RX_RXEN_, MAC_RX_RXD_); + if (ret < 0) + return ret; + + /* Stop the Rx FIFO */ + + ret = lan78xx_stop_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_, FCT_RX_CTL_DIS_); + if (ret < 0) + return ret; + + return 0; +} + +/* The caller must ensure the Rx path is stopped before calling + * lan78xx_flush_rx_fifo(). + */ +static int lan78xx_flush_rx_fifo(struct lan78xx_net *dev) +{ + return lan78xx_flush_fifo(dev, FCT_RX_CTL, FCT_RX_CTL_RST_); +} + static int lan78xx_reset(struct lan78xx_net *dev) { struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); - u32 buf; - int ret = 0; unsigned long timeout; + int ret; + u32 buf; u8 sig; ret = lan78xx_read_reg(dev, HW_CFG, &buf); + if (ret < 0) + return ret; + buf |= HW_CFG_LRST_; + ret = lan78xx_write_reg(dev, HW_CFG, buf); + if (ret < 0) + return ret; timeout = jiffies + HZ; do { mdelay(1); ret = lan78xx_read_reg(dev, HW_CFG, &buf); + if (ret < 0) + return ret; + if (time_after(jiffies, timeout)) { netdev_warn(dev->net, "timeout on completion of LiteReset"); - return -EIO; + ret = -ETIMEDOUT; + return ret; } } while (buf & HW_CFG_LRST_); @@ -2470,13 +2744,22 @@ static int lan78xx_reset(struct lan78xx_net *dev) /* save DEVID for later usage */ ret = lan78xx_read_reg(dev, ID_REV, &buf); + if (ret < 0) + return ret; + dev->chipid = (buf & ID_REV_CHIP_ID_MASK_) >> 16; dev->chiprev = buf & ID_REV_CHIP_REV_MASK_; /* Respond to the IN token with a NAK */ ret = lan78xx_read_reg(dev, USB_CFG0, &buf); + if (ret < 0) + return ret; + buf |= USB_CFG_BIR_; + ret = lan78xx_write_reg(dev, USB_CFG0, buf); + if (ret < 0) + return ret; /* Init LTM */ lan78xx_init_ltm(dev); @@ -2499,53 +2782,105 @@ static int lan78xx_reset(struct lan78xx_net *dev) } ret = lan78xx_write_reg(dev, BURST_CAP, buf); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + if (ret < 0) + return ret; ret = lan78xx_read_reg(dev, HW_CFG, &buf); + if (ret < 0) + return ret; + buf |= HW_CFG_MEF_; + ret = lan78xx_write_reg(dev, HW_CFG, buf); + if (ret < 0) + return ret; ret = lan78xx_read_reg(dev, USB_CFG0, &buf); + if (ret < 0) + return ret; + buf |= USB_CFG_BCE_; + ret = lan78xx_write_reg(dev, USB_CFG0, buf); + if (ret < 0) + return ret; /* set FIFO sizes */ buf = (MAX_RX_FIFO_SIZE - 512) / 512; + ret = lan78xx_write_reg(dev, FCT_RX_FIFO_END, buf); + if (ret < 0) + return ret; buf = (MAX_TX_FIFO_SIZE - 512) / 512; + ret = lan78xx_write_reg(dev, FCT_TX_FIFO_END, buf); + if (ret < 0) + return ret; ret = lan78xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, FLOW, 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, FCT_FLOW, 0); + if (ret < 0) + return ret; /* Don't need rfe_ctl_lock during initialisation */ ret = lan78xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); + if (ret < 0) + return ret; + pdata->rfe_ctl |= RFE_CTL_BCAST_EN_ | RFE_CTL_DA_PERFECT_; + ret = lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); + if (ret < 0) + return ret; /* Enable or disable checksum offload engines */ - lan78xx_set_features(dev->net, dev->net->features); + ret = lan78xx_set_features(dev->net, dev->net->features); + if (ret < 0) + return ret; lan78xx_set_multicast(dev->net); /* reset PHY */ ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + return ret; + buf |= PMT_CTL_PHY_RST_; + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + return ret; timeout = jiffies + HZ; do { mdelay(1); ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + return ret; + if (time_after(jiffies, timeout)) { netdev_warn(dev->net, "timeout waiting for PHY Reset"); - return -EIO; + ret = -ETIMEDOUT; + return ret; } } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_)); ret = lan78xx_read_reg(dev, MAC_CR, &buf); + if (ret < 0) + return ret; + /* LAN7801 only has RGMII mode */ if (dev->chipid == ID_REV_CHIP_ID_7801_) buf &= ~MAC_CR_GMII_EN_; @@ -2559,27 +2894,13 @@ static int lan78xx_reset(struct lan78xx_net *dev) } } ret = lan78xx_write_reg(dev, MAC_CR, buf); - - ret = lan78xx_read_reg(dev, MAC_TX, &buf); - buf |= MAC_TX_TXEN_; - ret = lan78xx_write_reg(dev, MAC_TX, buf); - - ret = lan78xx_read_reg(dev, FCT_TX_CTL, &buf); - buf |= FCT_TX_CTL_EN_; - ret = lan78xx_write_reg(dev, FCT_TX_CTL, buf); + if (ret < 0) + return ret; ret = lan78xx_set_rx_max_frame_length(dev, dev->net->mtu + VLAN_ETH_HLEN); - ret = lan78xx_read_reg(dev, MAC_RX, &buf); - buf |= MAC_RX_RXEN_; - ret = lan78xx_write_reg(dev, MAC_RX, buf); - - ret = lan78xx_read_reg(dev, FCT_RX_CTL, &buf); - buf |= FCT_RX_CTL_EN_; - ret = lan78xx_write_reg(dev, FCT_RX_CTL, buf); - - return 0; + return ret; } static void lan78xx_init_stats(struct lan78xx_net *dev) @@ -2613,9 +2934,13 @@ static int lan78xx_open(struct net_device *net) struct lan78xx_net *dev = netdev_priv(net); int ret; + netif_dbg(dev, ifup, dev->net, "open device"); + ret = usb_autopm_get_interface(dev->intf); if (ret < 0) - goto out; + return ret; + + mutex_lock(&dev->dev_mutex); phy_start(net->phydev); @@ -2631,6 +2956,20 @@ static int lan78xx_open(struct net_device *net) } } + ret = lan78xx_flush_rx_fifo(dev); + if (ret < 0) + goto done; + ret = lan78xx_flush_tx_fifo(dev); + if (ret < 0) + goto done; + + ret = lan78xx_start_tx_path(dev); + if (ret < 0) + goto done; + ret = lan78xx_start_rx_path(dev); + if (ret < 0) + goto done; + lan78xx_init_stats(dev); set_bit(EVENT_DEV_OPEN, &dev->flags); @@ -2641,9 +2980,10 @@ static int lan78xx_open(struct net_device *net) lan78xx_defer_kevent(dev, EVENT_LINK_RESET); done: + mutex_unlock(&dev->dev_mutex); + usb_autopm_put_interface(dev->intf); -out: return ret; } @@ -2660,53 +3000,74 @@ static void lan78xx_terminate_urbs(struct lan78xx_net *dev) temp = unlink_urbs(dev, &dev->txq) + unlink_urbs(dev, &dev->rxq); /* maybe wait for deletions to finish. */ - while (!skb_queue_empty(&dev->rxq) && - !skb_queue_empty(&dev->txq) && - !skb_queue_empty(&dev->done)) { + while (!skb_queue_empty(&dev->rxq) || + !skb_queue_empty(&dev->txq)) { schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS)); set_current_state(TASK_UNINTERRUPTIBLE); netif_dbg(dev, ifdown, dev->net, - "waited for %d urb completions\n", temp); + "waited for %d urb completions", temp); } set_current_state(TASK_RUNNING); dev->wait = NULL; remove_wait_queue(&unlink_wakeup, &wait); + + while (!skb_queue_empty(&dev->done)) { + struct skb_data *entry; + struct sk_buff *skb; + + skb = skb_dequeue(&dev->done); + entry = (struct skb_data *)(skb->cb); + usb_free_urb(entry->urb); + dev_kfree_skb(skb); + } } static int lan78xx_stop(struct net_device *net) { struct lan78xx_net *dev = netdev_priv(net); + netif_dbg(dev, ifup, dev->net, "stop device"); + + mutex_lock(&dev->dev_mutex); + if (timer_pending(&dev->stat_monitor)) del_timer_sync(&dev->stat_monitor); - if (net->phydev) - phy_stop(net->phydev); - clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue(net); + tasklet_kill(&dev->bh); + + lan78xx_terminate_urbs(dev); netif_info(dev, ifdown, dev->net, "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", net->stats.rx_packets, net->stats.tx_packets, net->stats.rx_errors, net->stats.tx_errors); - lan78xx_terminate_urbs(dev); + /* ignore errors that occur stopping the Tx and Rx data paths */ + lan78xx_stop_tx_path(dev); + lan78xx_stop_rx_path(dev); - usb_kill_urb(dev->urb_intr); + if (net->phydev) + phy_stop(net->phydev); - skb_queue_purge(&dev->rxq_pause); + usb_kill_urb(dev->urb_intr); /* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP. */ - dev->flags = 0; + clear_bit(EVENT_TX_HALT, &dev->flags); + clear_bit(EVENT_RX_HALT, &dev->flags); + clear_bit(EVENT_LINK_RESET, &dev->flags); + clear_bit(EVENT_STAT_UPDATE, &dev->flags); + cancel_delayed_work_sync(&dev->wq); - tasklet_kill(&dev->bh); usb_autopm_put_interface(dev->intf); + mutex_unlock(&dev->dev_mutex); + return 0; } @@ -2795,16 +3156,23 @@ static void tx_complete(struct urb *urb) /* software-driven interface shutdown */ case -ECONNRESET: case -ESHUTDOWN: + netif_dbg(dev, tx_err, dev->net, + "tx err interface gone %d\n", + entry->urb->status); break; case -EPROTO: case -ETIME: case -EILSEQ: netif_stop_queue(dev->net); + netif_dbg(dev, tx_err, dev->net, + "tx err queue stopped %d\n", + entry->urb->status); break; default: netif_dbg(dev, tx_err, dev->net, - "tx err %d\n", entry->urb->status); + "unknown tx err %d\n", + entry->urb->status); break; } } @@ -2829,6 +3197,9 @@ lan78xx_start_xmit(struct sk_buff *skb, struct net_device *net) struct lan78xx_net *dev = netdev_priv(net); struct sk_buff *skb2 = NULL; + if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) + schedule_delayed_work(&dev->wq, 0); + if (skb) { skb_tx_timestamp(skb); skb2 = lan78xx_tx_prep(dev, skb, GFP_ATOMIC); @@ -2988,11 +3359,6 @@ static void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb) { int status; - if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { - skb_queue_tail(&dev->rxq_pause, skb); - return; - } - dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; @@ -3140,6 +3506,7 @@ static int rx_submit(struct lan78xx_net *dev, struct urb *urb, gfp_t flags) lan78xx_defer_kevent(dev, EVENT_RX_HALT); break; case -ENODEV: + case -ENOENT: netif_dbg(dev, ifdown, dev->net, "device gone\n"); netif_device_detach(dev->net); break; @@ -3340,6 +3707,12 @@ gso_skb: lan78xx_defer_kevent(dev, EVENT_TX_HALT); usb_autopm_put_interface_async(dev->intf); break; + case -ENODEV: + case -ENOENT: + netif_dbg(dev, tx_err, dev->net, + "tx: submit urb err %d (disconnected?)", ret); + netif_device_detach(dev->net); + break; default: usb_autopm_put_interface_async(dev->intf); netif_dbg(dev, tx_err, dev->net, @@ -3356,9 +3729,10 @@ drop: if (skb) dev_kfree_skb_any(skb); usb_free_urb(urb); - } else + } else { netif_dbg(dev, tx_queued, dev->net, "> tx, len %d, type 0x%x\n", length, skb->protocol); + } } static void lan78xx_rx_bh(struct lan78xx_net *dev) @@ -3421,8 +3795,7 @@ static void lan78xx_bh(struct tasklet_struct *t) if (!skb_queue_empty(&dev->txq_pend)) lan78xx_tx_bh(dev); - if (!timer_pending(&dev->delay) && - !test_bit(EVENT_RX_HALT, &dev->flags)) + if (!test_bit(EVENT_RX_HALT, &dev->flags)) lan78xx_rx_bh(dev); } } @@ -3434,18 +3807,20 @@ static void lan78xx_delayedwork(struct work_struct *work) dev = container_of(work, struct lan78xx_net, wq.work); + if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags)) + return; + + if (usb_autopm_get_interface(dev->intf) < 0) + return; + if (test_bit(EVENT_TX_HALT, &dev->flags)) { unlink_urbs(dev, &dev->txq); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto fail_pipe; + status = usb_clear_halt(dev->udev, dev->pipe_out); - usb_autopm_put_interface(dev->intf); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_tx_err(dev)) -fail_pipe: netdev_err(dev->net, "can't clear tx halt, status %d\n", status); @@ -3455,18 +3830,14 @@ fail_pipe: netif_wake_queue(dev->net); } } + if (test_bit(EVENT_RX_HALT, &dev->flags)) { unlink_urbs(dev, &dev->rxq); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto fail_halt; status = usb_clear_halt(dev->udev, dev->pipe_in); - usb_autopm_put_interface(dev->intf); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_rx_err(dev)) -fail_halt: netdev_err(dev->net, "can't clear rx halt, status %d\n", status); @@ -3480,16 +3851,9 @@ fail_halt: int ret = 0; clear_bit(EVENT_LINK_RESET, &dev->flags); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto skip_reset; if (lan78xx_link_reset(dev) < 0) { - usb_autopm_put_interface(dev->intf); -skip_reset: netdev_info(dev->net, "link reset failed (%d)\n", ret); - } else { - usb_autopm_put_interface(dev->intf); } } @@ -3503,6 +3867,8 @@ skip_reset: dev->delta = min((dev->delta * 2), 50); } + + usb_autopm_put_interface(dev->intf); } static void intr_complete(struct urb *urb) @@ -3518,6 +3884,7 @@ static void intr_complete(struct urb *urb) /* software-driven interface shutdown */ case -ENOENT: /* urb killed */ + case -ENODEV: /* hardware gone */ case -ESHUTDOWN: /* hardware gone */ netif_dbg(dev, ifdown, dev->net, "intr shutdown, code %d\n", status); @@ -3531,14 +3898,29 @@ static void intr_complete(struct urb *urb) break; } - if (!netif_running(dev->net)) + if (!netif_device_present(dev->net) || + !netif_running(dev->net)) { + netdev_warn(dev->net, "not submitting new status URB"); return; + } memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); status = usb_submit_urb(urb, GFP_ATOMIC); - if (status != 0) + + switch (status) { + case 0: + break; + case -ENODEV: + case -ENOENT: + netif_dbg(dev, timer, dev->net, + "intr resubmit %d (disconnect?)", status); + netif_device_detach(dev->net); + break; + default: netif_err(dev, timer, dev->net, "intr resubmit --> %d\n", status); + break; + } } static void lan78xx_disconnect(struct usb_interface *intf) @@ -3553,8 +3935,15 @@ static void lan78xx_disconnect(struct usb_interface *intf) if (!dev) return; + set_bit(EVENT_DEV_DISCONNECT, &dev->flags); + udev = interface_to_usbdev(intf); net = dev->net; + + unregister_netdev(net); + + cancel_delayed_work_sync(&dev->wq); + phydev = net->phydev; phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); @@ -3565,12 +3954,11 @@ static void lan78xx_disconnect(struct usb_interface *intf) if (phy_is_pseudo_fixed_link(phydev)) fixed_phy_unregister(phydev); - unregister_netdev(net); - - cancel_delayed_work_sync(&dev->wq); - usb_scuttle_anchored_urbs(&dev->deferred); + if (timer_pending(&dev->stat_monitor)) + del_timer_sync(&dev->stat_monitor); + lan78xx_unbind(dev, intf); usb_kill_urb(dev->urb_intr); @@ -3632,8 +4020,8 @@ static int lan78xx_probe(struct usb_interface *intf, struct net_device *netdev; struct usb_device *udev; int ret; - unsigned maxp; - unsigned period; + unsigned int maxp; + unsigned int period; u8 *buf = NULL; udev = interface_to_usbdev(intf); @@ -3659,9 +4047,9 @@ static int lan78xx_probe(struct usb_interface *intf, skb_queue_head_init(&dev->rxq); skb_queue_head_init(&dev->txq); skb_queue_head_init(&dev->done); - skb_queue_head_init(&dev->rxq_pause); skb_queue_head_init(&dev->txq_pend); mutex_init(&dev->phy_mutex); + mutex_init(&dev->dev_mutex); tasklet_setup(&dev->bh, lan78xx_bh); INIT_DELAYED_WORK(&dev->wq, lan78xx_delayedwork); @@ -3798,37 +4186,119 @@ static u16 lan78xx_wakeframe_crc16(const u8 *buf, int len) return crc; } -static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) +static int lan78xx_set_auto_suspend(struct lan78xx_net *dev) { u32 buf; - int mask_index; - u16 crc; - u32 temp_wucsr; - u32 temp_pmt_ctl; + int ret; + + ret = lan78xx_stop_tx_path(dev); + if (ret < 0) + return ret; + + ret = lan78xx_stop_rx_path(dev); + if (ret < 0) + return ret; + + /* auto suspend (selective suspend) */ + + ret = lan78xx_write_reg(dev, WUCSR, 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUCSR2, 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); + if (ret < 0) + return ret; + + /* set goodframe wakeup */ + + ret = lan78xx_read_reg(dev, WUCSR, &buf); + if (ret < 0) + return ret; + + buf |= WUCSR_RFE_WAKE_EN_; + buf |= WUCSR_STORE_WAKE_; + + ret = lan78xx_write_reg(dev, WUCSR, buf); + if (ret < 0) + return ret; + + ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + return ret; + + buf &= ~PMT_CTL_RES_CLR_WKP_EN_; + buf |= PMT_CTL_RES_CLR_WKP_STS_; + buf |= PMT_CTL_PHY_WAKE_EN_; + buf |= PMT_CTL_WOL_EN_; + buf &= ~PMT_CTL_SUS_MODE_MASK_; + buf |= PMT_CTL_SUS_MODE_3_; + + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + return ret; + + ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + return ret; + + buf |= PMT_CTL_WUPS_MASK_; + + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + return ret; + + ret = lan78xx_start_rx_path(dev); + + return ret; +} + +static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) +{ const u8 ipv4_multicast[3] = { 0x01, 0x00, 0x5E }; const u8 ipv6_multicast[3] = { 0x33, 0x33 }; const u8 arp_type[2] = { 0x08, 0x06 }; + u32 temp_pmt_ctl; + int mask_index; + u32 temp_wucsr; + u32 buf; + u16 crc; + int ret; - lan78xx_read_reg(dev, MAC_TX, &buf); - buf &= ~MAC_TX_TXEN_; - lan78xx_write_reg(dev, MAC_TX, buf); - lan78xx_read_reg(dev, MAC_RX, &buf); - buf &= ~MAC_RX_RXEN_; - lan78xx_write_reg(dev, MAC_RX, buf); + ret = lan78xx_stop_tx_path(dev); + if (ret < 0) + return ret; + ret = lan78xx_stop_rx_path(dev); + if (ret < 0) + return ret; - lan78xx_write_reg(dev, WUCSR, 0); - lan78xx_write_reg(dev, WUCSR2, 0); - lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); + ret = lan78xx_write_reg(dev, WUCSR, 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUCSR2, 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); + if (ret < 0) + return ret; temp_wucsr = 0; temp_pmt_ctl = 0; - lan78xx_read_reg(dev, PMT_CTL, &temp_pmt_ctl); + + ret = lan78xx_read_reg(dev, PMT_CTL, &temp_pmt_ctl); + if (ret < 0) + return ret; + temp_pmt_ctl &= ~PMT_CTL_RES_CLR_WKP_EN_; temp_pmt_ctl |= PMT_CTL_RES_CLR_WKP_STS_; - for (mask_index = 0; mask_index < NUM_OF_WUF_CFG; mask_index++) - lan78xx_write_reg(dev, WUF_CFG(mask_index), 0); + for (mask_index = 0; mask_index < NUM_OF_WUF_CFG; mask_index++) { + ret = lan78xx_write_reg(dev, WUF_CFG(mask_index), 0); + if (ret < 0) + return ret; + } mask_index = 0; if (wol & WAKE_PHY) { @@ -3857,30 +4327,52 @@ static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) /* set WUF_CFG & WUF_MASK for IPv4 Multicast */ crc = lan78xx_wakeframe_crc16(ipv4_multicast, 3); - lan78xx_write_reg(dev, WUF_CFG(mask_index), + ret = lan78xx_write_reg(dev, WUF_CFG(mask_index), WUF_CFGX_EN_ | WUF_CFGX_TYPE_MCAST_ | (0 << WUF_CFGX_OFFSET_SHIFT_) | (crc & WUF_CFGX_CRC16_MASK_)); + if (ret < 0) + return ret; + + ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 7); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); + if (ret < 0) + return ret; - lan78xx_write_reg(dev, WUF_MASK0(mask_index), 7); - lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); mask_index++; /* for IPv6 Multicast */ crc = lan78xx_wakeframe_crc16(ipv6_multicast, 2); - lan78xx_write_reg(dev, WUF_CFG(mask_index), + ret = lan78xx_write_reg(dev, WUF_CFG(mask_index), WUF_CFGX_EN_ | WUF_CFGX_TYPE_MCAST_ | (0 << WUF_CFGX_OFFSET_SHIFT_) | (crc & WUF_CFGX_CRC16_MASK_)); + if (ret < 0) + return ret; + + ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 3); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); + if (ret < 0) + return ret; - lan78xx_write_reg(dev, WUF_MASK0(mask_index), 3); - lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); mask_index++; temp_pmt_ctl |= PMT_CTL_WOL_EN_; @@ -3901,16 +4393,27 @@ static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) * for packettype (offset 12,13) = ARP (0x0806) */ crc = lan78xx_wakeframe_crc16(arp_type, 2); - lan78xx_write_reg(dev, WUF_CFG(mask_index), + ret = lan78xx_write_reg(dev, WUF_CFG(mask_index), WUF_CFGX_EN_ | WUF_CFGX_TYPE_ALL_ | (0 << WUF_CFGX_OFFSET_SHIFT_) | (crc & WUF_CFGX_CRC16_MASK_)); + if (ret < 0) + return ret; + + ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 0x3000); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); + if (ret < 0) + return ret; + ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); + if (ret < 0) + return ret; - lan78xx_write_reg(dev, WUF_MASK0(mask_index), 0x3000); - lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); - lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); mask_index++; temp_pmt_ctl |= PMT_CTL_WOL_EN_; @@ -3918,7 +4421,9 @@ static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; } - lan78xx_write_reg(dev, WUCSR, temp_wucsr); + ret = lan78xx_write_reg(dev, WUCSR, temp_wucsr); + if (ret < 0) + return ret; /* when multiple WOL bits are set */ if (hweight_long((unsigned long)wol) > 1) { @@ -3926,33 +4431,45 @@ static int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; } - lan78xx_write_reg(dev, PMT_CTL, temp_pmt_ctl); + ret = lan78xx_write_reg(dev, PMT_CTL, temp_pmt_ctl); + if (ret < 0) + return ret; /* clear WUPS */ - lan78xx_read_reg(dev, PMT_CTL, &buf); + ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + return ret; + buf |= PMT_CTL_WUPS_MASK_; - lan78xx_write_reg(dev, PMT_CTL, buf); - lan78xx_read_reg(dev, MAC_RX, &buf); - buf |= MAC_RX_RXEN_; - lan78xx_write_reg(dev, MAC_RX, buf); + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + return ret; - return 0; + ret = lan78xx_start_rx_path(dev); + + return ret; } static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) { struct lan78xx_net *dev = usb_get_intfdata(intf); - struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); - u32 buf; + bool dev_open; int ret; - if (!dev->suspend_count++) { + mutex_lock(&dev->dev_mutex); + + netif_dbg(dev, ifdown, dev->net, + "suspending: pm event %#x", message.event); + + dev_open = test_bit(EVENT_DEV_OPEN, &dev->flags); + + if (dev_open) { spin_lock_irq(&dev->txq.lock); /* don't autosuspend while transmitting */ if ((skb_queue_len(&dev->txq) || skb_queue_len(&dev->txq_pend)) && - PMSG_IS_AUTO(message)) { + PMSG_IS_AUTO(message)) { spin_unlock_irq(&dev->txq.lock); ret = -EBUSY; goto out; @@ -3961,129 +4478,207 @@ static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) spin_unlock_irq(&dev->txq.lock); } - /* stop TX & RX */ - ret = lan78xx_read_reg(dev, MAC_TX, &buf); - buf &= ~MAC_TX_TXEN_; - ret = lan78xx_write_reg(dev, MAC_TX, buf); - ret = lan78xx_read_reg(dev, MAC_RX, &buf); - buf &= ~MAC_RX_RXEN_; - ret = lan78xx_write_reg(dev, MAC_RX, buf); + /* stop RX */ + ret = lan78xx_stop_rx_path(dev); + if (ret < 0) + goto out; + + ret = lan78xx_flush_rx_fifo(dev); + if (ret < 0) + goto out; + + /* stop Tx */ + ret = lan78xx_stop_tx_path(dev); + if (ret < 0) + goto out; - /* empty out the rx and queues */ + /* empty out the Rx and Tx queues */ netif_device_detach(dev->net); lan78xx_terminate_urbs(dev); usb_kill_urb(dev->urb_intr); /* reattach */ netif_device_attach(dev->net); - } - if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { del_timer(&dev->stat_monitor); if (PMSG_IS_AUTO(message)) { - /* auto suspend (selective suspend) */ - ret = lan78xx_read_reg(dev, MAC_TX, &buf); - buf &= ~MAC_TX_TXEN_; - ret = lan78xx_write_reg(dev, MAC_TX, buf); - ret = lan78xx_read_reg(dev, MAC_RX, &buf); - buf &= ~MAC_RX_RXEN_; - ret = lan78xx_write_reg(dev, MAC_RX, buf); + ret = lan78xx_set_auto_suspend(dev); + if (ret < 0) + goto out; + } else { + struct lan78xx_priv *pdata; - ret = lan78xx_write_reg(dev, WUCSR, 0); - ret = lan78xx_write_reg(dev, WUCSR2, 0); - ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); + pdata = (struct lan78xx_priv *)(dev->data[0]); + netif_carrier_off(dev->net); + ret = lan78xx_set_suspend(dev, pdata->wol); + if (ret < 0) + goto out; + } + } else { + /* Interface is down; don't allow WOL and PHY + * events to wake up the host + */ + u32 buf; - /* set goodframe wakeup */ - ret = lan78xx_read_reg(dev, WUCSR, &buf); + set_bit(EVENT_DEV_ASLEEP, &dev->flags); - buf |= WUCSR_RFE_WAKE_EN_; - buf |= WUCSR_STORE_WAKE_; + ret = lan78xx_write_reg(dev, WUCSR, 0); + if (ret < 0) + goto out; + ret = lan78xx_write_reg(dev, WUCSR2, 0); + if (ret < 0) + goto out; - ret = lan78xx_write_reg(dev, WUCSR, buf); + ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + goto out; + + buf &= ~PMT_CTL_RES_CLR_WKP_EN_; + buf |= PMT_CTL_RES_CLR_WKP_STS_; + buf &= ~PMT_CTL_SUS_MODE_MASK_; + buf |= PMT_CTL_SUS_MODE_3_; + + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + goto out; - ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + if (ret < 0) + goto out; + + buf |= PMT_CTL_WUPS_MASK_; + + ret = lan78xx_write_reg(dev, PMT_CTL, buf); + if (ret < 0) + goto out; + } - buf &= ~PMT_CTL_RES_CLR_WKP_EN_; - buf |= PMT_CTL_RES_CLR_WKP_STS_; + ret = 0; +out: + mutex_unlock(&dev->dev_mutex); - buf |= PMT_CTL_PHY_WAKE_EN_; - buf |= PMT_CTL_WOL_EN_; - buf &= ~PMT_CTL_SUS_MODE_MASK_; - buf |= PMT_CTL_SUS_MODE_3_; + return ret; +} - ret = lan78xx_write_reg(dev, PMT_CTL, buf); +static bool lan78xx_submit_deferred_urbs(struct lan78xx_net *dev) +{ + bool pipe_halted = false; + struct urb *urb; - ret = lan78xx_read_reg(dev, PMT_CTL, &buf); + while ((urb = usb_get_from_anchor(&dev->deferred))) { + struct sk_buff *skb = urb->context; + int ret; - buf |= PMT_CTL_WUPS_MASK_; + if (!netif_device_present(dev->net) || + !netif_carrier_ok(dev->net) || + pipe_halted) { + usb_free_urb(urb); + dev_kfree_skb(skb); + continue; + } - ret = lan78xx_write_reg(dev, PMT_CTL, buf); + ret = usb_submit_urb(urb, GFP_ATOMIC); - ret = lan78xx_read_reg(dev, MAC_RX, &buf); - buf |= MAC_RX_RXEN_; - ret = lan78xx_write_reg(dev, MAC_RX, buf); + if (ret == 0) { + netif_trans_update(dev->net); + lan78xx_queue_skb(&dev->txq, skb, tx_start); } else { - lan78xx_set_suspend(dev, pdata->wol); + usb_free_urb(urb); + dev_kfree_skb(skb); + + if (ret == -EPIPE) { + netif_stop_queue(dev->net); + pipe_halted = true; + } else if (ret == -ENODEV) { + netif_device_detach(dev->net); + } } } - ret = 0; -out: - return ret; + return pipe_halted; } static int lan78xx_resume(struct usb_interface *intf) { struct lan78xx_net *dev = usb_get_intfdata(intf); - struct sk_buff *skb; - struct urb *res; + bool dev_open; int ret; - u32 buf; - if (!timer_pending(&dev->stat_monitor)) { - dev->delta = 1; - mod_timer(&dev->stat_monitor, - jiffies + STAT_UPDATE_TIMER); - } + mutex_lock(&dev->dev_mutex); - if (!--dev->suspend_count) { - /* resume interrupt URBs */ - if (dev->urb_intr && test_bit(EVENT_DEV_OPEN, &dev->flags)) - usb_submit_urb(dev->urb_intr, GFP_NOIO); + netif_dbg(dev, ifup, dev->net, "resuming device"); + + dev_open = test_bit(EVENT_DEV_OPEN, &dev->flags); + + if (dev_open) { + bool pipe_halted = false; + + ret = lan78xx_flush_tx_fifo(dev); + if (ret < 0) + goto out; + + if (dev->urb_intr) { + int ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL); - spin_lock_irq(&dev->txq.lock); - while ((res = usb_get_from_anchor(&dev->deferred))) { - skb = (struct sk_buff *)res->context; - ret = usb_submit_urb(res, GFP_ATOMIC); if (ret < 0) { - dev_kfree_skb_any(skb); - usb_free_urb(res); - usb_autopm_put_interface_async(dev->intf); - } else { - netif_trans_update(dev->net); - lan78xx_queue_skb(&dev->txq, skb, tx_start); + if (ret == -ENODEV) + netif_device_detach(dev->net); + + netdev_warn(dev->net, "Failed to submit intr URB"); } } + spin_lock_irq(&dev->txq.lock); + + if (netif_device_present(dev->net)) { + pipe_halted = lan78xx_submit_deferred_urbs(dev); + + if (pipe_halted) + lan78xx_defer_kevent(dev, EVENT_TX_HALT); + } + clear_bit(EVENT_DEV_ASLEEP, &dev->flags); + spin_unlock_irq(&dev->txq.lock); - if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { - if (!(skb_queue_len(&dev->txq) >= dev->tx_qlen)) - netif_start_queue(dev->net); - tasklet_schedule(&dev->bh); + if (!pipe_halted && + netif_device_present(dev->net) && + (skb_queue_len(&dev->txq) < dev->tx_qlen)) + netif_start_queue(dev->net); + + ret = lan78xx_start_tx_path(dev); + if (ret < 0) + goto out; + + tasklet_schedule(&dev->bh); + + if (!timer_pending(&dev->stat_monitor)) { + dev->delta = 1; + mod_timer(&dev->stat_monitor, + jiffies + STAT_UPDATE_TIMER); } + + } else { + clear_bit(EVENT_DEV_ASLEEP, &dev->flags); } ret = lan78xx_write_reg(dev, WUCSR2, 0); + if (ret < 0) + goto out; ret = lan78xx_write_reg(dev, WUCSR, 0); + if (ret < 0) + goto out; ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); + if (ret < 0) + goto out; ret = lan78xx_write_reg(dev, WUCSR2, WUCSR2_NS_RCD_ | WUCSR2_ARP_RCD_ | WUCSR2_IPV6_TCPSYN_RCD_ | WUCSR2_IPV4_TCPSYN_RCD_); + if (ret < 0) + goto out; ret = lan78xx_write_reg(dev, WUCSR, WUCSR_EEE_TX_WAKE_ | WUCSR_EEE_RX_WAKE_ | @@ -4092,23 +4687,32 @@ static int lan78xx_resume(struct usb_interface *intf) WUCSR_WUFR_ | WUCSR_MPR_ | WUCSR_BCST_FR_); + if (ret < 0) + goto out; - ret = lan78xx_read_reg(dev, MAC_TX, &buf); - buf |= MAC_TX_TXEN_; - ret = lan78xx_write_reg(dev, MAC_TX, buf); + ret = 0; +out: + mutex_unlock(&dev->dev_mutex); - return 0; + return ret; } static int lan78xx_reset_resume(struct usb_interface *intf) { struct lan78xx_net *dev = usb_get_intfdata(intf); + int ret; - lan78xx_reset(dev); + netif_dbg(dev, ifup, dev->net, "(reset) resuming device"); + + ret = lan78xx_reset(dev); + if (ret < 0) + return ret; phy_start(dev->net->phydev); - return lan78xx_resume(intf); + ret = lan78xx_resume(intf); + + return ret; } static const struct usb_device_id products[] = { diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 44275908d61a..e31b98403f31 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -126,21 +126,17 @@ struct netfront_queue { /* * {tx,rx}_skbs store outstanding skbuffs. Free tx_skb entries - * are linked from tx_skb_freelist through skb_entry.link. - * - * NB. Freelist index entries are always going to be less than - * PAGE_OFFSET, whereas pointers to skbs will always be equal or - * greater than PAGE_OFFSET: we use this property to distinguish - * them. + * are linked from tx_skb_freelist through tx_link. */ - union skb_entry { - struct sk_buff *skb; - unsigned long link; - } tx_skbs[NET_TX_RING_SIZE]; + struct sk_buff *tx_skbs[NET_TX_RING_SIZE]; + unsigned short tx_link[NET_TX_RING_SIZE]; +#define TX_LINK_NONE 0xffff +#define TX_PENDING 0xfffe grant_ref_t gref_tx_head; grant_ref_t grant_tx_ref[NET_TX_RING_SIZE]; struct page *grant_tx_page[NET_TX_RING_SIZE]; unsigned tx_skb_freelist; + unsigned int tx_pend_queue; spinlock_t rx_lock ____cacheline_aligned_in_smp; struct xen_netif_rx_front_ring rx; @@ -173,6 +169,9 @@ struct netfront_info { bool netback_has_xdp_headroom; bool netfront_xdp_enabled; + /* Is device behaving sane? */ + bool broken; + atomic_t rx_gso_checksum_fixup; }; @@ -181,33 +180,25 @@ struct netfront_rx_info { struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX - 1]; }; -static void skb_entry_set_link(union skb_entry *list, unsigned short id) -{ - list->link = id; -} - -static int skb_entry_is_link(const union skb_entry *list) -{ - BUILD_BUG_ON(sizeof(list->skb) != sizeof(list->link)); - return (unsigned long)list->skb < PAGE_OFFSET; -} - /* * Access macros for acquiring freeing slots in tx_skbs[]. */ -static void add_id_to_freelist(unsigned *head, union skb_entry *list, - unsigned short id) +static void add_id_to_list(unsigned *head, unsigned short *list, + unsigned short id) { - skb_entry_set_link(&list[id], *head); + list[id] = *head; *head = id; } -static unsigned short get_id_from_freelist(unsigned *head, - union skb_entry *list) +static unsigned short get_id_from_list(unsigned *head, unsigned short *list) { unsigned int id = *head; - *head = list[id].link; + + if (id != TX_LINK_NONE) { + *head = list[id]; + list[id] = TX_LINK_NONE; + } return id; } @@ -363,7 +354,7 @@ static int xennet_open(struct net_device *dev) unsigned int i = 0; struct netfront_queue *queue = NULL; - if (!np->queues) + if (!np->queues || np->broken) return -ENODEV; for (i = 0; i < num_queues; ++i) { @@ -391,27 +382,47 @@ static void xennet_tx_buf_gc(struct netfront_queue *queue) unsigned short id; struct sk_buff *skb; bool more_to_do; + const struct device *dev = &queue->info->netdev->dev; BUG_ON(!netif_carrier_ok(queue->info->netdev)); do { prod = queue->tx.sring->rsp_prod; + if (RING_RESPONSE_PROD_OVERFLOW(&queue->tx, prod)) { + dev_alert(dev, "Illegal number of responses %u\n", + prod - queue->tx.rsp_cons); + goto err; + } rmb(); /* Ensure we see responses up to 'rp'. */ for (cons = queue->tx.rsp_cons; cons != prod; cons++) { - struct xen_netif_tx_response *txrsp; + struct xen_netif_tx_response txrsp; - txrsp = RING_GET_RESPONSE(&queue->tx, cons); - if (txrsp->status == XEN_NETIF_RSP_NULL) + RING_COPY_RESPONSE(&queue->tx, cons, &txrsp); + if (txrsp.status == XEN_NETIF_RSP_NULL) continue; - id = txrsp->id; - skb = queue->tx_skbs[id].skb; + id = txrsp.id; + if (id >= RING_SIZE(&queue->tx)) { + dev_alert(dev, + "Response has incorrect id (%u)\n", + id); + goto err; + } + if (queue->tx_link[id] != TX_PENDING) { + dev_alert(dev, + "Response for inactive request\n"); + goto err; + } + + queue->tx_link[id] = TX_LINK_NONE; + skb = queue->tx_skbs[id]; + queue->tx_skbs[id] = NULL; if (unlikely(gnttab_query_foreign_access( queue->grant_tx_ref[id]) != 0)) { - pr_alert("%s: warning -- grant still in use by backend domain\n", - __func__); - BUG(); + dev_alert(dev, + "Grant still in use by backend domain\n"); + goto err; } gnttab_end_foreign_access_ref( queue->grant_tx_ref[id], GNTMAP_readonly); @@ -419,7 +430,7 @@ static void xennet_tx_buf_gc(struct netfront_queue *queue) &queue->gref_tx_head, queue->grant_tx_ref[id]); queue->grant_tx_ref[id] = GRANT_INVALID_REF; queue->grant_tx_page[id] = NULL; - add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, id); + add_id_to_list(&queue->tx_skb_freelist, queue->tx_link, id); dev_kfree_skb_irq(skb); } @@ -429,13 +440,20 @@ static void xennet_tx_buf_gc(struct netfront_queue *queue) } while (more_to_do); xennet_maybe_wake_tx(queue); + + return; + + err: + queue->info->broken = true; + dev_alert(dev, "Disabled for further use\n"); } struct xennet_gnttab_make_txreq { struct netfront_queue *queue; struct sk_buff *skb; struct page *page; - struct xen_netif_tx_request *tx; /* Last request */ + struct xen_netif_tx_request *tx; /* Last request on ring page */ + struct xen_netif_tx_request tx_local; /* Last request local copy*/ unsigned int size; }; @@ -451,7 +469,7 @@ static void xennet_tx_setup_grant(unsigned long gfn, unsigned int offset, struct netfront_queue *queue = info->queue; struct sk_buff *skb = info->skb; - id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); + id = get_id_from_list(&queue->tx_skb_freelist, queue->tx_link); tx = RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); ref = gnttab_claim_grant_reference(&queue->gref_tx_head); WARN_ON_ONCE(IS_ERR_VALUE((unsigned long)(int)ref)); @@ -459,34 +477,37 @@ static void xennet_tx_setup_grant(unsigned long gfn, unsigned int offset, gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, gfn, GNTMAP_readonly); - queue->tx_skbs[id].skb = skb; + queue->tx_skbs[id] = skb; queue->grant_tx_page[id] = page; queue->grant_tx_ref[id] = ref; - tx->id = id; - tx->gref = ref; - tx->offset = offset; - tx->size = len; - tx->flags = 0; + info->tx_local.id = id; + info->tx_local.gref = ref; + info->tx_local.offset = offset; + info->tx_local.size = len; + info->tx_local.flags = 0; + + *tx = info->tx_local; + + /* + * Put the request in the pending queue, it will be set to be pending + * when the producer index is about to be raised. + */ + add_id_to_list(&queue->tx_pend_queue, queue->tx_link, id); info->tx = tx; - info->size += tx->size; + info->size += info->tx_local.size; } static struct xen_netif_tx_request *xennet_make_first_txreq( - struct netfront_queue *queue, struct sk_buff *skb, - struct page *page, unsigned int offset, unsigned int len) + struct xennet_gnttab_make_txreq *info, + unsigned int offset, unsigned int len) { - struct xennet_gnttab_make_txreq info = { - .queue = queue, - .skb = skb, - .page = page, - .size = 0, - }; + info->size = 0; - gnttab_for_one_grant(page, offset, len, xennet_tx_setup_grant, &info); + gnttab_for_one_grant(info->page, offset, len, xennet_tx_setup_grant, info); - return info.tx; + return info->tx; } static void xennet_make_one_txreq(unsigned long gfn, unsigned int offset, @@ -499,35 +520,27 @@ static void xennet_make_one_txreq(unsigned long gfn, unsigned int offset, xennet_tx_setup_grant(gfn, offset, len, data); } -static struct xen_netif_tx_request *xennet_make_txreqs( - struct netfront_queue *queue, struct xen_netif_tx_request *tx, - struct sk_buff *skb, struct page *page, +static void xennet_make_txreqs( + struct xennet_gnttab_make_txreq *info, + struct page *page, unsigned int offset, unsigned int len) { - struct xennet_gnttab_make_txreq info = { - .queue = queue, - .skb = skb, - .tx = tx, - }; - /* Skip unused frames from start of page */ page += offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; while (len) { - info.page = page; - info.size = 0; + info->page = page; + info->size = 0; gnttab_foreach_grant_in_range(page, offset, len, xennet_make_one_txreq, - &info); + info); page++; offset = 0; - len -= info.size; + len -= info->size; } - - return info.tx; } /* @@ -574,19 +587,34 @@ static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb, return queue_idx; } +static void xennet_mark_tx_pending(struct netfront_queue *queue) +{ + unsigned int i; + + while ((i = get_id_from_list(&queue->tx_pend_queue, queue->tx_link)) != + TX_LINK_NONE) + queue->tx_link[i] = TX_PENDING; +} + static int xennet_xdp_xmit_one(struct net_device *dev, struct netfront_queue *queue, struct xdp_frame *xdpf) { struct netfront_info *np = netdev_priv(dev); struct netfront_stats *tx_stats = this_cpu_ptr(np->tx_stats); + struct xennet_gnttab_make_txreq info = { + .queue = queue, + .skb = NULL, + .page = virt_to_page(xdpf->data), + }; int notify; - xennet_make_first_txreq(queue, NULL, - virt_to_page(xdpf->data), + xennet_make_first_txreq(&info, offset_in_page(xdpf->data), xdpf->len); + xennet_mark_tx_pending(queue); + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify); if (notify) notify_remote_via_irq(queue->tx_irq); @@ -611,6 +639,8 @@ static int xennet_xdp_xmit(struct net_device *dev, int n, int nxmit = 0; int i; + if (unlikely(np->broken)) + return -ENODEV; if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) return -EINVAL; @@ -638,7 +668,7 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev { struct netfront_info *np = netdev_priv(dev); struct netfront_stats *tx_stats = this_cpu_ptr(np->tx_stats); - struct xen_netif_tx_request *tx, *first_tx; + struct xen_netif_tx_request *first_tx; unsigned int i; int notify; int slots; @@ -647,6 +677,7 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev unsigned int len; unsigned long flags; struct netfront_queue *queue = NULL; + struct xennet_gnttab_make_txreq info = { }; unsigned int num_queues = dev->real_num_tx_queues; u16 queue_index; struct sk_buff *nskb; @@ -654,6 +685,8 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev /* Drop the packet if no queues are set up */ if (num_queues < 1) goto drop; + if (unlikely(np->broken)) + goto drop; /* Determine which queue to transmit this SKB on */ queue_index = skb_get_queue_mapping(skb); queue = &np->queues[queue_index]; @@ -704,21 +737,24 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev } /* First request for the linear area. */ - first_tx = tx = xennet_make_first_txreq(queue, skb, - page, offset, len); - offset += tx->size; + info.queue = queue; + info.skb = skb; + info.page = page; + first_tx = xennet_make_first_txreq(&info, offset, len); + offset += info.tx_local.size; if (offset == PAGE_SIZE) { page++; offset = 0; } - len -= tx->size; + len -= info.tx_local.size; if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */ - tx->flags |= XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated; + first_tx->flags |= XEN_NETTXF_csum_blank | + XEN_NETTXF_data_validated; else if (skb->ip_summed == CHECKSUM_UNNECESSARY) /* remote but checksummed. */ - tx->flags |= XEN_NETTXF_data_validated; + first_tx->flags |= XEN_NETTXF_data_validated; /* Optional extra info after the first request. */ if (skb_shinfo(skb)->gso_size) { @@ -727,7 +763,7 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev gso = (struct xen_netif_extra_info *) RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); - tx->flags |= XEN_NETTXF_extra_info; + first_tx->flags |= XEN_NETTXF_extra_info; gso->u.gso.size = skb_shinfo(skb)->gso_size; gso->u.gso.type = (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) ? @@ -741,12 +777,12 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev } /* Requests for the rest of the linear area. */ - tx = xennet_make_txreqs(queue, tx, skb, page, offset, len); + xennet_make_txreqs(&info, page, offset, len); /* Requests for all the frags. */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - tx = xennet_make_txreqs(queue, tx, skb, skb_frag_page(frag), + xennet_make_txreqs(&info, skb_frag_page(frag), skb_frag_off(frag), skb_frag_size(frag)); } @@ -757,6 +793,8 @@ static netdev_tx_t xennet_start_xmit(struct sk_buff *skb, struct net_device *dev /* timestamp packet in software */ skb_tx_timestamp(skb); + xennet_mark_tx_pending(queue); + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify); if (notify) notify_remote_via_irq(queue->tx_irq); @@ -814,7 +852,7 @@ static int xennet_get_extras(struct netfront_queue *queue, RING_IDX rp) { - struct xen_netif_extra_info *extra; + struct xen_netif_extra_info extra; struct device *dev = &queue->info->netdev->dev; RING_IDX cons = queue->rx.rsp_cons; int err = 0; @@ -830,24 +868,22 @@ static int xennet_get_extras(struct netfront_queue *queue, break; } - extra = (struct xen_netif_extra_info *) - RING_GET_RESPONSE(&queue->rx, ++cons); + RING_COPY_RESPONSE(&queue->rx, ++cons, &extra); - if (unlikely(!extra->type || - extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) { + if (unlikely(!extra.type || + extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) { if (net_ratelimit()) dev_warn(dev, "Invalid extra type: %d\n", - extra->type); + extra.type); err = -EINVAL; } else { - memcpy(&extras[extra->type - 1], extra, - sizeof(*extra)); + extras[extra.type - 1] = extra; } skb = xennet_get_rx_skb(queue, cons); ref = xennet_get_rx_ref(queue, cons); xennet_move_rx_slot(queue, skb, ref); - } while (extra->flags & XEN_NETIF_EXTRA_FLAG_MORE); + } while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE); queue->rx.rsp_cons = cons; return err; @@ -905,7 +941,7 @@ static int xennet_get_responses(struct netfront_queue *queue, struct sk_buff_head *list, bool *need_xdp_flush) { - struct xen_netif_rx_response *rx = &rinfo->rx; + struct xen_netif_rx_response *rx = &rinfo->rx, rx_local; int max = XEN_NETIF_NR_SLOTS_MIN + (rx->status <= RX_COPY_THRESHOLD); RING_IDX cons = queue->rx.rsp_cons; struct sk_buff *skb = xennet_get_rx_skb(queue, cons); @@ -989,7 +1025,8 @@ next: break; } - rx = RING_GET_RESPONSE(&queue->rx, cons + slots); + RING_COPY_RESPONSE(&queue->rx, cons + slots, &rx_local); + rx = &rx_local; skb = xennet_get_rx_skb(queue, cons + slots); ref = xennet_get_rx_ref(queue, cons + slots); slots++; @@ -1044,10 +1081,11 @@ static int xennet_fill_frags(struct netfront_queue *queue, struct sk_buff *nskb; while ((nskb = __skb_dequeue(list))) { - struct xen_netif_rx_response *rx = - RING_GET_RESPONSE(&queue->rx, ++cons); + struct xen_netif_rx_response rx; skb_frag_t *nfrag = &skb_shinfo(nskb)->frags[0]; + RING_COPY_RESPONSE(&queue->rx, ++cons, &rx); + if (skb_shinfo(skb)->nr_frags == MAX_SKB_FRAGS) { unsigned int pull_to = NETFRONT_SKB_CB(skb)->pull_to; @@ -1062,7 +1100,7 @@ static int xennet_fill_frags(struct netfront_queue *queue, skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, skb_frag_page(nfrag), - rx->offset, rx->status, PAGE_SIZE); + rx.offset, rx.status, PAGE_SIZE); skb_shinfo(nskb)->nr_frags = 0; kfree_skb(nskb); @@ -1156,12 +1194,19 @@ static int xennet_poll(struct napi_struct *napi, int budget) skb_queue_head_init(&tmpq); rp = queue->rx.sring->rsp_prod; + if (RING_RESPONSE_PROD_OVERFLOW(&queue->rx, rp)) { + dev_alert(&dev->dev, "Illegal number of responses %u\n", + rp - queue->rx.rsp_cons); + queue->info->broken = true; + spin_unlock(&queue->rx_lock); + return 0; + } rmb(); /* Ensure we see queued responses up to 'rp'. */ i = queue->rx.rsp_cons; work_done = 0; while ((i != rp) && (work_done < budget)) { - memcpy(rx, RING_GET_RESPONSE(&queue->rx, i), sizeof(*rx)); + RING_COPY_RESPONSE(&queue->rx, i, rx); memset(extras, 0, sizeof(rinfo.extras)); err = xennet_get_responses(queue, &rinfo, rp, &tmpq, @@ -1286,17 +1331,18 @@ static void xennet_release_tx_bufs(struct netfront_queue *queue) for (i = 0; i < NET_TX_RING_SIZE; i++) { /* Skip over entries which are actually freelist references */ - if (skb_entry_is_link(&queue->tx_skbs[i])) + if (!queue->tx_skbs[i]) continue; - skb = queue->tx_skbs[i].skb; + skb = queue->tx_skbs[i]; + queue->tx_skbs[i] = NULL; get_page(queue->grant_tx_page[i]); gnttab_end_foreign_access(queue->grant_tx_ref[i], GNTMAP_readonly, (unsigned long)page_address(queue->grant_tx_page[i])); queue->grant_tx_page[i] = NULL; queue->grant_tx_ref[i] = GRANT_INVALID_REF; - add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, i); + add_id_to_list(&queue->tx_skb_freelist, queue->tx_link, i); dev_kfree_skb_irq(skb); } } @@ -1376,6 +1422,9 @@ static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id) struct netfront_queue *queue = dev_id; unsigned long flags; + if (queue->info->broken) + return IRQ_HANDLED; + spin_lock_irqsave(&queue->tx_lock, flags); xennet_tx_buf_gc(queue); spin_unlock_irqrestore(&queue->tx_lock, flags); @@ -1388,6 +1437,9 @@ static irqreturn_t xennet_rx_interrupt(int irq, void *dev_id) struct netfront_queue *queue = dev_id; struct net_device *dev = queue->info->netdev; + if (queue->info->broken) + return IRQ_HANDLED; + if (likely(netif_carrier_ok(dev) && RING_HAS_UNCONSUMED_RESPONSES(&queue->rx))) napi_schedule(&queue->napi); @@ -1409,6 +1461,10 @@ static void xennet_poll_controller(struct net_device *dev) struct netfront_info *info = netdev_priv(dev); unsigned int num_queues = dev->real_num_tx_queues; unsigned int i; + + if (info->broken) + return; + for (i = 0; i < num_queues; ++i) xennet_interrupt(0, &info->queues[i]); } @@ -1480,6 +1536,11 @@ static int xennet_xdp_set(struct net_device *dev, struct bpf_prog *prog, static int xennet_xdp(struct net_device *dev, struct netdev_bpf *xdp) { + struct netfront_info *np = netdev_priv(dev); + + if (np->broken) + return -ENODEV; + switch (xdp->command) { case XDP_SETUP_PROG: return xennet_xdp_set(dev, xdp->prog, xdp->extack); @@ -1853,13 +1914,15 @@ static int xennet_init_queue(struct netfront_queue *queue) snprintf(queue->name, sizeof(queue->name), "vif%s-q%u", devid, queue->id); - /* Initialise tx_skbs as a free chain containing every entry. */ + /* Initialise tx_skb_freelist as a free chain containing every entry. */ queue->tx_skb_freelist = 0; + queue->tx_pend_queue = TX_LINK_NONE; for (i = 0; i < NET_TX_RING_SIZE; i++) { - skb_entry_set_link(&queue->tx_skbs[i], i+1); + queue->tx_link[i] = i + 1; queue->grant_tx_ref[i] = GRANT_INVALID_REF; queue->grant_tx_page[i] = NULL; } + queue->tx_link[NET_TX_RING_SIZE - 1] = TX_LINK_NONE; /* Clear out rx_skbs */ for (i = 0; i < NET_RX_RING_SIZE; i++) { @@ -2128,6 +2191,9 @@ static int talk_to_netback(struct xenbus_device *dev, if (info->queues) xennet_destroy_queues(info); + /* For the case of a reconnect reset the "broken" indicator. */ + info->broken = false; + err = xennet_create_queues(info, &num_queues); if (err < 0) { xenbus_dev_fatal(dev, err, "creating queues"); |