s390: scatter-gather for inbound traffic in qeth driver
[safe/jmp/linux-2.6] / drivers / s390 / net / qeth_eddp.c
index f94f1f2..70108fb 100644 (file)
@@ -1,6 +1,5 @@
 /*
- *
- * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.13 $)
+ * linux/drivers/s390/net/qeth_eddp.c
  *
  * Enhanced Device Driver Packing (EDDP) support for the qeth driver.
  *
@@ -8,10 +7,7 @@
  *
  *    Author(s): Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.13 $         $Date: 2005/05/04 20:19:18 $
- *
  */
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/ip.h>
 #include <linux/inetdevice.h>
@@ -53,7 +49,7 @@ qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *queue,
        return buffers_needed;
 }
 
-static inline void
+static void
 qeth_eddp_free_context(struct qeth_eddp_context *ctx)
 {
        int i;
@@ -62,8 +58,7 @@ qeth_eddp_free_context(struct qeth_eddp_context *ctx)
        for (i = 0; i < ctx->num_pages; ++i)
                free_page((unsigned long)ctx->pages[i]);
        kfree(ctx->pages);
-       if (ctx->elements != NULL)
-               kfree(ctx->elements);
+       kfree(ctx->elements);
        kfree(ctx);
 }
 
@@ -85,7 +80,7 @@ void
 qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf)
 {
        struct qeth_eddp_context_reference *ref;
-       
+
        QETH_DBF_TEXT(trace, 6, "eddprctx");
        while (!list_empty(&buf->ctx_list)){
                ref = list_entry(buf->ctx_list.next,
@@ -96,7 +91,7 @@ qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf)
        }
 }
 
-static inline int
+static int
 qeth_eddp_buf_ref_context(struct qeth_qdio_out_buffer *buf,
                          struct qeth_eddp_context *ctx)
 {
@@ -139,7 +134,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
                                           "buffer!\n");
                                goto out;
                        }
-               }               
+               }
                /* check if the whole next skb fits into current buffer */
                if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) -
                                        buf->next_element_to_fill)
@@ -152,7 +147,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
                         * and increment ctx's refcnt */
                        must_refcnt = 1;
                        continue;
-               }       
+               }
                if (must_refcnt){
                        must_refcnt = 0;
                        if (qeth_eddp_buf_ref_context(buf, ctx)){
@@ -184,9 +179,8 @@ out_check:
                        flush_cnt++;
                }
        } else {
-#ifdef CONFIG_QETH_PERF_STATS
-               queue->card->perf_stats.skbs_sent_pack++;
-#endif
+               if (queue->card->options.performance_stats)
+                       queue->card->perf_stats.skbs_sent_pack++;
                QETH_DBF_TEXT(trace, 6, "fillbfpa");
                if (buf->next_element_to_fill >=
                                QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
@@ -202,7 +196,7 @@ out:
        return flush_cnt;
 }
 
-static inline void
+static void
 qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx,
                              struct qeth_eddp_data *eddp, int data_len)
 {
@@ -262,18 +256,19 @@ qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx,
        ctx->offset += eddp->thl;
 }
 
-static inline void
+static void
 qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len,
-                       u32 *hcsum)
+                       __wsum *hcsum)
 {
        struct skb_frag_struct *frag;
        int left_in_frag;
        int copy_len;
        u8 *src;
-       
+
        QETH_DBF_TEXT(trace, 5, "eddpcdtc");
        if (skb_shinfo(eddp->skb)->nr_frags == 0) {
-               memcpy(dst, eddp->skb->data + eddp->skb_offset, len);
+               skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset,
+                                                dst, len);
                *hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len,
                                      *hcsum);
                eddp->skb_offset += len;
@@ -308,10 +303,10 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len,
        }
 }
 
-static inline void
+static void
 qeth_eddp_create_segment_data_tcp(struct qeth_eddp_context *ctx,
                                  struct qeth_eddp_data *eddp, int data_len,
-                                 u32 hcsum)
+                                 __wsum hcsum)
 {
        u8 *page;
        int page_remainder;
@@ -355,10 +350,10 @@ qeth_eddp_create_segment_data_tcp(struct qeth_eddp_context *ctx,
        ((struct tcphdr *)eddp->th_in_ctx)->check = csum_fold(hcsum);
 }
 
-static inline u32
+static __wsum
 qeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp, int data_len)
 {
-       u32 phcsum; /* pseudo header checksum */
+       __wsum phcsum; /* pseudo header checksum */
 
        QETH_DBF_TEXT(trace, 5, "eddpckt4");
        eddp->th.tcp.h.check = 0;
@@ -369,11 +364,11 @@ qeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp, int data_len)
        return csum_partial((u8 *)&eddp->th, eddp->thl, phcsum);
 }
 
