vserver 2.0 rc7
[linux-2.6.git] / drivers / s390 / net / qeth_main.c
index 535f46b..208127a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.191 $)
+ * 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.191 $        $Date: 2005/01/31 13:13:57 $
+ *    $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
@@ -61,6 +61,7 @@ qeth_eyecatcher(void)
 #include <linux/reboot.h>
 #include <linux/mii.h>
 #include <linux/rcupdate.h>
+#include <linux/ethtool.h>
 
 #include <net/arp.h>
 #include <net/ip.h>
@@ -76,8 +77,10 @@ qeth_eyecatcher(void)
 #include "qeth.h"
 #include "qeth_mpc.h"
 #include "qeth_fs.h"
+#include "qeth_eddp.h"
+#include "qeth_tso.h"
 
-#define VERSION_QETH_C "$Revision: 1.191 $"
+#define VERSION_QETH_C "$Revision: 1.214 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -87,7 +90,7 @@ static debug_info_t *qeth_dbf_setup = NULL;
 static debug_info_t *qeth_dbf_data = NULL;
 static debug_info_t *qeth_dbf_misc = NULL;
 static debug_info_t *qeth_dbf_control = NULL;
-static debug_info_t *qeth_dbf_trace = NULL;
+debug_info_t *qeth_dbf_trace = NULL;
 static debug_info_t *qeth_dbf_sense = NULL;
 static debug_info_t *qeth_dbf_qerr = NULL;
 
@@ -155,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);
 
@@ -507,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;
@@ -520,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;
@@ -536,6 +542,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev)
        return 0;
 }
 
+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);
 
@@ -950,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));
@@ -1070,6 +1082,35 @@ qeth_setup_card(struct qeth_card *card)
        return 0;
 }
 
+static int
+is_1920_device (struct qeth_card *card)
+{
+       int single_queue = 0;
+       struct ccw_device *ccwdev;
+       struct channelPath_dsc {
+               u8 flags;
+               u8 lsn;
+               u8 desc;
+               u8 chpid;
+               u8 swla;
+               u8 zeroes;
+               u8 chla;
+               u8 chpp;
+       } *chp_dsc;
+
+       QETH_DBF_TEXT(setup, 2, "chk_1920");
+
+       ccwdev = card->data.ccwdev;
+       chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0);
+       if (chp_dsc != NULL) {
+               /* CHPP field bit 6 == 1 -> single queue */
+               single_queue = ((chp_dsc->chpp & 0x02) == 0x02);
+               kfree(chp_dsc);
+       }
+       QETH_DBF_TEXT_(setup, 2, "rc:%x", single_queue);
+       return single_queue;
+}
+
 static int
 qeth_determine_card_type(struct qeth_card *card)
 {
@@ -1081,7 +1122,14 @@ qeth_determine_card_type(struct qeth_card *card)
                if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
                    (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
                        card->info.type = known_devices[i][4];
-                       card->qdio.no_out_queues = known_devices[i][8];
+                       if (is_1920_device(card)) {
+                               PRINT_INFO("Priority Queueing not able "
+                                          "due to hardware limitations!\n");
+                               card->qdio.no_out_queues = 1;
+                               card->qdio.default_out_queue = 0;
+                       } else {
+                               card->qdio.no_out_queues = known_devices[i][8];
+                       }
                        card->info.is_multicast_different = known_devices[i][9];
                        return 0;
                }
@@ -1112,6 +1160,10 @@ qeth_probe_device(struct ccwgroup_device *gdev)
                QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM);
                return -ENOMEM;
        }
+       card->read.ccwdev  = gdev->cdev[0];
+       card->write.ccwdev = gdev->cdev[1];
+       card->data.ccwdev  = gdev->cdev[2];
+
        if ((rc = qeth_setup_card(card))){
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
                put_device(dev);
@@ -1130,9 +1182,6 @@ qeth_probe_device(struct ccwgroup_device *gdev)
                qeth_free_card(card);
                return rc;
        }
-       card->read.ccwdev  = gdev->cdev[0];
-       card->write.ccwdev = gdev->cdev[1];
-       card->data.ccwdev  = gdev->cdev[2];
        if ((rc = qeth_determine_card_type(card))){
                PRINT_WARN("%s: not a valid card type\n", __func__);
                QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
@@ -1631,10 +1680,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                QETH_DBF_TEXT(trace,3, "irla");
                                break;
                        case IPA_CMD_UNREGISTER_LOCAL_ADDR:
-                               PRINT_WARN("probably problem on %s: "
-                                          "received IPA command 0x%X\n",
-                                          QETH_CARD_IFNAME(card),
-                                          cmd->hdr.command);
+                               QETH_DBF_TEXT(trace,3, "urla");
                                break;
                        default:
                                PRINT_WARN("Received data is IPA "
@@ -1714,7 +1760,7 @@ qeth_send_control_data_cb(struct qeth_channel *channel,
                                                        (unsigned long)iob);
                        }
                        if (cmd)
-                               reply->rc = (s16) cmd->hdr.return_code;
+                               reply->rc = (u16) cmd->hdr.return_code;
                        else if (iob->rc)
                                reply->rc = iob->rc;
                        if (keep_reply) {
@@ -2115,9 +2161,15 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,
        if (!skb_len)
                return NULL;
        if (card->options.fake_ll){
-               if (!(skb = qeth_get_skb(skb_len + QETH_FAKE_LL_LEN)))
-                       goto no_mem;
-               skb_pull(skb, QETH_FAKE_LL_LEN);
+               if(card->dev->type == ARPHRD_IEEE802_TR){
+                       if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR)))
+                               goto no_mem;
+                       skb_reserve(skb,QETH_FAKE_LL_LEN_TR);
+               } else {
+                       if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH)))
+                               goto no_mem;
+                       skb_reserve(skb,QETH_FAKE_LL_LEN_ETH);
+               }
        } else if (!(skb = qeth_get_skb(skb_len)))
                goto no_mem;
        data_ptr = element->addr + offset;
