qeth: l3 hw tx csum circumvent hw bug
[pandora-kernel.git] / drivers / s390 / net / qeth_l3_main.c
index a1abb37..d09b0c4 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "qeth_l3.h"
 
+
 static int qeth_l3_set_offline(struct ccwgroup_device *);
 static int qeth_l3_recover(void *);
 static int qeth_l3_stop(struct net_device *);
@@ -455,8 +456,11 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
        QETH_CARD_TEXT(card, 2, "sdiplist");
        QETH_CARD_HEX(card, 2, &card, sizeof(void *));
 
-       if (card->options.sniffer)
+       if ((card->state != CARD_STATE_UP &&
+            card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) {
                return;
+       }
+
        spin_lock_irqsave(&card->ip_lock, flags);
        tbd_list = card->ip_tbd_list;
        card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
@@ -2455,22 +2459,46 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
        return rc;
 }
 
-static void qeth_l3_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo,
-               struct qeth_arp_query_data *qdata, int entry_size,
-               int uentry_size)
+static __u32 get_arp_entry_size(struct qeth_card *card,
+                       struct qeth_arp_query_data *qdata,
+                       struct qeth_arp_entrytype *type, __u8 strip_entries)
 {
-       char *entry_ptr;
-       char *uentry_ptr;
-       int i;
+       __u32 rc;
+       __u8 is_hsi;
 
-       entry_ptr = (char *)&qdata->data;
-       uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset);
-       for (i = 0; i < qdata->no_entries; ++i) {
-               /* strip off 32 bytes "media specific information" */
-               memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32);
-               entry_ptr += entry_size;
-               uentry_ptr += uentry_size;
+       is_hsi = qdata->reply_bits == 5;
+       if (type->ip == QETHARP_IP_ADDR_V4) {
+               QETH_CARD_TEXT(card, 4, "arpev4");
+               if (strip_entries) {
+                       rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) :
+                               sizeof(struct qeth_arp_qi_entry7_short);
+               } else {
+                       rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) :
+                               sizeof(struct qeth_arp_qi_entry7);
+               }
+       } else if (type->ip == QETHARP_IP_ADDR_V6) {
+               QETH_CARD_TEXT(card, 4, "arpev6");
+               if (strip_entries) {
+                       rc = is_hsi ?
+                               sizeof(struct qeth_arp_qi_entry5_short_ipv6) :
+                               sizeof(struct qeth_arp_qi_entry7_short_ipv6);
+               } else {
+                       rc = is_hsi ?
+                               sizeof(struct qeth_arp_qi_entry5_ipv6) :
+                               sizeof(struct qeth_arp_qi_entry7_ipv6);
+               }
+       } else {
+               QETH_CARD_TEXT(card, 4, "arpinv");
+               rc = 0;
        }
+
+       return rc;
+}
+
+static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot)
+{
+       return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) ||
+               (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6);
 }
 
 static int qeth_l3_arp_query_cb(struct qeth_card *card,
@@ -2479,72 +2507,77 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
        struct qeth_ipa_cmd *cmd;
        struct qeth_arp_query_data *qdata;
        struct qeth_arp_query_info *qinfo;
-       int entry_size;
-       int uentry_size;
        int i;
+       int e;
+       int entrybytes_done;
+       int stripped_bytes;
+       __u8 do_strip_entries;
 
-       QETH_CARD_TEXT(card, 4, "arpquecb");
+       QETH_CARD_TEXT(card, 3, "arpquecb");
 
        qinfo = (struct qeth_arp_query_info *) reply->param;
        cmd = (struct qeth_ipa_cmd *) data;
+       QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);
        if (cmd->hdr.return_code) {
-               QETH_CARD_TEXT_(card, 4, "qaer1%i", cmd->hdr.return_code);
+               QETH_CARD_TEXT(card, 4, "arpcberr");
+               QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
                return 0;
        }
        if (cmd->data.setassparms.hdr.return_code) {
                cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
-               QETH_CARD_TEXT_(card, 4, "qaer2%i", cmd->hdr.return_code);
+               QETH_CARD_TEXT(card, 4, "setaperr");
+               QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
                return 0;
        }
        qdata = &cmd->data.setassparms.data.query_arp;
