diff options
author | Kulikov Vasiliy <segooon@gmail.com> | 2010-07-08 22:25:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-10 22:42:06 -0400 |
commit | f163530407280485034c836d908b49787a8189bc (patch) | |
tree | 0f03dbed022025a5f4d78d5e2b84b8656873c138 /drivers/net/82596.c | |
parent | 344dbf1073d1cea179ed0831547cab2b7ea4ea27 (diff) |
82596: do not panic on out of memory
If dev_alloc_skb() failed then free already allocated skbs.
remove_rx_bufs() can be called multiple times, so set rbd->skb to NULL
to avoid double free. remove_rx_bufs() was moved upwards to be seen by
init_rx_bufs().
Signed-off-by: Kulikov Vasiliy <segooon@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/82596.c')
-rw-r--r-- | drivers/net/82596.c | 42 |
1 files changed, 26 insertions, 16 deletions
diff --git a/drivers/net/82596.c b/drivers/net/82596.c index dd8dc15556cb..73073d0b6894 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c | |||
@@ -525,7 +525,21 @@ static irqreturn_t i596_error(int irq, void *dev_id) | |||
525 | } | 525 | } |
526 | #endif | 526 | #endif |
527 | 527 | ||
528 | static inline void init_rx_bufs(struct net_device *dev) | 528 | static inline void remove_rx_bufs(struct net_device *dev) |
529 | { | ||
530 | struct i596_private *lp = dev->ml_priv; | ||
531 | struct i596_rbd *rbd; | ||
532 | int i; | ||
533 | |||
534 | for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { | ||
535 | if (rbd->skb == NULL) | ||
536 | break; | ||
537 | dev_kfree_skb(rbd->skb); | ||
538 | rbd->skb = NULL; | ||
539 | } | ||
540 | } | ||
541 | |||
542 | static inline int init_rx_bufs(struct net_device *dev) | ||
529 | { | 543 | { |
530 | struct i596_private *lp = dev->ml_priv; | 544 | struct i596_private *lp = dev->ml_priv; |
531 | int i; | 545 | int i; |
@@ -537,8 +551,11 @@ static inline void init_rx_bufs(struct net_device *dev) | |||
537 | for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { | 551 | for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { |
538 | struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); | 552 | struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); |
539 | 553 | ||
540 | if (skb == NULL) | 554 | if (skb == NULL) { |
541 | panic("82596: alloc_skb() failed"); | 555 | remove_rx_bufs(dev); |
556 | return -ENOMEM; | ||
557 | } | ||
558 | |||
542 | skb->dev = dev; | 559 | skb->dev = dev; |
543 | rbd->v_next = rbd+1; | 560 | rbd->v_next = rbd+1; |
544 | rbd->b_next = WSWAPrbd(virt_to_bus(rbd+1)); | 561 | rbd->b_next = WSWAPrbd(virt_to_bus(rbd+1)); |
@@ -574,19 +591,8 @@ static inline void init_rx_bufs(struct net_device *dev) | |||
574 | rfd->v_next = lp->rfds; | 591 | rfd->v_next = lp->rfds; |
575 | rfd->b_next = WSWAPrfd(virt_to_bus(lp->rfds)); | 592 | rfd->b_next = WSWAPrfd(virt_to_bus(lp->rfds)); |
576 | rfd->cmd = CMD_EOL|CMD_FLEX; | 593 | rfd->cmd = CMD_EOL|CMD_FLEX; |
577 | } | ||
578 | |||
579 | static inline void remove_rx_bufs(struct net_device *dev) | ||
580 | { | ||
581 | struct i596_private *lp = dev->ml_priv; | ||
582 | struct i596_rbd *rbd; | ||
583 | int i; | ||
584 | 594 | ||
585 | for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { | 595 | return 0; |
586 | if (rbd->skb == NULL) | ||
587 | break; | ||
588 | dev_kfree_skb(rbd->skb); | ||
589 | } | ||
590 | } | 596 | } |
591 | 597 | ||
592 | 598 | ||
@@ -1013,7 +1019,11 @@ static int i596_open(struct net_device *dev) | |||
1013 | return -EAGAIN; | 1019 | return -EAGAIN; |
1014 | } | 1020 | } |
1015 | #endif | 1021 | #endif |
1016 | init_rx_bufs(dev); | 1022 | res = init_rx_bufs(dev); |
1023 | if (res) { | ||
1024 | free_irq(dev->irq, dev); | ||
1025 | return res; | ||
1026 | } | ||
1017 | 1027 | ||
1018 | netif_start_queue(dev); | 1028 | netif_start_queue(dev); |
1019 | 1029 | ||