@@ -2192,14 +2244,68 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
 }
 
 static inline void
-qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
+qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb,
+                        struct qeth_hdr *hdr)
+{
+       struct trh_hdr *fake_hdr;
+       struct trllc *fake_llc;
+       struct iphdr *ip_hdr;
+
+       QETH_DBF_TEXT(trace,5,"skbfktr");
+       skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR;
+       /* this is a fake ethernet header */
+       fake_hdr = (struct trh_hdr *) skb->mac.raw;
+
+       /* the destination MAC address */
+       switch (skb->pkt_type){
+       case PACKET_MULTICAST:
+               switch (skb->protocol){
+#ifdef CONFIG_QETH_IPV6
+               case __constant_htons(ETH_P_IPV6):
+                       ndisc_mc_map((struct in6_addr *)
+                                    skb->data + QETH_FAKE_LL_V6_ADDR_POS,
+                                    fake_hdr->daddr, card->dev, 0);
+                       break;
+#endif /* CONFIG_QETH_IPV6 */
+               case __constant_htons(ETH_P_IP):
+                       ip_hdr = (struct iphdr *)skb->data;
+                       ip_tr_mc_map(ip_hdr->daddr, fake_hdr->daddr);
+                       break;
+               default:
+                       memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
+               }
+               break;
+       case PACKET_BROADCAST:
+               memset(fake_hdr->daddr, 0xff, TR_ALEN);
+               break;
+       default:
+               memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
+       }
+       /* the source MAC address */
+       if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+               memcpy(fake_hdr->saddr, &hdr->hdr.l3.dest_addr[2], TR_ALEN);
+       else
+               memset(fake_hdr->saddr, 0, TR_ALEN);
+       fake_hdr->rcf=0;
+       fake_llc = (struct trllc*)&(fake_hdr->rcf);
+       fake_llc->dsap = EXTENDED_SAP;
+       fake_llc->ssap = EXTENDED_SAP;
+       fake_llc->llc  = UI_CMD;
+       fake_llc->protid[0] = 0;
+       fake_llc->protid[1] = 0;
+       fake_llc->protid[2] = 0;
+       fake_llc->ethertype = ETH_P_IP;
+}
+
+static inline void
+qeth_rebuild_skb_fake_ll_eth(struct qeth_card *card, struct sk_buff *skb,
                         struct qeth_hdr *hdr)
 {
        struct ethhdr *fake_hdr;
        struct iphdr *ip_hdr;
 
-       QETH_DBF_TEXT(trace,5,"skbfake");
-       skb->mac.raw = skb->data - QETH_FAKE_LL_LEN;
+       QETH_DBF_TEXT(trace,5,"skbfketh");
+       skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH;
        /* this is a fake ethernet header */
        fake_hdr = (struct ethhdr *) skb->mac.raw;
 
@@ -2216,10 +2322,7 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
 #endif /* CONFIG_QETH_IPV6 */
                case __constant_htons(ETH_P_IP):
                        ip_hdr = (struct iphdr *)skb->data;
-                       if (card->dev->type == ARPHRD_IEEE802_TR)
-                               ip_tr_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
-                       else
-                               ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
+                       ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
                        break;
                default:
                        memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
@@ -2240,6 +2343,16 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
        fake_hdr->h_proto = skb->protocol;
 }
 
+static inline void
+qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
+                       struct qeth_hdr *hdr)
+{
+       if (card->dev->type == ARPHRD_IEEE802_TR)
+               qeth_rebuild_skb_fake_ll_tr(card, skb, hdr);
+       else
+               qeth_rebuild_skb_fake_ll_eth(card, skb, hdr);
+}
+
 static inline void
 qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
                      struct qeth_hdr *hdr)
@@ -2263,19 +2376,25 @@ qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
                        struct qeth_hdr *hdr)
 {
        unsigned short vlan_id = 0;
+#ifdef CONFIG_QETH_VLAN
+       struct vlan_hdr *vhdr;
+#endif
 
        skb->pkt_type = PACKET_HOST;
+       skb->protocol = qeth_type_trans(skb, skb->dev);
        if (card->options.checksum_type == NO_CHECKSUMMING)
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        else
                skb->ip_summed = CHECKSUM_NONE;
 #ifdef CONFIG_QETH_VLAN
        if (hdr->hdr.l2.flags[2] & (QETH_LAYER2_FLAG_VLAN)) {
+               vhdr = (struct vlan_hdr *) skb->data;
+               skb->protocol =
+                       __constant_htons(vhdr->h_vlan_encapsulated_proto);
                vlan_id = hdr->hdr.l2.vlan_id;
                skb_pull(skb, VLAN_HLEN);
        }
 #endif
-       skb->protocol = qeth_type_trans(skb, skb->dev);
        return vlan_id;
 }
 
@@ -2421,14 +2540,15 @@ qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
        if (buf->buffer->element[0].flags & 0x40)
                atomic_dec(&queue->set_pci_flags_count);
 
