From 7286384bcf982242fd439d27a071e8cba7b2cfd6 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Fri, 18 Aug 2017 10:19:04 +0200 Subject: [PATCH] s390/qeth: split L2 xmit paths l2_hard_start_xmit() actually doesn't contain much shared code, and having device-specific paths makes isolated changes a lot easier. So split it into three routines for IQD, OSN and OSD/OSM/OSX. No functional change. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 225 +++++++++++++++++--------------- 1 file changed, 123 insertions(+), 102 deletions(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 438a7f29e99f..310bfa225e20 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -676,21 +676,119 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) qeth_promisc_to_bridge(card); } +static int qeth_l2_xmit_iqd(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int cast_type) +{ + unsigned int data_offset = ETH_HLEN; + struct qeth_hdr *hdr; + int rc; + + hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); + if (!hdr) + return -ENOMEM; + qeth_l2_fill_header(card, hdr, skb, cast_type); + hdr->hdr.l2.pkt_length = skb->len; + skb_copy_from_linear_data(skb, ((char *)hdr) + sizeof(*hdr), + data_offset); + + if (!qeth_get_elements_no(card, skb, 1, data_offset)) { + rc = -E2BIG; + goto out; + } + rc = qeth_do_send_packet_fast(card, queue, skb, hdr, data_offset, + data_offset); +out: + if (rc) + kmem_cache_free(qeth_core_header_cache, hdr); + return rc; +} + +static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int cast_type) +{ + unsigned int elements, nr_frags; + struct sk_buff *skb_copy; + struct qeth_hdr *hdr; + int rc; + + /* fix hardware limitation: as long as we do not have sbal + * chaining we can not send long frag lists + */ + if (!qeth_get_elements_no(card, skb, 0, 0)) { + rc = skb_linearize(skb); + + if (card->options.performance_stats) { + if (rc) + card->perf_stats.tx_linfail++; + else + card->perf_stats.tx_lin++; + } + if (rc) + return rc; + } + nr_frags = skb_shinfo(skb)->nr_frags; + + /* create a copy with writeable headroom */ + skb_copy = skb_realloc_headroom(skb, sizeof(struct qeth_hdr)); + if (!skb_copy) + return -ENOMEM; + hdr = skb_push(skb_copy, sizeof(struct qeth_hdr)); + qeth_l2_fill_header(card, hdr, skb_copy, cast_type); + if (skb_copy->ip_summed == CHECKSUM_PARTIAL) + qeth_l2_hdr_csum(card, hdr, skb_copy); + + elements = qeth_get_elements_no(card, skb_copy, 0, 0); + if (!elements) { + rc = -E2BIG; + goto out; + } + if (qeth_hdr_chk_and_bounce(skb_copy, &hdr, sizeof(*hdr))) { + rc = -EINVAL; + goto out; + } + rc = qeth_do_send_packet(card, queue, skb_copy, hdr, elements); +out: + if (!rc) { + /* tx success, free dangling original */ + dev_kfree_skb_any(skb); + if (card->options.performance_stats && nr_frags) { + card->perf_stats.sg_skbs_sent++; + /* nr_frags + skb->data */ + card->perf_stats.sg_frags_sent += nr_frags + 1; + } + } else { + /* tx fail, free copy */ + dev_kfree_skb_any(skb_copy); + } + return rc; +} + +static int qeth_l2_xmit_osn(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue) +{ + unsigned int elements; + struct qeth_hdr *hdr; + + if (skb->protocol == htons(ETH_P_IPV6)) + return -EPROTONOSUPPORT; + + hdr = (struct qeth_hdr *)skb->data; + elements = qeth_get_elements_no(card, skb, 0, 0); + if (!elements) + return -E2BIG; + if (qeth_hdr_chk_and_bounce(skb, &hdr, sizeof(*hdr))) + return -EINVAL; + return qeth_do_send_packet(card, queue, skb, hdr, elements); +} + static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { - int rc; - struct qeth_hdr *hdr = NULL; - int elements = 0; struct qeth_card *card = dev->ml_priv; - struct sk_buff *new_skb = skb; int cast_type = qeth_l2_get_cast_type(card, skb); struct qeth_qdio_out_q *queue; int tx_bytes = skb->len; - int data_offset = -1; - int elements_needed = 0; - int hd_len = 0; - unsigned int nr_frags; + int rc; if (card->qdio.do_prio_queueing || (cast_type && card->info.is_multicast_different)) @@ -704,115 +802,38 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, goto tx_drop; } - if ((card->info.type == QETH_CARD_TYPE_OSN) && - (skb->protocol == htons(ETH_P_IPV6))) - goto tx_drop; - if (card->options.performance_stats) { card->perf_stats.outbound_cnt++; card->perf_stats.outbound_start_time = qeth_get_micros(); } netif_stop_queue(dev); - /* fix hardware limitation: as long as we do not have sbal - * chaining we can not send long frag lists - */ - if ((card->info.type != QETH_CARD_TYPE_IQD) && - !qeth_get_elements_no(card, new_skb, 0, 0)) { - int lin_rc = skb_linearize(new_skb); - - if (card->options.performance_stats) { - if (lin_rc) - card->perf_stats.tx_linfail++; - else - card->perf_stats.tx_lin++; - } - if (lin_rc) - goto tx_drop; - } - nr_frags = skb_shinfo(new_skb)->nr_frags; - - if (card->info.type == QETH_CARD_TYPE_OSN) - hdr = (struct qeth_hdr *)skb->data; - else { - if (card->info.type == QETH_CARD_TYPE_IQD) { - new_skb = skb; - data_offset = ETH_HLEN; - hd_len = ETH_HLEN; - hdr = kmem_cache_alloc(qeth_core_header_cache, - GFP_ATOMIC); - if (!hdr) - goto tx_drop; - elements_needed++; - qeth_l2_fill_header(card, hdr, new_skb, cast_type); - hdr->hdr.l2.pkt_length = new_skb->len; - skb_copy_from_linear_data(new_skb, - ((char *)hdr) + sizeof(*hdr), - ETH_HLEN); - } else { - /* create a clone with writeable headroom */ - new_skb = skb_realloc_headroom(skb, - sizeof(struct qeth_hdr)); - if (!new_skb) - goto tx_drop; - hdr = skb_push(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); - } + switch (card->info.type) { + case QETH_CARD_TYPE_OSN: + rc = qeth_l2_xmit_osn(card, skb, queue); + break; + case QETH_CARD_TYPE_IQD: + rc = qeth_l2_xmit_iqd(card, skb, queue, cast_type); + break; + default: + rc = qeth_l2_xmit_osa(card, skb, queue, cast_type); } - elements = qeth_get_elements_no(card, new_skb, elements_needed, - (data_offset > 0) ? data_offset : 0); - if (!elements) { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); - goto tx_drop; - } - - if (card->info.type != QETH_CARD_TYPE_IQD) { - if (qeth_hdr_chk_and_bounce(new_skb, &hdr, - sizeof(struct qeth_hdr_layer2))) - goto tx_drop; - rc = qeth_do_send_packet(card, queue, new_skb, hdr, - elements); - } else - rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - data_offset, hd_len); if (!rc) { card->stats.tx_packets++; card->stats.tx_bytes += tx_bytes; - if (card->options.performance_stats && nr_frags) { - card->perf_stats.sg_skbs_sent++; - /* nr_frags + skb->data */ - card->perf_stats.sg_frags_sent += nr_frags + 1; - } - if (new_skb != skb) - dev_kfree_skb_any(skb); - rc = NETDEV_TX_OK; - } else { - if (data_offset >= 0) - kmem_cache_free(qeth_core_header_cache, hdr); - - if (rc == -EBUSY) { - if (new_skb != skb) - dev_kfree_skb_any(new_skb); - return NETDEV_TX_BUSY; - } else - goto tx_drop; - } - - netif_wake_queue(dev); - if (card->options.performance_stats) - card->perf_stats.outbound_time += qeth_get_micros() - - card->perf_stats.outbound_start_time; - return rc; + if (card->options.performance_stats) + card->perf_stats.outbound_time += qeth_get_micros() - + card->perf_stats.outbound_start_time; + netif_wake_queue(dev); + return NETDEV_TX_OK; + } else if (rc == -EBUSY) { + return NETDEV_TX_BUSY; + } /* else fall through */ tx_drop: card->stats.tx_dropped++; card->stats.tx_errors++; - if ((new_skb != skb) && new_skb) - dev_kfree_skb_any(new_skb); dev_kfree_skb_any(skb); netif_wake_queue(dev); return NETDEV_TX_OK;