-       switch (qdata->reply_bits) {
-       case 5:
-               uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5);
-               if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
-                       uentry_size = sizeof(struct qeth_arp_qi_entry5_short);
-               break;
-       case 7:
-               /* fall through to default */
-       default:
-               /* tr is the same as eth -> entry7 */
-               uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7);
-               if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
-                       uentry_size = sizeof(struct qeth_arp_qi_entry7_short);
-               break;
-       }
-       /* check if there is enough room in userspace */
-       if ((qinfo->udata_len - qinfo->udata_offset) <
-                       qdata->no_entries * uentry_size){
-               QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM);
-               cmd->hdr.return_code = -ENOMEM;
-               goto out_error;
-       }
-       QETH_CARD_TEXT_(card, 4, "anore%i",
-                      cmd->data.setassparms.hdr.number_of_replies);
-       QETH_CARD_TEXT_(card, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no);
        QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
 
-       if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) {
-               /* strip off "media specific information" */
-               qeth_l3_copy_arp_entries_stripped(qinfo, qdata, entry_size,
-                                              uentry_size);
-       } else
-               /*copy entries to user buffer*/
-               memcpy(qinfo->udata + qinfo->udata_offset,
-                      (char *)&qdata->data, qdata->no_entries*uentry_size);
+       do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0;
+       stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0;
+       entrybytes_done = 0;
+       for (e = 0; e < qdata->no_entries; ++e) {
+               char *cur_entry;
+               __u32 esize;
+               struct qeth_arp_entrytype *etype;
+
+               cur_entry = &qdata->data + entrybytes_done;
+               etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type;
+               if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) {
+                       QETH_CARD_TEXT(card, 4, "pmis");
+                       QETH_CARD_TEXT_(card, 4, "%i", etype->ip);
+                       break;
+               }
+               esize = get_arp_entry_size(card, qdata, etype,
+                       do_strip_entries);
+               QETH_CARD_TEXT_(card, 5, "esz%i", esize);
+               if (!esize)
+                       break;
 
-       qinfo->no_entries += qdata->no_entries;
-       qinfo->udata_offset += (qdata->no_entries*uentry_size);
+               if ((qinfo->udata_len - qinfo->udata_offset) < esize) {
+                       QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM);
+                       cmd->hdr.return_code = -ENOMEM;
+                       goto out_error;
+               }
+
+               memcpy(qinfo->udata + qinfo->udata_offset,
+                       &qdata->data + entrybytes_done + stripped_bytes,
+                       esize);
+               entrybytes_done += esize + stripped_bytes;
+               qinfo->udata_offset += esize;
+               ++qinfo->no_entries;
+       }
        /* check if all replies received ... */
        if (cmd->data.setassparms.hdr.seq_no <
            cmd->data.setassparms.hdr.number_of_replies)
                return 1;
+       QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);
        memcpy(qinfo->udata, &qinfo->no_entries, 4);
        /* keep STRIP_ENTRIES flag so the user program can distinguish
         * stripped entries from normal ones */
        if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
                qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
        memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2);
+       QETH_CARD_TEXT_(card, 4, "rc%i", 0);
        return 0;
 out_error:
        i = 0;
@@ -2567,45 +2600,86 @@ static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card,
                                      reply_cb, reply_param);
 }
 
-static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
+static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
+       enum qeth_prot_versions prot,
+       struct qeth_arp_query_info *qinfo)
 {
        struct qeth_cmd_buffer *iob;
-       struct qeth_arp_query_info qinfo = {0, };
+       struct qeth_ipa_cmd *cmd;
        int tmp;
        int rc;
 
+       QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
+
+       iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+                       IPA_CMD_ASS_ARP_QUERY_INFO,
+                       sizeof(struct qeth_arp_query_data) - sizeof(char),
+                       prot);
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+       cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
+       cmd->data.setassparms.data.query_arp.reply_bits = 0;
+       cmd->data.setassparms.data.query_arp.no_entries = 0;
+       rc = qeth_l3_send_ipa_arp_cmd(card, iob,
+                          QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
+                          qeth_l3_arp_query_cb, (void *)qinfo);
+       if (rc) {
+               tmp = rc;
+               QETH_DBF_MESSAGE(2,
+                       "Error while querying ARP cache on %s: %s "
+                       "(0x%x/%d)\n", QETH_CARD_IFNAME(card),
+                       qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+       }
+
+       return rc;
+}
+
+static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
+{
+       struct qeth_arp_query_info qinfo = {0, };
+       int rc;
+
        QETH_CARD_TEXT(card, 3, "arpquery");
 
        if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
                               IPA_ARP_PROCESSING)) {
-               return -EOPNOTSUPP;
+               QETH_CARD_TEXT(card, 3, "arpqnsup");
+               rc = -EOPNOTSUPP;
+               goto out;
        }
        /* get size of userspace buffer and mask_bits -> 6 bytes */