+       while ((skb = skb_dequeue(&buf->skb_list))){
+               atomic_dec(&skb->users);
+               dev_kfree_skb_any(skb);
+       }
+       qeth_eddp_buf_release_contexts(buf);
        for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i){
                buf->buffer->element[i].length = 0;
                buf->buffer->element[i].addr = NULL;
                buf->buffer->element[i].flags = 0;
-               while ((skb = skb_dequeue(&buf->skb_list))){
-                       atomic_dec(&skb->users);
-                       dev_kfree_skb_any(skb);
-               }
        }
        buf->next_element_to_fill = 0;
        atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
@@ -2578,7 +2698,7 @@ qeth_handle_send_error(struct qeth_card *card,
        return QETH_SEND_ERROR_LINK_FAILURE;
 }
 
-static inline void
+void
 qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                   int index, int count)
 {
@@ -2619,9 +2739,6 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                                atomic_inc(&queue->set_pci_flags_count);
                                buf->buffer->element[0].flags |= 0x40;
                        }
-#ifdef CONFIG_QETH_PERF_STATS
-                       queue->card->perf_stats.bufs_sent_pack++;
-#endif
                }
        }
 
@@ -2743,6 +2860,7 @@ qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
 {
        int index;
        int flush_cnt = 0;
+       int q_was_packing = 0;
 
        /*
         * check if weed have to switch to non-packing mode or if
@@ -2757,20 +2875,22 @@ qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
                         * do_send_packet. So, we check if there is a
                         * packing buffer to be flushed here.
                         */
-                       /* TODO: try if we get a performance improvement
-                        * by calling netif_stop_queue here */
-                       /* save start index for flushing */
+                       netif_stop_queue(queue->card->dev);
                        index = queue->next_buf_to_fill;
+                       q_was_packing = queue->do_pack;
                        flush_cnt += qeth_switch_to_nonpacking_if_needed(queue);
                        if (!flush_cnt &&
                            !atomic_read(&queue->set_pci_flags_count))
                                flush_cnt +=
                                        qeth_flush_buffers_on_no_pci(queue);
-                       /* were done with updating critical queue members */
-                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
-                       /* flushing can be done outside the lock */
+#ifdef CONFIG_QETH_PERF_STATS
+                       if (q_was_packing)
+                               queue->card->perf_stats.bufs_sent_pack +=
+                                       flush_cnt;
+#endif
                        if (flush_cnt)
                                qeth_flush_buffers(queue, 1, index, flush_cnt);
+                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                }
        }
 }
@@ -2800,7 +2920,6 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
                        return;
                }
        }
-
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_handler_cnt++;
        card->perf_stats.outbound_handler_start_time = qeth_get_micros();
@@ -2821,24 +2940,16 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        if (card->info.type != QETH_CARD_TYPE_IQD)
                qeth_check_outbound_queue(queue);
 
-       netif_wake_queue(card->dev);
+       netif_wake_queue(queue->card->dev);
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_handler_time += qeth_get_micros() -
                card->perf_stats.outbound_handler_start_time;
 #endif
 }
 
-static char*
-qeth_create_qib_param_field(struct qeth_card *card)
+static void
+qeth_create_qib_param_field(struct qeth_card *card, char *param_field)
 {
-       char *param_field;
-
-       param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
-                             GFP_KERNEL);
-       if (!param_field)
-               return NULL;
-
-       memset(param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
 
        param_field[0] = _ascebc['P'];
        param_field[1] = _ascebc['C'];
@@ -2847,8 +2958,18 @@ qeth_create_qib_param_field(struct qeth_card *card)
        *((unsigned int *) (&param_field[4])) = QETH_PCI_THRESHOLD_A(card);
        *((unsigned int *) (&param_field[8])) = QETH_PCI_THRESHOLD_B(card);
        *((unsigned int *) (&param_field[12])) = QETH_PCI_TIMER_VALUE(card);
+}
 
-       return param_field;
+static void
+qeth_create_qib_param_field_blkt(struct qeth_card *card, char *param_field)
+{
+       param_field[16] = _ascebc['B'];
+        param_field[17] = _ascebc['L'];
+        param_field[18] = _ascebc['K'];
+        param_field[19] = _ascebc['T'];
+        *((unsigned int *) (&param_field[20])) = card->info.blkt.time_total;
+        *((unsigned int *) (&param_field[24])) = card->info.blkt.inter_packet;
+        *((unsigned int *) (&param_field[28])) = card->info.blkt.inter_packet_jumbo;
 }
 
 static void
@@ -2898,7 +3019,7 @@ qeth_alloc_buffer_pool(struct qeth_card *card)
        void *ptr;
        int i, j;
 
-       QETH_DBF_TEXT(trace,5,"clwkpool");
+       QETH_DBF_TEXT(trace,5,"alocpool");
        for (i = 0; i < card->qdio.init_pool.buf_count; ++i){
                pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL);
                if (!pool_entry){
@@ -2986,12 +3107,13 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
                QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *));
                memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
                card->qdio.out_qs[i]->queue_no = i;
-               /* give inbound qeth_qdio_buffers their qdio_buffers */
+               /* give outbound qeth_qdio_buffers their qdio_buffers */
                for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
                        card->qdio.out_qs[i]->bufs[j].buffer =
                                &card->qdio.out_qs[i]->qdio_bufs[j];
                        skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j].
                                            skb_list);
+                       INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list);
                }
        }
        card->qdio.state = QETH_QDIO_ALLOCATED;
@@ -3108,10 +3230,17 @@ qeth_qdio_establish(struct qeth_card *card)
        int rc;
 
        QETH_DBF_TEXT(setup, 2, "qdioest");
