In order to reduce the interrupt times in the embedded system,
a receiving workqueue is introduced.
This modification also enhanced the overall throughput as the
benefits of reducing interrupt occurrence.
This work was derived from previous work:
u_ether: move hardware transmit to RX workqueue.
Which should be base on codeaurora's work.
However, the benchmark on my platform shows the throughput
with workqueue is slightly better than NAPI.
Signed-off-by: Weinn Jheng clanlab.p...@gmail.com
Cc: David S. Miller da...@davemloft.net
Cc: Stephen Hemminger shemmin...@vyatta.com
Cc: Felipe Balbi ba...@ti.com
Cc: Greg Kroah-Hartman gre...@linuxfoundation.org
Cc: Manu Gautam mgau...@codeaurora.org
Cc: Andrzej Pietrasiewicz andrze...@samsung.com
---
Changes for v2:
- Trying to fix the plug/unplug problem which cause KE.
Changes for v3:
- Fix plug/unplug problem in gether_disconenct.
- Refine the rx/tx_completion function assigment.
- This patch has been tested on beaglebone black.
Changes for v4:
- Move NAPI disable to a work queue and see if this won't cause KE
on replug.
drivers/usb/gadget/u_ether.c | 138 ++-
1 file changed, 98 insertions(+), 40 deletions(-)
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 97b0277..785d018 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -48,6 +48,8 @@
#define UETH__VERSION 29-May-2008
+#define GETHER_NAPI_WEIGHT 5
+
struct eth_dev {
/* lock is held while accessing port_usb
*/
@@ -72,6 +74,8 @@ struct eth_dev {
struct sk_buff_head *list);
struct work_struct work;
+ struct work_struct napi_work;
+ struct napi_struct rx_napi;
unsigned long todo;
#defineWORK_RX_MEMORY 0
@@ -187,6 +191,7 @@ static void defer_kevent(struct eth_dev *dev, int flag)
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+static void tx_complete(struct usb_ep *ep, struct usb_request *req);
static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
@@ -242,7 +247,6 @@ rx_submit(struct eth_dev *dev, struct usb_request *req,
gfp_t gfp_flags)
req-buf = skb-data;
req-length = size;
- req-complete = rx_complete;
req-context = skb;
retval = usb_ep_queue(out, req, gfp_flags);
@@ -253,18 +257,16 @@ enomem:
DBG(dev, rx submit -- %d\n, retval);
if (skb)
dev_kfree_skb_any(skb);
- spin_lock_irqsave(dev-req_lock, flags);
- list_add(req-list, dev-rx_reqs);
- spin_unlock_irqrestore(dev-req_lock, flags);
}
return retval;
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
- struct sk_buff *skb = req-context, *skb2;
+ struct sk_buff *skb = req-context;
struct eth_dev *dev = ep-driver_data;
int status = req-status;
+ boolrx_queue = 0;
switch (status) {
@@ -288,30 +290,8 @@ static void rx_complete(struct usb_ep *ep, struct
usb_request *req)
} else {
skb_queue_tail(dev-rx_frames, skb);
}
- skb = NULL;
-
- skb2 = skb_dequeue(dev-rx_frames);
- while (skb2) {
- if (status 0
- || ETH_HLEN skb2-len
- || skb2-len VLAN_ETH_FRAME_LEN) {
- dev-net-stats.rx_errors++;
- dev-net-stats.rx_length_errors++;
- DBG(dev, rx length %d\n, skb2-len);
- dev_kfree_skb_any(skb2);
- goto next_frame;
- }
- skb2-protocol = eth_type_trans(skb2, dev-net);
- dev-net-stats.rx_packets++;
- dev-net-stats.rx_bytes += skb2-len;
-
- /* no buffer copies needed, unless hardware can't
-* use skb buffers.
-*/
- status = netif_rx(skb2);
-next_frame:
- skb2 = skb_dequeue(dev-rx_frames);
- }
+ if (!status)
+ rx_queue = 1;
break;
/* software-driven interface shutdown */
@@ -334,28 +314,27 @@ quiesce:
/* FALLTHROUGH */
default:
+ rx_queue = 1;
+ dev_kfree_skb_any(skb);
dev-net-stats.rx_errors++;
DBG(dev, rx status %d\n, status);
break;
}
- if (skb)
- dev_kfree_skb_any(skb);
- if (!netif_running(dev-net)) {
clean:
- spin_lock(dev-req_lock);