-static inline u32
+static __wsum
 qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp, int data_len)
 {
-       u32 proto;
-       u32 phcsum; /* pseudo header checksum */
+       __be32 proto;
+       __wsum phcsum; /* pseudo header checksum */
 
        QETH_DBF_TEXT(trace, 5, "eddpckt6");
        eddp->th.tcp.h.check = 0;
@@ -387,15 +382,14 @@ qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp, int data_len)
        return phcsum;
 }
 
-static inline struct qeth_eddp_data *
+static struct qeth_eddp_data *
 qeth_eddp_create_eddp_data(struct qeth_hdr *qh, u8 *nh, u8 nhl, u8 *th, u8 thl)
 {
        struct qeth_eddp_data *eddp;
 
        QETH_DBF_TEXT(trace, 5, "eddpcrda");
-       eddp = kmalloc(sizeof(struct qeth_eddp_data), GFP_ATOMIC);
+       eddp = kzalloc(sizeof(struct qeth_eddp_data), GFP_ATOMIC);
        if (eddp){
-               memset(eddp, 0, sizeof(struct qeth_eddp_data));
                eddp->nhl = nhl;
                eddp->thl = thl;
                memcpy(&eddp->qh, qh, sizeof(struct qeth_hdr));
@@ -406,25 +400,31 @@ qeth_eddp_create_eddp_data(struct qeth_hdr *qh, u8 *nh, u8 nhl, u8 *th, u8 thl)
        return eddp;
 }
 
-static inline void
+static void
 __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
                             struct qeth_eddp_data *eddp)
 {
        struct tcphdr *tcph;
        int data_len;
-       u32 hcsum;
-       
+       __wsum hcsum;
+
        QETH_DBF_TEXT(trace, 5, "eddpftcp");
        eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl;
-       tcph = eddp->skb->h.th;
+       if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
+               eddp->skb_offset += sizeof(struct ethhdr);
+#ifdef CONFIG_QETH_VLAN
+               if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
+                       eddp->skb_offset += VLAN_HLEN;
+#endif /* CONFIG_QETH_VLAN */
+       }
+       tcph = tcp_hdr(eddp->skb);
        while (eddp->skb_offset < eddp->skb->len) {
-               data_len = min((int)skb_shinfo(eddp->skb)->tso_size,
+               data_len = min((int)skb_shinfo(eddp->skb)->gso_size,
                               (int)(eddp->skb->len - eddp->skb_offset));
                /* prepare qdio hdr */
                if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){
                        eddp->qh.hdr.l2.pkt_length = data_len + ETH_HLEN +
-                                                    eddp->nhl + eddp->thl -
-                                                    sizeof(struct qeth_hdr);
+                                                    eddp->nhl + eddp->thl;
 #ifdef CONFIG_QETH_VLAN
                        if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
                                eddp->qh.hdr.l2.pkt_length += VLAN_HLEN;
@@ -433,22 +433,22 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
                        eddp->qh.hdr.l3.length = data_len + eddp->nhl +
                                                 eddp->thl;
                /* prepare ip hdr */
-               if (eddp->skb->protocol == ETH_P_IP){
-                       eddp->nh.ip4.h.tot_len = data_len + eddp->nhl +
-                                                eddp->thl;
+               if (eddp->skb->protocol == htons(ETH_P_IP)){
+                       eddp->nh.ip4.h.tot_len = htons(data_len + eddp->nhl +
+                                                eddp->thl);
                        eddp->nh.ip4.h.check = 0;
                        eddp->nh.ip4.h.check =
                                ip_fast_csum((u8 *)&eddp->nh.ip4.h,
                                                eddp->nh.ip4.h.ihl);
                } else
-                       eddp->nh.ip6.h.payload_len = data_len + eddp->thl;
+                       eddp->nh.ip6.h.payload_len = htons(data_len + eddp->thl);
                /* prepare tcp hdr */
                if (data_len == (eddp->skb->len - eddp->skb_offset)){
                        /* last segment -> set FIN and PSH flags */
                        eddp->th.tcp.h.fin = tcph->fin;
                        eddp->th.tcp.h.psh = tcph->psh;
                }
-               if (eddp->skb->protocol == ETH_P_IP)
+               if (eddp->skb->protocol == htons(ETH_P_IP))
                        hcsum = qeth_eddp_check_tcp4_hdr(eddp, data_len);
                else
                        hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len);
@@ -458,38 +458,43 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
                if (eddp->skb_offset >= eddp->skb->len)
                        break;
                /* prepare headers for next round */
-               if (eddp->skb->protocol == ETH_P_IP)
-                       eddp->nh.ip4.h.id++;
-               eddp->th.tcp.h.seq += data_len;
+               if (eddp->skb->protocol == htons(ETH_P_IP))
+                       eddp->nh.ip4.h.id = htons(ntohs(eddp->nh.ip4.h.id) + 1);
+               eddp->th.tcp.h.seq = htonl(ntohl(eddp->th.tcp.h.seq) + data_len);
        }
 }
