From a702349a4099cd5a7bab0904689d8e0bf8dcd622 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:48 +0200 Subject: [PATCH 01/11] s390/qeth: fix race in used-buffer accounting By updating q->used_buffers only _after_ do_QDIO() has completed, there is a potential race against the buffer's TX completion. In the unlikely case that the TX completion path wins, qeth_qdio_output_handler() would decrement the counter before qeth_flush_buffers() even incremented it. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d80972b9bfc7..f7ddd6455638 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3506,13 +3506,14 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, qdio_flags = QDIO_FLAG_SYNC_OUTPUT; if (atomic_read(&queue->set_pci_flags_count)) qdio_flags |= QDIO_FLAG_PCI_OUT; + atomic_add(count, &queue->used_buffers); + rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags, queue->queue_no, index, count); if (queue->card->options.performance_stats) queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() - queue->card->perf_stats.outbound_do_qdio_start_time; - atomic_add(count, &queue->used_buffers); if (rc) { queue->card->stats.tx_errors += count; /* ignore temporary SIGA errors without busy condition */ From 70551dc46ffa3555a0b5f3545b0cd87ab67fd002 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:49 +0200 Subject: [PATCH 02/11] s390/qeth: reset layer2 attribute on layer switch After the subdriver's remove() routine has completed, the card's layer mode is undetermined again. Reflect this in the layer2 field. If qeth_dev_layer2_store() hits an error after remove() was called, the card _always_ requires a setup(), even if the previous layer mode is requested again. But qeth_dev_layer2_store() bails out early if the requested layer mode still matches the current one. So unless we reset the layer2 field, re-probing the card back to its previous mode is currently not possible. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_sys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index c3f18afb368b..cfb659747693 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -426,6 +426,7 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, if (card->discipline) { card->discipline->remove(card->gdev); qeth_core_free_discipline(card); + card->options.layer2 = -1; } rc = qeth_core_load_discipline(card, newdis); From addc5ee87242a3592fdc9134ddff54c7aa372805 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:50 +0200 Subject: [PATCH 03/11] s390/qeth: remove redundant netif_carrier_ok() checks netif_carrier_off() does its own checking. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 2 +- drivers/s390/net/qeth_l2_main.c | 2 +- drivers/s390/net/qeth_l3_main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f7ddd6455638..7c3b643550f6 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -653,7 +653,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, cmd->hdr.return_code, card); } card->lan_online = 0; - if (card->dev && netif_carrier_ok(card->dev)) + if (card->dev) netif_carrier_off(card->dev); return NULL; case IPA_CMD_STARTLAN: diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8ac243de7a9e..089bde458fd5 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1163,7 +1163,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev && netif_carrier_ok(card->dev)) + if (card->dev) netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 062f62b49294..ee99af08b2c4 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2773,7 +2773,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev && netif_carrier_ok(card->dev)) + if (card->dev) netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { From d3d1b205e89f1e4194b9f8924022c77ea749d113 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:51 +0200 Subject: [PATCH 04/11] s390/qeth: allocate netdevice early Allocation of the netdevice is currently delayed until a qeth card first goes online. This complicates matters in several places, where we need to cache values instead of applying them straight to the netdevice. Improve on this by moving the allocation up to where the qeth card itself is created. This is also one step in direction of eventually placing the qeth card into netdev_priv(). In all subsequent code, remove the now redundant checks whether card->dev is valid. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 + drivers/s390/net/qeth_core_main.c | 61 +++++++++++++++++++++++++------ drivers/s390/net/qeth_core_sys.c | 14 ++++++- drivers/s390/net/qeth_l2_main.c | 52 +++++++------------------- drivers/s390/net/qeth_l3_main.c | 48 ++++++++---------------- drivers/s390/net/qeth_l3_sys.c | 6 +-- 6 files changed, 94 insertions(+), 88 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index a932aac62d0e..4d6827c8aba4 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -966,6 +966,7 @@ extern struct qeth_card_list_struct qeth_core_card_list; extern struct kmem_cache *qeth_core_header_cache; extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS]; +struct net_device *qeth_clone_netdev(struct net_device *orig); void qeth_set_recovery_task(struct qeth_card *); void qeth_clear_recovery_task(struct qeth_card *); void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 7c3b643550f6..6df2226417f1 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -653,8 +653,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, cmd->hdr.return_code, card); } card->lan_online = 0; - if (card->dev) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); return NULL; case IPA_CMD_STARTLAN: dev_info(&card->gdev->dev, @@ -2350,9 +2349,8 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, } if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { /* frame size has changed */ - if (card->dev && - ((card->dev->mtu == card->info.initial_mtu) || - (card->dev->mtu > mtu))) + if ((card->dev->mtu == card->info.initial_mtu) || + (card->dev->mtu > mtu)) card->dev->mtu = mtu; qeth_free_qdio_buffers(card); } @@ -3578,7 +3576,7 @@ static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, { struct qeth_card *card = (struct qeth_card *)card_ptr; - if (card->dev && (card->dev->flags & IFF_UP)) + if (card->dev->flags & IFF_UP) napi_schedule(&card->napi); } @@ -4794,9 +4792,6 @@ int qeth_vm_request_mac(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "vmreqmac"); - if (!card->dev) - return -ENODEV; - request = kzalloc(sizeof(*request), GFP_KERNEL | GFP_DMA); response = kzalloc(sizeof(*response), GFP_KERNEL | GFP_DMA); if (!request || !response) { @@ -5676,6 +5671,44 @@ static void qeth_clear_dbf_list(void) mutex_unlock(&qeth_dbf_list_mutex); } +static struct net_device *qeth_alloc_netdev(struct qeth_card *card) +{ + struct net_device *dev; + + switch (card->info.type) { + case QETH_CARD_TYPE_IQD: + dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, ether_setup); + break; + case QETH_CARD_TYPE_OSN: + dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup); + break; + default: + dev = alloc_etherdev(0); + } + + if (!dev) + return NULL; + + dev->ml_priv = card; + dev->watchdog_timeo = QETH_TX_TIMEOUT; + dev->min_mtu = 64; + dev->max_mtu = ETH_MAX_MTU; + SET_NETDEV_DEV(dev, &card->gdev->dev); + netif_carrier_off(dev); + return dev; +} + +struct net_device *qeth_clone_netdev(struct net_device *orig) +{ + struct net_device *clone = qeth_alloc_netdev(orig->ml_priv); + + if (!clone) + return NULL; + + clone->dev_port = orig->dev_port; + return clone; +} + static int qeth_core_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card; @@ -5725,6 +5758,10 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) goto err_card; } + card->dev = qeth_alloc_netdev(card); + if (!card->dev) + goto err_card; + qeth_determine_capabilities(card); enforced_disc = qeth_enforce_discipline(card); switch (enforced_disc) { @@ -5735,7 +5772,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) card->info.layer_enforced = true; rc = qeth_core_load_discipline(card, enforced_disc); if (rc) - goto err_card; + goto err_load; gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) ? card->discipline->devtype @@ -5753,6 +5790,8 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) err_disc: qeth_core_free_discipline(card); +err_load: + free_netdev(card->dev); err_card: qeth_core_free_card(card); err_dev: @@ -5775,10 +5814,10 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev) write_lock_irqsave(&qeth_core_card_list.rwlock, flags); list_del(&card->list); write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags); + free_netdev(card->dev); qeth_core_free_card(card); dev_set_drvdata(&gdev->dev, NULL); put_device(&gdev->dev); - return; } static int qeth_core_set_online(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index cfb659747693..9bef19ed7e04 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -144,8 +144,7 @@ static ssize_t qeth_dev_portno_store(struct device *dev, goto out; } card->info.portno = portno; - if (card->dev) - card->dev->dev_port = portno; + card->dev->dev_port = portno; out: mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -388,6 +387,7 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); + struct net_device *ndev; char *tmp; int i, rc = 0; enum qeth_discipline_id newdis; @@ -424,9 +424,19 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, card->info.mac_bits = 0; if (card->discipline) { + /* start with a new, pristine netdevice: */ + ndev = qeth_clone_netdev(card->dev); + if (!ndev) { + rc = -ENOMEM; + goto out; + } + card->discipline->remove(card->gdev); qeth_core_free_discipline(card); card->options.layer2 = -1; + + free_netdev(card->dev); + card->dev = ndev; } rc = qeth_core_load_discipline(card, newdis); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 089bde458fd5..8392fc7e809c 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -899,13 +899,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) if (cgdev->state == CCWGROUP_ONLINE) qeth_l2_set_offline(cgdev); - - if (card->dev) { - unregister_netdev(card->dev); - free_netdev(card->dev); - card->dev = NULL; - } - return; + unregister_netdev(card->dev); } static const struct ethtool_ops qeth_l2_ethtool_ops = { @@ -944,29 +938,13 @@ static const struct net_device_ops qeth_l2_netdev_ops = { static int qeth_l2_setup_netdev(struct qeth_card *card) { - switch (card->info.type) { - case QETH_CARD_TYPE_IQD: - card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, - ether_setup); - break; - case QETH_CARD_TYPE_OSN: - card->dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, - ether_setup); - break; - default: - card->dev = alloc_etherdev(0); - } + int rc; - if (!card->dev) - return -ENODEV; + if (card->dev->netdev_ops) + return 0; - card->dev->ml_priv = card; card->dev->priv_flags |= IFF_UNICAST_FLT; - card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; - card->dev->min_mtu = 64; - card->dev->max_mtu = ETH_MAX_MTU; - card->dev->dev_port = card->info.portno; card->dev->netdev_ops = &qeth_l2_netdev_ops; if (card->info.type == QETH_CARD_TYPE_OSN) { card->dev->ethtool_ops = &qeth_l2_osn_ops; @@ -1006,12 +984,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->vlan_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_poll, QETH_NAPI_WEIGHT); - netif_carrier_off(card->dev); - return register_netdev(card->dev); + rc = register_netdev(card->dev); + if (rc) + card->dev->netdev_ops = NULL; + return rc; } static int qeth_l2_start_ipassists(struct qeth_card *card) @@ -1057,10 +1035,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) dev_info(&card->gdev->dev, "The device represents a Bridge Capable Port\n"); - if (!card->dev && qeth_l2_setup_netdev(card)) { - rc = -ENODEV; + rc = qeth_l2_setup_netdev(card); + if (rc) goto out_remove; - } if (card->info.type != QETH_CARD_TYPE_OSN && !qeth_l2_send_setmac(card, card->dev->dev_addr)) @@ -1163,8 +1140,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); @@ -1237,8 +1213,7 @@ static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->dev) - netif_device_detach(card->dev); + netif_device_detach(card->dev); qeth_set_allowed_threads(card, 0, 1); wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); if (gdev->state == CCWGROUP_OFFLINE) @@ -1271,8 +1246,7 @@ static int qeth_l2_pm_resume(struct ccwgroup_device *gdev) rc = __qeth_l2_set_online(card->gdev, 0); out: qeth_set_allowed_threads(card, 0xffffffff, 0); - if (card->dev) - netif_device_attach(card->dev); + netif_device_attach(card->dev); if (rc) dev_warn(&card->gdev->dev, "The qeth device driver " "failed to recover an error on the device\n"); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index ee99af08b2c4..72c8f323801b 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2536,6 +2536,9 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) { int rc; + if (card->dev->netdev_ops) + return 0; + if (card->info.type == QETH_CARD_TYPE_OSD || card->info.type == QETH_CARD_TYPE_OSX) { if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || @@ -2544,9 +2547,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) return -ENODEV; } - card->dev = alloc_etherdev(0); - if (!card->dev) - return -ENODEV; card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; /*IPv6 address autoconfiguration stuff*/ @@ -2567,27 +2567,19 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev->vlan_features |= NETIF_F_IPV6_CSUM; } } else if (card->info.type == QETH_CARD_TYPE_IQD) { - card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, - ether_setup); - if (!card->dev) - return -ENODEV; card->dev->flags |= IFF_NOARP; card->dev->netdev_ops = &qeth_l3_netdev_ops; rc = qeth_l3_iqd_read_initial_mac(card); if (rc) - return rc; + goto out; + if (card->options.hsuid[0]) memcpy(card->dev->perm_addr, card->options.hsuid, 9); } else return -ENODEV; - card->dev->ml_priv = card; - card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; - card->dev->min_mtu = 64; - card->dev->max_mtu = ETH_MAX_MTU; - card->dev->dev_port = card->info.portno; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; card->dev->priv_flags &= ~IFF_TX_SKB_SHARING; card->dev->needed_headroom = sizeof(struct qeth_hdr) - ETH_HLEN; @@ -2602,10 +2594,12 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) netif_set_gso_max_size(card->dev, PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1)); - SET_NETDEV_DEV(card->dev, &card->gdev->dev); netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); - netif_carrier_off(card->dev); - return register_netdev(card->dev); + rc = register_netdev(card->dev); +out: + if (rc) + card->dev->netdev_ops = NULL; + return rc; } static const struct device_type qeth_l3_devtype = { @@ -2643,15 +2637,9 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev) if (cgdev->state == CCWGROUP_ONLINE) qeth_l3_set_offline(cgdev); - if (card->dev) { - unregister_netdev(card->dev); - free_netdev(card->dev); - card->dev = NULL; - } - + unregister_netdev(card->dev); qeth_l3_clear_ip_htable(card, 0); qeth_l3_clear_ipato_list(card); - return; } static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) @@ -2673,10 +2661,9 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) goto out_remove; } - if (!card->dev && qeth_l3_setup_netdev(card)) { - rc = -ENODEV; + rc = qeth_l3_setup_netdev(card); + if (rc) goto out_remove; - } if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { if (card->info.hwtrap && @@ -2773,8 +2760,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, QETH_DBF_TEXT(SETUP, 3, "setoffl"); QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *)); - if (card->dev) - netif_carrier_off(card->dev); + netif_carrier_off(card->dev); recover_flag = card->state; if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) { qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); @@ -2842,8 +2828,7 @@ static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->dev) - netif_device_detach(card->dev); + netif_device_detach(card->dev); qeth_set_allowed_threads(card, 0, 1); wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); if (gdev->state == CCWGROUP_OFFLINE) @@ -2876,8 +2861,7 @@ static int qeth_l3_pm_resume(struct ccwgroup_device *gdev) rc = __qeth_l3_set_online(card->gdev, 0); out: qeth_set_allowed_threads(card, 0xffffffff, 0); - if (card->dev) - netif_device_attach(card->dev); + netif_device_attach(card->dev); if (rc) dev_warn(&card->gdev->dev, "The qeth device driver " "failed to recover an error on the device\n"); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index f61192a048f4..45ac6d8705c6 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -299,8 +299,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, if (strlen(tmp) == 0) { /* delete ip address only */ card->options.hsuid[0] = '\0'; - if (card->dev) - memcpy(card->dev->perm_addr, card->options.hsuid, 9); + memcpy(card->dev->perm_addr, card->options.hsuid, 9); qeth_configure_cq(card, QETH_CQ_DISABLED); return count; } @@ -311,8 +310,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, snprintf(card->options.hsuid, sizeof(card->options.hsuid), "%-8s", tmp); ASCEBC(card->options.hsuid, 8); - if (card->dev) - memcpy(card->dev->perm_addr, card->options.hsuid, 9); + memcpy(card->dev->perm_addr, card->options.hsuid, 9); rc = qeth_l3_modify_hsuid(card, true); From 92d2720969bc741c8aa7269cb3561c49227cd04f Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:52 +0200 Subject: [PATCH 05/11] s390/qeth: don't cache HW port number The netdevice is always available now, so get the portno from there. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_core_main.c | 7 +++---- drivers/s390/net/qeth_core_sys.c | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 4d6827c8aba4..04b900c7060d 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -658,7 +658,6 @@ struct qeth_card_info { char mcl_level[QETH_MCL_LENGTH + 1]; int guestlan; int mac_bits; - int portno; enum qeth_card_types type; enum qeth_link_types link_type; int initial_mtu; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6df2226417f1..39c429787c4d 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1920,7 +1920,7 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel, memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data), &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH); } - tmp = ((__u8)card->info.portno) | 0x80; + tmp = ((u8)card->dev->dev_port) | 0x80; memcpy(QETH_IDX_ACT_PNO(iob->data), &tmp, 1); memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(iob->data), &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH); @@ -2389,8 +2389,7 @@ static int qeth_ulp_enable(struct qeth_card *card) iob = qeth_wait_for_buffer(&card->write); memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE); - *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = - (__u8) card->info.portno; + *(QETH_ULP_ENABLE_LINKNUM(iob->data)) = (u8) card->dev->dev_port; if (card->options.layer2) if (card->info.type == QETH_CARD_TYPE_OSN) prot_type = QETH_PROT_OSN2; @@ -2918,7 +2917,7 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card, cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST; /* cmd->hdr.seqno is set by qeth_send_control_data() */ cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type); - cmd->hdr.rel_adapter_no = (__u8) card->info.portno; + cmd->hdr.rel_adapter_no = (u8) card->dev->dev_port; if (card->options.layer2) cmd->hdr.prim_version_no = 2; else diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 9bef19ed7e04..25d0be25bcb3 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -112,7 +112,7 @@ static ssize_t qeth_dev_portno_show(struct device *dev, if (!card) return -EINVAL; - return sprintf(buf, "%i\n", card->info.portno); + return sprintf(buf, "%i\n", card->dev->dev_port); } static ssize_t qeth_dev_portno_store(struct device *dev, @@ -143,7 +143,6 @@ static ssize_t qeth_dev_portno_store(struct device *dev, rc = -EINVAL; goto out; } - card->info.portno = portno; card->dev->dev_port = portno; out: mutex_unlock(&card->conf_mutex); From 8ce7a9e064db4a31e9d9d08300e2f3e9679eaffe Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:53 +0200 Subject: [PATCH 06/11] s390/qeth: simplify max MTU handling When the MPC initialization code discovers the HW-specific max MTU, apply the resulting changes straight to the netdevice. If this is the device's first initialization, also set its MTU (HiperSockets: the max MTU; else: a layer-specific default value). Then cap the current MTU by the new max MTU. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 - drivers/s390/net/qeth_core_main.c | 82 +++++++++++++++++-------------- drivers/s390/net/qeth_l2_main.c | 1 - drivers/s390/net/qeth_l3_main.c | 1 - 4 files changed, 45 insertions(+), 41 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 04b900c7060d..6f02a6cbe59e 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -660,8 +660,6 @@ struct qeth_card_info { int mac_bits; enum qeth_card_types type; enum qeth_link_types link_type; - int initial_mtu; - int max_mtu; int broadcast_capable; int unique_id; bool layer_enforced; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 39c429787c4d..ab3d63f98779 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2278,19 +2278,42 @@ static int qeth_cm_setup(struct qeth_card *card) } -static int qeth_get_initial_mtu_for_card(struct qeth_card *card) +static int qeth_update_max_mtu(struct qeth_card *card, unsigned int max_mtu) { - switch (card->info.type) { - case QETH_CARD_TYPE_IQD: - return card->info.max_mtu; - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSX: - if (!card->options.layer2) - return ETH_DATA_LEN - 8; /* L3: allow for LLC + SNAP */ - /* fall through */ - default: - return ETH_DATA_LEN; + struct net_device *dev = card->dev; + unsigned int new_mtu; + + if (!max_mtu) { + /* IQD needs accurate max MTU to set up its RX buffers: */ + if (IS_IQD(card)) + return -EINVAL; + /* tolerate quirky HW: */ + max_mtu = ETH_MAX_MTU; } + + rtnl_lock(); + if (IS_IQD(card)) { + /* move any device with default MTU to new max MTU: */ + new_mtu = (dev->mtu == dev->max_mtu) ? max_mtu : dev->mtu; + + /* adjust RX buffer size to new max MTU: */ + card->qdio.in_buf_size = max_mtu + 2 * PAGE_SIZE; + if (dev->max_mtu && dev->max_mtu != max_mtu) + qeth_free_qdio_buffers(card); + } else { + if (dev->mtu) + new_mtu = dev->mtu; + /* default MTUs for first setup: */ + else if (card->options.layer2) + new_mtu = ETH_DATA_LEN; + else + new_mtu = ETH_DATA_LEN - 8; /* allow for LLC + SNAP */ + } + + dev->max_mtu = max_mtu; + dev->mtu = min(new_mtu, max_mtu); + rtnl_unlock(); + return 0; } static int qeth_get_mtu_outof_framesize(int framesize) @@ -2316,8 +2339,7 @@ static int qeth_mtu_is_valid(struct qeth_card *card, int mtu) case QETH_CARD_TYPE_OSM: case QETH_CARD_TYPE_OSX: case QETH_CARD_TYPE_IQD: - return ((mtu >= 576) && - (mtu <= card->info.max_mtu)); + return ((mtu >= 576) && (mtu <= card->dev->max_mtu)); case QETH_CARD_TYPE_OSN: default: return 1; @@ -2342,28 +2364,10 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, if (card->info.type == QETH_CARD_TYPE_IQD) { memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2); mtu = qeth_get_mtu_outof_framesize(framesize); - if (!mtu) { - iob->rc = -EINVAL; - QETH_DBF_TEXT_(SETUP, 2, " rc%d", iob->rc); - return 0; - } - if (card->info.initial_mtu && (card->info.initial_mtu != mtu)) { - /* frame size has changed */ - if ((card->dev->mtu == card->info.initial_mtu) || - (card->dev->mtu > mtu)) - card->dev->mtu = mtu; - qeth_free_qdio_buffers(card); - } - card->info.initial_mtu = mtu; - card->info.max_mtu = mtu; - card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE; } else { - card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU( - iob->data); - card->info.initial_mtu = min(card->info.max_mtu, - qeth_get_initial_mtu_for_card(card)); - card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; + mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data); } + *(u16 *)reply->param = mtu; memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2); if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) { @@ -2382,6 +2386,7 @@ static int qeth_ulp_enable(struct qeth_card *card) int rc; char prot_type; struct qeth_cmd_buffer *iob; + u16 max_mtu; /*FIXME: trace view callbacks*/ QETH_DBF_TEXT(SETUP, 2, "ulpenabl"); @@ -2404,9 +2409,10 @@ static int qeth_ulp_enable(struct qeth_card *card) memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, - qeth_ulp_enable_cb, NULL); - return rc; - + qeth_ulp_enable_cb, &max_mtu); + if (rc) + return rc; + return qeth_update_max_mtu(card, max_mtu); } static int qeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply, @@ -5691,7 +5697,9 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card) dev->ml_priv = card; dev->watchdog_timeo = QETH_TX_TIMEOUT; dev->min_mtu = 64; - dev->max_mtu = ETH_MAX_MTU; + /* initialized when device first goes online: */ + dev->max_mtu = 0; + dev->mtu = 0; SET_NETDEV_DEV(dev, &card->gdev->dev); netif_carrier_off(dev); return dev; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8392fc7e809c..00d8bb1d2a41 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -944,7 +944,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) return 0; card->dev->priv_flags |= IFF_UNICAST_FLT; - card->dev->mtu = card->info.initial_mtu; card->dev->netdev_ops = &qeth_l2_netdev_ops; if (card->info.type == QETH_CARD_TYPE_OSN) { card->dev->ethtool_ops = &qeth_l2_osn_ops; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 72c8f323801b..e5fa8e5ac1b3 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2579,7 +2579,6 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) } else return -ENODEV; - card->dev->mtu = card->info.initial_mtu; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; card->dev->priv_flags &= ~IFF_TX_SKB_SHARING; card->dev->needed_headroom = sizeof(struct qeth_hdr) - ETH_HLEN; From 72f219da79d22384bbc809fc67ed305dbe824e39 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:54 +0200 Subject: [PATCH 07/11] s390/qeth: use core MTU range checking qeth's ndo_change_mtu() only applies some trivial bounds checking. Set up dev->min_mtu properly, so that dev_set_mtu() can do this for us. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_core_main.c | 34 +------------------------------ drivers/s390/net/qeth_core_mpc.h | 1 + drivers/s390/net/qeth_l2_main.c | 1 - drivers/s390/net/qeth_l3_main.c | 2 -- 5 files changed, 2 insertions(+), 37 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 6f02a6cbe59e..994ac7f434d5 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -993,7 +993,6 @@ void qeth_clear_cmd_buffers(struct qeth_channel *); void qeth_clear_qdio_buffers(struct qeth_card *); void qeth_setadp_promisc_mode(struct qeth_card *); struct net_device_stats *qeth_get_stats(struct net_device *); -int qeth_change_mtu(struct net_device *, int); int qeth_setadpparms_change_macaddr(struct qeth_card *); void qeth_tx_timeout(struct net_device *); void qeth_prepare_control_data(struct qeth_card *, int, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index ab3d63f98779..717511c167e7 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2332,20 +2332,6 @@ static int qeth_get_mtu_outof_framesize(int framesize) } } -static int qeth_mtu_is_valid(struct qeth_card *card, int mtu) -{ - switch (card->info.type) { - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSM: - case QETH_CARD_TYPE_OSX: - case QETH_CARD_TYPE_IQD: - return ((mtu >= 576) && (mtu <= card->dev->max_mtu)); - case QETH_CARD_TYPE_OSN: - default: - return 1; - } -} - static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { @@ -4204,24 +4190,6 @@ void qeth_setadp_promisc_mode(struct qeth_card *card) } EXPORT_SYMBOL_GPL(qeth_setadp_promisc_mode); -int qeth_change_mtu(struct net_device *dev, int new_mtu) -{ - struct qeth_card *card; - char dbf_text[15]; - - card = dev->ml_priv; - - QETH_CARD_TEXT(card, 4, "chgmtu"); - sprintf(dbf_text, "%8x", new_mtu); - QETH_CARD_TEXT(card, 4, dbf_text); - - if (!qeth_mtu_is_valid(card, new_mtu)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL_GPL(qeth_change_mtu); - struct net_device_stats *qeth_get_stats(struct net_device *dev) { struct qeth_card *card; @@ -5696,7 +5664,7 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card) dev->ml_priv = card; dev->watchdog_timeo = QETH_TX_TIMEOUT; - dev->min_mtu = 64; + dev->min_mtu = IS_OSN(card) ? 64 : 576; /* initialized when device first goes online: */ dev->max_mtu = 0; dev->mtu = 0; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 54c35224262a..cf5ad94e960a 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -65,6 +65,7 @@ enum qeth_card_types { }; #define IS_IQD(card) ((card)->info.type == QETH_CARD_TYPE_IQD) +#define IS_OSN(card) ((card)->info.type == QETH_CARD_TYPE_OSN) #define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18 /* only the first two bytes are looked at in qeth_get_cardname_short */ diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 00d8bb1d2a41..668f80680575 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -928,7 +928,6 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_set_rx_mode = qeth_l2_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, .ndo_set_mac_address = qeth_l2_set_mac_address, - .ndo_change_mtu = qeth_change_mtu, .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, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index e5fa8e5ac1b3..078b891a6d24 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2506,7 +2506,6 @@ static const struct net_device_ops qeth_l3_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, @@ -2523,7 +2522,6 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_rx_mode, .ndo_do_ioctl = qeth_do_ioctl, - .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, From d2a274b25be7218f8400037868a756640e8a4b0d Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:55 +0200 Subject: [PATCH 08/11] s390/qeth: add statistics for consumed buffer elements Nowadays an skb fragment typically spans over multiple pages. So replace the obsolete, SG-only 'fragments' counter with one that tracks the consumed buffer elements. This is what actually matters for performance. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 +- drivers/s390/net/qeth_core_main.c | 4 ++-- drivers/s390/net/qeth_l2_main.c | 13 +++++++------ drivers/s390/net/qeth_l3_main.c | 28 ++++++++++++++-------------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 994ac7f434d5..6d8005af67f5 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -104,6 +104,7 @@ struct qeth_dbf_info { struct qeth_perf_stats { unsigned int bufs_rec; unsigned int bufs_sent; + unsigned int buf_elements_sent; unsigned int skbs_sent_pack; unsigned int bufs_sent_pack; @@ -137,7 +138,6 @@ struct qeth_perf_stats { unsigned int large_send_bytes; unsigned int large_send_cnt; unsigned int sg_skbs_sent; - unsigned int sg_frags_sent; /* initial values when measuring starts */ unsigned long initial_rx_packets; unsigned long initial_tx_packets; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 717511c167e7..84f1e1e33f3f 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5970,7 +5970,7 @@ static struct { {"tx skbs packing"}, {"tx buffers packing"}, {"tx sg skbs"}, - {"tx sg frags"}, + {"tx buffer elements"}, /* 10 */{"rx sg skbs"}, {"rx sg frags"}, {"rx sg page allocs"}, @@ -6029,7 +6029,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev, data[6] = card->perf_stats.skbs_sent_pack; data[7] = card->perf_stats.bufs_sent_pack; data[8] = card->perf_stats.sg_skbs_sent; - data[9] = card->perf_stats.sg_frags_sent; + data[9] = card->perf_stats.buf_elements_sent; data[10] = card->perf_stats.sg_skbs_rx; data[11] = card->perf_stats.sg_frags_rx; data[12] = card->perf_stats.sg_alloc_page_rx; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 668f80680575..a785c5ff73cd 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -672,10 +672,11 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, int ipv) { int push_len = sizeof(struct qeth_hdr); - unsigned int elements, nr_frags; unsigned int hdr_elements = 0; struct qeth_hdr *hdr = NULL; unsigned int hd_len = 0; + unsigned int elements; + bool is_sg; int rc; /* fix hardware limitation: as long as we do not have sbal @@ -693,7 +694,6 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, if (rc) return rc; } - nr_frags = skb_shinfo(skb)->nr_frags; rc = skb_cow_head(skb, push_len); if (rc) @@ -720,15 +720,16 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, } elements += hdr_elements; + is_sg = skb_is_nonlinear(skb); /* TODO: remove the skb_orphan() once TX completion is fast enough */ skb_orphan(skb); rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements); out: if (!rc) { - 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 (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; } } else { if (hd_len) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 078b891a6d24..c12aeb7d8f26 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2166,12 +2166,13 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, int cast_type) { const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); - unsigned int frame_len, nr_frags; unsigned char eth_hdr[ETH_HLEN]; unsigned int hdr_elements = 0; struct qeth_hdr *hdr = NULL; int elements, push_len, rc; unsigned int hd_len = 0; + unsigned int frame_len; + bool is_sg; /* compress skb to fit into one IO buffer: */ if (!qeth_get_elements_no(card, skb, 0, 0)) { @@ -2194,7 +2195,6 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, skb_copy_from_linear_data(skb, eth_hdr, ETH_HLEN); skb_pull(skb, ETH_HLEN); frame_len = skb->len; - nr_frags = skb_shinfo(skb)->nr_frags; push_len = qeth_push_hdr(skb, &hdr, hw_hdr_len); if (push_len < 0) @@ -2217,6 +2217,7 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, else qeth_l3_fill_header(card, hdr, skb, ipv, cast_type, frame_len); + is_sg = skb_is_nonlinear(skb); if (IS_IQD(card)) { rc = qeth_do_send_packet_fast(queue, skb, hdr, 0, hd_len); } else { @@ -2227,10 +2228,10 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, } out: if (!rc) { - 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 (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; } } else { if (!push_len) @@ -2248,14 +2249,14 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, struct qeth_qdio_out_q *queue, int ipv, int cast_type) { - unsigned int hd_len, nr_frags; int elements, len, rc; __be16 *tag; struct qeth_hdr *hdr = NULL; int hdr_elements = 0; struct sk_buff *new_skb = NULL; int tx_bytes = skb->len; - bool use_tso; + unsigned int hd_len; + bool use_tso, is_sg; /* Ignore segment size from skb_is_gso(), 1 page is always used. */ use_tso = skb_is_gso(skb) && @@ -2297,7 +2298,6 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, if (rc) goto out; } - nr_frags = skb_shinfo(new_skb)->nr_frags; if (use_tso) { hdr = skb_push(new_skb, sizeof(struct qeth_hdr_tso)); @@ -2334,6 +2334,8 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, rc = -EINVAL; goto out; } + + is_sg = skb_is_nonlinear(new_skb); rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len, hd_len, elements); out: @@ -2341,15 +2343,13 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, if (new_skb != skb) dev_kfree_skb_any(skb); if (card->options.performance_stats) { + card->perf_stats.buf_elements_sent += elements; + if (is_sg) + card->perf_stats.sg_skbs_sent++; if (use_tso) { card->perf_stats.large_send_bytes += tx_bytes; card->perf_stats.large_send_cnt++; } - if (nr_frags) { - card->perf_stats.sg_skbs_sent++; - /* nr_frags + skb->data */ - card->perf_stats.sg_frags_sent += nr_frags + 1; - } } } else { if (new_skb != skb) From ba86ceee9d1b5aa71fe3db75b2ec5452c9a48307 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:56 +0200 Subject: [PATCH 09/11] s390/qeth: merge linearize-check into HW header construction When checking whether an skb needs to be linearized to fit into an IO buffer, it's desirable to consider the skb's final size and layout (ie. after the HW header was added). But a subsequent linearization can then cause the re-positioned HW header to violate its alignment restrictions. Dealing with this situation in two different code paths is quite tricky. This patch integrates a) linearize-check and b) HW header construction into one 3 step-sequence: 1. evaluate how the HW header needs to be added (to identify if it takes up an additional buffer element), then 2. check if the required buffer elements exceed the device's limit. Linearize when necessary and re-evaluate the HW header placement. 3. Add the HW header in the best-possible way: a) push, without taking up an additional buffer element b) push, but consume another buffer element c) allocate a header object from the cache. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 4 +- drivers/s390/net/qeth_core_main.c | 84 +++++++++++++++++++++++++------ drivers/s390/net/qeth_l2_main.c | 29 +---------- drivers/s390/net/qeth_l3_main.c | 31 ++---------- 4 files changed, 79 insertions(+), 69 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 6d8005af67f5..2a5ec99643df 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -1047,7 +1047,9 @@ netdev_features_t qeth_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features); int qeth_vm_request_mac(struct qeth_card *card); -int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len); +int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr **hdr, unsigned int len, + unsigned int *elements); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 84f1e1e33f3f..e7b34624df1e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3831,6 +3831,17 @@ int qeth_get_elements_for_frags(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); +static unsigned int qeth_count_elements(struct sk_buff *skb, int data_offset) +{ + unsigned int elements = qeth_get_elements_for_frags(skb); + addr_t end = (addr_t)skb->data + skb_headlen(skb); + addr_t start = (addr_t)skb->data + data_offset; + + if (start != end) + elements += qeth_get_elements_for_range(start, end); + return elements; +} + /** * qeth_get_elements_no() - find number of SBALEs for skb data, inc. frags. * @card: qeth card structure, to check max. elems. @@ -3846,12 +3857,7 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags); int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb, int extra_elems, int data_offset) { - addr_t end = (addr_t)skb->data + skb_headlen(skb); - int elements = qeth_get_elements_for_frags(skb); - addr_t start = (addr_t)skb->data + data_offset; - - if (start != end) - elements += qeth_get_elements_for_range(start, end); + int elements = qeth_count_elements(skb, data_offset); if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { QETH_DBF_MESSAGE(2, "Invalid size of IP packet " @@ -3885,22 +3891,72 @@ int qeth_hdr_chk_and_bounce(struct sk_buff *skb, struct qeth_hdr **hdr, int len) EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); /** - * qeth_push_hdr() - push a qeth_hdr onto an skb. - * @skb: skb that the qeth_hdr should be pushed onto. + * qeth_add_hw_header() - add a HW header to an skb. + * @skb: skb that the HW header should be added to. * @hdr: double pointer to a qeth_hdr. When returning with >= 0, * it contains a valid pointer to a qeth_hdr. - * @len: length of the hdr that needs to be pushed on. + * @len: length of the HW header. * * Returns the pushed length. If the header can't be pushed on * (eg. because it would cross a page boundary), it is allocated from * the cache instead and 0 is returned. + * The number of needed buffer elements is returned in @elements. * Error to create the hdr is indicated by returning with < 0. */ -int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len) +int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr **hdr, unsigned int len, + unsigned int *elements) { - if (skb_headroom(skb) >= len && - qeth_get_elements_for_range((addr_t)skb->data - len, - (addr_t)skb->data) == 1) { + const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card); + unsigned int __elements; + addr_t start, end; + bool push_ok; + int rc; + +check_layout: + start = (addr_t)skb->data - len; + end = (addr_t)skb->data; + + if (qeth_get_elements_for_range(start, end + 1) == 1) { + /* Push HW header into same page as first protocol header. */ + push_ok = true; + __elements = qeth_count_elements(skb, 0); + } else { + __elements = 1 + qeth_count_elements(skb, 0); + if (qeth_get_elements_for_range(start, end) == 1) + /* Push HW header into a new page. */ + push_ok = true; + else + /* Use header cache. */ + push_ok = false; + } + + /* Compress skb to fit into one IO buffer: */ + if (__elements > max_elements) { + if (!skb_is_nonlinear(skb)) { + /* Drop it, no easy way of shrinking it further. */ + QETH_DBF_MESSAGE(2, "Dropped an oversized skb (Max Elements=%u / Actual=%u / Length=%u).\n", + max_elements, __elements, skb->len); + return -E2BIG; + } + + 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; + + /* Linearization changed the layout, re-evaluate: */ + goto check_layout; + } + + *elements = __elements; + /* Add the header: */ + if (push_ok) { *hdr = skb_push(skb, len); return len; } @@ -3910,7 +3966,7 @@ int qeth_push_hdr(struct sk_buff *skb, struct qeth_hdr **hdr, unsigned int len) return -ENOMEM; return 0; } -EXPORT_SYMBOL_GPL(qeth_push_hdr); +EXPORT_SYMBOL_GPL(qeth_add_hw_header); static void __qeth_fill_buffer(struct sk_buff *skb, struct qeth_qdio_out_buffer *buf, diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a785c5ff73cd..905f3bb3a87c 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -672,39 +672,21 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, int ipv) { int push_len = sizeof(struct qeth_hdr); - unsigned int hdr_elements = 0; struct qeth_hdr *hdr = NULL; unsigned int hd_len = 0; unsigned int elements; bool is_sg; 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; - } - rc = skb_cow_head(skb, push_len); if (rc) return rc; - push_len = qeth_push_hdr(skb, &hdr, push_len); + push_len = qeth_add_hw_header(card, skb, &hdr, push_len, &elements); if (push_len < 0) return push_len; if (!push_len) { /* hdr was allocated from cache */ hd_len = sizeof(*hdr); - hdr_elements = 1; } qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len); if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -713,18 +695,11 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, card->perf_stats.tx_csum++; } - elements = qeth_get_elements_no(card, skb, hdr_elements, 0); - if (!elements) { - rc = -E2BIG; - goto out; - } - elements += hdr_elements; - is_sg = skb_is_nonlinear(skb); /* TODO: remove the skb_orphan() once TX completion is fast enough */ skb_orphan(skb); rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements); -out: + if (!rc) { if (card->options.performance_stats) { card->perf_stats.buf_elements_sent += elements; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c12aeb7d8f26..f7bcc4853c45 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2166,28 +2166,13 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, int cast_type) { const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); + unsigned int frame_len, elements; unsigned char eth_hdr[ETH_HLEN]; - unsigned int hdr_elements = 0; struct qeth_hdr *hdr = NULL; - int elements, push_len, rc; unsigned int hd_len = 0; - unsigned int frame_len; + int push_len, rc; bool is_sg; - /* compress skb to fit into one IO buffer: */ - 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; - } - /* re-use the L2 header area for the HW header: */ rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN); if (rc) @@ -2196,22 +2181,14 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, skb_pull(skb, ETH_HLEN); frame_len = skb->len; - push_len = qeth_push_hdr(skb, &hdr, hw_hdr_len); + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, &elements); if (push_len < 0) return push_len; if (!push_len) { /* hdr was added discontiguous from skb->data */ hd_len = hw_hdr_len; - hdr_elements = 1; } - elements = qeth_get_elements_no(card, skb, hdr_elements, 0); - if (!elements) { - rc = -E2BIG; - goto out; - } - elements += hdr_elements; - if (skb->protocol == htons(ETH_P_AF_IUCV)) qeth_l3_fill_af_iucv_hdr(hdr, skb, frame_len); else @@ -2226,7 +2203,7 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements); } -out: + if (!rc) { if (card->options.performance_stats) { card->perf_stats.buf_elements_sent += elements; From a7c2f4a33290fbad615a0c4e977f317f37d7a057 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:57 +0200 Subject: [PATCH 10/11] s390/qeth: add support for constrained HW headers Some transmit modes require that the HW header is located in the same page as the initial protocol headers in skb->data. Let callers specify the size of this contiguous header range, and enforce it when building the HW header. While at it, apply some gentle renaming to the relevant L2 code so that it matches the L3 code. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 4 ++-- drivers/s390/net/qeth_core_main.c | 33 ++++++++++++++++++------------- drivers/s390/net/qeth_l2_main.c | 12 ++++++----- drivers/s390/net/qeth_l3_main.c | 3 ++- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 2a5ec99643df..605ec4706773 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -1048,8 +1048,8 @@ netdev_features_t qeth_features_check(struct sk_buff *skb, netdev_features_t features); int qeth_vm_request_mac(struct qeth_card *card); int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, - struct qeth_hdr **hdr, unsigned int len, - unsigned int *elements); + struct qeth_hdr **hdr, unsigned int hdr_len, + unsigned int proto_len, unsigned int *elements); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index e7b34624df1e..732b517369c7 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3895,7 +3895,9 @@ EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); * @skb: skb that the HW header should be added to. * @hdr: double pointer to a qeth_hdr. When returning with >= 0, * it contains a valid pointer to a qeth_hdr. - * @len: length of the HW header. + * @hdr_len: length of the HW header. + * @proto_len: length of protocol headers that need to be in same page as the + * HW header. * * Returns the pushed length. If the header can't be pushed on * (eg. because it would cross a page boundary), it is allocated from @@ -3904,31 +3906,32 @@ EXPORT_SYMBOL_GPL(qeth_hdr_chk_and_bounce); * Error to create the hdr is indicated by returning with < 0. */ int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, - struct qeth_hdr **hdr, unsigned int len, - unsigned int *elements) + struct qeth_hdr **hdr, unsigned int hdr_len, + unsigned int proto_len, unsigned int *elements) { const unsigned int max_elements = QETH_MAX_BUFFER_ELEMENTS(card); + const unsigned int contiguous = proto_len ? proto_len : 1; unsigned int __elements; addr_t start, end; bool push_ok; int rc; check_layout: - start = (addr_t)skb->data - len; + start = (addr_t)skb->data - hdr_len; end = (addr_t)skb->data; - if (qeth_get_elements_for_range(start, end + 1) == 1) { + if (qeth_get_elements_for_range(start, end + contiguous) == 1) { /* Push HW header into same page as first protocol header. */ push_ok = true; __elements = qeth_count_elements(skb, 0); - } else { + } else if (!proto_len && qeth_get_elements_for_range(start, end) == 1) { + /* Push HW header into a new page. */ + push_ok = true; __elements = 1 + qeth_count_elements(skb, 0); - if (qeth_get_elements_for_range(start, end) == 1) - /* Push HW header into a new page. */ - push_ok = true; - else - /* Use header cache. */ - push_ok = false; + } else { + /* Use header cache, copy protocol headers up. */ + push_ok = false; + __elements = 1 + qeth_count_elements(skb, proto_len); } /* Compress skb to fit into one IO buffer: */ @@ -3957,13 +3960,15 @@ int qeth_add_hw_header(struct qeth_card *card, struct sk_buff *skb, *elements = __elements; /* Add the header: */ if (push_ok) { - *hdr = skb_push(skb, len); - return len; + *hdr = skb_push(skb, hdr_len); + return hdr_len; } /* fall back */ *hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); if (!*hdr) return -ENOMEM; + /* Copy protocol headers behind HW header: */ + skb_copy_from_linear_data(skb, ((char *)*hdr) + hdr_len, proto_len); return 0; } EXPORT_SYMBOL_GPL(qeth_add_hw_header); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 905f3bb3a87c..c302002e422f 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -671,17 +671,19 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, struct qeth_qdio_out_q *queue, int cast_type, int ipv) { - int push_len = sizeof(struct qeth_hdr); + const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); struct qeth_hdr *hdr = NULL; unsigned int hd_len = 0; unsigned int elements; + int push_len, rc; bool is_sg; - int rc; - rc = skb_cow_head(skb, push_len); + rc = skb_cow_head(skb, hw_hdr_len); if (rc) return rc; - push_len = qeth_add_hw_header(card, skb, &hdr, push_len, &elements); + + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, 0, + &elements); if (push_len < 0) return push_len; if (!push_len) { @@ -707,7 +709,7 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, card->perf_stats.sg_skbs_sent++; } } else { - if (hd_len) + if (!push_len) kmem_cache_free(qeth_core_header_cache, hdr); if (rc == -EBUSY) /* roll back to ETH header */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index f7bcc4853c45..b8e828556cf6 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2181,7 +2181,8 @@ static int qeth_l3_xmit_offload(struct qeth_card *card, struct sk_buff *skb, skb_pull(skb, ETH_HLEN); frame_len = skb->len; - push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, &elements); + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, 0, + &elements); if (push_len < 0) return push_len; if (!push_len) { From 5f89eca577776952325ee35da54786d26de1120a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 19 Jul 2018 12:43:58 +0200 Subject: [PATCH 11/11] s390/qeth: speed up L2 IQD xmit Modify the L2 OSA xmit path so that it also supports L2 IQD devices (in particular, their HW header requirements). This allows IQD devices to advertise NETIF_F_SG support, and eliminates the allocation overhead for the HW header. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 7 +++ drivers/s390/net/qeth_l2_main.c | 76 ++++++++++--------------------- drivers/s390/net/qeth_l3_main.c | 3 -- 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 732b517369c7..d09a7110b381 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5731,6 +5731,13 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card) dev->mtu = 0; SET_NETDEV_DEV(dev, &card->gdev->dev); netif_carrier_off(dev); + + if (!IS_OSN(card)) { + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->hw_features |= NETIF_F_SG; + dev->vlan_features |= NETIF_F_SG; + } + return dev; } diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index c302002e422f..c1829a4b955d 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -641,37 +641,13 @@ 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(hdr, skb, cast_type, 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(queue, skb, hdr, data_offset, - sizeof(*hdr) + 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, - int ipv) +static int qeth_l2_xmit(struct qeth_card *card, struct sk_buff *skb, + struct qeth_qdio_out_q *queue, int cast_type, int ipv) { + const unsigned int proto_len = IS_IQD(card) ? ETH_HLEN : 0; const unsigned int hw_hdr_len = sizeof(struct qeth_hdr); + unsigned int frame_len = skb->len; + unsigned int data_offset = 0; struct qeth_hdr *hdr = NULL; unsigned int hd_len = 0; unsigned int elements; @@ -682,15 +658,16 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, if (rc) return rc; - push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, 0, + push_len = qeth_add_hw_header(card, skb, &hdr, hw_hdr_len, proto_len, &elements); if (push_len < 0) return push_len; if (!push_len) { - /* hdr was allocated from cache */ - hd_len = sizeof(*hdr); + /* HW header needs its own buffer element. */ + hd_len = hw_hdr_len + proto_len; + data_offset = proto_len; } - qeth_l2_fill_header(hdr, skb, cast_type, skb->len - push_len); + qeth_l2_fill_header(hdr, skb, cast_type, frame_len); if (skb->ip_summed == CHECKSUM_PARTIAL) { qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv); if (card->options.performance_stats) @@ -698,9 +675,15 @@ static int qeth_l2_xmit_osa(struct qeth_card *card, struct sk_buff *skb, } is_sg = skb_is_nonlinear(skb); - /* TODO: remove the skb_orphan() once TX completion is fast enough */ - skb_orphan(skb); - rc = qeth_do_send_packet(card, queue, skb, hdr, 0, hd_len, elements); + if (IS_IQD(card)) { + rc = qeth_do_send_packet_fast(queue, skb, hdr, data_offset, + hd_len); + } else { + /* TODO: drop skb_orphan() once TX completion is fast enough */ + skb_orphan(skb); + rc = qeth_do_send_packet(card, queue, skb, hdr, data_offset, + hd_len, elements); + } if (!rc) { if (card->options.performance_stats) { @@ -759,16 +742,10 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, } netif_stop_queue(dev); - switch (card->info.type) { - case QETH_CARD_TYPE_OSN: + if (IS_OSN(card)) 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, ipv); - } + else + rc = qeth_l2_xmit(card, skb, queue, cast_type, ipv); if (!rc) { card->stats.tx_packets++; @@ -927,6 +904,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->flags |= IFF_NOARP; } else { card->dev->ethtool_ops = &qeth_l2_ethtool_ops; + card->dev->needed_headroom = sizeof(struct qeth_hdr); } if (card->info.type == QETH_CARD_TYPE_OSM) @@ -934,14 +912,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) else card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; - if (card->info.type != QETH_CARD_TYPE_OSN && - card->info.type != QETH_CARD_TYPE_IQD) { - card->dev->priv_flags &= ~IFF_TX_SKB_SHARING; - card->dev->needed_headroom = sizeof(struct qeth_hdr); - card->dev->hw_features |= NETIF_F_SG; - card->dev->vlan_features |= NETIF_F_SG; - } - if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) { card->dev->features |= NETIF_F_SG; /* OSA 3S and earlier has no RX/TX support */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index b8e828556cf6..1833e7505aca 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2556,13 +2556,10 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) return -ENODEV; card->dev->ethtool_ops = &qeth_l3_ethtool_ops; - card->dev->priv_flags &= ~IFF_TX_SKB_SHARING; card->dev->needed_headroom = sizeof(struct qeth_hdr) - ETH_HLEN; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; - card->dev->hw_features |= NETIF_F_SG; - card->dev->vlan_features |= NETIF_F_SG; netif_keep_dst(card->dev); if (card->dev->hw_features & NETIF_F_TSO)