Skip to content

Commit

Permalink
qeth: add layer 2 RX/TX checksum offloading
Browse files Browse the repository at this point in the history
Checksum offloading for send and receive is already
supported for layer 3 (IP layer). This patch
adds support for RX and TX hardware checksum offloading
for layer 2 (MAC layer). The hardware calculates the checksum
for IP UDP and TCP packets.

This patch moves the hardware checksum offloading setup
to the set of common functions in qeth_core_main.c.
Layer 2 and layer 3 now simply call the same common functions.

Also note that TX checksum offloading is always enabled.
The device driver relies on the TCP/IP stack to make use of
this feature.

Signed-off-by: Thomas Richter <tmricht@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Reviewed-by: Eugene Crosser <Eugene.Crosser@ru.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Thomas Richter authored and David S. Miller committed Sep 21, 2015
1 parent 239ff40 commit 4d7def2
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 147 deletions.
9 changes: 9 additions & 0 deletions drivers/s390/net/qeth_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
void qeth_trace_features(struct qeth_card *);
void qeth_close_dev(struct qeth_card *);
int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs,
__u16, long);
int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
long,
int (*reply_cb)(struct qeth_card *,
struct qeth_reply *, unsigned long),
void *);
int qeth_start_ipa_tx_checksum(struct qeth_card *);
int qeth_set_rx_csum(struct qeth_card *, int);

/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
Expand Down
178 changes: 171 additions & 7 deletions drivers/s390/net/qeth_core_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4978,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card)
void qeth_trace_features(struct qeth_card *card)
{
QETH_CARD_TEXT(card, 2, "features");
QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs);
QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support);
QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4));
QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6));
QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp));
QETH_CARD_HEX(card, 2, &card->info.diagass_support,
sizeof(card->info.diagass_support));
}
EXPORT_SYMBOL_GPL(qeth_trace_features);

Expand Down Expand Up @@ -5083,6 +5081,7 @@ int qeth_core_hardsetup_card(struct qeth_card *card)
}

card->options.ipa4.supported_funcs = 0;
card->options.ipa6.supported_funcs = 0;
card->options.adp.supported_funcs = 0;
card->options.sbp.supported_funcs = 0;
card->info.diagass_support = 0;
Expand Down Expand Up @@ -5268,6 +5267,102 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
}
EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);

static int qeth_setassparms_cb(struct qeth_card *card,
struct qeth_reply *reply, unsigned long data)
{
struct qeth_ipa_cmd *cmd;

QETH_CARD_TEXT(card, 4, "defadpcb");

cmd = (struct qeth_ipa_cmd *) data;
if (cmd->hdr.return_code == 0) {
cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
if (cmd->hdr.prot_version == QETH_PROT_IPV4)
card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
if (cmd->hdr.prot_version == QETH_PROT_IPV6)
card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
}
if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM &&
cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
card->info.csum_mask = cmd->data.setassparms.data.flags_32bit;
QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask);
}
if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM &&
cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
card->info.tx_csum_mask =
cmd->data.setassparms.data.flags_32bit;
QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask);
}

return 0;
}

static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func,
__u16 cmd_code, __u16 len,
enum qeth_prot_versions prot)
{
struct qeth_cmd_buffer *iob;
struct qeth_ipa_cmd *cmd;

QETH_CARD_TEXT(card, 4, "getasscm");
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);

if (iob) {
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
cmd->data.setassparms.hdr.assist_no = ipa_func;
cmd->data.setassparms.hdr.length = 8 + len;
cmd->data.setassparms.hdr.command_code = cmd_code;
cmd->data.setassparms.hdr.return_code = 0;
cmd->data.setassparms.hdr.seq_no = 0;
}

return iob;
}

int qeth_send_setassparms(struct qeth_card *card,
struct qeth_cmd_buffer *iob, __u16 len, long data,
int (*reply_cb)(struct qeth_card *,
struct qeth_reply *, unsigned long),
void *reply_param)
{
int rc;
struct qeth_ipa_cmd *cmd;

QETH_CARD_TEXT(card, 4, "sendassp");

cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
if (len <= sizeof(__u32))
cmd->data.setassparms.data.flags_32bit = (__u32) data;
else /* (len > sizeof(__u32)) */
memcpy(&cmd->data.setassparms.data, (void *) data, len);

rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
return rc;
}
EXPORT_SYMBOL_GPL(qeth_send_setassparms);

int qeth_send_simple_setassparms(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func,
__u16 cmd_code, long data)
{
int rc;
int length = 0;
struct qeth_cmd_buffer *iob;

QETH_CARD_TEXT(card, 4, "simassp4");
if (data)
length = sizeof(__u32);
iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
length, QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
rc = qeth_send_setassparms(card, iob, length, data,
qeth_setassparms_cb, NULL);
return rc;
}
EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms);

static void qeth_unregister_dbf_views(void)
{
int x;
Expand Down Expand Up @@ -5954,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);

static int qeth_send_checksum_command(struct qeth_card *card)
{
int rc;

rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
"failed, using SW checksumming\n",
QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
IPA_CMD_ASS_ENABLE,
card->info.csum_mask);
if (rc) {
dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
"failed, using SW checksumming\n",
QETH_CARD_IFNAME(card));
return rc;
}
return 0;
}

