diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 8533b88974b2..370764681749 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -731,12 +731,43 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb) kfree(rx_buf); } +struct reg_in_work { + struct urb *urb; + struct hif_device_usb *hif_dev; + struct work_struct work; +}; + +static void ath9k_hif_usb_reg_in_resubmit(struct work_struct *work) +{ + struct reg_in_work *rw = container_of(work, + struct reg_in_work, + work); + struct urb *urb = rw->urb; + struct rx_buf *rx_buf = urb->context; + + int ret; + + usb_anchor_urb(rw->urb, &rw->hif_dev->reg_in_submitted); + ret = usb_submit_urb(rw->urb, GFP_KERNEL); + + if (ret) { + usb_unanchor_urb(rw->urb); + if (rx_buf) { + kfree_skb(rx_buf->skb); + kfree(rx_buf); + urb->context = NULL; + } + } + + usb_put_urb(urb); + kfree(rw); +} + static void ath9k_hif_usb_reg_in_cb(struct urb *urb) { struct rx_buf *rx_buf = urb->context; struct hif_device_usb *hif_dev = rx_buf->hif_dev; struct sk_buff *skb = rx_buf->skb; - int ret; if (!skb) return; @@ -786,14 +817,20 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb) } resubmit: - usb_anchor_urb(urb, &hif_dev->reg_in_submitted); - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) { - usb_unanchor_urb(urb); - goto free_skb; + { + struct reg_in_work *rw; + + rw = kmalloc_obj(*rw, GFP_ATOMIC); + if (!rw) + goto free_skb; + + rw->urb = urb; + rw->hif_dev = hif_dev; + usb_get_urb(urb); + INIT_WORK(&rw->work, ath9k_hif_usb_reg_in_resubmit); + queue_work(hif_dev->reg_in_wq, &rw->work); + return; } - - return; free_skb: kfree_skb(skb); free_rx_buf: @@ -959,6 +996,8 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev) static void ath9k_hif_usb_dealloc_reg_in_urbs(struct hif_device_usb *hif_dev) { usb_kill_anchored_urbs(&hif_dev->reg_in_submitted); + if (hif_dev->reg_in_wq) + destroy_workqueue(hif_dev->reg_in_wq); } static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev) @@ -969,6 +1008,9 @@ static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev) int i, ret; init_usb_anchor(&hif_dev->reg_in_submitted); + hif_dev->reg_in_wq = alloc_ordered_workqueue("ath9k_reg_in", 0); + if (!hif_dev->reg_in_wq) + return -ENOMEM; for (i = 0; i < MAX_REG_IN_URB_NUM; i++) { diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index b3e66b0485a5..38f17a12fd5f 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -124,6 +124,7 @@ struct hif_device_usb { struct usb_anchor regout_submitted; struct usb_anchor rx_submitted; struct usb_anchor reg_in_submitted; + struct workqueue_struct *reg_in_wq; struct usb_anchor mgmt_submitted; struct sk_buff *remain_skb; char fw_name[64];