-       qib_param_field = qeth_create_qib_param_field(card);
-       if (!qib_param_field)
+
+       qib_param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
+                             GFP_KERNEL);
+       if (!qib_param_field)
                return -ENOMEM;
 
+       memset(qib_param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
+
+       qeth_create_qib_param_field(card, qib_param_field);
+       qeth_create_qib_param_field_blkt(card, qib_param_field);
+
        in_sbal_ptrs = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),
                               GFP_KERNEL);
        if (!in_sbal_ptrs) {
@@ -3387,18 +3516,25 @@ qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
                     unsigned short type, void *daddr, void *saddr,
                     unsigned len)
 {
-       struct ethhdr *hdr;
-       struct qeth_card *card;
+       if(dev->type == ARPHRD_IEEE802_TR){
+               struct trh_hdr *hdr;
+               hdr = (struct trh_hdr *)skb_push(skb, QETH_FAKE_LL_LEN_TR);
+               memcpy(hdr->saddr, dev->dev_addr, TR_ALEN);
+               memcpy(hdr->daddr, "FAKELL", TR_ALEN);
+               return QETH_FAKE_LL_LEN_TR;
 
-       card = (struct qeth_card *)dev->priv;
-        hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN);
-       memcpy(hdr->h_source, card->dev->dev_addr, ETH_ALEN);
-        memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
-        if (type != ETH_P_802_3)
-                hdr->h_proto = htons(type);
-        else
-                hdr->h_proto = htons(len);
-       return QETH_FAKE_LL_LEN;
+       } else {
+               struct ethhdr *hdr;
+               hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN_ETH);
+               memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN);
+               memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
+               if (type != ETH_P_802_3)
+                       hdr->h_proto = htons(type);
+               else
+                       hdr->h_proto = htons(len);
+               return QETH_FAKE_LL_LEN_ETH;
+
+       }
 }
 
 static inline int
@@ -3430,32 +3566,19 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        card->perf_stats.outbound_cnt++;
        card->perf_stats.outbound_start_time = qeth_get_micros();
 #endif
-       if (dev->hard_header == qeth_fake_header) {
-               if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) {
-                        card->stats.tx_dropped++;
-                        dev_kfree_skb_irq(skb);
-                        return 0;
-                }
-                skb_pull(skb, QETH_FAKE_LL_LEN);
-       }
-       /*
-        * We only call netif_stop_queue in case of errors. Since we've
-        * got our own synchronization on queues we can keep the stack's
-        * queue running.
-        */
-       if ((rc = qeth_send_packet(card, skb))){
+       netif_stop_queue(dev);
+       if ((rc = qeth_send_packet(card, skb))) {
                if (rc == -EBUSY) {
-                       netif_stop_queue(dev);
-                       rc = NETDEV_TX_BUSY;
+                       return NETDEV_TX_BUSY;
                } else {
                        card->stats.tx_errors++;
                        card->stats.tx_dropped++;
                        dev_kfree_skb_any(skb);
-                       /* set to OK; otherwise ksoftirqd goes to 100% */
+                       /*set to OK; otherwise ksoftirqd goes to 100% */
                        rc = NETDEV_TX_OK;
                }
        }
-
+       netif_wake_queue(dev);
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_time += qeth_get_micros() -
                card->perf_stats.outbound_start_time;
@@ -3649,8 +3772,9 @@ qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
                        /* TODO: IPv6!!! */
                }
                return card->qdio.default_out_queue;
+       case 1: /* fallthrough for single-out-queue 1920-device */
        default:
-               return 0;
+               return card->qdio.default_out_queue;
        }
 }
 
