diff options
Diffstat (limited to 'drivers/net/usb/usbnet.c')
-rw-r--r-- | drivers/net/usb/usbnet.c | 69 |
1 files changed, 52 insertions, 17 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index c04110ba677..51f3192f393 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -380,6 +380,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) unsigned long lockflags; size_t size = dev->rx_urb_size; + /* prevent rx skb allocation when error ratio is high */ + if (test_bit(EVENT_RX_KILL, &dev->flags)) { + usb_free_urb(urb); + return -ENOLINK; + } + skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); @@ -539,6 +545,17 @@ block: break; } + /* stop rx if packet error rate is high */ + if (++dev->pkt_cnt > 30) { + dev->pkt_cnt = 0; + dev->pkt_err = 0; + } else { + if (state == rx_cleanup) + dev->pkt_err++; + if (dev->pkt_err > 20) + set_bit(EVENT_RX_KILL, &dev->flags); + } + state = defer_bh(dev, skb, &dev->rxq, state); if (urb) { @@ -719,7 +736,8 @@ int usbnet_stop (struct net_device *net) dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); - if (info->manage_power) + if (info->manage_power && + !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags)) info->manage_power(dev, 0); else usb_autopm_put_interface(dev->intf); @@ -790,18 +808,23 @@ int usbnet_open (struct net_device *net) (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" : "simple"); + /* reset rx error state */ + dev->pkt_cnt = 0; + dev->pkt_err = 0; + clear_bit(EVENT_RX_KILL, &dev->flags); + // delay posting reads until we're fully open tasklet_schedule (&dev->bh); if (info->manage_power) { retval = info->manage_power(dev, 1); - if (retval < 0) - goto done_manage_power_error; - usb_autopm_put_interface(dev->intf); + if (retval < 0) { + retval = 0; + set_bit(EVENT_NO_RUNTIME_PM, &dev->flags); + } else { + usb_autopm_put_interface(dev->intf); + } } return retval; - -done_manage_power_error: - clear_bit(EVENT_DEV_OPEN, &dev->flags); done: usb_autopm_put_interface(dev->intf); done_nopm: @@ -1102,13 +1125,11 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, if (info->tx_fixup) { skb = info->tx_fixup (dev, skb, GFP_ATOMIC); if (!skb) { - if (netif_msg_tx_err(dev)) { - netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); - goto drop; - } else { - /* cdc_ncm collected packet; waits for more */ + /* packet collected; minidriver waiting for more */ + if (info->flags & FLAG_MULTI_PACKET) goto not_drop; - } + netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); + goto drop; } } length = skb->len; @@ -1253,6 +1274,9 @@ static void usbnet_bh (unsigned long param) } } + /* restart RX again after disabling due to high error rate */ + clear_bit(EVENT_RX_KILL, &dev->flags); + // waiting for all pending urbs to complete? if (dev->wait) { if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { @@ -1447,6 +1471,10 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if ((dev->driver_info->flags & FLAG_WWAN) != 0) strcpy(net->name, "wwan%d"); + /* devices that cannot do ARP */ + if ((dev->driver_info->flags & FLAG_NOARP) != 0) + net->flags |= IFF_NOARP; + /* maybe the remote can't receive an Ethernet MTU */ if (net->mtu > (dev->hard_mtu - net->hard_header_len)) net->mtu = dev->hard_mtu - net->hard_header_len; @@ -1615,6 +1643,16 @@ void usbnet_device_suggests_idle(struct usbnet *dev) } EXPORT_SYMBOL(usbnet_device_suggests_idle); +/* + * For devices that can do without special commands + */ +int usbnet_manage_power(struct usbnet *dev, int on) +{ + dev->intf->needs_remote_wakeup = on; + return 0; +} +EXPORT_SYMBOL(usbnet_manage_power); + /*-------------------------------------------------------------------------*/ static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, u16 value, u16 index, void *data, u16 size) @@ -1775,11 +1813,8 @@ int usbnet_write_cmd_async(struct usbnet *dev, u8 cmd, u8 reqtype, } req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); - if (!req) { - netdev_err(dev->net, "Failed to allocate memory for %s\n", - __func__); + if (!req) goto fail_free_buf; - } req->bRequestType = reqtype; req->bRequest = cmd; |