diff options
author | Frank Pavlic <pavlic@de.ibm.com> | 2005-05-12 14:39:09 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-05-15 18:06:17 -0400 |
commit | 05e08a2a297371564020f76d1bf8b3a931d5e1ed (patch) | |
tree | 0aac8cafdec747a792c46dff1b934db642b0a054 /drivers | |
parent | 9a4558193662e933588ee53e0202c103a68c9366 (diff) |
[PATCH] s390: qeth bug fixes
[patch 10/10] s390: qeth bug fixes.
From: Frank Pavlic <pavlic@de.ibm.com>
qeth network driver related changes:
- due to OSA hardware changes in TCP Segmentation Offload
support we are able now to pack TSO packets too.
This fits perfectly in design of qeth buffer handling and
sending data respectively.
- remove skb_realloc_headroom from the sending path since
hard_header_len value provides enough headroom now.
- device recovery behaviour improvement
- bug fixed in Enhanced Device Driver Packing functionality
Signed-off-by: Frank Pavlic <pavlic@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth.h | 41 | ||||
-rw-r--r-- | drivers/s390/net/qeth_eddp.c | 40 | ||||
-rw-r--r-- | drivers/s390/net/qeth_main.c | 145 | ||||
-rw-r--r-- | drivers/s390/net/qeth_tso.c | 256 | ||||
-rw-r--r-- | drivers/s390/net/qeth_tso.h | 193 |
6 files changed, 273 insertions, 404 deletions
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 85b590c5701d..90d4d0ef3dd4 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile | |||
@@ -10,6 +10,6 @@ obj-$(CONFIG_SMSGIUCV) += smsgiucv.o | |||
10 | obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o | 10 | obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o |
11 | obj-$(CONFIG_LCS) += lcs.o cu3088.o | 11 | obj-$(CONFIG_LCS) += lcs.o cu3088.o |
12 | obj-$(CONFIG_CLAW) += claw.o cu3088.o | 12 | obj-$(CONFIG_CLAW) += claw.o cu3088.o |
13 | qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o qeth_tso.o | 13 | qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o |
14 | qeth-$(CONFIG_PROC_FS) += qeth_proc.o | 14 | qeth-$(CONFIG_PROC_FS) += qeth_proc.o |
15 | obj-$(CONFIG_QETH) += qeth.o | 15 | obj-$(CONFIG_QETH) += qeth.o |
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index d13c105f74e6..a755b57db46b 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | #include "qeth_mpc.h" | 25 | #include "qeth_mpc.h" |
26 | 26 | ||
27 | #define VERSION_QETH_H "$Revision: 1.137 $" | 27 | #define VERSION_QETH_H "$Revision: 1.139 $" |
28 | 28 | ||
29 | #ifdef CONFIG_QETH_IPV6 | 29 | #ifdef CONFIG_QETH_IPV6 |
30 | #define QETH_VERSION_IPV6 ":IPv6" | 30 | #define QETH_VERSION_IPV6 ":IPv6" |
@@ -370,6 +370,25 @@ struct qeth_hdr { | |||
370 | } hdr; | 370 | } hdr; |
371 | } __attribute__ ((packed)); | 371 | } __attribute__ ((packed)); |
372 | 372 | ||
373 | /*TCP Segmentation Offload header*/ | ||
374 | struct qeth_hdr_ext_tso { | ||
375 | __u16 hdr_tot_len; | ||
376 | __u8 imb_hdr_no; | ||
377 | __u8 reserved; | ||
378 | __u8 hdr_type; | ||
379 | __u8 hdr_version; | ||
380 | __u16 hdr_len; | ||
381 | __u32 payload_len; | ||
382 | __u16 mss; | ||
383 | __u16 dg_hdr_len; | ||
384 | __u8 padding[16]; | ||
385 | } __attribute__ ((packed)); | ||
386 | |||
387 | struct qeth_hdr_tso { | ||
388 | struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/ | ||
389 | struct qeth_hdr_ext_tso ext; | ||
390 | } __attribute__ ((packed)); | ||
391 | |||
373 | 392 | ||
374 | /* flags for qeth_hdr.flags */ | 393 | /* flags for qeth_hdr.flags */ |
375 | #define QETH_HDR_PASSTHRU 0x10 | 394 | #define QETH_HDR_PASSTHRU 0x10 |
@@ -867,16 +886,6 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) | |||
867 | return hdr; | 886 | return hdr; |
868 | } | 887 | } |
869 | 888 | ||
870 | static inline int | ||
871 | qeth_get_skb_data_len(struct sk_buff *skb) | ||
872 | { | ||
873 | int len = skb->len; | ||
874 | int i; | ||
875 | |||
876 | for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) | ||
877 | len -= skb_shinfo(skb)->frags[i].size; | ||
878 | return len; | ||
879 | } | ||
880 | 889 | ||
881 | inline static int | 890 | inline static int |
882 | qeth_get_hlen(__u8 link_type) | 891 | qeth_get_hlen(__u8 link_type) |
@@ -885,19 +894,19 @@ qeth_get_hlen(__u8 link_type) | |||
885 | switch (link_type) { | 894 | switch (link_type) { |
886 | case QETH_LINK_TYPE_HSTR: | 895 | case QETH_LINK_TYPE_HSTR: |
887 | case QETH_LINK_TYPE_LANE_TR: | 896 | case QETH_LINK_TYPE_LANE_TR: |
888 | return sizeof(struct qeth_hdr) + TR_HLEN; | 897 | return sizeof(struct qeth_hdr_tso) + TR_HLEN; |
889 | default: | 898 | default: |
890 | #ifdef CONFIG_QETH_VLAN | 899 | #ifdef CONFIG_QETH_VLAN |
891 | return sizeof(struct qeth_hdr) + VLAN_ETH_HLEN; | 900 | return sizeof(struct qeth_hdr_tso) + VLAN_ETH_HLEN; |
892 | #else | 901 | #else |
893 | return sizeof(struct qeth_hdr) + ETH_HLEN; | 902 | return sizeof(struct qeth_hdr_tso) + ETH_HLEN; |
894 | #endif | 903 | #endif |
895 | } | 904 | } |
896 | #else /* CONFIG_QETH_IPV6 */ | 905 | #else /* CONFIG_QETH_IPV6 */ |
897 | #ifdef CONFIG_QETH_VLAN | 906 | #ifdef CONFIG_QETH_VLAN |
898 | return sizeof(struct qeth_hdr) + VLAN_HLEN; | 907 | return sizeof(struct qeth_hdr_tso) + VLAN_HLEN; |
899 | #else | 908 | #else |
900 | return sizeof(struct qeth_hdr); | 909 | return sizeof(struct qeth_hdr_tso); |
901 | #endif | 910 | #endif |
902 | #endif /* CONFIG_QETH_IPV6 */ | 911 | #endif /* CONFIG_QETH_IPV6 */ |
903 | } | 912 | } |
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c index 45aa4a962daf..f94f1f25eec6 100644 --- a/drivers/s390/net/qeth_eddp.c +++ b/drivers/s390/net/qeth_eddp.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * | 2 | * |
3 | * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.12 $) | 3 | * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.13 $) |
4 | * | 4 | * |
5 | * Enhanced Device Driver Packing (EDDP) support for the qeth driver. | 5 | * Enhanced Device Driver Packing (EDDP) support for the qeth driver. |
6 | * | 6 | * |
@@ -8,7 +8,7 @@ | |||
8 | * | 8 | * |
9 | * Author(s): Thomas Spatzier <tspat@de.ibm.com> | 9 | * Author(s): Thomas Spatzier <tspat@de.ibm.com> |
10 | * | 10 | * |
11 | * $Revision: 1.12 $ $Date: 2005/04/01 21:40:40 $ | 11 | * $Revision: 1.13 $ $Date: 2005/05/04 20:19:18 $ |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | #include <linux/config.h> | 14 | #include <linux/config.h> |
@@ -85,7 +85,7 @@ void | |||
85 | qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf) | 85 | qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf) |
86 | { | 86 | { |
87 | struct qeth_eddp_context_reference *ref; | 87 | struct qeth_eddp_context_reference *ref; |
88 | 88 | ||
89 | QETH_DBF_TEXT(trace, 6, "eddprctx"); | 89 | QETH_DBF_TEXT(trace, 6, "eddprctx"); |
90 | while (!list_empty(&buf->ctx_list)){ | 90 | while (!list_empty(&buf->ctx_list)){ |
91 | ref = list_entry(buf->ctx_list.next, | 91 | ref = list_entry(buf->ctx_list.next, |
@@ -139,7 +139,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue, | |||
139 | "buffer!\n"); | 139 | "buffer!\n"); |
140 | goto out; | 140 | goto out; |
141 | } | 141 | } |
142 | } | 142 | } |
143 | /* check if the whole next skb fits into current buffer */ | 143 | /* check if the whole next skb fits into current buffer */ |
144 | if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) - | 144 | if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) - |
145 | buf->next_element_to_fill) | 145 | buf->next_element_to_fill) |
@@ -152,7 +152,7 @@ qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue, | |||
152 | * and increment ctx's refcnt */ | 152 | * and increment ctx's refcnt */ |
153 | must_refcnt = 1; | 153 | must_refcnt = 1; |
154 | continue; | 154 | continue; |
155 | } | 155 | } |
156 | if (must_refcnt){ | 156 | if (must_refcnt){ |
157 | must_refcnt = 0; | 157 | must_refcnt = 0; |
158 | if (qeth_eddp_buf_ref_context(buf, ctx)){ | 158 | if (qeth_eddp_buf_ref_context(buf, ctx)){ |
@@ -204,27 +204,27 @@ out: | |||
204 | 204 | ||
205 | static inline void | 205 | static inline void |
206 | qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx, | 206 | qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx, |
207 | struct qeth_eddp_data *eddp) | 207 | struct qeth_eddp_data *eddp, int data_len) |
208 | { | 208 | { |
209 | u8 *page; | 209 | u8 *page; |
210 | int page_remainder; | 210 | int page_remainder; |
211 | int page_offset; | 211 | int page_offset; |
212 | int hdr_len; | 212 | int pkt_len; |
213 | struct qeth_eddp_element *element; | 213 | struct qeth_eddp_element *element; |
214 | 214 | ||
215 | QETH_DBF_TEXT(trace, 5, "eddpcrsh"); | 215 | QETH_DBF_TEXT(trace, 5, "eddpcrsh"); |
216 | page = ctx->pages[ctx->offset >> PAGE_SHIFT]; | 216 | page = ctx->pages[ctx->offset >> PAGE_SHIFT]; |
217 | page_offset = ctx->offset % PAGE_SIZE; | 217 | page_offset = ctx->offset % PAGE_SIZE; |
218 | element = &ctx->elements[ctx->num_elements]; | 218 | element = &ctx->elements[ctx->num_elements]; |
219 | hdr_len = eddp->nhl + eddp->thl; | 219 | pkt_len = eddp->nhl + eddp->thl + data_len; |
220 | /* FIXME: layer2 and VLAN !!! */ | 220 | /* FIXME: layer2 and VLAN !!! */ |
221 | if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) | 221 | if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2) |
222 | hdr_len += ETH_HLEN; | 222 | pkt_len += ETH_HLEN; |
223 | if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) | 223 | if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) |
224 | hdr_len += VLAN_HLEN; | 224 | pkt_len += VLAN_HLEN; |
225 | /* does complete header fit in current page ? */ | 225 | /* does complete packet fit in current page ? */ |
226 | page_remainder = PAGE_SIZE - page_offset; | 226 | page_remainder = PAGE_SIZE - page_offset; |
227 | if (page_remainder < (sizeof(struct qeth_hdr) + hdr_len)){ | 227 | if (page_remainder < (sizeof(struct qeth_hdr) + pkt_len)){ |
228 | /* no -> go to start of next page */ | 228 | /* no -> go to start of next page */ |
229 | ctx->offset += page_remainder; | 229 | ctx->offset += page_remainder; |
230 | page = ctx->pages[ctx->offset >> PAGE_SHIFT]; | 230 | page = ctx->pages[ctx->offset >> PAGE_SHIFT]; |
@@ -270,7 +270,7 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len, | |||
270 | int left_in_frag; | 270 | int left_in_frag; |
271 | int copy_len; | 271 | int copy_len; |
272 | u8 *src; | 272 | u8 *src; |
273 | 273 | ||
274 | QETH_DBF_TEXT(trace, 5, "eddpcdtc"); | 274 | QETH_DBF_TEXT(trace, 5, "eddpcdtc"); |
275 | if (skb_shinfo(eddp->skb)->nr_frags == 0) { | 275 | if (skb_shinfo(eddp->skb)->nr_frags == 0) { |
276 | memcpy(dst, eddp->skb->data + eddp->skb_offset, len); | 276 | memcpy(dst, eddp->skb->data + eddp->skb_offset, len); |
@@ -281,7 +281,7 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len, | |||
281 | while (len > 0) { | 281 | while (len > 0) { |
282 | if (eddp->frag < 0) { | 282 | if (eddp->frag < 0) { |
283 | /* we're in skb->data */ | 283 | /* we're in skb->data */ |
284 | left_in_frag = qeth_get_skb_data_len(eddp->skb) | 284 | left_in_frag = (eddp->skb->len - eddp->skb->data_len) |
285 | - eddp->skb_offset; | 285 | - eddp->skb_offset; |
286 | src = eddp->skb->data + eddp->skb_offset; | 286 | src = eddp->skb->data + eddp->skb_offset; |
287 | } else { | 287 | } else { |
@@ -413,7 +413,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, | |||
413 | struct tcphdr *tcph; | 413 | struct tcphdr *tcph; |
414 | int data_len; | 414 | int data_len; |
415 | u32 hcsum; | 415 | u32 hcsum; |
416 | 416 | ||
417 | QETH_DBF_TEXT(trace, 5, "eddpftcp"); | 417 | QETH_DBF_TEXT(trace, 5, "eddpftcp"); |
418 | eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl; | 418 | eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl; |
419 | tcph = eddp->skb->h.th; | 419 | tcph = eddp->skb->h.th; |
@@ -453,7 +453,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, | |||
453 | else | 453 | else |
454 | hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len); | 454 | hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len); |
455 | /* fill the next segment into the context */ | 455 | /* fill the next segment into the context */ |
456 | qeth_eddp_create_segment_hdrs(ctx, eddp); | 456 | qeth_eddp_create_segment_hdrs(ctx, eddp, data_len); |
457 | qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum); | 457 | qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum); |
458 | if (eddp->skb_offset >= eddp->skb->len) | 458 | if (eddp->skb_offset >= eddp->skb->len) |
459 | break; | 459 | break; |
@@ -463,13 +463,13 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, | |||
463 | eddp->th.tcp.h.seq += data_len; | 463 | eddp->th.tcp.h.seq += data_len; |
464 | } | 464 | } |
465 | } | 465 | } |
466 | 466 | ||
467 | static inline int | 467 | static inline int |
468 | qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, | 468 | qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, |
469 | struct sk_buff *skb, struct qeth_hdr *qhdr) | 469 | struct sk_buff *skb, struct qeth_hdr *qhdr) |
470 | { | 470 | { |
471 | struct qeth_eddp_data *eddp = NULL; | 471 | struct qeth_eddp_data *eddp = NULL; |
472 | 472 | ||
473 | QETH_DBF_TEXT(trace, 5, "eddpficx"); | 473 | QETH_DBF_TEXT(trace, 5, "eddpficx"); |
474 | /* create our segmentation headers and copy original headers */ | 474 | /* create our segmentation headers and copy original headers */ |
475 | if (skb->protocol == ETH_P_IP) | 475 | if (skb->protocol == ETH_P_IP) |
@@ -509,7 +509,7 @@ qeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb, | |||
509 | int hdr_len) | 509 | int hdr_len) |
510 | { | 510 | { |
511 | int skbs_per_page; | 511 | int skbs_per_page; |
512 | 512 | ||
513 | QETH_DBF_TEXT(trace, 5, "eddpcanp"); | 513 | QETH_DBF_TEXT(trace, 5, "eddpcanp"); |
514 | /* can we put multiple skbs in one page? */ | 514 | /* can we put multiple skbs in one page? */ |
515 | skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len); | 515 | skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len); |
@@ -589,7 +589,7 @@ qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb, | |||
589 | struct qeth_hdr *qhdr) | 589 | struct qeth_hdr *qhdr) |
590 | { | 590 | { |
591 | struct qeth_eddp_context *ctx = NULL; | 591 | struct qeth_eddp_context *ctx = NULL; |
592 | 592 | ||
593 | QETH_DBF_TEXT(trace, 5, "creddpct"); | 593 | QETH_DBF_TEXT(trace, 5, "creddpct"); |
594 | if (skb->protocol == ETH_P_IP) | 594 | if (skb->protocol == ETH_P_IP) |
595 | ctx = qeth_eddp_create_context_generic(card, skb, | 595 | ctx = qeth_eddp_create_context_generic(card, skb, |
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index e18bcf9fb588..208127a5033a 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * | 2 | * |
3 | * linux/drivers/s390/net/qeth_main.c ($Revision: 1.210 $) | 3 | * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $) |
4 | * | 4 | * |
5 | * Linux on zSeries OSA Express and HiperSockets support | 5 | * Linux on zSeries OSA Express and HiperSockets support |
6 | * | 6 | * |
@@ -12,7 +12,7 @@ | |||
12 | * Frank Pavlic (pavlic@de.ibm.com) and | 12 | * Frank Pavlic (pavlic@de.ibm.com) and |
13 | * Thomas Spatzier <tspat@de.ibm.com> | 13 | * Thomas Spatzier <tspat@de.ibm.com> |
14 | * | 14 | * |
15 | * $Revision: 1.210 $ $Date: 2005/04/18 17:27:39 $ | 15 | * $Revision: 1.214 $ $Date: 2005/05/04 20:19:18 $ |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or modify | 17 | * This program is free software; you can redistribute it and/or modify |
18 | * it under the terms of the GNU General Public License as published by | 18 | * it under the terms of the GNU General Public License as published by |
@@ -80,7 +80,7 @@ qeth_eyecatcher(void) | |||
80 | #include "qeth_eddp.h" | 80 | #include "qeth_eddp.h" |
81 | #include "qeth_tso.h" | 81 | #include "qeth_tso.h" |
82 | 82 | ||
83 | #define VERSION_QETH_C "$Revision: 1.210 $" | 83 | #define VERSION_QETH_C "$Revision: 1.214 $" |
84 | static const char *version = "qeth S/390 OSA-Express driver"; | 84 | static const char *version = "qeth S/390 OSA-Express driver"; |
85 | 85 | ||
86 | /** | 86 | /** |
@@ -158,6 +158,9 @@ qeth_irq_tasklet(unsigned long); | |||
158 | static int | 158 | static int |
159 | qeth_set_online(struct ccwgroup_device *); | 159 | qeth_set_online(struct ccwgroup_device *); |
160 | 160 | ||
161 | static int | ||
162 | __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode); | ||
163 | |||
161 | static struct qeth_ipaddr * | 164 | static struct qeth_ipaddr * |
162 | qeth_get_addr_buffer(enum qeth_prot_versions); | 165 | qeth_get_addr_buffer(enum qeth_prot_versions); |
163 | 166 | ||
@@ -510,10 +513,10 @@ qeth_irq_tasklet(unsigned long data) | |||
510 | wake_up(&card->wait_q); | 513 | wake_up(&card->wait_q); |
511 | } | 514 | } |
512 | 515 | ||
513 | static int qeth_stop_card(struct qeth_card *); | 516 | static int qeth_stop_card(struct qeth_card *, int); |
514 | 517 | ||
515 | static int | 518 | static int |
516 | qeth_set_offline(struct ccwgroup_device *cgdev) | 519 | __qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode) |
517 | { | 520 | { |
518 | struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; | 521 | struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data; |
519 | int rc = 0; | 522 | int rc = 0; |
@@ -523,7 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) | |||
523 | QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); | 526 | QETH_DBF_HEX(setup, 3, &card, sizeof(void *)); |
524 | 527 | ||
525 | recover_flag = card->state; | 528 | recover_flag = card->state; |
526 | if (qeth_stop_card(card) == -ERESTARTSYS){ | 529 | if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){ |
527 | PRINT_WARN("Stopping card %s interrupted by user!\n", | 530 | PRINT_WARN("Stopping card %s interrupted by user!\n", |
528 | CARD_BUS_ID(card)); | 531 | CARD_BUS_ID(card)); |
529 | return -ERESTARTSYS; | 532 | return -ERESTARTSYS; |
@@ -540,6 +543,12 @@ qeth_set_offline(struct ccwgroup_device *cgdev) | |||
540 | } | 543 | } |
541 | 544 | ||
542 | static int | 545 | static int |
546 | qeth_set_offline(struct ccwgroup_device *cgdev) | ||
547 | { | ||
548 | return __qeth_set_offline(cgdev, 0); | ||
549 | } | ||
550 | |||
551 | static int | ||
543 | qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); | 552 | qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); |
544 | 553 | ||
545 | 554 | ||
@@ -953,8 +962,8 @@ qeth_recover(void *ptr) | |||
953 | PRINT_WARN("Recovery of device %s started ...\n", | 962 | PRINT_WARN("Recovery of device %s started ...\n", |
954 | CARD_BUS_ID(card)); | 963 | CARD_BUS_ID(card)); |
955 | card->use_hard_stop = 1; | 964 | card->use_hard_stop = 1; |
956 | qeth_set_offline(card->gdev); | 965 | __qeth_set_offline(card->gdev,1); |
957 | rc = qeth_set_online(card->gdev); | 966 | rc = __qeth_set_online(card->gdev,1); |
958 | if (!rc) | 967 | if (!rc) |
959 | PRINT_INFO("Device %s successfully recovered!\n", | 968 | PRINT_INFO("Device %s successfully recovered!\n", |
960 | CARD_BUS_ID(card)); | 969 | CARD_BUS_ID(card)); |
@@ -3786,16 +3795,12 @@ static inline int | |||
3786 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, | 3795 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, |
3787 | struct qeth_hdr **hdr, int ipv) | 3796 | struct qeth_hdr **hdr, int ipv) |
3788 | { | 3797 | { |
3789 | int rc = 0; | ||
3790 | #ifdef CONFIG_QETH_VLAN | 3798 | #ifdef CONFIG_QETH_VLAN |
3791 | u16 *tag; | 3799 | u16 *tag; |
3792 | #endif | 3800 | #endif |
3793 | 3801 | ||
3794 | QETH_DBF_TEXT(trace, 6, "prepskb"); | 3802 | QETH_DBF_TEXT(trace, 6, "prepskb"); |
3795 | 3803 | ||
3796 | rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); | ||
3797 | if (rc) | ||
3798 | return rc; | ||
3799 | #ifdef CONFIG_QETH_VLAN | 3804 | #ifdef CONFIG_QETH_VLAN |
3800 | if (card->vlangrp && vlan_tx_tag_present(*skb) && | 3805 | if (card->vlangrp && vlan_tx_tag_present(*skb) && |
3801 | ((ipv == 6) || card->options.layer2) ) { | 3806 | ((ipv == 6) || card->options.layer2) ) { |
@@ -3977,25 +3982,28 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, | |||
3977 | 3982 | ||
3978 | static inline void | 3983 | static inline void |
3979 | __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, | 3984 | __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer, |
3980 | int *next_element_to_fill) | 3985 | int is_tso, int *next_element_to_fill) |
3981 | { | 3986 | { |
3982 | int length = skb->len; | 3987 | int length = skb->len; |
3983 | int length_here; | 3988 | int length_here; |
3984 | int element; | 3989 | int element; |
3985 | char *data; | 3990 | char *data; |
3986 | int first_lap = 1; | 3991 | int first_lap ; |
3987 | 3992 | ||
3988 | element = *next_element_to_fill; | 3993 | element = *next_element_to_fill; |
3989 | data = skb->data; | 3994 | data = skb->data; |
3995 | first_lap = (is_tso == 0 ? 1 : 0); | ||
3996 | |||
3990 | while (length > 0) { | 3997 | while (length > 0) { |
3991 | /* length_here is the remaining amount of data in this page */ | 3998 | /* length_here is the remaining amount of data in this page */ |
3992 | length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); | 3999 | length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); |
3993 | if (length < length_here) | 4000 | if (length < length_here) |
3994 | length_here = length; | 4001 | length_here = length; |
4002 | |||
3995 | buffer->element[element].addr = data; | 4003 | buffer->element[element].addr = data; |
3996 | buffer->element[element].length = length_here; | 4004 | buffer->element[element].length = length_here; |
3997 | length -= length_here; | 4005 | length -= length_here; |
3998 | if (!length){ | 4006 | if (!length) { |
3999 | if (first_lap) | 4007 | if (first_lap) |
4000 | buffer->element[element].flags = 0; | 4008 | buffer->element[element].flags = 0; |
4001 | else | 4009 | else |
@@ -4022,17 +4030,35 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, | |||
4022 | struct sk_buff *skb) | 4030 | struct sk_buff *skb) |
4023 | { | 4031 | { |
4024 | struct qdio_buffer *buffer; | 4032 | struct qdio_buffer *buffer; |
4025 | int flush_cnt = 0; | 4033 | struct qeth_hdr_tso *hdr; |
4034 | int flush_cnt = 0, hdr_len, large_send = 0; | ||
4026 | 4035 | ||
4027 | QETH_DBF_TEXT(trace, 6, "qdfillbf"); | 4036 | QETH_DBF_TEXT(trace, 6, "qdfillbf"); |
4037 | |||
4028 | buffer = buf->buffer; | 4038 | buffer = buf->buffer; |
4029 | atomic_inc(&skb->users); | 4039 | atomic_inc(&skb->users); |
4030 | skb_queue_tail(&buf->skb_list, skb); | 4040 | skb_queue_tail(&buf->skb_list, skb); |
4041 | |||
4042 | hdr = (struct qeth_hdr_tso *) skb->data; | ||
4043 | /*check first on TSO ....*/ | ||
4044 | if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) { | ||
4045 | int element = buf->next_element_to_fill; | ||
4046 | |||
4047 | hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; | ||
4048 | /*fill first buffer entry only with header information */ | ||
4049 | buffer->element[element].addr = skb->data; | ||
4050 | buffer->element[element].length = hdr_len; | ||
4051 | buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; | ||
4052 | buf->next_element_to_fill++; | ||
4053 | skb->data += hdr_len; | ||
4054 | skb->len -= hdr_len; | ||
4055 | large_send = 1; | ||
4056 | } | ||
4031 | if (skb_shinfo(skb)->nr_frags == 0) | 4057 | if (skb_shinfo(skb)->nr_frags == 0) |
4032 | __qeth_fill_buffer(skb, buffer, | 4058 | __qeth_fill_buffer(skb, buffer, large_send, |
4033 | (int *)&buf->next_element_to_fill); | 4059 | (int *)&buf->next_element_to_fill); |
4034 | else | 4060 | else |
4035 | __qeth_fill_buffer_frag(skb, buffer, 0, | 4061 | __qeth_fill_buffer_frag(skb, buffer, large_send, |
4036 | (int *)&buf->next_element_to_fill); | 4062 | (int *)&buf->next_element_to_fill); |
4037 | 4063 | ||
4038 | if (!queue->do_pack) { | 4064 | if (!queue->do_pack) { |
@@ -4225,6 +4251,25 @@ out: | |||
4225 | } | 4251 | } |
4226 | 4252 | ||
4227 | static inline int | 4253 | static inline int |
4254 | qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) | ||
4255 | { | ||
4256 | int elements_needed = 0; | ||
4257 | |||
4258 | if (skb_shinfo(skb)->nr_frags > 0) { | ||
4259 | elements_needed = (skb_shinfo(skb)->nr_frags + 1); | ||
4260 | } | ||
4261 | if (elements_needed == 0 ) | ||
4262 | elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) | ||
4263 | + skb->len) >> PAGE_SHIFT); | ||
4264 | if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ | ||
4265 | PRINT_ERR("qeth_do_send_packet: invalid size of " | ||
4266 | "IP packet. Discarded."); | ||
4267 | return 0; | ||
4268 | } | ||
4269 | return elements_needed; | ||
4270 | } | ||
4271 | |||
4272 | static inline int | ||
4228 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | 4273 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) |
4229 | { | 4274 | { |
4230 | int ipv = 0; | 4275 | int ipv = 0; |
@@ -4266,19 +4311,25 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4266 | if (skb_shinfo(skb)->tso_size) | 4311 | if (skb_shinfo(skb)->tso_size) |
4267 | large_send = card->options.large_send; | 4312 | large_send = card->options.large_send; |
4268 | 4313 | ||
4269 | if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){ | ||
4270 | QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); | ||
4271 | return rc; | ||
4272 | } | ||
4273 | /*are we able to do TSO ? If so ,prepare and send it from here */ | 4314 | /*are we able to do TSO ? If so ,prepare and send it from here */ |
4274 | if ((large_send == QETH_LARGE_SEND_TSO) && | 4315 | if ((large_send == QETH_LARGE_SEND_TSO) && |
4275 | (cast_type == RTN_UNSPEC)) { | 4316 | (cast_type == RTN_UNSPEC)) { |
4276 | rc = qeth_tso_send_packet(card, skb, queue, | 4317 | rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); |
4277 | ipv, cast_type); | 4318 | if (rc) { |
4278 | goto do_statistics; | 4319 | card->stats.tx_dropped++; |
4320 | card->stats.tx_errors++; | ||
4321 | dev_kfree_skb_any(skb); | ||
4322 | return NETDEV_TX_OK; | ||
4323 | } | ||
4324 | elements_needed++; | ||
4325 | } else { | ||
4326 | if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { | ||
4327 | QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); | ||
4328 | return rc; | ||
4329 | } | ||
4330 | qeth_fill_header(card, hdr, skb, ipv, cast_type); | ||
4279 | } | 4331 | } |
4280 | 4332 | ||
4281 | qeth_fill_header(card, hdr, skb, ipv, cast_type); | ||
4282 | if (large_send == QETH_LARGE_SEND_EDDP) { | 4333 | if (large_send == QETH_LARGE_SEND_EDDP) { |
4283 | ctx = qeth_eddp_create_context(card, skb, hdr); | 4334 | ctx = qeth_eddp_create_context(card, skb, hdr); |
4284 | if (ctx == NULL) { | 4335 | if (ctx == NULL) { |
@@ -4286,7 +4337,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4286 | return -EINVAL; | 4337 | return -EINVAL; |
4287 | } | 4338 | } |
4288 | } else { | 4339 | } else { |
4289 | elements_needed = qeth_get_elements_no(card,(void*) hdr, skb); | 4340 | elements_needed += qeth_get_elements_no(card,(void*) hdr, skb); |
4290 | if (!elements_needed) | 4341 | if (!elements_needed) |
4291 | return -EINVAL; | 4342 | return -EINVAL; |
4292 | } | 4343 | } |
@@ -4297,12 +4348,12 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4297 | else | 4348 | else |
4298 | rc = qeth_do_send_packet_fast(card, queue, skb, hdr, | 4349 | rc = qeth_do_send_packet_fast(card, queue, skb, hdr, |
4299 | elements_needed, ctx); | 4350 | elements_needed, ctx); |
4300 | do_statistics: | ||
4301 | if (!rc){ | 4351 | if (!rc){ |
4302 | card->stats.tx_packets++; | 4352 | card->stats.tx_packets++; |
4303 | card->stats.tx_bytes += skb->len; | 4353 | card->stats.tx_bytes += skb->len; |
4304 | #ifdef CONFIG_QETH_PERF_STATS | 4354 | #ifdef CONFIG_QETH_PERF_STATS |
4305 | if (skb_shinfo(skb)->tso_size) { | 4355 | if (skb_shinfo(skb)->tso_size && |
4356 | !(large_send == QETH_LARGE_SEND_NO)) { | ||
4306 | card->perf_stats.large_send_bytes += skb->len; | 4357 | card->perf_stats.large_send_bytes += skb->len; |
4307 | card->perf_stats.large_send_cnt++; | 4358 | card->perf_stats.large_send_cnt++; |
4308 | } | 4359 | } |
@@ -7199,7 +7250,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads) | |||
7199 | } | 7250 | } |
7200 | 7251 | ||
7201 | static int | 7252 | static int |
7202 | qeth_stop_card(struct qeth_card *card) | 7253 | qeth_stop_card(struct qeth_card *card, int recovery_mode) |
7203 | { | 7254 | { |
7204 | int rc = 0; | 7255 | int rc = 0; |
7205 | 7256 | ||
@@ -7212,9 +7263,13 @@ qeth_stop_card(struct qeth_card *card) | |||
7212 | if (card->read.state == CH_STATE_UP && | 7263 | if (card->read.state == CH_STATE_UP && |
7213 | card->write.state == CH_STATE_UP && | 7264 | card->write.state == CH_STATE_UP && |
7214 | (card->state == CARD_STATE_UP)) { | 7265 | (card->state == CARD_STATE_UP)) { |
7215 | rtnl_lock(); | 7266 | if(recovery_mode) { |
7216 | dev_close(card->dev); | 7267 | qeth_stop(card->dev); |
7217 | rtnl_unlock(); | 7268 | } else { |
7269 | rtnl_lock(); | ||
7270 | dev_close(card->dev); | ||
7271 | rtnl_unlock(); | ||
7272 | } | ||
7218 | if (!card->use_hard_stop) { | 7273 | if (!card->use_hard_stop) { |
7219 | __u8 *mac = &card->dev->dev_addr[0]; | 7274 | __u8 *mac = &card->dev->dev_addr[0]; |
7220 | rc = qeth_layer2_send_delmac(card, mac); | 7275 | rc = qeth_layer2_send_delmac(card, mac); |
@@ -7386,13 +7441,17 @@ qeth_register_netdev(struct qeth_card *card) | |||
7386 | } | 7441 | } |
7387 | 7442 | ||
7388 | static void | 7443 | static void |
7389 | qeth_start_again(struct qeth_card *card) | 7444 | qeth_start_again(struct qeth_card *card, int recovery_mode) |
7390 | { | 7445 | { |
7391 | QETH_DBF_TEXT(setup ,2, "startag"); | 7446 | QETH_DBF_TEXT(setup ,2, "startag"); |
7392 | 7447 | ||
7393 | rtnl_lock(); | 7448 | if(recovery_mode) { |
7394 | dev_open(card->dev); | 7449 | qeth_open(card->dev); |
7395 | rtnl_unlock(); | 7450 | } else { |
7451 | rtnl_lock(); | ||
7452 | dev_open(card->dev); | ||
7453 | rtnl_unlock(); | ||
7454 | } | ||
7396 | /* this also sets saved unicast addresses */ | 7455 | /* this also sets saved unicast addresses */ |
7397 | qeth_set_multicast_list(card->dev); | 7456 | qeth_set_multicast_list(card->dev); |
7398 | } | 7457 | } |
@@ -7449,7 +7508,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card) | |||
7449 | 7508 | ||
7450 | 7509 | ||
7451 | static int | 7510 | static int |
7452 | qeth_set_online(struct ccwgroup_device *gdev) | 7511 | __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode) |
7453 | { | 7512 | { |
7454 | struct qeth_card *card = gdev->dev.driver_data; | 7513 | struct qeth_card *card = gdev->dev.driver_data; |
7455 | int rc = 0; | 7514 | int rc = 0; |
@@ -7509,12 +7568,12 @@ qeth_set_online(struct ccwgroup_device *gdev) | |||
7509 | * we can also use this state for recovery purposes*/ | 7568 | * we can also use this state for recovery purposes*/ |
7510 | qeth_set_allowed_threads(card, 0xffffffff, 0); | 7569 | qeth_set_allowed_threads(card, 0xffffffff, 0); |
7511 | if (recover_flag == CARD_STATE_RECOVER) | 7570 | if (recover_flag == CARD_STATE_RECOVER) |
7512 | qeth_start_again(card); | 7571 | qeth_start_again(card, recovery_mode); |
7513 | qeth_notify_processes(); | 7572 | qeth_notify_processes(); |
7514 | return 0; | 7573 | return 0; |
7515 | out_remove: | 7574 | out_remove: |
7516 | card->use_hard_stop = 1; | 7575 | card->use_hard_stop = 1; |
7517 | qeth_stop_card(card); | 7576 | qeth_stop_card(card, 0); |
7518 | ccw_device_set_offline(CARD_DDEV(card)); | 7577 | ccw_device_set_offline(CARD_DDEV(card)); |
7519 | ccw_device_set_offline(CARD_WDEV(card)); | 7578 | ccw_device_set_offline(CARD_WDEV(card)); |
7520 | ccw_device_set_offline(CARD_RDEV(card)); | 7579 | ccw_device_set_offline(CARD_RDEV(card)); |
@@ -7525,6 +7584,12 @@ out_remove: | |||
7525 | return -ENODEV; | 7584 | return -ENODEV; |
7526 | } | 7585 | } |
7527 | 7586 | ||
7587 | static int | ||
7588 | qeth_set_online(struct ccwgroup_device *gdev) | ||
7589 | { | ||
7590 | return __qeth_set_online(gdev, 0); | ||
7591 | } | ||
7592 | |||
7528 | static struct ccw_device_id qeth_ids[] = { | 7593 | static struct ccw_device_id qeth_ids[] = { |
7529 | {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, | 7594 | {CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE}, |
7530 | {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, | 7595 | {CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD}, |
diff --git a/drivers/s390/net/qeth_tso.c b/drivers/s390/net/qeth_tso.c deleted file mode 100644 index 4e58f19cb71c..000000000000 --- a/drivers/s390/net/qeth_tso.c +++ /dev/null | |||
@@ -1,256 +0,0 @@ | |||
1 | /* | ||
2 | * linux/drivers/s390/net/qeth_tso.c ($Revision: 1.7 $) | ||
3 | * | ||
4 | * Header file for qeth TCP Segmentation Offload support. | ||
5 | * | ||
6 | * Copyright 2004 IBM Corporation | ||
7 | * | ||
8 | * Author(s): Frank Pavlic <pavlic@de.ibm.com> | ||
9 | * | ||
10 | * $Revision: 1.7 $ $Date: 2005/04/01 21:40:41 $ | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/tcp.h> | ||
16 | #include <linux/ip.h> | ||
17 | #include <linux/ipv6.h> | ||
18 | #include <net/ip6_checksum.h> | ||
19 | #include "qeth.h" | ||
20 | #include "qeth_mpc.h" | ||
21 | #include "qeth_tso.h" | ||
22 | |||
23 | /** | ||
24 | * skb already partially prepared | ||
25 | * classic qdio header in skb->data | ||
26 | * */ | ||
27 | static inline struct qeth_hdr_tso * | ||
28 | qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) | ||
29 | { | ||
30 | int rc = 0; | ||
31 | |||
32 | QETH_DBF_TEXT(trace, 5, "tsoprsk"); | ||
33 | rc = qeth_realloc_headroom(card, skb,sizeof(struct qeth_hdr_ext_tso)); | ||
34 | if (rc) | ||
35 | return NULL; | ||
36 | |||
37 | return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_ext_tso)); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * fill header for a TSO packet | ||
42 | */ | ||
43 | static inline void | ||
44 | qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) | ||
45 | { | ||
46 | struct qeth_hdr_tso *hdr; | ||
47 | struct tcphdr *tcph; | ||
48 | struct iphdr *iph; | ||
49 | |||
50 | QETH_DBF_TEXT(trace, 5, "tsofhdr"); | ||
51 | |||
52 | hdr = (struct qeth_hdr_tso *) skb->data; | ||
53 | iph = skb->nh.iph; | ||
54 | tcph = skb->h.th; | ||
55 | /*fix header to TSO values ...*/ | ||
56 | hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; | ||
57 | /*set values which are fix for the first approach ...*/ | ||
58 | hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); | ||
59 | hdr->ext.imb_hdr_no = 1; | ||
60 | hdr->ext.hdr_type = 1; | ||
61 | hdr->ext.hdr_version = 1; | ||
62 | hdr->ext.hdr_len = 28; | ||
63 | /*insert non-fix values */ | ||
64 | hdr->ext.mss = skb_shinfo(skb)->tso_size; | ||
65 | hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); | ||
66 | hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - | ||
67 | sizeof(struct qeth_hdr_tso)); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * change some header values as requested by hardware | ||
72 | */ | ||
73 | static inline void | ||
74 | qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) | ||
75 | { | ||
76 | struct iphdr *iph; | ||
77 | struct ipv6hdr *ip6h; | ||
78 | struct tcphdr *tcph; | ||
79 | |||
80 | iph = skb->nh.iph; | ||
81 | ip6h = skb->nh.ipv6h; | ||
82 | tcph = skb->h.th; | ||
83 | |||
84 | tcph->check = 0; | ||
85 | if (skb->protocol == ETH_P_IPV6) { | ||
86 | ip6h->payload_len = 0; | ||
87 | tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | ||
88 | 0, IPPROTO_TCP, 0); | ||
89 | return; | ||
90 | } | ||
91 | /*OSA want us to set these values ...*/ | ||
92 | tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
93 | 0, IPPROTO_TCP, 0); | ||
94 | iph->tot_len = 0; | ||
95 | iph->check = 0; | ||
96 | } | ||
97 | |||
98 | static inline struct qeth_hdr_tso * | ||
99 | qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb, | ||
100 | int ipv, int cast_type) | ||
101 | { | ||
102 | struct qeth_hdr_tso *hdr; | ||
103 | int rc = 0; | ||
104 | |||
105 | QETH_DBF_TEXT(trace, 5, "tsoprep"); | ||
106 | |||
107 | /*get headroom for tso qdio header */ | ||
108 | hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb); | ||
109 | if (hdr == NULL) { | ||
110 | QETH_DBF_TEXT_(trace, 4, "2err%d", rc); | ||
111 | return NULL; | ||
112 | } | ||
113 | memset(hdr, 0, sizeof(struct qeth_hdr_tso)); | ||
114 | /*fill first 32 bytes of qdio header as used | ||
115 | *FIXME: TSO has two struct members | ||
116 | * with different names but same size | ||
117 | * */ | ||
118 | qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type); | ||
119 | qeth_tso_fill_header(card, skb); | ||
120 | qeth_tso_set_tcpip_header(card, skb); | ||
121 | return hdr; | ||
122 | } | ||
123 | |||
124 | static inline int | ||
125 | qeth_tso_get_queue_buffer(struct qeth_qdio_out_q *queue) | ||
126 | { | ||
127 | struct qeth_qdio_out_buffer *buffer; | ||
128 | int flush_cnt = 0; | ||
129 | |||
130 | QETH_DBF_TEXT(trace, 5, "tsobuf"); | ||
131 | |||
132 | /* force to non-packing*/ | ||
133 | if (queue->do_pack) | ||
134 | queue->do_pack = 0; | ||
135 | buffer = &queue->bufs[queue->next_buf_to_fill]; | ||
136 | /* get a new buffer if current is already in use*/ | ||
137 | if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) && | ||
138 | (buffer->next_element_to_fill > 0)) { | ||
139 | atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED); | ||
140 | queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % | ||
141 | QDIO_MAX_BUFFERS_PER_Q; | ||
142 | flush_cnt++; | ||
143 | } | ||
144 | return flush_cnt; | ||
145 | } | ||
146 | |||
147 | |||
148 | static inline int | ||
149 | qeth_tso_fill_buffer(struct qeth_qdio_out_buffer *buf, | ||
150 | struct sk_buff *skb) | ||
151 | { | ||
152 | int length, length_here, element; | ||
153 | int hdr_len; | ||
154 | struct qdio_buffer *buffer; | ||
155 | struct qeth_hdr_tso *hdr; | ||
156 | char *data; | ||
157 | |||
158 | QETH_DBF_TEXT(trace, 3, "tsfilbuf"); | ||
159 | |||
160 | /*increment user count and queue skb ...*/ | ||
161 | atomic_inc(&skb->users); | ||
162 | skb_queue_tail(&buf->skb_list, skb); | ||
163 | |||
164 | /*initialize all variables...*/ | ||
165 | buffer = buf->buffer; | ||
166 | hdr = (struct qeth_hdr_tso *)skb->data; | ||
167 | hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; | ||
168 | data = skb->data + hdr_len; | ||
169 | length = skb->len - hdr_len; | ||
170 | element = buf->next_element_to_fill; | ||
171 | /*fill first buffer entry only with header information */ | ||
172 | buffer->element[element].addr = skb->data; | ||
173 | buffer->element[element].length = hdr_len; | ||
174 | buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; | ||
175 | buf->next_element_to_fill++; | ||
176 | /*check if we have frags ...*/ | ||
177 | if (skb_shinfo(skb)->nr_frags > 0) { | ||
178 | skb->len = length; | ||
179 | skb->data = data; | ||
180 | __qeth_fill_buffer_frag(skb, buffer,1, | ||
181 | (int *)&buf->next_element_to_fill); | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | /*... if not, use this */ | ||
186 | element++; | ||
187 | while (length > 0) { | ||
188 | /* length_here is the remaining amount of data in this page */ | ||
189 | length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); | ||
190 | if (length < length_here) | ||
191 | length_here = length; | ||
192 | buffer->element[element].addr = data; | ||
193 | buffer->element[element].length = length_here; | ||
194 | length -= length_here; | ||
195 | if (!length) | ||
196 | buffer->element[element].flags = | ||
197 | SBAL_FLAGS_LAST_FRAG; | ||
198 | else | ||
199 | buffer->element[element].flags = | ||
200 | SBAL_FLAGS_MIDDLE_FRAG; | ||
201 | data += length_here; | ||
202 | element++; | ||
203 | } | ||
204 | buf->next_element_to_fill = element; | ||
205 | out: | ||
206 | /*prime buffer now ...*/ | ||
207 | atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED); | ||
208 | return 1; | ||
209 | } | ||
210 | |||
211 | int | ||
212 | qeth_tso_send_packet(struct qeth_card *card, struct sk_buff *skb, | ||
213 | struct qeth_qdio_out_q *queue, int ipv, int cast_type) | ||
214 | { | ||
215 | int flush_cnt = 0; | ||
216 | struct qeth_hdr_tso *hdr; | ||
217 | struct qeth_qdio_out_buffer *buffer; | ||
218 | int start_index; | ||
219 | |||
220 | QETH_DBF_TEXT(trace, 3, "tsosend"); | ||
221 | |||
222 | if (!(hdr = qeth_tso_prepare_packet(card, skb, ipv, cast_type))) | ||
223 | return -ENOMEM; | ||
224 | /*check if skb fits in one SBAL ...*/ | ||
225 | if (!(qeth_get_elements_no(card, (void*)hdr, skb))) | ||
226 | return -EINVAL; | ||
227 | /*lock queue, force switching to non-packing and send it ...*/ | ||
228 | while (atomic_compare_and_swap(QETH_OUT_Q_UNLOCKED, | ||
229 | QETH_OUT_Q_LOCKED, | ||
230 | &queue->state)); | ||
231 | start_index = queue->next_buf_to_fill; | ||
232 | buffer = &queue->bufs[queue->next_buf_to_fill]; | ||
233 | /*check if card is too busy ...*/ | ||
234 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){ | ||
235 | card->stats.tx_dropped++; | ||
236 | goto out; | ||
237 | } | ||
238 | /*let's force to non-packing and get a new SBAL*/ | ||
239 | flush_cnt += qeth_tso_get_queue_buffer(queue); | ||
240 | buffer = &queue->bufs[queue->next_buf_to_fill]; | ||
241 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { | ||
242 | card->stats.tx_dropped++; | ||
243 | goto out; | ||
244 | } | ||
245 | flush_cnt += qeth_tso_fill_buffer(buffer, skb); | ||
246 | queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % | ||
247 | QDIO_MAX_BUFFERS_PER_Q; | ||
248 | out: | ||
249 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | ||
250 | if (flush_cnt) | ||
251 | qeth_flush_buffers(queue, 0, start_index, flush_cnt); | ||
252 | /*do some statistics */ | ||
253 | card->stats.tx_packets++; | ||
254 | card->stats.tx_bytes += skb->len; | ||
255 | return 0; | ||
256 | } | ||
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h index ff585ae49b6c..ad33e6f466f1 100644 --- a/drivers/s390/net/qeth_tso.h +++ b/drivers/s390/net/qeth_tso.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.5 $) | 2 | * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $) |
3 | * | 3 | * |
4 | * Header file for qeth TCP Segmentation Offload support. | 4 | * Header file for qeth TCP Segmentation Offload support. |
5 | * | 5 | * |
@@ -7,97 +7,148 @@ | |||
7 | * | 7 | * |
8 | * Author(s): Frank Pavlic <pavlic@de.ibm.com> | 8 | * Author(s): Frank Pavlic <pavlic@de.ibm.com> |
9 | * | 9 | * |
10 | * $Revision: 1.5 $ $Date: 2005/04/01 21:40:41 $ | 10 | * $Revision: 1.7 $ $Date: 2005/05/04 20:19:18 $ |
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | #ifndef __QETH_TSO_H__ | 13 | #ifndef __QETH_TSO_H__ |
14 | #define __QETH_TSO_H__ | 14 | #define __QETH_TSO_H__ |
15 | 15 | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/tcp.h> | ||
18 | #include <linux/ip.h> | ||
19 | #include <linux/ipv6.h> | ||
20 | #include <net/ip6_checksum.h> | ||
21 | #include "qeth.h" | ||
22 | #include "qeth_mpc.h" | ||
16 | 23 | ||
17 | extern int | ||
18 | qeth_tso_send_packet(struct qeth_card *, struct sk_buff *, | ||
19 | struct qeth_qdio_out_q *, int , int); | ||
20 | 24 | ||
21 | struct qeth_hdr_ext_tso { | 25 | static inline struct qeth_hdr_tso * |
22 | __u16 hdr_tot_len; | 26 | qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) |
23 | __u8 imb_hdr_no; | 27 | { |
24 | __u8 reserved; | 28 | QETH_DBF_TEXT(trace, 5, "tsoprsk"); |
25 | __u8 hdr_type; | 29 | return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso)); |
26 | __u8 hdr_version; | 30 | } |
27 | __u16 hdr_len; | 31 | |
28 | __u32 payload_len; | 32 | /** |
29 | __u16 mss; | 33 | * fill header for a TSO packet |
30 | __u16 dg_hdr_len; | 34 | */ |
31 | __u8 padding[16]; | 35 | static inline void |
32 | } __attribute__ ((packed)); | 36 | qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) |
37 | { | ||
38 | struct qeth_hdr_tso *hdr; | ||
39 | struct tcphdr *tcph; | ||
40 | struct iphdr *iph; | ||
33 | 41 | ||
34 | struct qeth_hdr_tso { | 42 | QETH_DBF_TEXT(trace, 5, "tsofhdr"); |
35 | struct qeth_hdr hdr; /*hdr->hdr.l3.xxx*/ | 43 | |
36 | struct qeth_hdr_ext_tso ext; | 44 | hdr = (struct qeth_hdr_tso *) skb->data; |
37 | } __attribute__ ((packed)); | 45 | iph = skb->nh.iph; |
46 | tcph = skb->h.th; | ||
47 | /*fix header to TSO values ...*/ | ||
48 | hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; | ||
49 | /*set values which are fix for the first approach ...*/ | ||
50 | hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso); | ||
51 | hdr->ext.imb_hdr_no = 1; | ||
52 | hdr->ext.hdr_type = 1; | ||
53 | hdr->ext.hdr_version = 1; | ||
54 | hdr->ext.hdr_len = 28; | ||
55 | /*insert non-fix values */ | ||
56 | hdr->ext.mss = skb_shinfo(skb)->tso_size; | ||
57 | hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4); | ||
58 | hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - | ||
59 | sizeof(struct qeth_hdr_tso)); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * change some header values as requested by hardware | ||
64 | */ | ||
65 | static inline void | ||
66 | qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) | ||
67 | { | ||
68 | struct iphdr *iph; | ||
69 | struct ipv6hdr *ip6h; | ||
70 | struct tcphdr *tcph; | ||
71 | |||
72 | iph = skb->nh.iph; | ||
73 | ip6h = skb->nh.ipv6h; | ||
74 | tcph = skb->h.th; | ||
75 | |||
76 | tcph->check = 0; | ||
77 | if (skb->protocol == ETH_P_IPV6) { | ||
78 | ip6h->payload_len = 0; | ||
79 | tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | ||
80 | 0, IPPROTO_TCP, 0); | ||
81 | return; | ||
82 | } | ||
83 | /*OSA want us to set these values ...*/ | ||
84 | tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | ||
85 | 0, IPPROTO_TCP, 0); | ||
86 | iph->tot_len = 0; | ||
87 | iph->check = 0; | ||
88 | } | ||
38 | 89 | ||
39 | /*some helper functions*/ | ||
40 | static inline int | 90 | static inline int |
41 | qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb) | 91 | qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb, |
92 | int ipv, int cast_type) | ||
42 | { | 93 | { |
43 | int elements_needed = 0; | 94 | struct qeth_hdr_tso *hdr; |
44 | 95 | ||
45 | if (skb_shinfo(skb)->nr_frags > 0) | 96 | QETH_DBF_TEXT(trace, 5, "tsoprep"); |
46 | elements_needed = (skb_shinfo(skb)->nr_frags + 1); | 97 | |
47 | if (elements_needed == 0 ) | 98 | hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb); |
48 | elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) | 99 | if (hdr == NULL) { |
49 | + skb->len) >> PAGE_SHIFT); | 100 | QETH_DBF_TEXT(trace, 4, "tsoperr"); |
50 | if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){ | 101 | return -ENOMEM; |
51 | PRINT_ERR("qeth_do_send_packet: invalid size of " | 102 | } |
52 | "IP packet. Discarded."); | 103 | memset(hdr, 0, sizeof(struct qeth_hdr_tso)); |
53 | return 0; | 104 | /*fill first 32 bytes of qdio header as used |
54 | } | 105 | *FIXME: TSO has two struct members |
55 | return elements_needed; | 106 | * with different names but same size |
107 | * */ | ||
108 | qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type); | ||
109 | qeth_tso_fill_header(card, skb); | ||
110 | qeth_tso_set_tcpip_header(card, skb); | ||
111 | return 0; | ||
56 | } | 112 | } |
57 | 113 | ||
58 | static inline void | 114 | static inline void |
59 | __qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer, | 115 | __qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer, |
60 | int is_tso, int *next_element_to_fill) | 116 | int is_tso, int *next_element_to_fill) |
61 | { | 117 | { |
62 | int length = skb->len; | ||
63 | struct skb_frag_struct *frag; | 118 | struct skb_frag_struct *frag; |
64 | int fragno; | 119 | int fragno; |
65 | unsigned long addr; | 120 | unsigned long addr; |
66 | int element; | 121 | int element, cnt, dlen; |
67 | int first_lap = 1; | 122 | |
68 | 123 | fragno = skb_shinfo(skb)->nr_frags; | |
69 | fragno = skb_shinfo(skb)->nr_frags; /* start with last frag */ | 124 | element = *next_element_to_fill; |
70 | element = *next_element_to_fill + fragno; | 125 | dlen = 0; |
71 | while (length > 0) { | 126 | |
72 | if (fragno > 0) { | 127 | if (is_tso) |
73 | frag = &skb_shinfo(skb)->frags[fragno - 1]; | 128 | buffer->element[element].flags = |
74 | addr = (page_to_pfn(frag->page) << PAGE_SHIFT) + | 129 | SBAL_FLAGS_MIDDLE_FRAG; |
75 | frag->page_offset; | 130 | else |
76 | buffer->element[element].addr = (char *)addr; | 131 | buffer->element[element].flags = |
77 | buffer->element[element].length = frag->size; | 132 | SBAL_FLAGS_FIRST_FRAG; |
78 | length -= frag->size; | 133 | if ( (dlen = (skb->len - skb->data_len)) ) { |
79 | if (first_lap) | 134 | buffer->element[element].addr = skb->data; |
80 | buffer->element[element].flags = | 135 | buffer->element[element].length = dlen; |
81 | SBAL_FLAGS_LAST_FRAG; | 136 | element++; |
82 | else | ||
83 | buffer->element[element].flags = | ||
84 | SBAL_FLAGS_MIDDLE_FRAG; | ||
85 | } else { | ||
86 | buffer->element[element].addr = skb->data; | ||
87 | buffer->element[element].length = length; | ||
88 | length = 0; | ||
89 | if (is_tso) | ||
90 | buffer->element[element].flags = | ||
91 | SBAL_FLAGS_MIDDLE_FRAG; | ||
92 | else | ||
93 | buffer->element[element].flags = | ||
94 | SBAL_FLAGS_FIRST_FRAG; | ||
95 | } | ||
96 | element--; | ||
97 | fragno--; | ||
98 | first_lap = 0; | ||
99 | } | 137 | } |
100 | *next_element_to_fill += skb_shinfo(skb)->nr_frags + 1; | 138 | for (cnt = 0; cnt < fragno; cnt++) { |
139 | frag = &skb_shinfo(skb)->frags[cnt]; | ||
140 | addr = (page_to_pfn(frag->page) << PAGE_SHIFT) + | ||
141 | frag->page_offset; | ||
142 | buffer->element[element].addr = (char *)addr; | ||
143 | buffer->element[element].length = frag->size; | ||
144 | if (cnt < (fragno - 1)) | ||
145 | buffer->element[element].flags = | ||
146 | SBAL_FLAGS_MIDDLE_FRAG; | ||
147 | else | ||
148 | buffer->element[element].flags = | ||
149 | SBAL_FLAGS_LAST_FRAG; | ||
150 | element++; | ||
151 | } | ||
152 | *next_element_to_fill = element; | ||
101 | } | 153 | } |
102 | |||
103 | #endif /* __QETH_TSO_H__ */ | 154 | #endif /* __QETH_TSO_H__ */ |