diff options
Diffstat (limited to 'drivers/s390/net/qeth_main.c')
-rw-r--r-- | drivers/s390/net/qeth_main.c | 145 |
1 files changed, 105 insertions, 40 deletions
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index e18bcf9fb58..208127a5033 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/qeth_main.c ($Revision: 1.210 $) + * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $) * * Linux on zSeries OSA Express and HiperSockets support * @@ -12,7 +12,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Thomas Spatzier <tspat@de.ibm.com> * - * $Revision: 1.210 $ $Date: 2005/04/18 17:27:39 $ + * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,7 +80,7 @@ qeth_eyecatcher(void) #include "qeth_eddp.h" #include "qeth_tso.h" -#define VERSION_QETH_C "$Revision: 1.210 $" +#define VERSION_QETH_C "$Revision: 1.214 $" static const char *version = "qeth S/390 OSA-Express driver"; /** @@ -158,6 +158,9 @@ qeth_irq_tasklet(unsigned long); static int qeth_set_online(struct ccwgroup_device *); +static int +__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode); + static struct qeth_ipaddr * qeth_get_addr_buffer(enum qeth_prot_versions); @@ -510,10 +513,10 @@ qeth_irq_tasklet(unsigned long data) wake_up(&card->wait_q); } -static int qeth_stop_card(struct qeth_card *); +static int qeth_stop_card(struct qeth_card *, int); static int -qeth_set_offline(struct ccwgroup_device *cgdev) +__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode) { struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; int rc = 0; @@ -523,7 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); recover_flag = card->state; - if (qeth_stop_card(card) == -ERESTARTSYS){ + if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){ PRINT_WARN("Stopping card %s interrupted by user!\n", CARD_BUS_ID(card)); return -ERESTARTSYS; @@ -540,6 +543,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev) } static int +qeth_set_offline(struct ccwgroup_device *cgdev) +{ + return __qeth_set_offline(cgdev, 0); +} + +static int qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); @@ -953,8 +962,8 @@ qeth_recover(void *ptr) PRINT_WARN("Recovery of device %s started ...\n", CARD_BUS_ID(card)); card->use_hard_stop = 1; - qeth_set_offline(card->gdev); - rc = qeth_set_online(card->gdev); + __qeth_set_offline(card->gdev,1); + rc = __qeth_set_online(card->gdev,1); if (!rc) PRINT_INFO("Device %s successfully recovered!\n", CARD_BUS_ID(card)); @@ -3786,16 +3795,12 @@ static inline int qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, struct qeth_hdr **hdr, int ipv) { - int rc = 0; #ifdef CONFIG_QETH_VLAN u16 *tag; #endif QETH_DBF_TEXT(trace, 6, "prepskb"); - rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); - if (rc) - return rc; #ifdef CONFIG_QETH_VLAN if (card->vlangrp && vlan_tx_tag_present(*skb) && ((ipv == 6) || card->options.layer2) ) { @@ -3977,25 +3982,28 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, static inline void __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, - int *next_element_to_fill) + int is_tso, int *next_element_to_fill) { int length = skb->len; int length_here; int element; char *data; - int first_lap = 1; + int first_lap ; element = *next_element_to_fill; data = skb->data; + first_lap = (is_tso == 0 ? 1 : 0); + while (length > 0) { /* length_here is the remaining amount of data in this page */ length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); if (length < length_here) length_here = length; + buffer->element[element].addr = data; buffer->element[element].length = length_here; length -= length_here; - if (!length){ + if (!length) { if (first_lap) buffer->element[element].flags = 0; else @@ -4022,17 +4030,35 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct sk_buff *skb) { struct qdio_buffer *buffer; - int flush_cnt = 0; + struct qeth_hdr_tso *hdr; + int flush_cnt = 0, hdr_len, large_send = 0; QETH_DBF_TEXT(trace, 6, "qdfillbf"); + buffer = buf->buffer; atomic_inc(&skb->users); skb_queue_tail(&buf->skb_list, skb); + + hdr = (struct qeth_hdr_tso *) skb->data; + /*check first on TSO ....*/ + if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) { + int element = buf->next_element_to_fill; + + hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; + /*fill first buffer entry only with header information */ + buffer->element[element].addr = skb->data; + buffer->element[element].length = hdr_len; + buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; + buf->next_element_to_fill++; + skb->data += hdr_len; + skb->len -= hdr_len; + large_send = 1; + } if (skb_shinfo(skb)->nr_frags == 0) - __qeth_fill_buffer(skb, buffer, + __qeth_fill_buffer(skb, buffer, large_send, (int *)&buf->next_element_to_fill); else - __qeth_fill_buffer_frag(skb, buffer, 0, + __qeth_fill_buffer_frag(skb, buffer, large_send, (int *)&buf->next_element_to_fill); if (!queue->do_pack) { @@ -4225,6 +4251,25 @@ out: } static inline int +qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) +{ + int elements_needed = 0; + + if (skb_shinfo(skb)->nr_frags > 0) { + elements_needed = (skb_shinfo(skb)->nr_frags + 1); + } + if (elements_needed == 0 ) + elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + + skb->len) >> PAGE_SHIFT); + if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ + PRINT_ERR("qeth_do_send_packet: invalid size of " + "IP packet. Discarded."); + return 0; + } + return elements_needed; +} + +static inline int qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) { int ipv = 0; @@ -4266,19 +4311,25 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) if (skb_shinfo(skb)->tso_size) large_send = card->options.large_send; - if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ - QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); - return rc; - } /*are we able to do TSO ? If so ,prepare and send it from here */ if ((large_send == QETH_LARGE_SEND_TSO) && (cast_type == RTN_UNSPEC)) { - rc = qeth_tso_send_packet(card, skb, queue, - ipv, cast_type); - goto do_statistics; + rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); + if (rc) { + card->stats.tx_dropped++; + card->stats.tx_errors++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + elements_needed++; + } else { + if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { + QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); + return rc; + } + qeth_fill_header(card, hdr, skb, ipv, cast_type); } - qeth_fill_header(card, hdr, skb, ipv, cast_type); if (large_send == QETH_LARGE_SEND_EDDP) { ctx = qeth_eddp_create_context(card, skb, hdr); if (ctx == NULL) { @@ -4286,7 +4337,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) return -EINVAL; } } else { - elements_needed = qeth_get_elements_no(card,(void*) hdr, skb); + elements_needed += qeth_get_elements_no(card,(void*) hdr, skb); if (!elements_needed) return -EINVAL; } @@ -4297,12 +4348,12 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) else rc = qeth_do_send_packet_fast(card, queue, skb, hdr, elements_needed, ctx); -do_statistics: if (!rc){ card->stats.tx_packets++; card->stats.tx_bytes += skb->len; #ifdef CONFIG_QETH_PERF_STATS - if (skb_shinfo(skb)->tso_size) { + if (skb_shinfo(skb)->tso_size && + !(large_send == QETH_LARGE_SEND_NO)) { card->perf_stats.large_send_bytes += skb->len; card->perf_stats.large_send_cnt++; } @@ -7199,7 +7250,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) } static int -qeth_stop_card(struct qeth_card *card) +qeth_stop_card(struct qeth_card *card, int recovery_mode) { int rc = 0; @@ -7212,9 +7263,13 @@ qeth_stop_card(struct qeth_card *card) if (card->read.state == CH_STATE_UP && card->write.state == CH_STATE_UP && (card->state == CARD_STATE_UP)) { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); + if(recovery_mode) { + qeth_stop(card->dev); + } else { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } if (!card->use_hard_stop) { __u8 *mac = &card->dev->dev_addr[0]; rc = qeth_layer2_send_delmac(card, mac); @@ -7386,13 +7441,17 @@ qeth_register_netdev(struct qeth_card *card) } static void -qeth_start_again(struct qeth_card *card) +qeth_start_again(struct qeth_card *card, int recovery_mode) { QETH_DBF_TEXT(setup ,2, "startag"); - rtnl_lock(); - dev_open(card->dev); - rtnl_unlock(); + if(recovery_mode) { + qeth_open(card->dev); + } else { + rtnl_lock(); + dev_open(card->dev); + rtnl_unlock(); + } /* this also sets saved unicast addresses */ qeth_set_multicast_list(card->dev); } @@ -7449,7 +7508,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card) static int -qeth_set_online(struct ccwgroup_device *gdev) +__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode) { struct qeth_card *card = gdev->dev.driver_data; int rc = 0; @@ -7509,12 +7568,12 @@ qeth_set_online(struct ccwgroup_device *gdev) * we can also use this state for recovery purposes*/ qeth_set_allowed_threads(card, 0xffffffff, 0); if (recover_flag == CARD_STATE_RECOVER) - qeth_start_again(card); + qeth_start_again(card, recovery_mode); qeth_notify_processes(); return 0; out_remove: card->use_hard_stop = 1; - qeth_stop_card(card); + qeth_stop_card(card, 0); ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); @@ -7525,6 +7584,12 @@ out_remove: return -ENODEV; } +static int +qeth_set_online(struct ccwgroup_device *gdev) +{ + return __qeth_set_online(gdev, 0); +} + static struct ccw_device_id qeth_ids[] = { {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, |