diff options
Diffstat (limited to 'drivers/s390')
-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__ */ |