int qeth_set_rx_csum(struct qeth_card *card, int on)
{
int rc;

if (on) {
rc = qeth_send_checksum_command(card);
if (rc)
return -EIO;
dev_info(&card->gdev->dev,
"HW Checksumming (inbound) enabled\n");
} else {
rc = qeth_send_simple_setassparms(card,
IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
if (rc)
return -EIO;
}
return 0;
}
EXPORT_SYMBOL_GPL(qeth_set_rx_csum);

int qeth_start_ipa_tx_checksum(struct qeth_card *card)
{
int rc = 0;

if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
return rc;
rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
IPA_CMD_ASS_START, 0);
if (rc)
goto err_out;
rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
IPA_CMD_ASS_ENABLE,
card->info.tx_csum_mask);
if (rc)
goto err_out;

dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
return rc;
err_out:
dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
"failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
return rc;
}
EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum);

static int __init qeth_core_init(void)
{
int rc;
Expand Down
87 changes: 80 additions & 7 deletions drivers/s390/net/qeth_l2_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
return RTN_UNSPEC;
}

static inline void qeth_l2_hdr_csum(struct qeth_card *card,
struct qeth_hdr *hdr, struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);

/* tcph->check contains already the pseudo hdr checksum
* so just set the header flags
*/
if (iph->protocol == IPPROTO_UDP)
hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP;
hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
QETH_HDR_EXT_CSUM_HDR_REQ;
iph->check = 0;
if (card->options.performance_stats)
card->perf_stats.tx_csum++;
}

static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int cast_type)
{
Expand Down Expand Up @@ -390,6 +407,38 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
return rc;
}

static netdev_features_t qeth_l2_fix_features(struct net_device *dev,
netdev_features_t features)
{
struct qeth_card *card = dev->ml_priv;

QETH_DBF_TEXT(SETUP, 2, "fixfeat");
if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
features &= ~NETIF_F_IP_CSUM;
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
features &= ~NETIF_F_RXCSUM;
QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
return features;
}

static int qeth_l2_set_features(struct net_device *dev,
netdev_features_t features)
{
struct qeth_card *card = dev->ml_priv;
netdev_features_t changed = dev->features ^ features;

QETH_DBF_TEXT(SETUP, 2, "setfeat");
QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));

if (card->state == CARD_STATE_DOWN ||
card->state == CARD_STATE_RECOVER)
return 0;

if (!(changed & NETIF_F_RXCSUM))
return 0;
return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
}

static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
{
QETH_DBF_TEXT(SETUP , 2, "stopcard");
Expand Down Expand Up @@ -450,7 +499,15 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
case QETH_HEADER_TYPE_LAYER2:
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
skb->ip_summed = CHECKSUM_NONE;
if ((card->dev->features & NETIF_F_RXCSUM)
&& ((hdr->hdr.l2.flags[1] &
(QETH_HDR_EXT_CSUM_HDR_REQ |
QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
(QETH_HDR_EXT_CSUM_HDR_REQ |
QETH_HDR_EXT_CSUM_TRANSP_REQ)))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
if (skb->protocol == htons(ETH_P_802_2))
*((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
len = skb->len;
Expand Down Expand Up @@ -803,6 +860,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
sizeof(struct qeth_hdr));
skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
qeth_l2_fill_header(card, hdr, new_skb, cast_type);
if (new_skb->ip_summed == CHECKSUM_PARTIAL)
qeth_l2_hdr_csum(card, hdr, new_skb);
}
}

Expand Down Expand Up @@ -968,6 +1027,8 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid,
.ndo_tx_timeout = qeth_tx_timeout,
.ndo_fix_features = qeth_l2_fix_features,
.ndo_set_features = qeth_l2_set_features
};

static int qeth_l2_setup_netdev(struct qeth_card *card)
Expand Down Expand Up @@ -997,13 +1058,29 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
(card->info.type != QETH_CARD_TYPE_OSN) ?
&qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
/* Turn on RX offloading per default */
card->dev->features |= NETIF_F_RXCSUM;
}
card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card);
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT);
return register_netdev(card->dev);
}

static int qeth_l2_start_ipassists(struct qeth_card *card)
{
/* configure isolation level */
if (qeth_set_access_ctrl_online(card, 0))
return -ENODEV;
if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
qeth_set_rx_csum(card, 1);
qeth_start_ipa_tx_checksum(card);
return 0;
}

static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
Expand Down Expand Up @@ -1069,12 +1146,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
contin:
if ((card->info.type == QETH_CARD_TYPE_OSD) ||
(card->info.type == QETH_CARD_TYPE_OSX)) {
/* configure isolation level */
rc = qeth_set_access_ctrl_online(card, 0);
if (rc) {
rc = -ENODEV;
if (qeth_l2_start_ipassists(card))
goto out_remove;
}
}

if (card->info.type != QETH_CARD_TYPE_OSN &&
Expand Down Expand Up @@ -1453,7 +1526,7 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card,
}
if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
snprintf(str[i], sizeof(str[i]), "MAC=%pM",
addr_lnid->mac);
addr_lnid->mac);
env[i] = str[i]; i++;
}
snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
Expand Down
Loading

0 comments on commit 4d7def2

Please sign in to comment.