diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-10-20 00:10:27 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-10-20 00:10:27 -0300 |
commit | d0b2f91bede3bd5e3d24dd6803e56eee959c1797 (patch) | |
tree | 7fee4ab0509879c373c4f2cbd5b8a5be5b4041ee /drivers/s390/net/qeth_core_main.c | |
parent | e914f8eb445e8f74b00303c19c2ffceaedd16a05 (diff) |
Linux-libre 4.8.2-gnupck-4.8.2-gnu
Diffstat (limited to 'drivers/s390/net/qeth_core_main.c')
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 263 |
1 files changed, 185 insertions, 78 deletions
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index b7b74776e..20cf29613 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1464,8 +1464,6 @@ static int qeth_setup_card(struct qeth_card *card) card->thread_allowed_mask = 0; card->thread_running_mask = 0; INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread); - INIT_LIST_HEAD(&card->ip_list); - INIT_LIST_HEAD(card->ip_tbd_list); INIT_LIST_HEAD(&card->cmd_waiter_list); init_waitqueue_head(&card->wait_q); /* initial options */ @@ -1500,11 +1498,6 @@ static struct qeth_card *qeth_alloc_card(void) if (!card) goto out; QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); - card->ip_tbd_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); - if (!card->ip_tbd_list) { - QETH_DBF_TEXT(SETUP, 0, "iptbdnom"); - goto out_card; - } if (qeth_setup_channel(&card->read)) goto out_ip; if (qeth_setup_channel(&card->write)) @@ -1517,8 +1510,6 @@ static struct qeth_card *qeth_alloc_card(void) out_channel: qeth_clean_channel(&card->read); out_ip: - kfree(card->ip_tbd_list); -out_card: kfree(card); out: return NULL; @@ -3628,7 +3619,8 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, int e; e = 0; - while (buffer->element[e].addr) { + while ((e < QDIO_MAX_ELEMENTS_PER_BUFFER) && + buffer->element[e].addr) { unsigned long phys_aob_addr; phys_aob_addr = (unsigned long) buffer->element[e].addr; @@ -3757,6 +3749,14 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev, } EXPORT_SYMBOL_GPL(qeth_qdio_output_handler); +/* We cannot use outbound queue 3 for unicast packets on HiperSockets */ +static inline int qeth_cut_iqd_prio(struct qeth_card *card, int queue_num) +{ + if ((card->info.type == QETH_CARD_TYPE_IQD) && (queue_num == 3)) + return 2; + return queue_num; +} + /** * Note: Function assumes that we have 4 outbound queues. */ @@ -3784,9 +3784,9 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, return card->qdio.default_out_queue; } if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC) - return ~tos >> 6 & 3; + return qeth_cut_iqd_prio(card, ~tos >> 6 & 3); if (tos & IPTOS_MINCOST) - return 3; + return qeth_cut_iqd_prio(card, 3); if (tos & IPTOS_RELIABILITY) return 2; if (tos & IPTOS_THROUGHPUT) @@ -3797,11 +3797,12 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, case QETH_PRIO_Q_ING_SKB: if (skb->priority > 5) return 0; - return ~skb->priority >> 1 & 3; + return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3); case QETH_PRIO_Q_ING_VLAN: tci = &((struct ethhdr *)skb->data)->h_proto; if (*tci == ETH_P_8021Q) - return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3; + return qeth_cut_iqd_prio(card, ~*(tci + 1) >> + (VLAN_PRIO_SHIFT + 1) & 3); break; default: break; @@ -3810,41 +3811,54 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(qeth_get_priority_queue); +/** + * qeth_get_elements_for_frags() - find number of SBALEs for skb frags. + * @skb: SKB address + * + * Returns the number of pages, and thus QDIO buffer elements, needed to cover + * fragmented part of the SKB. Returns zero for linear SKB. + */ int qeth_get_elements_for_frags(struct sk_buff *skb) { - int cnt, length, e, elements = 0; - struct skb_frag_struct *frag; - char *data; + int cnt, elements = 0; for (cnt = 0; cnt < skb_shinfo(skb)->nr_frags; cnt++) { - frag = &skb_shinfo(skb)->frags[cnt]; - data = (char *)page_to_phys(skb_frag_page(frag)) + - frag->page_offset; - length = frag->size; - e = PFN_UP((unsigned long)data + length - 1) - - PFN_DOWN((unsigned long)data); - elements += e; + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[cnt]; + + elements += qeth_get_elements_for_range( + (addr_t)skb_frag_address(frag), + (addr_t)skb_frag_address(frag) + skb_frag_size(frag)); } return elements; } EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); +/** + * qeth_get_elements_no() - find number of SBALEs for skb data, inc. frags. + * @card: qeth card structure, to check max. elems. + * @skb: SKB address + * @extra_elems: extra elems needed, to check against max. + * + * Returns the number of pages, and thus QDIO buffer elements, needed to cover + * skb data, including linear part and fragments. Checks if the result plus + * extra_elems fits under the limit for the card. Returns 0 if it does not. + * Note: extra_elems is not included in the returned result. + */ int qeth_get_elements_no(struct qeth_card *card, - struct sk_buff *skb, int elems) + struct sk_buff *skb, int extra_elems) { - int dlen = skb->len - skb->data_len; - int elements_needed = PFN_UP((unsigned long)skb->data + dlen - 1) - - PFN_DOWN((unsigned long)skb->data); - - elements_needed += qeth_get_elements_for_frags(skb); + int elements = qeth_get_elements_for_range( + (addr_t)skb->data, + (addr_t)skb->data + skb_headlen(skb)) + + qeth_get_elements_for_frags(skb); - if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { + if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { QETH_DBF_MESSAGE(2, "Invalid size of IP packet " "(Number=%d / Length=%d). Discarded.\n", - (elements_needed+elems), skb->len); + elements + extra_elems, skb->len); return 0; } - return elements_needed; + return elements; } EXPORT_SYMBOL_GPL(qeth_get_elements_no); @@ -3859,7 +3873,7 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) rest = len - inpage; if (rest > hroom) return 1; - memmove(skb->data - rest, skb->data, skb->len - skb->data_len); + memmove(skb->data - rest, skb->data, skb_headlen(skb)); skb->data -= rest; skb->tail -= rest; *hdr = (struct qeth_hdr *)skb->data; @@ -3873,7 +3887,7 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill, int offset) { - int length = skb->len - skb->data_len; + int length = skb_headlen(skb); int length_here; int element; char *data; @@ -4967,7 +4981,6 @@ static void qeth_core_free_card(struct qeth_card *card) qeth_clean_channel(&card->write); if (card->dev) free_netdev(card->dev); - kfree(card->ip_tbd_list); qeth_free_qdio_buffers(card); unregister_service_level(&card->qeth_service_level); kfree(card); @@ -5265,8 +5278,8 @@ no_mem: } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); -static int qeth_setassparms_cb(struct qeth_card *card, - struct qeth_reply *reply, unsigned long data) +int qeth_setassparms_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) { struct qeth_ipa_cmd *cmd; @@ -5294,6 +5307,7 @@ static int qeth_setassparms_cb(struct qeth_card *card, return 0; } +EXPORT_SYMBOL_GPL(qeth_setassparms_cb); struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, @@ -5788,6 +5802,7 @@ static struct { {"tx do_QDIO count"}, {"tx csum"}, {"tx lin"}, + {"tx linfail"}, {"cq handler count"}, {"cq handler time"} }; @@ -5848,8 +5863,9 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[32] = card->perf_stats.outbound_do_qdio_cnt; data[33] = card->perf_stats.tx_csum; data[34] = card->perf_stats.tx_lin; - data[35] = card->perf_stats.cq_cnt; - data[36] = card->perf_stats.cq_time; + data[35] = card->perf_stats.tx_linfail; + data[36] = card->perf_stats.cq_cnt; + data[37] = card->perf_stats.cq_time; } EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats); @@ -6048,74 +6064,165 @@ 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) +static int qeth_send_checksum_on(struct qeth_card *card, int cstype) { + long rxtx_arg; int rc; - rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, cstype, 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)); + 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); + rxtx_arg = (cstype == IPA_OUTBOUND_CHECKSUM) ? card->info.tx_csum_mask + : card->info.csum_mask; + rc = qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_ENABLE, + rxtx_arg); if (rc) { - dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); + dev_warn(&card->gdev->dev, + "Enabling HW checksumming for %s failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); return rc; } + + dev_info(&card->gdev->dev, "HW Checksumming (%sbound) enabled\n", + cstype == IPA_INBOUND_CHECKSUM ? "in" : "out"); return 0; } -int qeth_set_rx_csum(struct qeth_card *card, int on) +static int qeth_set_ipa_csum(struct qeth_card *card, int on, int cstype) { int rc; if (on) { - rc = qeth_send_checksum_command(card); + rc = qeth_send_checksum_on(card, cstype); 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); + rc = qeth_send_simple_setassparms(card, cstype, + 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) +static int qeth_set_ipa_tso(struct qeth_card *card, int on) { - int rc = 0; + int rc; - 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; + QETH_CARD_TEXT(card, 3, "sttso"); - 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)); + if (on) { + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, + IPA_CMD_ASS_START, 0); + if (rc) { + dev_warn(&card->gdev->dev, + "Starting outbound TCP segmentation offload for %s failed\n", + QETH_CARD_IFNAME(card)); + return -EIO; + } + dev_info(&card->gdev->dev, "Outbound TSO enabled\n"); + } else { + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, + IPA_CMD_ASS_STOP, 0); + } return rc; } -EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum); + +/* try to restore device features on a device after recovery */ +int qeth_recover_features(struct net_device *dev) +{ + struct qeth_card *card = dev->ml_priv; + netdev_features_t recover = dev->features; + + if (recover & NETIF_F_IP_CSUM) { + if (qeth_set_ipa_csum(card, 1, IPA_OUTBOUND_CHECKSUM)) + recover ^= NETIF_F_IP_CSUM; + } + if (recover & NETIF_F_RXCSUM) { + if (qeth_set_ipa_csum(card, 1, IPA_INBOUND_CHECKSUM)) + recover ^= NETIF_F_RXCSUM; + } + if (recover & NETIF_F_TSO) { + if (qeth_set_ipa_tso(card, 1)) + recover ^= NETIF_F_TSO; + } + + if (recover == dev->features) + return 0; + + dev_warn(&card->gdev->dev, + "Device recovery failed to restore all offload features\n"); + dev->features = recover; + return -EIO; +} +EXPORT_SYMBOL_GPL(qeth_recover_features); + +int qeth_set_features(struct net_device *dev, netdev_features_t features) +{ + struct qeth_card *card = dev->ml_priv; + netdev_features_t changed = dev->features ^ features; + int rc = 0; + + QETH_DBF_TEXT(SETUP, 2, "setfeat"); + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + + if ((changed & NETIF_F_IP_CSUM)) { + rc = qeth_set_ipa_csum(card, + features & NETIF_F_IP_CSUM ? 1 : 0, + IPA_OUTBOUND_CHECKSUM); + if (rc) + changed ^= NETIF_F_IP_CSUM; + } + if ((changed & NETIF_F_RXCSUM)) { + rc = qeth_set_ipa_csum(card, + features & NETIF_F_RXCSUM ? 1 : 0, + IPA_INBOUND_CHECKSUM); + if (rc) + changed ^= NETIF_F_RXCSUM; + } + if ((changed & NETIF_F_TSO)) { + rc = qeth_set_ipa_tso(card, features & NETIF_F_TSO ? 1 : 0); + if (rc) + changed ^= NETIF_F_TSO; + } + + /* everything changed successfully? */ + if ((dev->features ^ features) == changed) + return 0; + /* something went wrong. save changed features and return error */ + dev->features ^= changed; + return -EIO; +} +EXPORT_SYMBOL_GPL(qeth_set_features); + +netdev_features_t qeth_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; + if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) { + features &= ~NETIF_F_TSO; + dev_info(&card->gdev->dev, "Outbound TSO not supported on %s\n", + QETH_CARD_IFNAME(card)); + } + /* if the card isn't up, remove features that require hw changes */ + if (card->state == CARD_STATE_DOWN || + card->state == CARD_STATE_RECOVER) + features = features & ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM | + NETIF_F_TSO); + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + return features; +} +EXPORT_SYMBOL_GPL(qeth_fix_features); static int __init qeth_core_init(void) { |