diff options
author | Masakazu Mokuno <mokuno@sm.sony.co.jp> | 2007-07-20 04:24:56 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-07-24 16:28:39 -0400 |
commit | 48544cc267da96a85e4d38aa1999a011229948d6 (patch) | |
tree | 0abb855ec54f74ce85207d6ba7585dc8e8d18796 /drivers/net | |
parent | ea6992aa1f6ed514fe450f46befa56d8d2b6a7fb (diff) |
ps3: tx descriptor handling cleanup
gelic: TX descriptor handling cleanup
- Emitted return value of NETDEV_TX_LOCKED when DMA map or kick
failure.
Now it would free the skb, update drop packet statistics
and return OK. Requested from Jeff Garzik.
- Enable tx queue if number of free descriptors are more than 2
- Fixed descriptor leak if dma map for second descriptor failed
- Stopped calling xmit handler from interrupt handler in order
to recheck tx queue. Instead, call appropriate helper functions.
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ps3_gelic_net.c | 124 |
1 files changed, 71 insertions, 53 deletions
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index f0610fba4c9..143d7bdf787 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c | |||
@@ -408,22 +408,25 @@ static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop) | |||
408 | break; | 408 | break; |
409 | 409 | ||
410 | case GELIC_NET_DESCR_COMPLETE: | 410 | case GELIC_NET_DESCR_COMPLETE: |
411 | card->netdev_stats.tx_packets++; | 411 | if (tx_chain->tail->skb) { |
412 | card->netdev_stats.tx_bytes += | 412 | card->netdev_stats.tx_packets++; |
413 | tx_chain->tail->skb->len; | 413 | card->netdev_stats.tx_bytes += |
414 | tx_chain->tail->skb->len; | ||
415 | } | ||
414 | break; | 416 | break; |
415 | 417 | ||
416 | case GELIC_NET_DESCR_CARDOWNED: | 418 | case GELIC_NET_DESCR_CARDOWNED: |
417 | /* pending tx request */ | 419 | /* pending tx request */ |
418 | default: | 420 | default: |
419 | /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */ | 421 | /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */ |
420 | goto out; | 422 | if (!stop) |
423 | goto out; | ||
421 | } | 424 | } |
422 | gelic_net_release_tx_descr(card, tx_chain->tail); | 425 | gelic_net_release_tx_descr(card, tx_chain->tail); |
423 | release = 1; | 426 | release ++; |
424 | } | 427 | } |
425 | out: | 428 | out: |
426 | if (!stop && release) | 429 | if (!stop && (2 < release)) |
427 | netif_wake_queue(card->netdev); | 430 | netif_wake_queue(card->netdev); |
428 | } | 431 | } |
429 | 432 | ||
@@ -660,19 +663,21 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, | |||
660 | { | 663 | { |
661 | dma_addr_t buf[2]; | 664 | dma_addr_t buf[2]; |
662 | unsigned int vlan_len; | 665 | unsigned int vlan_len; |
666 | struct gelic_net_descr *sec_descr = descr->next; | ||
663 | 667 | ||
664 | if (skb->len < GELIC_NET_VLAN_POS) | 668 | if (skb->len < GELIC_NET_VLAN_POS) |
665 | return -EINVAL; | 669 | return -EINVAL; |
666 | 670 | ||
667 | memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS); | 671 | vlan_len = GELIC_NET_VLAN_POS; |
672 | memcpy(&descr->vlan, skb->data, vlan_len); | ||
668 | if (card->vlan_index != -1) { | 673 | if (card->vlan_index != -1) { |
674 | /* internal vlan tag used */ | ||
669 | descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/ | 675 | descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/ |
670 | descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]); | 676 | descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]); |
671 | vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */ | 677 | vlan_len += VLAN_HLEN; /* added for above two lines */ |
672 | } else | 678 | } |
673 | vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */ | ||
674 | 679 | ||
675 | /* first descr */ | 680 | /* map data area */ |
676 | buf[0] = dma_map_single(ctodev(card), &descr->vlan, | 681 | buf[0] = dma_map_single(ctodev(card), &descr->vlan, |
677 | vlan_len, DMA_TO_DEVICE); | 682 | vlan_len, DMA_TO_DEVICE); |
678 | 683 | ||
@@ -683,20 +688,6 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, | |||
683 | return -ENOMEM; | 688 | return -ENOMEM; |
684 | } | 689 | } |
685 | 690 | ||
686 | descr->buf_addr = buf[0]; | ||
687 | descr->buf_size = vlan_len; | ||
688 | descr->skb = skb; /* not used */ | ||
689 | descr->data_status = 0; | ||
690 | gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */ | ||
691 | |||
692 | /* second descr */ | ||
693 | card->tx_chain.head = card->tx_chain.head->next; | ||
694 | descr->next_descr_addr = descr->next->bus_addr; | ||
695 | descr = descr->next; | ||
696 | if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE) | ||
697 | /* XXX will be removed */ | ||
698 | dev_err(ctodev(card), "descr is not free!\n"); | ||
699 | |||
700 | buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS, | 691 | buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS, |
701 | skb->len - GELIC_NET_VLAN_POS, | 692 | skb->len - GELIC_NET_VLAN_POS, |
702 | DMA_TO_DEVICE); | 693 | DMA_TO_DEVICE); |
@@ -711,13 +702,24 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, | |||
711 | return -ENOMEM; | 702 | return -ENOMEM; |
712 | } | 703 | } |
713 | 704 | ||
714 | descr->buf_addr = buf[1]; | 705 | /* first descr */ |
715 | descr->buf_size = skb->len - GELIC_NET_VLAN_POS; | 706 | descr->buf_addr = buf[0]; |
716 | descr->skb = skb; | 707 | descr->buf_size = vlan_len; |
708 | descr->skb = NULL; /* not used */ | ||
717 | descr->data_status = 0; | 709 | descr->data_status = 0; |
718 | descr->next_descr_addr = 0; /* terminate hw descr */ | 710 | descr->next_descr_addr = descr->next->bus_addr; |
719 | gelic_net_set_txdescr_cmdstat(descr, skb, 0); | 711 | gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */ |
720 | 712 | ||
713 | /* second descr */ | ||
714 | sec_descr->buf_addr = buf[1]; | ||
715 | sec_descr->buf_size = skb->len - GELIC_NET_VLAN_POS; | ||
716 | sec_descr->skb = skb; | ||
717 | sec_descr->data_status = 0; | ||
718 | sec_descr->next_descr_addr = 0; /* terminate hw descr */ | ||
719 | gelic_net_set_txdescr_cmdstat(sec_descr, skb, 0); | ||
720 | |||
721 | /* bump free descriptor pointer */ | ||
722 | card->tx_chain.head = sec_descr->next; | ||
721 | return 0; | 723 | return 0; |
722 | } | 724 | } |
723 | 725 | ||
@@ -730,7 +732,7 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, | |||
730 | static int gelic_net_kick_txdma(struct gelic_net_card *card, | 732 | static int gelic_net_kick_txdma(struct gelic_net_card *card, |
731 | struct gelic_net_descr *descr) | 733 | struct gelic_net_descr *descr) |
732 | { | 734 | { |
733 | int status = -ENXIO; | 735 | int status = 0; |
734 | int count = 10; | 736 | int count = 10; |
735 | 737 | ||
736 | if (card->tx_dma_progress) | 738 | if (card->tx_dma_progress) |
@@ -764,47 +766,62 @@ static int gelic_net_kick_txdma(struct gelic_net_card *card, | |||
764 | static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) | 766 | static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) |
765 | { | 767 | { |
766 | struct gelic_net_card *card = netdev_priv(netdev); | 768 | struct gelic_net_card *card = netdev_priv(netdev); |
767 | struct gelic_net_descr *descr = NULL; | 769 | struct gelic_net_descr *descr; |
768 | int result; | 770 | int result; |
769 | unsigned long flags; | 771 | unsigned long flags; |
770 | 772 | ||
771 | spin_lock_irqsave(&card->tx_dma_lock, flags); | 773 | spin_lock_irqsave(&card->tx_dma_lock, flags); |
772 | 774 | ||
773 | gelic_net_release_tx_chain(card, 0); | 775 | gelic_net_release_tx_chain(card, 0); |
774 | if (!skb) | 776 | |
775 | goto kick; | ||
776 | descr = gelic_net_get_next_tx_descr(card); | 777 | descr = gelic_net_get_next_tx_descr(card); |
777 | if (!descr) { | 778 | if (!descr) { |
779 | /* | ||
780 | * no more descriptors free | ||
781 | */ | ||
778 | netif_stop_queue(netdev); | 782 | netif_stop_queue(netdev); |
779 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); | 783 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); |
780 | return NETDEV_TX_BUSY; | 784 | return NETDEV_TX_BUSY; |
781 | } | 785 | } |
782 | result = gelic_net_prepare_tx_descr_v(card, descr, skb); | ||
783 | |||
784 | if (result) | ||
785 | goto error; | ||
786 | |||
787 | card->tx_chain.head = card->tx_chain.head->next; | ||
788 | 786 | ||
789 | if (descr->prev) | 787 | result = gelic_net_prepare_tx_descr_v(card, descr, skb); |
790 | descr->prev->next_descr_addr = descr->bus_addr; | 788 | if (result) { |
791 | kick: | 789 | /* |
790 | * DMA map failed. As chanses are that failure | ||
791 | * would continue, just release skb and return | ||
792 | */ | ||
793 | card->netdev_stats.tx_dropped++; | ||
794 | dev_kfree_skb_any(skb); | ||
795 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); | ||
796 | return NETDEV_TX_OK; | ||
797 | } | ||
798 | /* | ||
799 | * link this prepared descriptor to previous one | ||
800 | * to achieve high performance | ||
801 | */ | ||
802 | descr->prev->next_descr_addr = descr->bus_addr; | ||
792 | /* | 803 | /* |
793 | * as hardware descriptor is modified in the above lines, | 804 | * as hardware descriptor is modified in the above lines, |
794 | * ensure that the hardware sees it | 805 | * ensure that the hardware sees it |
795 | */ | 806 | */ |
796 | wmb(); | 807 | wmb(); |
797 | if (gelic_net_kick_txdma(card, card->tx_chain.tail)) | 808 | if (gelic_net_kick_txdma(card, descr)) { |
798 | goto error; | 809 | /* |
810 | * kick failed. | ||
811 | * release descriptors which were just prepared | ||
812 | */ | ||
813 | card->netdev_stats.tx_dropped++; | ||
814 | gelic_net_release_tx_descr(card, descr); | ||
815 | gelic_net_release_tx_descr(card, descr->next); | ||
816 | card->tx_chain.tail = descr->next->next; | ||
817 | dev_info(ctodev(card), "%s: kick failure\n", __func__); | ||
818 | } else { | ||
819 | /* OK, DMA started/reserved */ | ||
820 | netdev->trans_start = jiffies; | ||
821 | } | ||
799 | 822 | ||
800 | netdev->trans_start = jiffies; | ||
801 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); | 823 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); |
802 | return NETDEV_TX_OK; | 824 | return NETDEV_TX_OK; |
803 | |||
804 | error: | ||
805 | card->netdev_stats.tx_dropped++; | ||
806 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); | ||
807 | return NETDEV_TX_LOCKED; | ||
808 | } | 825 | } |
809 | 826 | ||
810 | /** | 827 | /** |
@@ -1025,9 +1042,10 @@ static irqreturn_t gelic_net_interrupt(int irq, void *ptr) | |||
1025 | if (status & GELIC_NET_TXINT) { | 1042 | if (status & GELIC_NET_TXINT) { |
1026 | spin_lock_irqsave(&card->tx_dma_lock, flags); | 1043 | spin_lock_irqsave(&card->tx_dma_lock, flags); |
1027 | card->tx_dma_progress = 0; | 1044 | card->tx_dma_progress = 0; |
1045 | gelic_net_release_tx_chain(card, 0); | ||
1046 | /* kick outstanding tx descriptor if any */ | ||
1047 | gelic_net_kick_txdma(card, card->tx_chain.tail); | ||
1028 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); | 1048 | spin_unlock_irqrestore(&card->tx_dma_lock, flags); |
1029 | /* start pending DMA */ | ||
1030 | gelic_net_xmit(NULL, netdev); | ||
1031 | } | 1049 | } |
1032 | return IRQ_HANDLED; | 1050 | return IRQ_HANDLED; |
1033 | } | 1051 | } |