@@ -3671,22 +3795,12 @@ static inline int
 qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                 struct qeth_hdr **hdr, int ipv)
 {
-       struct sk_buff *new_skb;
 #ifdef CONFIG_QETH_VLAN
        u16 *tag;
 #endif
 
        QETH_DBF_TEXT(trace, 6, "prepskb");
-       if (skb_headroom(*skb) < sizeof(struct qeth_hdr)){
-               new_skb = skb_realloc_headroom(*skb, sizeof(struct qeth_hdr));
-               if (!new_skb) {
-                       PRINT_ERR("qeth_prepare_skb: could "
-                                 "not realloc headroom for qeth_hdr "
-                                 "on interface %s", QETH_CARD_IFNAME(card));
-                       return -ENOMEM;
-               }
-               *skb = new_skb;
-       }
+
 #ifdef CONFIG_QETH_VLAN
        if (card->vlangrp && vlan_tx_tag_present(*skb) &&
            ((ipv == 6) || card->options.layer2) ) {
@@ -3708,20 +3822,10 @@ qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                *(tag + 1) = htons(vlan_tx_tag_get(*skb));
        }
 #endif
-       *hdr = (struct qeth_hdr *) skb_push(*skb, sizeof(struct qeth_hdr));
-       /*
-        * sanity check, the Linux memory allocation scheme should
-        * never present us cases like this one (the 32bytes header plus
-        * the first 40 bytes of the paket cross a 4k boundary)
-        */
-       if ((((unsigned long) *hdr) & (~(PAGE_SIZE - 1))) !=
-           (((unsigned long) *hdr + sizeof(struct qeth_hdr) +
-             QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
-               PRINT_ERR("qeth_prepare_skb: misaligned "
-                         "packet on interface %s. Discarded.",
-                         QETH_CARD_IFNAME(card));
+       *hdr = (struct qeth_hdr *)
+               qeth_push_skb(card, skb, sizeof(struct qeth_hdr));
+       if (hdr == NULL)
                return -EINVAL;
-       }
        return 0;
 }
 
@@ -3813,12 +3917,13 @@ qeth_layer2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
 #endif
 }
 
-static inline void
+void
 qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                struct sk_buff *skb, int ipv, int cast_type)
 {
        QETH_DBF_TEXT(trace, 6, "fillhdr");
 
+       memset(hdr, 0, sizeof(struct qeth_hdr));
        if (card->options.layer2) {
                qeth_layer2_fill_header(card, hdr, skb, cast_type);
                return;
@@ -3858,9 +3963,15 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16);
                }
        } else { /* passthrough */
-               if (!memcmp(skb->data + sizeof(struct qeth_hdr),
+                if((skb->dev->type == ARPHRD_IEEE802_TR) &&
+                   !memcmp(skb->data + sizeof(struct qeth_hdr) + 
+                   sizeof(__u16), skb->dev->broadcast, 6)) {
+                       hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+                                               QETH_HDR_PASSTHRU;
+               } else if (!memcmp(skb->data + sizeof(struct qeth_hdr),
                            skb->dev->broadcast, 6)) {   /* broadcast? */
-                       hdr->hdr.l3.flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
+                       hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+                                               QETH_HDR_PASSTHRU;
                } else {
                        hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
                                QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
@@ -3869,30 +3980,30 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
        }
 }
 
-static inline int
-qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf,
-                char *data, struct sk_buff *skb)
+static inline void
+__qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
+                  int is_tso, int *next_element_to_fill)
 {
-       struct qdio_buffer *buffer;
        int length = skb->len;
        int length_here;
        int element;
-       int first_lap = 1;
+       char *data;
+       int first_lap ;
+
+       element = *next_element_to_fill;
+       data = skb->data;
+       first_lap = (is_tso == 0 ? 1 : 0);
 
-       QETH_DBF_TEXT(trace, 6, "qdfillbf");
-       buffer = buf->buffer;
-       atomic_inc(&skb->users);
-       skb_queue_tail(&buf->skb_list, skb);
-       element = buf->next_element_to_fill;
        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
@@ -3910,11 +4021,51 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
                element++;
                first_lap = 0;
        }
-       buf->next_element_to_fill = element;
+       *next_element_to_fill = element;
+}
+
+static inline int
+qeth_fill_buffer(struct qeth_qdio_out_q *queue,
+                struct qeth_qdio_out_buffer *buf,
+                struct sk_buff *skb)
+{
+       struct qdio_buffer *buffer;
+       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, large_send,
+                                  (int *)&buf->next_element_to_fill);
+       else
+               __qeth_fill_buffer_frag(skb, buffer, large_send,
+                                       (int *)&buf->next_element_to_fill);
+
        if (!queue->do_pack) {
                QETH_DBF_TEXT(trace, 6, "fillbfnp");
                /* set state to PRIMED -> will be flushed */
                atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+               flush_cnt = 1;
        } else {
                QETH_DBF_TEXT(trace, 6, "fillbfpa");
 #ifdef CONFIG_QETH_PERF_STATS
@@ -3927,17 +4078,21 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
                         * -> will be flushed
                         */
                        atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+                       flush_cnt = 1;
                }
        }
-       return 0;
+       return flush_cnt;
 }
 
 static inline int
 qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                         struct sk_buff *skb, struct qeth_hdr *hdr,
-                        int elements_needed)
+                        int elements_needed,
+                        struct qeth_eddp_context *ctx)
 {
        struct qeth_qdio_out_buffer *buffer;
+       int buffers_needed = 0;
+       int flush_cnt = 0;
        int index;
 
        QETH_DBF_TEXT(trace, 6, "dosndpfa");
@@ -3958,22 +4113,42 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                return -EBUSY;
        }
-       queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-                                 QDIO_MAX_BUFFERS_PER_Q;
+       if (ctx == NULL)
+               queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+                                         QDIO_MAX_BUFFERS_PER_Q;
+       else {
+               buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx);
+               if (buffers_needed < 0) {
+                       card->stats.tx_dropped++;
+                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+                       return -EBUSY;
+               }
+               queue->next_buf_to_fill =
+                       (queue->next_buf_to_fill + buffers_needed) %
+                       QDIO_MAX_BUFFERS_PER_Q;
+       }
        atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
-       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
-       qeth_flush_buffers(queue, 0, index, 1);
+       if (ctx == NULL) {
+               qeth_fill_buffer(queue, buffer, skb);
+               qeth_flush_buffers(queue, 0, index, 1);
+       } else {
+               flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
+               WARN_ON(buffers_needed != flush_cnt);
+               qeth_flush_buffers(queue, 0, index, flush_cnt);
+       }
        return 0;
 }
 
 static inline int
 qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                    struct sk_buff *skb, struct qeth_hdr *hdr,
