ChangeSet 1.1348.19.5, 2003/06/24 14:51:08-07:00, oliver@neukum.org [PATCH] USB: make kaweth deal with ENOMEM this fixes the logic kaweth uses to deal with ENOMEM from usb_submit_urb(). Using the interrupt endpoint's completion handller is longer an option because automagic resubmission has been removed. Thus we use workqueues which can be delayed so that kswapd can do its job and can use GFP_NOIO which is much likelier to succeed. drivers/usb/net/kaweth.c | 67 +++++++++++++++++++++++++++++++++++++---------- 1 files changed, 53 insertions(+), 14 deletions(-) diff -Nru a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c --- a/drivers/usb/net/kaweth.c Tue Jun 24 15:29:12 2003 +++ b/drivers/usb/net/kaweth.c Tue Jun 24 15:29:12 2003 @@ -217,8 +217,10 @@ __u32 status; int end; int removed; - int suspend_lowmem; + int suspend_lowmem_rx; + int suspend_lowmem_ctrl; int linkstate; + struct work_struct lowmem_work; struct usb_device *dev; struct net_device *net; @@ -477,14 +479,29 @@ /**************************************************************** int_callback *****************************************************************/ + +static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, int mf) +{ + int status; + + status = usb_submit_urb (kaweth->irq_urb, mf); + if (unlikely(status == -ENOMEM)) { + kaweth->suspend_lowmem_ctrl = 1; + schedule_delayed_work(&kaweth->lowmem_work, HZ/4); + } else { + kaweth->suspend_lowmem_ctrl = 0; + } + + if (status) + err ("can't resubmit intr, %s-%s, status %d", + kaweth->dev->bus->bus_name, + kaweth->dev->devpath, status); +} + static void int_callback(struct urb *u, struct pt_regs *regs) { struct kaweth_device *kaweth = u->context; - int act_state, status; - - /* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */ - if (kaweth->suspend_lowmem) - kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); + int act_state; switch (u->status) { case 0: /* success */ @@ -508,13 +525,24 @@ kaweth->linkstate = act_state; } resubmit: - status = usb_submit_urb (u, SLAB_ATOMIC); - if (status) - err ("can't resubmit intr, %s-%s, status %d", - kaweth->dev->bus->bus_name, - kaweth->dev->devpath, status); + kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC); +} + +static void kaweth_resubmit_tl(void *d) +{ + struct kaweth_device *kaweth = (struct kaweth_device *)d; + + if (kaweth->status | KAWETH_STATUS_CLOSING) + return; + + if (kaweth->suspend_lowmem_rx) + kaweth_resubmit_rx_urb(kaweth, GFP_NOIO); + + if (kaweth->suspend_lowmem_ctrl) + kaweth_resubmit_int_urb(kaweth, GFP_NOIO); } + /**************************************************************** * kaweth_resubmit_rx_urb ****************************************************************/ @@ -534,11 +562,13 @@ kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle; if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) { - if (result == -ENOMEM) - kaweth->suspend_lowmem = 1; + if (result == -ENOMEM) { + kaweth->suspend_lowmem_rx = 1; + schedule_delayed_work(&kaweth->lowmem_work, HZ/4); + } kaweth_err("resubmitting rx_urb %d failed", result); } else { - kaweth->suspend_lowmem = 0; + kaweth->suspend_lowmem_rx = 0; } return result; @@ -667,6 +697,13 @@ usb_unlink_urb(kaweth->irq_urb); usb_unlink_urb(kaweth->rx_urb); + flush_scheduled_work(); + + /* a scheduled work may have resubmitted, + we hit them again */ + usb_unlink_urb(kaweth->irq_urb); + usb_unlink_urb(kaweth->rx_urb); + kaweth->status &= ~KAWETH_STATUS_CLOSING; return 0; @@ -1076,6 +1113,8 @@ kaweth->net->mtu = le16_to_cpu(kaweth->configuration.segment_size); memset(&kaweth->stats, 0, sizeof(kaweth->stats)); + + INIT_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl, (void *)kaweth); SET_MODULE_OWNER(netdev);