usb: ks_bridge: Avoid allocating large buffers in atomic context
The current code allocates a new Rx buffer in Rx completion handler.
Rx buffers are freed after user space consumes them. This results in
allocating larger buffers in atomic context which may fail some times.
Don't queue the Rx buffer immediately. Defer it till user space consumes
it and reuse the same buffer. The test results indicate no throughput
degradation. The Rx buffers will be freed when interface is suspended
or disconnected.
CRs-Fixed: 430595
Change-Id: I8e69e27df2e11b38497e0384a8edd87ecedcb24b
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 410b5c4..cdf754e 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -74,7 +74,6 @@
struct usb_anchor submitted;
unsigned long flags;
- unsigned int alloced_read_pkts;
#define DBG_MSG_LEN 40
#define DBG_MAX_MSG 500
@@ -141,6 +140,8 @@
}
+static void
+submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt);
static ssize_t ksb_fs_read(struct file *fp, char __user *buf,
size_t count, loff_t *pos)
{
@@ -179,7 +180,6 @@
if (ret) {
pr_err("copy_to_user failed err:%d\n", ret);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
return ret;
}
@@ -189,9 +189,16 @@
spin_lock_irqsave(&ksb->lock, flags);
if (pkt->n_read == pkt->len) {
+ /*
+ * re-init the packet and queue it
+ * for more data.
+ */
list_del_init(&pkt->list);
- ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
+ pkt->n_read = 0;
+ pkt->len = MAX_DATA_PKT_SIZE;
+ spin_unlock_irqrestore(&ksb->lock, flags);
+ submit_one_urb(ksb, GFP_KERNEL, pkt);
+ spin_lock_irqsave(&ksb->lock, flags);
}
}
spin_unlock_irqrestore(&ksb->lock, flags);
@@ -408,25 +415,18 @@
MODULE_DEVICE_TABLE(usb, ksb_usb_ids);
static void ksb_rx_cb(struct urb *urb);
-static void submit_one_urb(struct ks_bridge *ksb)
+static void
+submit_one_urb(struct ks_bridge *ksb, gfp_t flags, struct data_pkt *pkt)
{
- struct data_pkt *pkt;
struct urb *urb;
int ret;
- pkt = ksb_alloc_data_pkt(MAX_DATA_PKT_SIZE, GFP_ATOMIC, ksb);
- if (IS_ERR(pkt)) {
- pr_err("unable to allocate data pkt");
- return;
- }
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, flags);
if (!urb) {
pr_err("unable to allocate urb");
ksb_free_data_pkt(pkt);
return;
}
- ksb->alloced_read_pkts++;
usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe,
pkt->buf, pkt->len,
@@ -437,18 +437,16 @@
usb_unanchor_urb(urb);
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
return;
}
atomic_inc(&ksb->rx_pending_cnt);
- ret = usb_submit_urb(urb, GFP_ATOMIC);
+ ret = usb_submit_urb(urb, flags);
if (ret) {
pr_err("in urb submission failed");
usb_unanchor_urb(urb);
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
atomic_dec(&ksb->rx_pending_cnt);
wake_up(&ksb->pending_urb_wait);
return;
@@ -477,14 +475,12 @@
pr_err_ratelimited("urb failed with err:%d",
urb->status);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
goto done;
}
if (urb->actual_length == 0) {
- ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
- goto resubmit_urb;
+ submit_one_urb(ksb, GFP_ATOMIC, pkt);
+ goto done;
}
add_to_list:
@@ -496,8 +492,6 @@
/* wake up read thread */
wake_up(&ksb->ks_wait_q);
-resubmit_urb:
- submit_one_urb(ksb);
done:
atomic_dec(&ksb->rx_pending_cnt);
wake_up(&ksb->pending_urb_wait);
@@ -537,7 +531,6 @@
ksb_free_data_pkt(pkt);
return;
}
- ksb->alloced_read_pkts++;
usb_fill_bulk_urb(urb, ksb->udev, ksb->in_pipe,
pkt->buf, pkt->len,
@@ -553,7 +546,6 @@
usb_unanchor_urb(urb);
usb_free_urb(urb);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
usb_autopm_put_interface(ksb->ifc);
atomic_dec(&ksb->rx_pending_cnt);
wake_up(&ksb->pending_urb_wait);
@@ -637,7 +629,6 @@
struct data_pkt, list);
list_del_init(&pkt->list);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
}
while (!list_empty(&ksb->to_mdm_list)) {
pkt = list_first_entry(&ksb->to_mdm_list,
@@ -664,8 +655,6 @@
dbg_log_event(ksb, "SUSPEND", 0, 0);
- pr_debug("read cnt: %d", ksb->alloced_read_pkts);
-
usb_kill_anchored_urbs(&ksb->submitted);
return 0;
@@ -712,7 +701,6 @@
struct data_pkt, list);
list_del_init(&pkt->list);
ksb_free_data_pkt(pkt);
- ksb->alloced_read_pkts--;
}
while (!list_empty(&ksb->to_mdm_list)) {
pkt = list_first_entry(&ksb->to_mdm_list,