Commit 70c37bf9 authored by Bjørn Mork's avatar Bjørn Mork Committed by David S. Miller

net: usbnet: prevent buggy devices from killing us

A device sending 0 length frames as fast as it can has been
observed killing the host system due to the resulting memory
pressure.

Temporarily disable RX skb allocation and URB submission when
the current error ratio is high, preventing us from trying to
allocate an infinite number of skbs.  Reenable as soon as we
are finished processing the done queue, allowing the device
to continue working after short error bursts.
Signed-off-by: default avatarBjørn Mork <bjorn@mork.no>
Acked-by: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6cdd20c3
...@@ -380,6 +380,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) ...@@ -380,6 +380,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
unsigned long lockflags; unsigned long lockflags;
size_t size = dev->rx_urb_size; 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); skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
if (!skb) { if (!skb) {
netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
...@@ -539,6 +545,17 @@ static void rx_complete (struct urb *urb) ...@@ -539,6 +545,17 @@ static void rx_complete (struct urb *urb)
break; 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); state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) { if (urb) {
...@@ -791,6 +808,11 @@ int usbnet_open (struct net_device *net) ...@@ -791,6 +808,11 @@ int usbnet_open (struct net_device *net)
(dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" : (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" :
"simple"); "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 // delay posting reads until we're fully open
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
if (info->manage_power) { if (info->manage_power) {
...@@ -1254,6 +1276,9 @@ static void usbnet_bh (unsigned long param) ...@@ -1254,6 +1276,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? // waiting for all pending urbs to complete?
if (dev->wait) { if (dev->wait) {
if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
......
...@@ -33,6 +33,7 @@ struct usbnet { ...@@ -33,6 +33,7 @@ struct usbnet {
wait_queue_head_t *wait; wait_queue_head_t *wait;
struct mutex phy_mutex; struct mutex phy_mutex;
unsigned char suspend_count; unsigned char suspend_count;
unsigned char pkt_cnt, pkt_err;
/* i/o info: pipes etc */ /* i/o info: pipes etc */
unsigned in, out; unsigned in, out;
...@@ -70,6 +71,7 @@ struct usbnet { ...@@ -70,6 +71,7 @@ struct usbnet {
# define EVENT_DEV_OPEN 7 # define EVENT_DEV_OPEN 7
# define EVENT_DEVICE_REPORT_IDLE 8 # define EVENT_DEVICE_REPORT_IDLE 8
# define EVENT_NO_RUNTIME_PM 9 # define EVENT_NO_RUNTIME_PM 9
# define EVENT_RX_KILL 10
}; };
static inline struct usb_driver *driver_of(struct usb_interface *intf) static inline struct usb_driver *driver_of(struct usb_interface *intf)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment