Commit 1e4e0767ecb1cf53a43343518c0e09ad7ee5e23a

Authored by Asier Llano
Committed by David S. Miller
1 parent 4b860abf63

net/mpc5200: Fix locking on fec_mpc52xx driver

Fix the locking scheme on the fec_mpc52xx driver.  This device can
receive IRQs from three sources; the FEC itself, the tx DMA, and the
rx DMA.  Mutual exclusion was handled by taking a spin_lock() in the
critical regions, but because the handlers are run with IRQs enabled,
spin_lock() is insufficient and the driver can end up interrupting
a critical region anyway from another IRQ.

Asier Llano discovered that this occurs when an error IRQ is raised
in the middle of handling rx irqs which resulted in an sk_buff memory
leak.

In addition, locking is spotty at best in the driver and inspection
revealed quite a few places with insufficient locking.

This patch is based on Asier's initial work, but reworks a number of
things so that locks are held for as short a time as possible, so
that spin_lock_irqsave() is used everywhere, and so the locks are
dropped when calling into the network stack (because the lock only
protects the hardware interface; not the network stack).

Boot tested on a lite5200 with an NFS root.  Has not been performance
tested.

Signed-off-by: Asier Llano <a.llano@ziv.es>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 62 additions and 59 deletions Side-by-side Diff

drivers/net/fec_mpc52xx.c
... ... @@ -85,11 +85,15 @@
85 85  
86 86 static void mpc52xx_fec_tx_timeout(struct net_device *dev)
87 87 {
  88 + struct mpc52xx_fec_priv *priv = netdev_priv(dev);
  89 + unsigned long flags;
  90 +
88 91 dev_warn(&dev->dev, "transmit timed out\n");
89 92  
  93 + spin_lock_irqsave(&priv->lock, flags);
90 94 mpc52xx_fec_reset(dev);
91   -
92 95 dev->stats.tx_errors++;
  96 + spin_unlock_irqrestore(&priv->lock, flags);
93 97  
94 98 netif_wake_queue(dev);
95 99 }
96 100  
97 101  
98 102  
99 103  
100 104  
... ... @@ -135,28 +139,32 @@
135 139 }
136 140 }
137 141  
  142 +static void
  143 +mpc52xx_fec_rx_submit(struct net_device *dev, struct sk_buff *rskb)
  144 +{
  145 + struct mpc52xx_fec_priv *priv = netdev_priv(dev);
  146 + struct bcom_fec_bd *bd;
  147 +
  148 + bd = (struct bcom_fec_bd *) bcom_prepare_next_buffer(priv->rx_dmatsk);
  149 + bd->status = FEC_RX_BUFFER_SIZE;
  150 + bd->skb_pa = dma_map_single(dev->dev.parent, rskb->data,
  151 + FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
  152 + bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
  153 +}
  154 +
138 155 static int mpc52xx_fec_alloc_rx_buffers(struct net_device *dev, struct bcom_task *rxtsk)
139 156 {
140   - while (!bcom_queue_full(rxtsk)) {
141   - struct sk_buff *skb;
142   - struct bcom_fec_bd *bd;
  157 + struct sk_buff *skb;
143 158  
  159 + while (!bcom_queue_full(rxtsk)) {
144 160 skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
145   - if (skb == NULL)
  161 + if (!skb)
146 162 return -EAGAIN;
147 163  
148 164 /* zero out the initial receive buffers to aid debugging */
149 165 memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
150   -
151   - bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk);
152   -
153   - bd->status = FEC_RX_BUFFER_SIZE;
154   - bd->skb_pa = dma_map_single(dev->dev.parent, skb->data,
155   - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
156   -
157   - bcom_submit_next_buffer(rxtsk, skb);
  166 + mpc52xx_fec_rx_submit(dev, skb);
158 167 }
159   -
160 168 return 0;
161 169 }
162 170  
163 171  
... ... @@ -328,13 +336,12 @@
328 336 DMA_TO_DEVICE);
329 337  
330 338 bcom_submit_next_buffer(priv->tx_dmatsk, skb);
  339 + spin_unlock_irqrestore(&priv->lock, flags);
331 340  
332 341 if (bcom_queue_full(priv->tx_dmatsk)) {
333 342 netif_stop_queue(dev);
334 343 }
335 344  
336   - spin_unlock_irqrestore(&priv->lock, flags);
337   -
338 345 return NETDEV_TX_OK;
339 346 }
340 347  
341 348  
... ... @@ -359,9 +366,9 @@
359 366 {
360 367 struct net_device *dev = dev_id;
361 368 struct mpc52xx_fec_priv *priv = netdev_priv(dev);
  369 + unsigned long flags;
362 370  
363   - spin_lock(&priv->lock);
364   -
  371 + spin_lock_irqsave(&priv->lock, flags);
365 372 while (bcom_buffer_done(priv->tx_dmatsk)) {
366 373 struct sk_buff *skb;
367 374 struct bcom_fec_bd *bd;
368 375  
... ... @@ -372,11 +379,10 @@
372 379  
373 380 dev_kfree_skb_irq(skb);
374 381 }
  382 + spin_unlock_irqrestore(&priv->lock, flags);
375 383  
376 384 netif_wake_queue(dev);
377 385  
378   - spin_unlock(&priv->lock);
379   -
380 386 return IRQ_HANDLED;
381 387 }
382 388  
383 389  
384 390  
385 391  
386 392  
387 393  
388 394  
389 395  
390 396  
391 397  
392 398  
393 399  
394 400  
... ... @@ -384,67 +390,60 @@
384 390 {
385 391 struct net_device *dev = dev_id;
386 392 struct mpc52xx_fec_priv *priv = netdev_priv(dev);
  393 + struct sk_buff *rskb; /* received sk_buff */
  394 + struct sk_buff *skb; /* new sk_buff to enqueue in its place */
  395 + struct bcom_fec_bd *bd;
  396 + u32 status, physaddr;
  397 + int length;
  398 + unsigned long flags;
387 399  
  400 + spin_lock_irqsave(&priv->lock, flags);
  401 +
388 402 while (bcom_buffer_done(priv->rx_dmatsk)) {
389   - struct sk_buff *skb;
390   - struct sk_buff *rskb;
391   - struct bcom_fec_bd *bd;
392   - u32 status;
393 403  
394 404 rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status,
395   - (struct bcom_bd **)&bd);
396   - dma_unmap_single(dev->dev.parent, bd->skb_pa, rskb->len,
397   - DMA_FROM_DEVICE);
  405 + (struct bcom_bd **)&bd);
  406 + physaddr = bd->skb_pa;
398 407  
399 408 /* Test for errors in received frame */
400 409 if (status & BCOM_FEC_RX_BD_ERRORS) {
401 410 /* Drop packet and reuse the buffer */
402   - bd = (struct bcom_fec_bd *)
403   - bcom_prepare_next_buffer(priv->rx_dmatsk);
404   -
405   - bd->status = FEC_RX_BUFFER_SIZE;
406   - bd->skb_pa = dma_map_single(dev->dev.parent,
407   - rskb->data,
408   - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
409   -
410   - bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
411   -
  411 + mpc52xx_fec_rx_submit(dev, rskb);
412 412 dev->stats.rx_dropped++;
413   -
414 413 continue;
415 414 }
416 415  
417 416 /* skbs are allocated on open, so now we allocate a new one,
418 417 * and remove the old (with the packet) */
419 418 skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
420   - if (skb) {
421   - /* Process the received skb */
422   - int length = status & BCOM_FEC_RX_BD_LEN_MASK;
423   -
424   - skb_put(rskb, length - 4); /* length without CRC32 */
425   -
426   - rskb->dev = dev;
427   - rskb->protocol = eth_type_trans(rskb, dev);
428   -
429   - netif_rx(rskb);
430   - } else {
  419 + if (!skb) {
431 420 /* Can't get a new one : reuse the same & drop pkt */
432   - dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
  421 + dev_notice(&dev->dev, "Low memory - dropped packet.\n");
  422 + mpc52xx_fec_rx_submit(dev, rskb);
433 423 dev->stats.rx_dropped++;
434   -
435   - skb = rskb;
  424 + continue;
436 425 }
437 426  
438   - bd = (struct bcom_fec_bd *)
439   - bcom_prepare_next_buffer(priv->rx_dmatsk);
  427 + /* Enqueue the new sk_buff back on the hardware */
  428 + mpc52xx_fec_rx_submit(dev, skb);
440 429  
441   - bd->status = FEC_RX_BUFFER_SIZE;
442   - bd->skb_pa = dma_map_single(dev->dev.parent, skb->data,
443   - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
  430 + /* Process the received skb - Drop the spin lock while
  431 + * calling into the network stack */
  432 + spin_unlock_irqrestore(&priv->lock, flags);
444 433  
445   - bcom_submit_next_buffer(priv->rx_dmatsk, skb);
  434 + dma_unmap_single(dev->dev.parent, physaddr, rskb->len,
  435 + DMA_FROM_DEVICE);
  436 + length = status & BCOM_FEC_RX_BD_LEN_MASK;
  437 + skb_put(rskb, length - 4); /* length without CRC32 */
  438 + rskb->dev = dev;
  439 + rskb->protocol = eth_type_trans(rskb, dev);
  440 + netif_rx(rskb);
  441 +
  442 + spin_lock_irqsave(&priv->lock, flags);
446 443 }
447 444  
  445 + spin_unlock_irqrestore(&priv->lock, flags);
  446 +
448 447 return IRQ_HANDLED;
449 448 }
450 449  
... ... @@ -454,6 +453,7 @@
454 453 struct mpc52xx_fec_priv *priv = netdev_priv(dev);
455 454 struct mpc52xx_fec __iomem *fec = priv->fec;
456 455 u32 ievent;
  456 + unsigned long flags;
457 457  
458 458 ievent = in_be32(&fec->ievent);
459 459  
460 460  
461 461  
... ... @@ -471,9 +471,10 @@
471 471 if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
472 472 dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
473 473  
  474 + spin_lock_irqsave(&priv->lock, flags);
474 475 mpc52xx_fec_reset(dev);
  476 + spin_unlock_irqrestore(&priv->lock, flags);
475 477  
476   - netif_wake_queue(dev);
477 478 return IRQ_HANDLED;
478 479 }
479 480  
... ... @@ -768,6 +769,8 @@
768 769 bcom_enable(priv->tx_dmatsk);
769 770  
770 771 mpc52xx_fec_start(dev);
  772 +
  773 + netif_wake_queue(dev);
771 774 }
772 775  
773 776