Commit 571f9dd8 authored by Kittipon Meesompop's avatar Kittipon Meesompop Committed by David S. Miller

s390/qeth: add IPv6 TX checksum offload support

Check if a qeth device supports IPv6 TX checksum offload, and advertise
NETIF_F_IPV6_CSUM accordingly. Add support for setting the relevant bits
in IPv6 packet descriptors.

Currently this has only limited use (ie. UDP, or Jumbo Frames). For any
TCP traffic with a standard MSS, the TCP checksum gets calculated
as part of the linear GSO segmentation.
Signed-off-by: default avatarKittipon Meesompop <kmeesomp@linux.vnet.ibm.com>
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a8155b00
...@@ -878,14 +878,17 @@ static inline void qeth_rx_csum(struct qeth_card *card, struct sk_buff *skb, ...@@ -878,14 +878,17 @@ static inline void qeth_rx_csum(struct qeth_card *card, struct sk_buff *skb,
} }
} }
static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags) static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv)
{ {
*flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ;
if (ip_hdr(skb)->protocol == IPPROTO_UDP) if ((ipv == 4 && ip_hdr(skb)->protocol == IPPROTO_UDP) ||
(ipv == 6 && ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))
*flags |= QETH_HDR_EXT_UDP; *flags |= QETH_HDR_EXT_UDP;
/* some HW requires combined L3+L4 csum offload: */ if (ipv == 4) {
*flags |= QETH_HDR_EXT_CSUM_HDR_REQ; /* some HW requires combined L3+L4 csum offload: */
ip_hdr(skb)->check = 0; *flags |= QETH_HDR_EXT_CSUM_HDR_REQ;
ip_hdr(skb)->check = 0;
}
} }
static inline void qeth_put_buffer_pool_entry(struct qeth_card *card, static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,
......
...@@ -6349,12 +6349,12 @@ static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, ...@@ -6349,12 +6349,12 @@ static int qeth_ipa_checksum_run_cmd(struct qeth_card *card,
static int qeth_send_checksum_on(struct qeth_card *card, int cstype, static int qeth_send_checksum_on(struct qeth_card *card, int cstype,
enum qeth_prot_versions prot) enum qeth_prot_versions prot)
{ {
const __u32 required_features = QETH_IPA_CHECKSUM_IP_HDR | u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP;
QETH_IPA_CHECKSUM_UDP |
QETH_IPA_CHECKSUM_TCP;
struct qeth_checksum_cmd chksum_cb; struct qeth_checksum_cmd chksum_cb;
int rc; int rc;
if (prot == QETH_PROT_IPV4)
required_features |= QETH_IPA_CHECKSUM_IP_HDR;
rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0,
&chksum_cb, prot); &chksum_cb, prot);
if (!rc) { if (!rc) {
...@@ -6430,8 +6430,8 @@ static int qeth_set_ipa_tso(struct qeth_card *card, int on) ...@@ -6430,8 +6430,8 @@ static int qeth_set_ipa_tso(struct qeth_card *card, int on)
return rc; return rc;
} }
#define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO) #define QETH_HW_FEATURES (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO | \
NETIF_F_IPV6_CSUM)
/** /**
* qeth_recover_features() - Restore device features after recovery * qeth_recover_features() - Restore device features after recovery
* @dev: the recovering net_device * @dev: the recovering net_device
...@@ -6471,6 +6471,12 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features) ...@@ -6471,6 +6471,12 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features)
if (rc) if (rc)
changed ^= NETIF_F_IP_CSUM; changed ^= NETIF_F_IP_CSUM;
} }
if (changed & NETIF_F_IPV6_CSUM) {
rc = qeth_set_ipa_csum(card, features & NETIF_F_IPV6_CSUM,
IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6);
if (rc)
changed ^= NETIF_F_IPV6_CSUM;
}
if ((changed & NETIF_F_RXCSUM)) { if ((changed & NETIF_F_RXCSUM)) {
rc = qeth_set_ipa_csum(card, features & NETIF_F_RXCSUM, rc = qeth_set_ipa_csum(card, features & NETIF_F_RXCSUM,
IPA_INBOUND_CHECKSUM, QETH_PROT_IPV4); IPA_INBOUND_CHECKSUM, QETH_PROT_IPV4);
...@@ -6500,6 +6506,8 @@ netdev_features_t qeth_fix_features(struct net_device *dev, ...@@ -6500,6 +6506,8 @@ netdev_features_t qeth_fix_features(struct net_device *dev,
QETH_DBF_TEXT(SETUP, 2, "fixfeat"); QETH_DBF_TEXT(SETUP, 2, "fixfeat");
if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
features &= ~NETIF_F_IP_CSUM; features &= ~NETIF_F_IP_CSUM;
if (!qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6))
features &= ~NETIF_F_IPV6_CSUM;
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
features &= ~NETIF_F_RXCSUM; features &= ~NETIF_F_RXCSUM;
if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) if (!qeth_is_supported(card, IPA_OUTBOUND_TSO))
......
...@@ -246,6 +246,7 @@ enum qeth_ipa_funcs { ...@@ -246,6 +246,7 @@ enum qeth_ipa_funcs {
IPA_QUERY_ARP_ASSIST = 0x00040000L, IPA_QUERY_ARP_ASSIST = 0x00040000L,
IPA_INBOUND_TSO = 0x00080000L, IPA_INBOUND_TSO = 0x00080000L,
IPA_OUTBOUND_TSO = 0x00100000L, IPA_OUTBOUND_TSO = 0x00100000L,
IPA_OUTBOUND_CHECKSUM_V6 = 0x00800000L,
}; };
/* SETIP/DELIP IPA Command: ***************************************************/ /* SETIP/DELIP IPA Command: ***************************************************/
......
...@@ -660,7 +660,8 @@ static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb, ...@@ -660,7 +660,8 @@ static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb,
} }
static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
struct qeth_qdio_out_q *queue, int cast_type) struct qeth_qdio_out_q *queue, int cast_type,
int ipv)
{ {
int push_len = sizeof(struct qeth_hdr); int push_len = sizeof(struct qeth_hdr);
unsigned int elements, nr_frags; unsigned int elements, nr_frags;
...@@ -699,7 +700,7 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, ...@@ -699,7 +700,7 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb,
} }
qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len); qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len);
if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb->ip_summed == CHECKSUM_PARTIAL) {
qeth_tx_csum(skb, &hdr->hdr.l2.flags[1]); qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv);
if (card->options.performance_stats) if (card->options.performance_stats)
card->perf_stats.tx_csum++; card->perf_stats.tx_csum++;
} }
...@@ -754,6 +755,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, ...@@ -754,6 +755,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
{ {
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
int cast_type = qeth_l2_get_cast_type(card, skb); int cast_type = qeth_l2_get_cast_type(card, skb);
int ipv = qeth_get_ip_version(skb);
struct qeth_qdio_out_q *queue; struct qeth_qdio_out_q *queue;
int tx_bytes = skb->len; int tx_bytes = skb->len;
int rc; int rc;
...@@ -761,7 +763,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, ...@@ -761,7 +763,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
if (card->qdio.do_prio_queueing || (cast_type && if (card->qdio.do_prio_queueing || (cast_type &&
card->info.is_multicast_different)) card->info.is_multicast_different))
queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb, queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
qeth_get_ip_version(skb), cast_type)]; ipv, cast_type)];
else else
queue = card->qdio.out_qs[card->qdio.default_out_queue]; queue = card->qdio.out_qs[card->qdio.default_out_queue];
...@@ -784,7 +786,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, ...@@ -784,7 +786,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type); rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type);
break; break;
default: default:
rc = qeth_l2_xmit_osa(card, skb, queue, cast_type); rc = qeth_l2_xmit_osa(card, skb, queue, cast_type, ipv);
} }
if (!rc) { if (!rc) {
...@@ -995,6 +997,10 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) ...@@ -995,6 +997,10 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
card->dev->vlan_features |= NETIF_F_RXCSUM; card->dev->vlan_features |= NETIF_F_RXCSUM;
} }
} }
if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
card->dev->hw_features |= NETIF_F_IPV6_CSUM;
card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
}
card->info.broadcast_capable = 1; card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card); qeth_l2_request_initial_mac(card);
......
...@@ -2281,7 +2281,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, ...@@ -2281,7 +2281,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
} }
if (new_skb->ip_summed == CHECKSUM_PARTIAL) { if (new_skb->ip_summed == CHECKSUM_PARTIAL) {
qeth_tx_csum(new_skb, &hdr->hdr.l3.ext_flags); qeth_tx_csum(new_skb, &hdr->hdr.l3.ext_flags, ipv);
if (card->options.performance_stats) if (card->options.performance_stats)
card->perf_stats.tx_csum++; card->perf_stats.tx_csum++;
} }
...@@ -2507,6 +2507,11 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) ...@@ -2507,6 +2507,11 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
card->dev->vlan_features |= NETIF_F_TSO | card->dev->vlan_features |= NETIF_F_TSO |
NETIF_F_RXCSUM | NETIF_F_IP_CSUM; NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
} }
if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
card->dev->hw_features |= NETIF_F_IPV6_CSUM;
card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
}
} else if (card->info.type == QETH_CARD_TYPE_IQD) { } else if (card->info.type == QETH_CARD_TYPE_IQD) {
card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN,
ether_setup); ether_setup);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment