diff options
author | Brice Goglin <brice@myri.com> | 2007-10-13 06:34:01 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-10-15 14:24:07 -0400 |
commit | 4f93fde06b0623319bc1cd6c9adad8d730813433 (patch) | |
tree | 5e80f5b57da6a0b9003b1d4ce73ab1c81e705839 /drivers/net/myri10ge | |
parent | eabd7e35c0061dc250fcb8b77c472cb66d770774 (diff) |
myri10ge: add IPv6 TSO support
Add support for IPv6 TSO to the myri10ge driver.
Signed-off-by: Brice Goglin <brice@myri.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/myri10ge')
-rw-r--r-- | drivers/net/myri10ge/myri10ge.c | 87 |
1 files changed, 79 insertions, 8 deletions
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 5c103128e248..6d5af080d7d5 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c | |||
@@ -214,6 +214,8 @@ struct myri10ge_priv { | |||
214 | unsigned long serial_number; | 214 | unsigned long serial_number; |
215 | int vendor_specific_offset; | 215 | int vendor_specific_offset; |
216 | int fw_multicast_support; | 216 | int fw_multicast_support; |
217 | unsigned long features; | ||
218 | u32 max_tso6; | ||
217 | u32 read_dma; | 219 | u32 read_dma; |
218 | u32 write_dma; | 220 | u32 write_dma; |
219 | u32 read_write_dma; | 221 | u32 read_write_dma; |
@@ -311,6 +313,7 @@ MODULE_PARM_DESC(myri10ge_wcfifo, "Enable WC Fifo when WC is enabled\n"); | |||
311 | #define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8) | 313 | #define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8) |
312 | 314 | ||
313 | static void myri10ge_set_multicast_list(struct net_device *dev); | 315 | static void myri10ge_set_multicast_list(struct net_device *dev); |
316 | static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev); | ||
314 | 317 | ||
315 | static inline void put_be32(__be32 val, __be32 __iomem * p) | 318 | static inline void put_be32(__be32 val, __be32 __iomem * p) |
316 | { | 319 | { |
@@ -612,6 +615,7 @@ static int myri10ge_load_firmware(struct myri10ge_priv *mgp) | |||
612 | __be32 buf[16]; | 615 | __be32 buf[16]; |
613 | u32 dma_low, dma_high, size; | 616 | u32 dma_low, dma_high, size; |
614 | int status, i; | 617 | int status, i; |
618 | struct myri10ge_cmd cmd; | ||
615 | 619 | ||
616 | size = 0; | 620 | size = 0; |
617 | status = myri10ge_load_hotplug_firmware(mgp, &size); | 621 | status = myri10ge_load_hotplug_firmware(mgp, &size); |
@@ -688,6 +692,14 @@ static int myri10ge_load_firmware(struct myri10ge_priv *mgp) | |||
688 | dev_info(&mgp->pdev->dev, "handoff confirmed\n"); | 692 | dev_info(&mgp->pdev->dev, "handoff confirmed\n"); |
689 | myri10ge_dummy_rdma(mgp, 1); | 693 | myri10ge_dummy_rdma(mgp, 1); |
690 | 694 | ||
695 | /* probe for IPv6 TSO support */ | ||
696 | mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO; | ||
697 | status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE, | ||
698 | &cmd, 0); | ||
699 | if (status == 0) { | ||
700 | mgp->max_tso6 = cmd.data0; | ||
701 | mgp->features |= NETIF_F_TSO6; | ||
702 | } | ||
691 | return 0; | 703 | return 0; |
692 | } | 704 | } |
693 | 705 | ||
@@ -1384,6 +1396,18 @@ static int myri10ge_set_rx_csum(struct net_device *netdev, u32 csum_enabled) | |||
1384 | return 0; | 1396 | return 0; |
1385 | } | 1397 | } |
1386 | 1398 | ||
1399 | static int myri10ge_set_tso(struct net_device *netdev, u32 tso_enabled) | ||
1400 | { | ||
1401 | struct myri10ge_priv *mgp = netdev_priv(netdev); | ||
1402 | unsigned long flags = mgp->features & (NETIF_F_TSO6 | NETIF_F_TSO); | ||
1403 | |||
1404 | if (tso_enabled) | ||
1405 | netdev->features |= flags; | ||
1406 | else | ||
1407 | netdev->features &= ~flags; | ||
1408 | return 0; | ||
1409 | } | ||
1410 | |||
1387 | static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = { | 1411 | static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = { |
1388 | "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", | 1412 | "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", |
1389 | "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", | 1413 | "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", |
@@ -1508,7 +1532,7 @@ static const struct ethtool_ops myri10ge_ethtool_ops = { | |||
1508 | .set_rx_csum = myri10ge_set_rx_csum, | 1532 | .set_rx_csum = myri10ge_set_rx_csum, |
1509 | .set_tx_csum = ethtool_op_set_tx_hw_csum, | 1533 | .set_tx_csum = ethtool_op_set_tx_hw_csum, |
1510 | .set_sg = ethtool_op_set_sg, | 1534 | .set_sg = ethtool_op_set_sg, |
1511 | .set_tso = ethtool_op_set_tso, | 1535 | .set_tso = myri10ge_set_tso, |
1512 | .get_link = ethtool_op_get_link, | 1536 | .get_link = ethtool_op_get_link, |
1513 | .get_strings = myri10ge_get_strings, | 1537 | .get_strings = myri10ge_get_strings, |
1514 | .get_sset_count = myri10ge_get_sset_count, | 1538 | .get_sset_count = myri10ge_get_sset_count, |
@@ -2166,7 +2190,8 @@ again: | |||
2166 | pseudo_hdr_offset = cksum_offset + skb->csum_offset; | 2190 | pseudo_hdr_offset = cksum_offset + skb->csum_offset; |
2167 | /* If the headers are excessively large, then we must | 2191 | /* If the headers are excessively large, then we must |
2168 | * fall back to a software checksum */ | 2192 | * fall back to a software checksum */ |
2169 | if (unlikely(cksum_offset > 255 || pseudo_hdr_offset > 127)) { | 2193 | if (unlikely(!mss && (cksum_offset > 255 || |
2194 | pseudo_hdr_offset > 127))) { | ||
2170 | if (skb_checksum_help(skb)) | 2195 | if (skb_checksum_help(skb)) |
2171 | goto drop; | 2196 | goto drop; |
2172 | cksum_offset = 0; | 2197 | cksum_offset = 0; |
@@ -2186,9 +2211,18 @@ again: | |||
2186 | /* negative cum_len signifies to the | 2211 | /* negative cum_len signifies to the |
2187 | * send loop that we are still in the | 2212 | * send loop that we are still in the |
2188 | * header portion of the TSO packet. | 2213 | * header portion of the TSO packet. |
2189 | * TSO header must be at most 134 bytes long */ | 2214 | * TSO header can be at most 1KB long */ |
2190 | cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb)); | 2215 | cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb)); |
2191 | 2216 | ||
2217 | /* for IPv6 TSO, the checksum offset stores the | ||
2218 | * TCP header length, to save the firmware from | ||
2219 | * the need to parse the headers */ | ||
2220 | if (skb_is_gso_v6(skb)) { | ||
2221 | cksum_offset = tcp_hdrlen(skb); | ||
2222 | /* Can only handle headers <= max_tso6 long */ | ||
2223 | if (unlikely(-cum_len > mgp->max_tso6)) | ||
2224 | return myri10ge_sw_tso(skb, dev); | ||
2225 | } | ||
2192 | /* for TSO, pseudo_hdr_offset holds mss. | 2226 | /* for TSO, pseudo_hdr_offset holds mss. |
2193 | * The firmware figures out where to put | 2227 | * The firmware figures out where to put |
2194 | * the checksum by parsing the header. */ | 2228 | * the checksum by parsing the header. */ |
@@ -2303,10 +2337,12 @@ again: | |||
2303 | req++; | 2337 | req++; |
2304 | count++; | 2338 | count++; |
2305 | rdma_count++; | 2339 | rdma_count++; |
2306 | if (unlikely(cksum_offset > seglen)) | 2340 | if (cksum_offset != 0 && !(mss && skb_is_gso_v6(skb))) { |
2307 | cksum_offset -= seglen; | 2341 | if (unlikely(cksum_offset > seglen)) |
2308 | else | 2342 | cksum_offset -= seglen; |
2309 | cksum_offset = 0; | 2343 | else |
2344 | cksum_offset = 0; | ||
2345 | } | ||
2310 | } | 2346 | } |
2311 | if (frag_idx == frag_cnt) | 2347 | if (frag_idx == frag_cnt) |
2312 | break; | 2348 | break; |
@@ -2389,6 +2425,41 @@ drop: | |||
2389 | 2425 | ||
2390 | } | 2426 | } |
2391 | 2427 | ||
2428 | static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev) | ||
2429 | { | ||
2430 | struct sk_buff *segs, *curr; | ||
2431 | struct myri10ge_priv *mgp = dev->priv; | ||
2432 | int status; | ||
2433 | |||
2434 | segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO6); | ||
2435 | if (unlikely(IS_ERR(segs))) | ||
2436 | goto drop; | ||
2437 | |||
2438 | while (segs) { | ||
2439 | curr = segs; | ||
2440 | segs = segs->next; | ||
2441 | curr->next = NULL; | ||
2442 | status = myri10ge_xmit(curr, dev); | ||
2443 | if (status != 0) { | ||
2444 | dev_kfree_skb_any(curr); | ||
2445 | if (segs != NULL) { | ||
2446 | curr = segs; | ||
2447 | segs = segs->next; | ||
2448 | curr->next = NULL; | ||
2449 | dev_kfree_skb_any(segs); | ||
2450 | } | ||
2451 | goto drop; | ||
2452 | } | ||
2453 | } | ||
2454 | dev_kfree_skb_any(skb); | ||
2455 | return 0; | ||
2456 | |||
2457 | drop: | ||
2458 | dev_kfree_skb_any(skb); | ||
2459 | mgp->stats.tx_dropped += 1; | ||
2460 | return 0; | ||
2461 | } | ||
2462 | |||
2392 | static struct net_device_stats *myri10ge_get_stats(struct net_device *dev) | 2463 | static struct net_device_stats *myri10ge_get_stats(struct net_device *dev) |
2393 | { | 2464 | { |
2394 | struct myri10ge_priv *mgp = netdev_priv(dev); | 2465 | struct myri10ge_priv *mgp = netdev_priv(dev); |
@@ -3076,7 +3147,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
3076 | netdev->change_mtu = myri10ge_change_mtu; | 3147 | netdev->change_mtu = myri10ge_change_mtu; |
3077 | netdev->set_multicast_list = myri10ge_set_multicast_list; | 3148 | netdev->set_multicast_list = myri10ge_set_multicast_list; |
3078 | netdev->set_mac_address = myri10ge_set_mac_address; | 3149 | netdev->set_mac_address = myri10ge_set_mac_address; |
3079 | netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO; | 3150 | netdev->features = mgp->features; |
3080 | if (dac_enabled) | 3151 | if (dac_enabled) |
3081 | netdev->features |= NETIF_F_HIGHDMA; | 3152 | netdev->features |= NETIF_F_HIGHDMA; |
3082 | 3153 | ||