Commit 9189a330 authored by Felipe Balbi's avatar Felipe Balbi

Revert "usb: gadget: u_ether: move hardware transmit to RX NAPI"

This reverts commit 716fb91d.

That commit caused a regression which would end up in a kernel
BUG() as below:

[  101.554300] g_ether gadget: full-speed config #1: CDC Subset/SAFE
[  101.585186] ------------[ cut here ]------------
[  101.600587] kernel BUG at include/linux/netdevice.h:495!
[  101.615850] Internal error: Oops - BUG: 0 [#1] PREEMPT ARM
[  101.645539] Modules linked in:
[  101.660483] CPU: 0 PID: 0 Comm: swapper Not tainted 3.15.0-rc1+ #104
[  101.690175] task: c05dc5c8 ti: c05d2000 task.ti: c05d2000
[  101.705579] PC is at eth_start+0x64/0x8c
[  101.720981] LR is at __netif_schedule+0x7c/0x90
[  101.736455] pc : [<c0299174>]    lr : [<c036a134>]    psr: 60000093
[  101.736455] sp : c05d3d18  ip : c05d3cf8  fp : c05d3d2c
[  101.782340] r10: 00000000  r9 : c196c1f0  r8 : c196c1a0
[  101.797823] r7 : 00000000  r6 : 00000002  r5 : c1976400  r4 : c1976400
[  101.828058] r3 : 00000000  r2 : c05d3ce8  r1 : 00000001  r0 : 00000002
[  101.858722] Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM Segment kernel
Reported-by: default avatarRobert Jarzmik <robert.jarzmik@free.fr>
Signed-of-by: default avatarFelipe Balbi <balbi@ti.com>
parent 252455c4
...@@ -48,8 +48,6 @@ ...@@ -48,8 +48,6 @@
#define UETH__VERSION "29-May-2008" #define UETH__VERSION "29-May-2008"
#define GETHER_NAPI_WEIGHT 32
struct eth_dev { struct eth_dev {
/* lock is held while accessing port_usb /* lock is held while accessing port_usb
*/ */
...@@ -74,7 +72,6 @@ struct eth_dev { ...@@ -74,7 +72,6 @@ struct eth_dev {
struct sk_buff_head *list); struct sk_buff_head *list);
struct work_struct work; struct work_struct work;
struct napi_struct rx_napi;
unsigned long todo; unsigned long todo;
#define WORK_RX_MEMORY 0 #define WORK_RX_MEMORY 0
...@@ -256,16 +253,18 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) ...@@ -256,16 +253,18 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
DBG(dev, "rx submit --> %d\n", retval); DBG(dev, "rx submit --> %d\n", retval);
if (skb) if (skb)
dev_kfree_skb_any(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; return retval;
} }
static void rx_complete(struct usb_ep *ep, struct usb_request *req) static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{ {
struct sk_buff *skb = req->context; struct sk_buff *skb = req->context, *skb2;
struct eth_dev *dev = ep->driver_data; struct eth_dev *dev = ep->driver_data;
int status = req->status; int status = req->status;
bool rx_queue = 0;
switch (status) { switch (status) {
...@@ -289,8 +288,30 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -289,8 +288,30 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
} else { } else {
skb_queue_tail(&dev->rx_frames, skb); skb_queue_tail(&dev->rx_frames, skb);
} }
if (!status) skb = NULL;
rx_queue = 1;
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);
}
break; break;
/* software-driven interface shutdown */ /* software-driven interface shutdown */
...@@ -313,20 +334,22 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -313,20 +334,22 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
rx_queue = 1;
dev_kfree_skb_any(skb);
dev->net->stats.rx_errors++; dev->net->stats.rx_errors++;
DBG(dev, "rx status %d\n", status); DBG(dev, "rx status %d\n", status);
break; break;
} }
if (skb)
dev_kfree_skb_any(skb);
if (!netif_running(dev->net)) {
clean: clean:
spin_lock(&dev->req_lock); spin_lock(&dev->req_lock);
list_add(&req->list, &dev->rx_reqs); list_add(&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock); spin_unlock(&dev->req_lock);
req = NULL;
if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) }
__napi_schedule(&dev->rx_napi); if (req)
rx_submit(dev, req, GFP_ATOMIC);
} }
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
...@@ -391,24 +414,16 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -391,24 +414,16 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{ {
struct usb_request *req; struct usb_request *req;
unsigned long flags; unsigned long flags;
int rx_counts = 0;
/* fill unused rxq slots with some skb */ /* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags); spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) { while (!list_empty(&dev->rx_reqs)) {
if (++rx_counts > qlen(dev->gadget, dev->qmult))
break;
req = container_of(dev->rx_reqs.next, req = container_of(dev->rx_reqs.next,
struct usb_request, list); struct usb_request, list);
list_del_init(&req->list); list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags); spin_unlock_irqrestore(&dev->req_lock, flags);
if (rx_submit(dev, req, gfp_flags) < 0) { if (rx_submit(dev, req, gfp_flags) < 0) {
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
defer_kevent(dev, WORK_RX_MEMORY); defer_kevent(dev, WORK_RX_MEMORY);
return; return;
} }
...@@ -418,41 +433,6 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -418,41 +433,6 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
spin_unlock_irqrestore(&dev->req_lock, flags); spin_unlock_irqrestore(&dev->req_lock, flags);
} }
static int gether_poll(struct napi_struct *napi, int budget)
{
struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi);
struct sk_buff *skb;
unsigned int work_done = 0;
int status = 0;
while ((skb = skb_dequeue(&dev->rx_frames))) {
if (status < 0
|| ETH_HLEN > skb->len
|| skb->len > VLAN_ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb->len);
dev_kfree_skb_any(skb);
continue;
}
skb->protocol = eth_type_trans(skb, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
status = netif_rx_ni(skb);
}
if (netif_running(dev->net)) {
rx_fill(dev, GFP_KERNEL);
work_done++;
}
if (work_done < budget)
napi_complete(&dev->rx_napi);
return work_done;
}
static void eth_work(struct work_struct *work) static void eth_work(struct work_struct *work)
{ {
struct eth_dev *dev = container_of(work, struct eth_dev, work); struct eth_dev *dev = container_of(work, struct eth_dev, work);
...@@ -645,7 +625,6 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -645,7 +625,6 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
/* and open the tx floodgates */ /* and open the tx floodgates */
atomic_set(&dev->tx_qlen, 0); atomic_set(&dev->tx_qlen, 0);
netif_wake_queue(dev->net); netif_wake_queue(dev->net);
napi_enable(&dev->rx_napi);
} }
static int eth_open(struct net_device *net) static int eth_open(struct net_device *net)
...@@ -672,7 +651,6 @@ static int eth_stop(struct net_device *net) ...@@ -672,7 +651,6 @@ static int eth_stop(struct net_device *net)
unsigned long flags; unsigned long flags;
VDBG(dev, "%s\n", __func__); VDBG(dev, "%s\n", __func__);
napi_disable(&dev->rx_napi);
netif_stop_queue(net); netif_stop_queue(net);
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
...@@ -790,7 +768,6 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, ...@@ -790,7 +768,6 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dev = netdev_priv(net); dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock); spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work); INIT_WORK(&dev->work, eth_work);
...@@ -853,7 +830,6 @@ struct net_device *gether_setup_name_default(const char *netname) ...@@ -853,7 +830,6 @@ struct net_device *gether_setup_name_default(const char *netname)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dev = netdev_priv(net); dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock); spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work); INIT_WORK(&dev->work, eth_work);
...@@ -1137,7 +1113,6 @@ void gether_disconnect(struct gether *link) ...@@ -1137,7 +1113,6 @@ void gether_disconnect(struct gether *link)
{ {
struct eth_dev *dev = link->ioport; struct eth_dev *dev = link->ioport;
struct usb_request *req; struct usb_request *req;
struct sk_buff *skb;
WARN_ON(!dev); WARN_ON(!dev);
if (!dev) if (!dev)
...@@ -1164,12 +1139,6 @@ void gether_disconnect(struct gether *link) ...@@ -1164,12 +1139,6 @@ void gether_disconnect(struct gether *link)
spin_lock(&dev->req_lock); spin_lock(&dev->req_lock);
} }
spin_unlock(&dev->req_lock); spin_unlock(&dev->req_lock);
spin_lock(&dev->rx_frames.lock);
while ((skb = __skb_dequeue(&dev->rx_frames)))
dev_kfree_skb_any(skb);
spin_unlock(&dev->rx_frames.lock);
link->in_ep->driver_data = NULL; link->in_ep->driver_data = NULL;
link->in_ep->desc = NULL; link->in_ep->desc = NULL;
......
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