-                          
-static inline int
+
+static int
 qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
                           struct sk_buff *skb, struct qeth_hdr *qhdr)
 {
        struct qeth_eddp_data *eddp = NULL;
-       
+
        QETH_DBF_TEXT(trace, 5, "eddpficx");
        /* create our segmentation headers and copy original headers */
-       if (skb->protocol == ETH_P_IP)
-               eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph,
-                               skb->nh.iph->ihl*4,
-                               (u8 *)skb->h.th, skb->h.th->doff*4);
+       if (skb->protocol == htons(ETH_P_IP))
+               eddp = qeth_eddp_create_eddp_data(qhdr,
+                                                 skb_network_header(skb),
+                                                 ip_hdrlen(skb),
+                                                 skb_transport_header(skb),
+                                                 tcp_hdrlen(skb));
        else
-               eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h,
-                               sizeof(struct ipv6hdr),
-                               (u8 *)skb->h.th, skb->h.th->doff*4);
+               eddp = qeth_eddp_create_eddp_data(qhdr,
+                                                 skb_network_header(skb),
+                                                 sizeof(struct ipv6hdr),
+                                                 skb_transport_header(skb),
+                                                 tcp_hdrlen(skb));
 
        if (eddp == NULL) {
                QETH_DBF_TEXT(trace, 2, "eddpfcnm");
                return -ENOMEM;
        }
        if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
+               skb_set_mac_header(skb, sizeof(struct qeth_hdr));
                memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);
 #ifdef CONFIG_QETH_VLAN
                if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) {
-                       eddp->vlan[0] = __constant_htons(skb->protocol);
+                       eddp->vlan[0] = skb->protocol;
                        eddp->vlan[1] = htons(vlan_tx_tag_get(skb));
                }
 #endif /* CONFIG_QETH_VLAN */
@@ -504,31 +509,31 @@ qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
        return 0;
 }
 
-static inline void
+static void
 qeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb,
                         int hdr_len)
 {
        int skbs_per_page;
-       
+
        QETH_DBF_TEXT(trace, 5, "eddpcanp");
        /* can we put multiple skbs in one page? */
-       skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len);
+       skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->gso_size + hdr_len);
        if (skbs_per_page > 1){
-               ctx->num_pages = (skb_shinfo(skb)->tso_segs + 1) /
+               ctx->num_pages = (skb_shinfo(skb)->gso_segs + 1) /
                                 skbs_per_page + 1;
                ctx->elements_per_skb = 1;
        } else {
                /* no -> how many elements per skb? */
-               ctx->elements_per_skb = (skb_shinfo(skb)->tso_size + hdr_len +
+               ctx->elements_per_skb = (skb_shinfo(skb)->gso_size + hdr_len +
                                     PAGE_SIZE) >> PAGE_SHIFT;
                ctx->num_pages = ctx->elements_per_skb *
-                                (skb_shinfo(skb)->tso_segs + 1);
+                                (skb_shinfo(skb)->gso_segs + 1);
        }
        ctx->num_elements = ctx->elements_per_skb *
-                           (skb_shinfo(skb)->tso_segs + 1);
+                           (skb_shinfo(skb)->gso_segs + 1);
 }
 