-       if (copy_from_user(&qinfo, udata, 6))
-               return -EFAULT;
+       if (copy_from_user(&qinfo, udata, 6)) {
+               rc = -EFAULT;
+               goto out;
+       }
        qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
-       if (!qinfo.udata)
-               return -ENOMEM;
+       if (!qinfo.udata) {
+               rc = -ENOMEM;
+               goto out;
+       }
        qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
-       iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
-                                      IPA_CMD_ASS_ARP_QUERY_INFO,
-                                      sizeof(int), QETH_PROT_IPV4);
-
-       rc = qeth_l3_send_ipa_arp_cmd(card, iob,
-                                  QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
-                                  qeth_l3_arp_query_cb, (void *)&qinfo);
+       rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);
        if (rc) {
-               tmp = rc;
-               QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s "
-                       "(0x%x/%d)\n", QETH_CARD_IFNAME(card),
-                       qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
                if (copy_to_user(udata, qinfo.udata, 4))
                        rc = -EFAULT;
+                       goto free_and_out;
        } else {
-               if (copy_to_user(udata, qinfo.udata, qinfo.udata_len))
+#ifdef CONFIG_QETH_IPV6
+               if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) {
+                       /* fails in case of GuestLAN QDIO mode */
+                       qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6,
+                               &qinfo);
+               }
+#endif
+               if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) {
+                       QETH_CARD_TEXT(card, 4, "qactf");
                        rc = -EFAULT;
+                       goto free_and_out;
+               }
+               QETH_CARD_TEXT_(card, 4, "qacts");
        }
+free_and_out:
        kfree(qinfo.udata);
+out:
        return rc;
 }
 
@@ -2924,7 +2998,9 @@ static inline void qeth_l3_hdr_csum(struct qeth_card *card,
         */
        if (iph->protocol == IPPROTO_UDP)
                hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP;
-       hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ;
+       hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+               QETH_HDR_EXT_CSUM_HDR_REQ;
+       iph->check = 0;
        if (card->options.performance_stats)
                card->perf_stats.tx_csum++;
 }
@@ -3041,7 +3117,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                skb_pull(new_skb, ETH_HLEN);
                }
 
-               if (ipv == 6 && card->vlangrp &&
+               if (ipv != 4 && card->vlangrp &&
                                vlan_tx_tag_present(new_skb)) {
                        skb_push(new_skb, VLAN_HLEN);
                        skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4);
@@ -3166,12 +3242,14 @@ tx_drop:
        return NETDEV_TX_OK;
 }
 
-static int qeth_l3_open(struct net_device *dev)
+static int __qeth_l3_open(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
        QETH_CARD_TEXT(card, 4, "qethopen");
+       if (card->state == CARD_STATE_UP)
+               return rc;
        if (card->state != CARD_STATE_SOFTSETUP)
                return -ENODEV;
        card->data.state = CH_STATE_UP;
@@ -3186,6 +3264,18 @@ static int qeth_l3_open(struct net_device *dev)
        return rc;
 }
 
+static int qeth_l3_open(struct net_device *dev)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       QETH_CARD_TEXT(card, 5, "qethope_");
+       if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
+               QETH_CARD_TEXT(card, 3, "openREC");
+               return -ERESTARTSYS;
+       }
+       return __qeth_l3_open(dev);
+}
+
 static int qeth_l3_stop(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
@@ -3490,7 +3580,7 @@ contin:
                netif_carrier_off(card->dev);
        if (recover_flag == CARD_STATE_RECOVER) {
                if (recovery_mode)
-                       qeth_l3_open(card->dev);
+                       __qeth_l3_open(card->dev);
                else {
                        rtnl_lock();
                        dev_open(card->dev);