-                   int elements_needed)
+                   int elements_needed, struct qeth_eddp_context *ctx)
 {
        struct qeth_qdio_out_buffer *buffer;
        int start_index;
        int flush_count = 0;
+       int do_pack = 0;
+       int tmp;
        int rc = 0;
 
        QETH_DBF_TEXT(trace, 6, "dosndpkt");
@@ -3996,34 +4171,56 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
        /* check if we need to switch packing state of this queue */
        qeth_switch_to_packing_if_needed(queue);
        if (queue->do_pack){
-               /* does packet fit in current buffer? */
-               if((QETH_MAX_BUFFER_ELEMENTS(card) -
-                   buffer->next_element_to_fill) < elements_needed){
-                       /* ... no -> set state PRIMED */
-                       atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
-                       flush_count++;
-                       queue->next_buf_to_fill =
-                               (queue->next_buf_to_fill + 1) %
-                               QDIO_MAX_BUFFERS_PER_Q;
-                       buffer = &queue->bufs[queue->next_buf_to_fill];
-                       /* we did a step forward, so check buffer state again */
-                       if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+               do_pack = 1;
+               if (ctx == NULL) {
+                       /* does packet fit in current buffer? */
+                       if((QETH_MAX_BUFFER_ELEMENTS(card) -
+                           buffer->next_element_to_fill) < elements_needed){
+                               /* ... no -> set state PRIMED */
+                               atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
+                               flush_count++;
+                               queue->next_buf_to_fill =
+                                       (queue->next_buf_to_fill + 1) %
+                                       QDIO_MAX_BUFFERS_PER_Q;
+                               buffer = &queue->bufs[queue->next_buf_to_fill];
+                               /* we did a step forward, so check buffer state
+                                * again */
+                               if (atomic_read(&buffer->state) !=
+                                               QETH_QDIO_BUF_EMPTY){
+                                       card->stats.tx_dropped++;
+                                       qeth_flush_buffers(queue, 0, start_index, flush_count);
+                                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+                                       return -EBUSY;
+                               }
+                       }
+               } else {
+                       /* check if we have enough elements (including following
+                        * free buffers) to handle eddp context */
+                       if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
+                               printk("eddp tx_dropped 1\n");
                                card->stats.tx_dropped++;
-                               /* return EBUSY because we sent old packet, not
-                                * the current one */
                                rc = -EBUSY;
-                               atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                                goto out;
                        }
                }
        }
-       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
-       if (atomic_read(&buffer->state) == QETH_QDIO_BUF_PRIMED){
-               /* next time fill the next buffer */
-               flush_count++;
-               queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-                       QDIO_MAX_BUFFERS_PER_Q;
+       if (ctx == NULL)
+               tmp = qeth_fill_buffer(queue, buffer, skb);
+       else {
+               tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill);
+               if (tmp < 0) {
+                       printk("eddp tx_dropped 2\n");
+                       card->stats.tx_dropped++;
+                       rc = - EBUSY;
+                       goto out;
+               }
        }
+       queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
+                                 QDIO_MAX_BUFFERS_PER_Q;
+       flush_count += tmp;
+out:
+       if (flush_count)
+               qeth_flush_buffers(queue, 0, start_index, flush_count);
        /*
         * queue->state will go from LOCKED -> UNLOCKED or from
         * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
@@ -4031,6 +4228,8 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
         * In that case we will enter this loop
         */
        while (atomic_dec_return(&queue->state)){
+               flush_count = 0;
+               start_index = queue->next_buf_to_fill;
                /* check if we can go back to non-packing state */
                flush_count += qeth_switch_to_nonpacking_if_needed(queue);
                /*
@@ -4039,15 +4238,37 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                 */
                if (!flush_count && !atomic_read(&queue->set_pci_flags_count))
                        flush_count += qeth_flush_buffers_on_no_pci(queue);
+               if (flush_count)
+                       qeth_flush_buffers(queue, 0, start_index, flush_count);
        }
        /* at this point the queue is UNLOCKED again */
-out:
-       if (flush_count)
-               qeth_flush_buffers(queue, 0, start_index, flush_count);
+#ifdef CONFIG_QETH_PERF_STATS
+       if (do_pack)
+               queue->card->perf_stats.bufs_sent_pack += flush_count;
+#endif /* CONFIG_QETH_PERF_STATS */
 
        return rc;
 }
 
+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)
 {
@@ -4055,40 +4276,102 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
        int cast_type;
        struct qeth_qdio_out_q *queue;
        struct qeth_hdr *hdr;
-       int elements_needed;
+       int elements_needed = 0;
+       enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
+       struct qeth_eddp_context *ctx = NULL;
        int rc;
 
        QETH_DBF_TEXT(trace, 6, "sendpkt");
 
-       if (!card->options.layer2)
+       if (!card->options.layer2) {
                ipv = qeth_get_ip_version(skb);
+               if ((card->dev->hard_header == qeth_fake_header) && ipv) {
+                               if ((skb = qeth_pskb_unshare(skb,GFP_ATOMIC)) == NULL) {
+                               card->stats.tx_dropped++;
+                               dev_kfree_skb_irq(skb);
+                               return 0;
+                       }
+                       if(card->dev->type == ARPHRD_IEEE802_TR){
+                               skb_pull(skb, QETH_FAKE_LL_LEN_TR);
+                       } else {
+                               skb_pull(skb, QETH_FAKE_LL_LEN_ETH);
+                       }
+               }
+       }
        cast_type = qeth_get_cast_type(card, skb);
+       if ((cast_type == RTN_BROADCAST) && (card->info.broadcast_capable == 0)){
+               card->stats.tx_dropped++;
+               card->stats.tx_errors++;
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
        queue = card->qdio.out_qs
                [qeth_get_priority_queue(card, skb, ipv, cast_type)];
 
-       if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
-               QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
-               return rc;
+       if (skb_shinfo(skb)->tso_size)
+               large_send = card->options.large_send;
+
+       /*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_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);
-       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 -EINVAL;
+
+       if (large_send == QETH_LARGE_SEND_EDDP) {
+               ctx = qeth_eddp_create_context(card, skb, hdr);
+               if (ctx == NULL) {
+                       PRINT_WARN("could not create eddp context\n");
+                       return -EINVAL;
+               }
+       } else {
+               elements_needed += qeth_get_elements_no(card,(void*) hdr, skb);
+               if (!elements_needed)
+                       return -EINVAL;
        }
 
        if (card->info.type != QETH_CARD_TYPE_IQD)
                rc = qeth_do_send_packet(card, queue, skb, hdr,
-                                        elements_needed);
+                                        elements_needed, ctx);
        else
                rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
-                                             elements_needed);
-
+                                             elements_needed, ctx);
        if (!rc){
                card->stats.tx_packets++;
                card->stats.tx_bytes += skb->len;
+#ifdef CONFIG_QETH_PERF_STATS
+               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++;
+               }
+               if (skb_shinfo(skb)->nr_frags > 0){
+                       card->perf_stats.sg_skbs_sent++;
+                       /* nr_frags + skb->data */
+                       card->perf_stats.sg_frags_sent +=
+                               skb_shinfo(skb)->nr_frags + 1;
+               }
+#endif /* CONFIG_QETH_PERF_STATS */
+       }
+       if (ctx != NULL) {
+               /* drop creator's reference */
+               qeth_eddp_put_context(ctx);
+               /* free skb; it's not referenced by a buffer */
+               if (rc == 0)
+                       dev_kfree_skb_any(skb);
+
        }
        return rc;
 }
