[PATCH] s390: qeth bug fixes
[linux-2.6.git] / drivers / s390 / net / qeth_tso.h
index ff585ae49b6c5ed8bb13f6346a9d1776311e712c..ad33e6f466f1bd819547f687e5b9e719a6c19f5e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.5 $)
+ * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $)
  *
  * Header file for qeth TCP Segmentation Offload support.
  *
  *
  *    Author(s): Frank Pavlic <pavlic@de.ibm.com>
  *
- *    $Revision: 1.5 $  $Date: 2005/04/01 21:40:41 $
+ *    $Revision: 1.7 $  $Date: 2005/05/04 20:19:18 $
  *
  */
 #ifndef __QETH_TSO_H__
 #define __QETH_TSO_H__
 
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip6_checksum.h>
+#include "qeth.h"
+#include "qeth_mpc.h"
 
-extern int
-qeth_tso_send_packet(struct qeth_card *, struct sk_buff *,
-                    struct qeth_qdio_out_q *, int , int);
 
-struct qeth_hdr_ext_tso {
-        __u16 hdr_tot_len;
-        __u8  imb_hdr_no;
-        __u8  reserved;
-        __u8  hdr_type;
-        __u8  hdr_version;
-        __u16 hdr_len;
-        __u32 payload_len;
-        __u16 mss;
-        __u16 dg_hdr_len;
-        __u8  padding[16];
-} __attribute__ ((packed));
+static inline struct qeth_hdr_tso *
+qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
+{
+       QETH_DBF_TEXT(trace, 5, "tsoprsk");
+       return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso));
+}
+
+/**
+ * fill header for a TSO packet
+ */
+static inline void
+qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
+{
+       struct qeth_hdr_tso *hdr;
+       struct tcphdr *tcph;
+       struct iphdr *iph;
 
-struct qeth_hdr_tso {
-        struct qeth_hdr hdr;   /*hdr->hdr.l3.xxx*/
-       struct qeth_hdr_ext_tso ext;
-} __attribute__ ((packed));
+       QETH_DBF_TEXT(trace, 5, "tsofhdr");
+
+       hdr  = (struct qeth_hdr_tso *) skb->data;
+       iph  = skb->nh.iph;
+       tcph = skb->h.th;
+       /*fix header to TSO values ...*/
+       hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
+       /*set values which are fix for the first approach ...*/
+       hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
+       hdr->ext.imb_hdr_no  = 1;
+       hdr->ext.hdr_type    = 1;
+       hdr->ext.hdr_version = 1;
+       hdr->ext.hdr_len     = 28;
+       /*insert non-fix values */
+       hdr->ext.mss = skb_shinfo(skb)->tso_size;
+       hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
+       hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
+                                      sizeof(struct qeth_hdr_tso));
+}
+
+/**
+ * change some header values as requested by hardware
+ */
+static inline void
+qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
+{
+       struct iphdr *iph;
+       struct ipv6hdr *ip6h;
+       struct tcphdr *tcph;
+
+       iph  = skb->nh.iph;
+       ip6h = skb->nh.ipv6h;
+       tcph = skb->h.th;
+
+       tcph->check = 0;
+       if (skb->protocol == ETH_P_IPV6) {
+               ip6h->payload_len = 0;
+               tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+                                              0, IPPROTO_TCP, 0);
+               return;
+       }
+       /*OSA want us to set these values ...*/
+       tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                        0, IPPROTO_TCP, 0);
+       iph->tot_len = 0;
+       iph->check = 0;
+}
 
-/*some helper functions*/
 static inline int
-qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb)
+qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb,
+                       int ipv, int cast_type)
 {
-       int elements_needed = 0;
+       struct qeth_hdr_tso *hdr;
 
-        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;
+       QETH_DBF_TEXT(trace, 5, "tsoprep");
+
+       hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb);
+       if (hdr == NULL) {
+               QETH_DBF_TEXT(trace, 4, "tsoperr");
+               return -ENOMEM;
+       }
+       memset(hdr, 0, sizeof(struct qeth_hdr_tso));
+       /*fill first 32 bytes of  qdio header as used
+        *FIXME: TSO has two struct members
+        * with different names but same size
+        * */
+       qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type);
+       qeth_tso_fill_header(card, skb);
+       qeth_tso_set_tcpip_header(card, skb);
+       return 0;
 }
 
 static inline void
 __qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer,
                        int is_tso, int *next_element_to_fill)
 {
-       int length = skb->len;
        struct skb_frag_struct *frag;
        int fragno;
        unsigned long addr;
-       int element;
-       int first_lap = 1;
-
-       fragno = skb_shinfo(skb)->nr_frags; /* start with last frag */
-       element = *next_element_to_fill + fragno;
-       while (length > 0) {
-               if (fragno > 0) {
-                       frag = &skb_shinfo(skb)->frags[fragno - 1];
-                       addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
-                               frag->page_offset;
-                       buffer->element[element].addr = (char *)addr;
-                       buffer->element[element].length = frag->size;
-                       length -= frag->size;
-                       if (first_lap)
-                               buffer->element[element].flags =
-                                   SBAL_FLAGS_LAST_FRAG;
-                       else
-                               buffer->element[element].flags =
-                                   SBAL_FLAGS_MIDDLE_FRAG;
-               } else {
-                       buffer->element[element].addr = skb->data;
-                       buffer->element[element].length = length;
-                       length = 0;
-                       if (is_tso)
-                               buffer->element[element].flags =
-                                       SBAL_FLAGS_MIDDLE_FRAG;
-                       else
-                               buffer->element[element].flags =
-                                       SBAL_FLAGS_FIRST_FRAG;
-               }
-               element--;
-               fragno--;
-               first_lap = 0;
+       int element, cnt, dlen;
+       
+       fragno = skb_shinfo(skb)->nr_frags;
+       element = *next_element_to_fill;
+       dlen = 0;
+       
+       if (is_tso)
+               buffer->element[element].flags =
+                       SBAL_FLAGS_MIDDLE_FRAG;
+       else
+               buffer->element[element].flags =
+                       SBAL_FLAGS_FIRST_FRAG;
+       if ( (dlen = (skb->len - skb->data_len)) ) {
+               buffer->element[element].addr = skb->data;
+               buffer->element[element].length = dlen;
+               element++;
        }
-       *next_element_to_fill += skb_shinfo(skb)->nr_frags + 1;
+       for (cnt = 0; cnt < fragno; cnt++) {
+               frag = &skb_shinfo(skb)->frags[cnt];
+               addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
+                       frag->page_offset;
+               buffer->element[element].addr = (char *)addr;
+               buffer->element[element].length = frag->size;
+               if (cnt < (fragno - 1))
+                       buffer->element[element].flags =
+                               SBAL_FLAGS_MIDDLE_FRAG;
+               else
+                       buffer->element[element].flags =
+                               SBAL_FLAGS_LAST_FRAG;
+               element++;
+       }
+       *next_element_to_fill = element;
 }
-
 #endif /* __QETH_TSO_H__ */