-static inline struct qeth_eddp_context *
+static struct qeth_eddp_context *
 qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
                                 int hdr_len)
 {
@@ -538,12 +543,11 @@ qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
 
        QETH_DBF_TEXT(trace, 5, "creddpcg");
        /* create the context and allocate pages */
-       ctx = kmalloc(sizeof(struct qeth_eddp_context), GFP_ATOMIC);
+       ctx = kzalloc(sizeof(struct qeth_eddp_context), GFP_ATOMIC);
        if (ctx == NULL){
                QETH_DBF_TEXT(trace, 2, "ceddpcn1");
                return NULL;
        }
-       memset(ctx, 0, sizeof(struct qeth_eddp_context));
        ctx->type = QETH_LARGE_SEND_EDDP;
        qeth_eddp_calc_num_pages(ctx, skb, hdr_len);
        if (ctx->elements_per_skb > QETH_MAX_BUFFER_ELEMENTS(card)){
@@ -551,13 +555,12 @@ qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
                kfree(ctx);
                return NULL;
        }
-       ctx->pages = kmalloc(ctx->num_pages * sizeof(u8 *), GFP_ATOMIC);
+       ctx->pages = kcalloc(ctx->num_pages, sizeof(u8 *), GFP_ATOMIC);
        if (ctx->pages == NULL){
                QETH_DBF_TEXT(trace, 2, "ceddpcn2");
                kfree(ctx);
                return NULL;
        }
-       memset(ctx->pages, 0, ctx->num_pages * sizeof(u8 *));
        for (i = 0; i < ctx->num_pages; ++i){
                addr = (u8 *)__get_free_page(GFP_ATOMIC);
                if (addr == NULL){
@@ -569,36 +572,35 @@ qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
                memset(addr, 0, PAGE_SIZE);
                ctx->pages[i] = addr;
        }
-       ctx->elements = kmalloc(ctx->num_elements *
+       ctx->elements = kcalloc(ctx->num_elements,
                                sizeof(struct qeth_eddp_element), GFP_ATOMIC);
        if (ctx->elements == NULL){
                QETH_DBF_TEXT(trace, 2, "ceddpcn4");
                qeth_eddp_free_context(ctx);
                return NULL;
        }
-       memset(ctx->elements, 0,
-              ctx->num_elements * sizeof(struct qeth_eddp_element));
        /* reset num_elements; will be incremented again in fill_buffer to
         * reflect number of actually used elements */
        ctx->num_elements = 0;
        return ctx;
 }
 
-static inline struct qeth_eddp_context *
+static struct qeth_eddp_context *
 qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb,
                             struct qeth_hdr *qhdr)
 {
        struct qeth_eddp_context *ctx = NULL;
-       
+
        QETH_DBF_TEXT(trace, 5, "creddpct");
-       if (skb->protocol == ETH_P_IP)
+       if (skb->protocol == htons(ETH_P_IP))
                ctx = qeth_eddp_create_context_generic(card, skb,
-                       sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 +
-                       skb->h.th->doff*4);
-       else if (skb->protocol == ETH_P_IPV6)
+                                                      (sizeof(struct qeth_hdr) +
+                                                       ip_hdrlen(skb) +
+                                                       tcp_hdrlen(skb)));
+       else if (skb->protocol == htons(ETH_P_IPV6))
                ctx = qeth_eddp_create_context_generic(card, skb,
                        sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) +
-                       skb->h.th->doff*4);
+                       tcp_hdrlen(skb));
        else
                QETH_DBF_TEXT(trace, 2, "cetcpinv");
 
@@ -617,10 +619,10 @@ qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb,
 
 struct qeth_eddp_context *
 qeth_eddp_create_context(struct qeth_card *card, struct sk_buff *skb,
-                        struct qeth_hdr *qhdr)
+                        struct qeth_hdr *qhdr, unsigned char sk_protocol)
 {
        QETH_DBF_TEXT(trace, 5, "creddpc");
-       switch (skb->sk->sk_protocol){
+       switch (sk_protocol) {
        case IPPROTO_TCP:
                return qeth_eddp_create_context_tcp(card, skb, qhdr);
        default:
@@ -628,5 +630,3 @@ qeth_eddp_create_context(struct qeth_card *card, struct sk_buff *skb,
        }
        return NULL;
 }
-
-