diff options
author | Vasanthy Kolluri <vkolluri@cisco.com> | 2010-06-24 06:51:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-25 23:50:24 -0400 |
commit | 1825aca667196f75b193e2d509ea96ffdc8db0ca (patch) | |
tree | ae8814b42ad649e983c41cebdf8af1c91d06e7fd /drivers/net/enic | |
parent | b5bab85c15ed3d1ae7f917a7c077086ac6c04572 (diff) |
enic: Feature Add: Add loopback capability to enic devices
Hardware has the loopback capability to queue the packets transmitted from
a device to the receive queue of the same device. enic now supports the
loopback capability.
Signed-off-by: Scott Feldman <scofeldm@cisco.com>
Signed-off-by: Vasanthy Kolluri <vkolluri@cisco.com>
Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/enic')
-rw-r--r-- | drivers/net/enic/enic.h | 2 | ||||
-rw-r--r-- | drivers/net/enic/enic_main.c | 42 | ||||
-rw-r--r-- | drivers/net/enic/enic_res.c | 1 | ||||
-rw-r--r-- | drivers/net/enic/enic_res.h | 25 | ||||
-rw-r--r-- | drivers/net/enic/vnic_enet.h | 2 |
5 files changed, 45 insertions, 27 deletions
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h index 3588ef5e7e4c..7280314804a3 100644 --- a/drivers/net/enic/enic.h +++ b/drivers/net/enic/enic.h | |||
@@ -110,6 +110,8 @@ struct enic { | |||
110 | spinlock_t wq_lock[ENIC_WQ_MAX]; | 110 | spinlock_t wq_lock[ENIC_WQ_MAX]; |
111 | unsigned int wq_count; | 111 | unsigned int wq_count; |
112 | struct vlan_group *vlan_group; | 112 | struct vlan_group *vlan_group; |
113 | u16 loop_enable; | ||
114 | u16 loop_tag; | ||
113 | 115 | ||
114 | /* receive queue cache line section */ | 116 | /* receive queue cache line section */ |
115 | ____cacheline_aligned struct vnic_rq rq[ENIC_RQ_MAX]; | 117 | ____cacheline_aligned struct vnic_rq rq[ENIC_RQ_MAX]; |
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index 413e362e3275..eda5530004dc 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c | |||
@@ -594,7 +594,7 @@ static irqreturn_t enic_isr_msix_notify(int irq, void *data) | |||
594 | 594 | ||
595 | static inline void enic_queue_wq_skb_cont(struct enic *enic, | 595 | static inline void enic_queue_wq_skb_cont(struct enic *enic, |
596 | struct vnic_wq *wq, struct sk_buff *skb, | 596 | struct vnic_wq *wq, struct sk_buff *skb, |
597 | unsigned int len_left) | 597 | unsigned int len_left, int loopback) |
598 | { | 598 | { |
599 | skb_frag_t *frag; | 599 | skb_frag_t *frag; |
600 | 600 | ||
@@ -606,13 +606,14 @@ static inline void enic_queue_wq_skb_cont(struct enic *enic, | |||
606 | frag->page_offset, frag->size, | 606 | frag->page_offset, frag->size, |
607 | PCI_DMA_TODEVICE), | 607 | PCI_DMA_TODEVICE), |
608 | frag->size, | 608 | frag->size, |
609 | (len_left == 0)); /* EOP? */ | 609 | (len_left == 0), /* EOP? */ |
610 | loopback); | ||
610 | } | 611 | } |
611 | } | 612 | } |
612 | 613 | ||
613 | static inline void enic_queue_wq_skb_vlan(struct enic *enic, | 614 | static inline void enic_queue_wq_skb_vlan(struct enic *enic, |
614 | struct vnic_wq *wq, struct sk_buff *skb, | 615 | struct vnic_wq *wq, struct sk_buff *skb, |
615 | int vlan_tag_insert, unsigned int vlan_tag) | 616 | int vlan_tag_insert, unsigned int vlan_tag, int loopback) |
616 | { | 617 | { |
617 | unsigned int head_len = skb_headlen(skb); | 618 | unsigned int head_len = skb_headlen(skb); |
618 | unsigned int len_left = skb->len - head_len; | 619 | unsigned int len_left = skb->len - head_len; |
@@ -628,15 +629,15 @@ static inline void enic_queue_wq_skb_vlan(struct enic *enic, | |||
628 | head_len, PCI_DMA_TODEVICE), | 629 | head_len, PCI_DMA_TODEVICE), |
629 | head_len, | 630 | head_len, |
630 | vlan_tag_insert, vlan_tag, | 631 | vlan_tag_insert, vlan_tag, |
631 | eop); | 632 | eop, loopback); |
632 | 633 | ||
633 | if (!eop) | 634 | if (!eop) |
634 | enic_queue_wq_skb_cont(enic, wq, skb, len_left); | 635 | enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); |
635 | } | 636 | } |
636 | 637 | ||
637 | static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, | 638 | static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, |
638 | struct vnic_wq *wq, struct sk_buff *skb, | 639 | struct vnic_wq *wq, struct sk_buff *skb, |
639 | int vlan_tag_insert, unsigned int vlan_tag) | 640 | int vlan_tag_insert, unsigned int vlan_tag, int loopback) |
640 | { | 641 | { |
641 | unsigned int head_len = skb_headlen(skb); | 642 | unsigned int head_len = skb_headlen(skb); |
642 | unsigned int len_left = skb->len - head_len; | 643 | unsigned int len_left = skb->len - head_len; |
@@ -656,15 +657,15 @@ static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, | |||
656 | csum_offset, | 657 | csum_offset, |
657 | hdr_len, | 658 | hdr_len, |
658 | vlan_tag_insert, vlan_tag, | 659 | vlan_tag_insert, vlan_tag, |
659 | eop); | 660 | eop, loopback); |
660 | 661 | ||
661 | if (!eop) | 662 | if (!eop) |
662 | enic_queue_wq_skb_cont(enic, wq, skb, len_left); | 663 | enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); |
663 | } | 664 | } |
664 | 665 | ||
665 | static inline void enic_queue_wq_skb_tso(struct enic *enic, | 666 | static inline void enic_queue_wq_skb_tso(struct enic *enic, |
666 | struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, | 667 | struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, |
667 | int vlan_tag_insert, unsigned int vlan_tag) | 668 | int vlan_tag_insert, unsigned int vlan_tag, int loopback) |
668 | { | 669 | { |
669 | unsigned int frag_len_left = skb_headlen(skb); | 670 | unsigned int frag_len_left = skb_headlen(skb); |
670 | unsigned int len_left = skb->len - frag_len_left; | 671 | unsigned int len_left = skb->len - frag_len_left; |
@@ -701,7 +702,7 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, | |||
701 | len, | 702 | len, |
702 | mss, hdr_len, | 703 | mss, hdr_len, |
703 | vlan_tag_insert, vlan_tag, | 704 | vlan_tag_insert, vlan_tag, |
704 | eop && (len == frag_len_left)); | 705 | eop && (len == frag_len_left), loopback); |
705 | frag_len_left -= len; | 706 | frag_len_left -= len; |
706 | offset += len; | 707 | offset += len; |
707 | } | 708 | } |
@@ -727,7 +728,8 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, | |||
727 | dma_addr, | 728 | dma_addr, |
728 | len, | 729 | len, |
729 | (len_left == 0) && | 730 | (len_left == 0) && |
730 | (len == frag_len_left)); /* EOP? */ | 731 | (len == frag_len_left), /* EOP? */ |
732 | loopback); | ||
731 | frag_len_left -= len; | 733 | frag_len_left -= len; |
732 | offset += len; | 734 | offset += len; |
733 | } | 735 | } |
@@ -740,22 +742,26 @@ static inline void enic_queue_wq_skb(struct enic *enic, | |||
740 | unsigned int mss = skb_shinfo(skb)->gso_size; | 742 | unsigned int mss = skb_shinfo(skb)->gso_size; |
741 | unsigned int vlan_tag = 0; | 743 | unsigned int vlan_tag = 0; |
742 | int vlan_tag_insert = 0; | 744 | int vlan_tag_insert = 0; |
745 | int loopback = 0; | ||
743 | 746 | ||
744 | if (enic->vlan_group && vlan_tx_tag_present(skb)) { | 747 | if (enic->vlan_group && vlan_tx_tag_present(skb)) { |
745 | /* VLAN tag from trunking driver */ | 748 | /* VLAN tag from trunking driver */ |
746 | vlan_tag_insert = 1; | 749 | vlan_tag_insert = 1; |
747 | vlan_tag = vlan_tx_tag_get(skb); | 750 | vlan_tag = vlan_tx_tag_get(skb); |
751 | } else if (enic->loop_enable) { | ||
752 | vlan_tag = enic->loop_tag; | ||
753 | loopback = 1; | ||
748 | } | 754 | } |
749 | 755 | ||
750 | if (mss) | 756 | if (mss) |
751 | enic_queue_wq_skb_tso(enic, wq, skb, mss, | 757 | enic_queue_wq_skb_tso(enic, wq, skb, mss, |
752 | vlan_tag_insert, vlan_tag); | 758 | vlan_tag_insert, vlan_tag, loopback); |
753 | else if (skb->ip_summed == CHECKSUM_PARTIAL) | 759 | else if (skb->ip_summed == CHECKSUM_PARTIAL) |
754 | enic_queue_wq_skb_csum_l4(enic, wq, skb, | 760 | enic_queue_wq_skb_csum_l4(enic, wq, skb, |
755 | vlan_tag_insert, vlan_tag); | 761 | vlan_tag_insert, vlan_tag, loopback); |
756 | else | 762 | else |
757 | enic_queue_wq_skb_vlan(enic, wq, skb, | 763 | enic_queue_wq_skb_vlan(enic, wq, skb, |
758 | vlan_tag_insert, vlan_tag); | 764 | vlan_tag_insert, vlan_tag, loopback); |
759 | } | 765 | } |
760 | 766 | ||
761 | /* netif_tx_lock held, process context with BHs disabled, or BH */ | 767 | /* netif_tx_lock held, process context with BHs disabled, or BH */ |
@@ -1275,7 +1281,7 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) | |||
1275 | struct enic *enic = vnic_dev_priv(rq->vdev); | 1281 | struct enic *enic = vnic_dev_priv(rq->vdev); |
1276 | struct net_device *netdev = enic->netdev; | 1282 | struct net_device *netdev = enic->netdev; |
1277 | struct sk_buff *skb; | 1283 | struct sk_buff *skb; |
1278 | unsigned int len = netdev->mtu + ETH_HLEN; | 1284 | unsigned int len = netdev->mtu + VLAN_ETH_HLEN; |
1279 | unsigned int os_buf_index = 0; | 1285 | unsigned int os_buf_index = 0; |
1280 | dma_addr_t dma_addr; | 1286 | dma_addr_t dma_addr; |
1281 | 1287 | ||
@@ -2441,6 +2447,12 @@ static int __devinit enic_probe(struct pci_dev *pdev, | |||
2441 | netdev->ethtool_ops = &enic_ethtool_ops; | 2447 | netdev->ethtool_ops = &enic_ethtool_ops; |
2442 | 2448 | ||
2443 | netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; | 2449 | netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; |
2450 | if (ENIC_SETTING(enic, LOOP)) { | ||
2451 | netdev->features &= ~NETIF_F_HW_VLAN_TX; | ||
2452 | enic->loop_enable = 1; | ||
2453 | enic->loop_tag = enic->config.loop_tag; | ||
2454 | dev_info(dev, "loopback tag=0x%04x\n", enic->loop_tag); | ||
2455 | } | ||
2444 | if (ENIC_SETTING(enic, TXCSUM)) | 2456 | if (ENIC_SETTING(enic, TXCSUM)) |
2445 | netdev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; | 2457 | netdev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; |
2446 | if (ENIC_SETTING(enic, TSO)) | 2458 | if (ENIC_SETTING(enic, TSO)) |
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c index 478928b7cc09..2cc7e278caae 100644 --- a/drivers/net/enic/enic_res.c +++ b/drivers/net/enic/enic_res.c | |||
@@ -70,6 +70,7 @@ int enic_get_vnic_config(struct enic *enic) | |||
70 | GET_CONFIG(intr_timer_type); | 70 | GET_CONFIG(intr_timer_type); |
71 | GET_CONFIG(intr_mode); | 71 | GET_CONFIG(intr_mode); |
72 | GET_CONFIG(intr_timer_usec); | 72 | GET_CONFIG(intr_timer_usec); |
73 | GET_CONFIG(loop_tag); | ||
73 | 74 | ||
74 | c->wq_desc_count = | 75 | c->wq_desc_count = |
75 | min_t(u32, ENIC_MAX_WQ_DESCS, | 76 | min_t(u32, ENIC_MAX_WQ_DESCS, |
diff --git a/drivers/net/enic/enic_res.h b/drivers/net/enic/enic_res.h index 3f6e039c2fd2..8b25a07a67d1 100644 --- a/drivers/net/enic/enic_res.h +++ b/drivers/net/enic/enic_res.h | |||
@@ -43,7 +43,7 @@ static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq, | |||
43 | void *os_buf, dma_addr_t dma_addr, unsigned int len, | 43 | void *os_buf, dma_addr_t dma_addr, unsigned int len, |
44 | unsigned int mss_or_csum_offset, unsigned int hdr_len, | 44 | unsigned int mss_or_csum_offset, unsigned int hdr_len, |
45 | int vlan_tag_insert, unsigned int vlan_tag, | 45 | int vlan_tag_insert, unsigned int vlan_tag, |
46 | int offload_mode, int cq_entry, int sop, int eop) | 46 | int offload_mode, int cq_entry, int sop, int eop, int loopback) |
47 | { | 47 | { |
48 | struct wq_enet_desc *desc = vnic_wq_next_desc(wq); | 48 | struct wq_enet_desc *desc = vnic_wq_next_desc(wq); |
49 | 49 | ||
@@ -56,61 +56,62 @@ static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq, | |||
56 | 0, /* fcoe_encap */ | 56 | 0, /* fcoe_encap */ |
57 | (u8)vlan_tag_insert, | 57 | (u8)vlan_tag_insert, |
58 | (u16)vlan_tag, | 58 | (u16)vlan_tag, |
59 | 0 /* loopback */); | 59 | (u8)loopback); |
60 | 60 | ||
61 | vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop); | 61 | vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop); |
62 | } | 62 | } |
63 | 63 | ||
64 | static inline void enic_queue_wq_desc_cont(struct vnic_wq *wq, | 64 | static inline void enic_queue_wq_desc_cont(struct vnic_wq *wq, |
65 | void *os_buf, dma_addr_t dma_addr, unsigned int len, int eop) | 65 | void *os_buf, dma_addr_t dma_addr, unsigned int len, |
66 | int eop, int loopback) | ||
66 | { | 67 | { |
67 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, | 68 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, |
68 | 0, 0, 0, 0, 0, | 69 | 0, 0, 0, 0, 0, |
69 | eop, 0 /* !SOP */, eop); | 70 | eop, 0 /* !SOP */, eop, loopback); |
70 | } | 71 | } |
71 | 72 | ||
72 | static inline void enic_queue_wq_desc(struct vnic_wq *wq, void *os_buf, | 73 | static inline void enic_queue_wq_desc(struct vnic_wq *wq, void *os_buf, |
73 | dma_addr_t dma_addr, unsigned int len, int vlan_tag_insert, | 74 | dma_addr_t dma_addr, unsigned int len, int vlan_tag_insert, |
74 | unsigned int vlan_tag, int eop) | 75 | unsigned int vlan_tag, int eop, int loopback) |
75 | { | 76 | { |
76 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, | 77 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, |
77 | 0, 0, vlan_tag_insert, vlan_tag, | 78 | 0, 0, vlan_tag_insert, vlan_tag, |
78 | WQ_ENET_OFFLOAD_MODE_CSUM, | 79 | WQ_ENET_OFFLOAD_MODE_CSUM, |
79 | eop, 1 /* SOP */, eop); | 80 | eop, 1 /* SOP */, eop, loopback); |
80 | } | 81 | } |
81 | 82 | ||
82 | static inline void enic_queue_wq_desc_csum(struct vnic_wq *wq, | 83 | static inline void enic_queue_wq_desc_csum(struct vnic_wq *wq, |
83 | void *os_buf, dma_addr_t dma_addr, unsigned int len, | 84 | void *os_buf, dma_addr_t dma_addr, unsigned int len, |
84 | int ip_csum, int tcpudp_csum, int vlan_tag_insert, | 85 | int ip_csum, int tcpudp_csum, int vlan_tag_insert, |
85 | unsigned int vlan_tag, int eop) | 86 | unsigned int vlan_tag, int eop, int loopback) |
86 | { | 87 | { |
87 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, | 88 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, |
88 | (ip_csum ? 1 : 0) + (tcpudp_csum ? 2 : 0), | 89 | (ip_csum ? 1 : 0) + (tcpudp_csum ? 2 : 0), |
89 | 0, vlan_tag_insert, vlan_tag, | 90 | 0, vlan_tag_insert, vlan_tag, |
90 | WQ_ENET_OFFLOAD_MODE_CSUM, | 91 | WQ_ENET_OFFLOAD_MODE_CSUM, |
91 | eop, 1 /* SOP */, eop); | 92 | eop, 1 /* SOP */, eop, loopback); |
92 | } | 93 | } |
93 | 94 | ||
94 | static inline void enic_queue_wq_desc_csum_l4(struct vnic_wq *wq, | 95 | static inline void enic_queue_wq_desc_csum_l4(struct vnic_wq *wq, |
95 | void *os_buf, dma_addr_t dma_addr, unsigned int len, | 96 | void *os_buf, dma_addr_t dma_addr, unsigned int len, |
96 | unsigned int csum_offset, unsigned int hdr_len, | 97 | unsigned int csum_offset, unsigned int hdr_len, |
97 | int vlan_tag_insert, unsigned int vlan_tag, int eop) | 98 | int vlan_tag_insert, unsigned int vlan_tag, int eop, int loopback) |
98 | { | 99 | { |
99 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, | 100 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, |
100 | csum_offset, hdr_len, vlan_tag_insert, vlan_tag, | 101 | csum_offset, hdr_len, vlan_tag_insert, vlan_tag, |
101 | WQ_ENET_OFFLOAD_MODE_CSUM_L4, | 102 | WQ_ENET_OFFLOAD_MODE_CSUM_L4, |
102 | eop, 1 /* SOP */, eop); | 103 | eop, 1 /* SOP */, eop, loopback); |
103 | } | 104 | } |
104 | 105 | ||
105 | static inline void enic_queue_wq_desc_tso(struct vnic_wq *wq, | 106 | static inline void enic_queue_wq_desc_tso(struct vnic_wq *wq, |
106 | void *os_buf, dma_addr_t dma_addr, unsigned int len, | 107 | void *os_buf, dma_addr_t dma_addr, unsigned int len, |
107 | unsigned int mss, unsigned int hdr_len, int vlan_tag_insert, | 108 | unsigned int mss, unsigned int hdr_len, int vlan_tag_insert, |
108 | unsigned int vlan_tag, int eop) | 109 | unsigned int vlan_tag, int eop, int loopback) |
109 | { | 110 | { |
110 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, | 111 | enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, |
111 | mss, hdr_len, vlan_tag_insert, vlan_tag, | 112 | mss, hdr_len, vlan_tag_insert, vlan_tag, |
112 | WQ_ENET_OFFLOAD_MODE_TSO, | 113 | WQ_ENET_OFFLOAD_MODE_TSO, |
113 | eop, 1 /* SOP */, eop); | 114 | eop, 1 /* SOP */, eop, loopback); |
114 | } | 115 | } |
115 | 116 | ||
116 | static inline void enic_queue_rq_desc(struct vnic_rq *rq, | 117 | static inline void enic_queue_rq_desc(struct vnic_rq *rq, |
diff --git a/drivers/net/enic/vnic_enet.h b/drivers/net/enic/vnic_enet.h index 8eeb6758491b..42baaa13ce59 100644 --- a/drivers/net/enic/vnic_enet.h +++ b/drivers/net/enic/vnic_enet.h | |||
@@ -35,6 +35,7 @@ struct vnic_enet_config { | |||
35 | u8 intr_mode; | 35 | u8 intr_mode; |
36 | char devname[16]; | 36 | char devname[16]; |
37 | u32 intr_timer_usec; | 37 | u32 intr_timer_usec; |
38 | u16 loop_tag; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | #define VENETF_TSO 0x1 /* TSO enabled */ | 41 | #define VENETF_TSO 0x1 /* TSO enabled */ |
@@ -48,5 +49,6 @@ struct vnic_enet_config { | |||
48 | #define VENETF_RSSHASH_TCPIPV6 0x100 /* Hash on TCP + IPv6 fields */ | 49 | #define VENETF_RSSHASH_TCPIPV6 0x100 /* Hash on TCP + IPv6 fields */ |
49 | #define VENETF_RSSHASH_IPV6_EX 0x200 /* Hash on IPv6 extended fields */ | 50 | #define VENETF_RSSHASH_IPV6_EX 0x200 /* Hash on IPv6 extended fields */ |
50 | #define VENETF_RSSHASH_TCPIPV6_EX 0x400 /* Hash on TCP + IPv6 ext. fields */ | 51 | #define VENETF_RSSHASH_TCPIPV6_EX 0x400 /* Hash on TCP + IPv6 ext. fields */ |
52 | #define VENETF_LOOP 0x800 /* Loopback enabled */ | ||
51 | 53 | ||
52 | #endif /* _VNIC_ENIC_H_ */ | 54 | #endif /* _VNIC_ENIC_H_ */ |