@@ -4943,6 +5226,7 @@ out:
 static void
 qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
 {
+#ifdef CONFIG_QETH_IPV6
        struct inet6_dev *in6_dev;
        struct inet6_ifaddr *ifa;
        struct qeth_ipaddr *addr;
@@ -4965,6 +5249,7 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
                }
        }
        in6_dev_put(in6_dev);
+#endif /* CONFIG_QETH_IPV6 */
 }
 
 static void
@@ -5242,7 +5527,8 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
        struct inet6_dev *in6_dev;
 
        QETH_DBF_TEXT(trace,4,"chkmcv6");
-       if (!qeth_is_supported(card, IPA_IPV6))
+       if ((card->options.layer2 == 0) &&
+           (!qeth_is_supported(card, IPA_IPV6)) )
                return ;
 
        in6_dev = in6_dev_get(card->dev);
@@ -5563,7 +5849,7 @@ static int
 qeth_layer3_register_addr_entry(struct qeth_card *card,
                                struct qeth_ipaddr *addr)
 {
-       //char buf[50];
+       char buf[50];
        int rc;
        int cnt = 3;
 
@@ -5589,12 +5875,9 @@ qeth_layer3_register_addr_entry(struct qeth_card *card,
        } while ((--cnt > 0) && rc);
        if (rc){
                QETH_DBF_TEXT(trace, 2, "FAILED");
-               /* TODO: re-activate this warning as soon as we have a
-                * clean mirco code
                qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
-               PRINT_WARN("Could not register IP address %s (rc=%x)\n",
-                          buf, rc);
-               */
+               PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n",
+                          buf, rc, rc);
        }
        return rc;
 }
@@ -5652,6 +5935,111 @@ qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
        return qeth_layer3_deregister_addr_entry(card, addr);
 }
 
+static u32
+qeth_ethtool_get_tx_csum(struct net_device *dev)
+{
+       /* We may need to say that we support tx csum offload if
+        * we do EDDP or TSO. There are discussions going on to
+        * enforce rules in the stack and in ethtool that make
+        * SG and TSO depend on HW_CSUM. At the moment there are
+        * no such rules....
+        * If we say yes here, we have to checksum outbound packets
+        * any time. */
+       return 0;
+}
+
+static int
+qeth_ethtool_set_tx_csum(struct net_device *dev, u32 data)
+{
+       return -EINVAL;
+}
+
+static u32
+qeth_ethtool_get_rx_csum(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return (card->options.checksum_type == HW_CHECKSUMMING);
+}
+
+static int
+qeth_ethtool_set_rx_csum(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if ((card->state != CARD_STATE_DOWN) &&
+           (card->state != CARD_STATE_RECOVER))
+               return -EPERM;
+       if (data)
+               card->options.checksum_type = HW_CHECKSUMMING;
+       else
+               card->options.checksum_type = SW_CHECKSUMMING;
+       return 0;
+}
+
+static u32
+qeth_ethtool_get_sg(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
+               (dev->features & NETIF_F_SG));
+}
+
+static int
+qeth_ethtool_set_sg(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if (data) {
+               if (card->options.large_send != QETH_LARGE_SEND_NO)
+                       dev->features |= NETIF_F_SG;
+               else {
+                       dev->features &= ~NETIF_F_SG;
+                       return -EINVAL;
+               }
+       } else
+               dev->features &= ~NETIF_F_SG;
+       return 0;
+}
+
+static u32
+qeth_ethtool_get_tso(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
+               (dev->features & NETIF_F_TSO));
+}
+
+static int
+qeth_ethtool_set_tso(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if (data) {
+               if (card->options.large_send != QETH_LARGE_SEND_NO)
+                       dev->features |= NETIF_F_TSO;
+               else {
+                       dev->features &= ~NETIF_F_TSO;
+                       return -EINVAL;
+               }
+       } else
+               dev->features &= ~NETIF_F_TSO;
+       return 0;
+}
+
+static struct ethtool_ops qeth_ethtool_ops = {
+       .get_tx_csum = qeth_ethtool_get_tx_csum,
+       .set_tx_csum = qeth_ethtool_set_tx_csum,
+       .get_rx_csum = qeth_ethtool_get_rx_csum,
+       .set_rx_csum = qeth_ethtool_set_rx_csum,
+       .get_sg      = qeth_ethtool_get_sg,
+       .set_sg      = qeth_ethtool_set_sg,
+       .get_tso     = qeth_ethtool_get_tso,
+       .set_tso     = qeth_ethtool_set_tso,
+};
+
 static int
 qeth_netdev_init(struct net_device *dev)
 {
@@ -5701,6 +6089,8 @@ qeth_netdev_init(struct net_device *dev)
        dev->addr_len = OSA_ADDR_LEN;
        dev->mtu = card->info.initial_mtu;
 
+       SET_ETHTOOL_OPS(dev, &qeth_ethtool_ops);
+
        SET_MODULE_OWNER(dev);
        return 0;
 }
@@ -6092,6 +6482,9 @@ qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply,
                card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
 #endif
        }
+       QETH_DBF_TEXT(setup, 2, "suppenbl");
+       QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_supported);
+       QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_enabled);
        return 0;
 }
 
@@ -6492,26 +6885,33 @@ qeth_start_ipa_checksum(struct qeth_card *card)
        return rc;
 }
 
-/*
-static inline void
-qeth_print_ipassist_status(struct qeth_card *card)
+static int
+qeth_start_ipa_tso(struct qeth_card *card)
 {
-       char buf[255];
-       int offset = 0;
+       int rc;
+
+       QETH_DBF_TEXT(trace,3,"sttso");
 
-       offset += sprintf(buf, "IPAssist options of %s: ", card->info.if_name);
-       if (qeth_is_enabled(card, IPA_ARP_PROCESSING))
-               offset += sprintf(buf+offset, "ARP ");
-       if (qeth_is_enabled(card, IPA_IP_FRAGMENTATION))
-               offset += sprintf(buf+offset, "IP_FRAG");
-       if (qeth_is_enabled(card, IPA_SOURCE_MAC))
-               offset += sprintf(buf+offset, "SRC_MAC");
-       if (qeth_is_enabled(card, IPA_FULL_VLAN))
-               offset += sprintf(buf+offset, "VLAN");
-       if (qeth_is_enabled(card, IPA_VLAN_PRIO))
-               offset += sprintf(buf+offset, "VLAN_PRIO");
+       if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
+               PRINT_WARN("Outbound TSO not supported on %s\n",
+                          QETH_CARD_IFNAME(card));
+               rc = -EOPNOTSUPP;
+       } else {
+               rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+                                                 IPA_CMD_ASS_START,0);
+               if (rc)
+                       PRINT_WARN("Could not start outbound TSO "
+                                  "assist on %s: rc=%i\n",
+                                  QETH_CARD_IFNAME(card), rc);
+               else
+                       PRINT_INFO("Outbound TSO enabled\n");
+       }
+       if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)){
+               card->options.large_send = QETH_LARGE_SEND_NO;
+               card->dev->features &= ~ (NETIF_F_TSO | NETIF_F_SG);
+       }
+       return rc;
 }
-*/
 
 static int
 qeth_start_ipassists(struct qeth_card *card)
@@ -6525,6 +6925,7 @@ qeth_start_ipassists(struct qeth_card *card)
        qeth_start_ipa_ipv6(card);              /* go on*/
        qeth_start_ipa_broadcast(card);         /* go on*/
        qeth_start_ipa_checksum(card);          /* go on*/
+       qeth_start_ipa_tso(card);               /* go on*/
        return 0;
 }
 
@@ -6636,6 +7037,40 @@ qeth_setrouting_v6(struct qeth_card *card)
        return rc;
 }
 
+int
+qeth_set_large_send(struct qeth_card *card)
+{
+       int rc = 0;
+
+       if (card->dev == NULL)
+               return 0;
+
+       netif_stop_queue(card->dev);
+       switch (card->options.large_send) {
+       case QETH_LARGE_SEND_EDDP:
+               card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+               break;
+       case QETH_LARGE_SEND_TSO:
+               if (qeth_is_supported(card, IPA_OUTBOUND_TSO)){
+                       card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+               } else {
+                       PRINT_WARN("TSO not supported on %s. "
+                                  "large_send set to 'no'.\n",
+                                  card->dev->name);
+                       card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+                       card->options.large_send = QETH_LARGE_SEND_NO;
+                       rc = -EOPNOTSUPP;
+               }
+               break;
+       default: /* includes QETH_LARGE_SEND_NO */
+               card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+               break;
+       }
+
+       netif_wake_queue(card->dev);
+       return rc;
+}
+
 /*
  * softsetup card: init IPA stuff
  */
@@ -6673,6 +7108,12 @@ qeth_softsetup_card(struct qeth_card *card)
 #endif
                goto out;
        }
+       if ((card->options.large_send == QETH_LARGE_SEND_EDDP) ||
+           (card->options.large_send == QETH_LARGE_SEND_TSO))
+               card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+       else
+               card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+
        if ((rc = qeth_setadapter_parms(card)))
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
        if ((rc = qeth_start_ipassists(card)))
@@ -6809,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;
 
@@ -6822,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);
@@ -6996,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);
 }
@@ -7059,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;
@@ -7117,17 +7566,14 @@ qeth_set_online(struct ccwgroup_device *gdev)
        }
 /*maybe it was set offline without ifconfig down
  * we can also use this state for recovery purposes*/
-       if (card->options.layer2)
-               qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 0);
-       else
-               qeth_set_allowed_threads(card, 0xffffffff, 0);
+       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));
@@ -7138,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},