diff options
author | Frank Pavlic <fpavlic@de.ibm.com> | 2006-09-15 10:26:19 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-17 01:03:07 -0400 |
commit | f7b65d70a3e6f1c97eb614964270816992d0d4b4 (patch) | |
tree | fae68b2d6c215abacd3074b81bc320f5b3e3a0e0 /drivers/s390 | |
parent | 1fda1a120b7dcddf382ad105b4783a69e81c7a2b (diff) |
[PATCH] s390: qeth driver fixes [3/6]
[PATCH 6/9] s390: qeth driver fixes [3/6]
From: Frank Pavlic <fpavlic@de.ibm.com>
fixed kernel panic caused by qeth driver:
Using a bonding device qeth driver will realloc
headroom for every skb coming from the bond device.
Once this happens qeth frees the original skb and
set the skb pointer to the new realloced skb.
Under heavy transmit workload (e.g.UDP streams) through bond
network device the qdio output queue might get full.
In this case we return with EBUSY from qeth_send_packet.
Returning to qeth_hard_start_xmit routine
the skb address on the stack still points to the old address,
which has been freed before.
Returning from qeth_hard_start_xmit with EBUSY results in
requeuing the skb. In this case it corrupts the qdisc queue
and results in kernel panic.
Signed-off-by: Frank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth.h | 35 | ||||
-rw-r--r-- | drivers/s390/net/qeth_main.c | 193 | ||||
-rw-r--r-- | drivers/s390/net/qeth_tso.h | 2 |
3 files changed, 114 insertions, 116 deletions
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index e8bd8c5ced1f..c04ee915dc1b 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h | |||
@@ -859,23 +859,18 @@ qeth_get_ipa_adp_type(enum qeth_link_types link_type) | |||
859 | } | 859 | } |
860 | } | 860 | } |
861 | 861 | ||
862 | static inline int | 862 | static inline struct sk_buff * |
863 | qeth_realloc_headroom(struct qeth_card *card, struct sk_buff **skb, int size) | 863 | qeth_realloc_headroom(struct qeth_card *card, struct sk_buff *skb, int size) |
864 | { | 864 | { |
865 | struct sk_buff *new_skb = NULL; | 865 | struct sk_buff *new_skb = skb; |
866 | 866 | ||
867 | if (skb_headroom(*skb) < size){ | 867 | if (skb_headroom(skb) >= size) |
868 | new_skb = skb_realloc_headroom(*skb, size); | 868 | return skb; |
869 | if (!new_skb) { | 869 | new_skb = skb_realloc_headroom(skb, size); |
870 | PRINT_ERR("qeth_prepare_skb: could " | 870 | if (!new_skb) |
871 | "not realloc headroom for qeth_hdr " | 871 | PRINT_ERR("Could not realloc headroom for qeth_hdr " |
872 | "on interface %s", QETH_CARD_IFNAME(card)); | 872 | "on interface %s", QETH_CARD_IFNAME(card)); |
873 | return -ENOMEM; | 873 | return new_skb; |
874 | } | ||
875 | kfree_skb(*skb); | ||
876 | *skb = new_skb; | ||
877 | } | ||
878 | return 0; | ||
879 | } | 874 | } |
880 | 875 | ||
881 | static inline struct sk_buff * | 876 | static inline struct sk_buff * |
@@ -885,16 +880,15 @@ qeth_pskb_unshare(struct sk_buff *skb, int pri) | |||
885 | if (!skb_cloned(skb)) | 880 | if (!skb_cloned(skb)) |
886 | return skb; | 881 | return skb; |
887 | nskb = skb_copy(skb, pri); | 882 | nskb = skb_copy(skb, pri); |
888 | kfree_skb(skb); /* free our shared copy */ | ||
889 | return nskb; | 883 | return nskb; |
890 | } | 884 | } |
891 | 885 | ||
892 | static inline void * | 886 | static inline void * |
893 | qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) | 887 | qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, int size) |
894 | { | 888 | { |
895 | void *hdr; | 889 | void *hdr; |
896 | 890 | ||
897 | hdr = (void *) skb_push(*skb, size); | 891 | hdr = (void *) skb_push(skb, size); |
898 | /* | 892 | /* |
899 | * sanity check, the Linux memory allocation scheme should | 893 | * sanity check, the Linux memory allocation scheme should |
900 | * never present us cases like this one (the qdio header size plus | 894 | * never present us cases like this one (the qdio header size plus |
@@ -903,8 +897,7 @@ qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) | |||
903 | if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) != | 897 | if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) != |
904 | (((unsigned long) hdr + size + | 898 | (((unsigned long) hdr + size + |
905 | QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { | 899 | QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { |
906 | PRINT_ERR("qeth_prepare_skb: misaligned " | 900 | PRINT_ERR("Misaligned packet on interface %s. Discarded.", |
907 | "packet on interface %s. Discarded.", | ||
908 | QETH_CARD_IFNAME(card)); | 901 | QETH_CARD_IFNAME(card)); |
909 | return NULL; | 902 | return NULL; |
910 | } | 903 | } |
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index ffff7a12b54d..522fb9dd551e 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c | |||
@@ -3919,49 +3919,59 @@ qeth_get_ip_version(struct sk_buff *skb) | |||
3919 | } | 3919 | } |
3920 | } | 3920 | } |
3921 | 3921 | ||
3922 | static inline int | 3922 | static inline struct qeth_hdr * |
3923 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, | 3923 | __qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv) |
3924 | struct qeth_hdr **hdr, int ipv) | ||
3925 | { | 3924 | { |
3926 | int rc = 0; | ||
3927 | #ifdef CONFIG_QETH_VLAN | 3925 | #ifdef CONFIG_QETH_VLAN |
3928 | u16 *tag; | 3926 | u16 *tag; |
3929 | #endif | 3927 | if (card->vlangrp && vlan_tx_tag_present(skb) && |
3930 | |||
3931 | QETH_DBF_TEXT(trace, 6, "prepskb"); | ||
3932 | if (card->info.type == QETH_CARD_TYPE_OSN) { | ||
3933 | *hdr = (struct qeth_hdr *)(*skb)->data; | ||
3934 | return rc; | ||
3935 | } | ||
3936 | rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); | ||
3937 | if (rc) | ||
3938 | return rc; | ||
3939 | #ifdef CONFIG_QETH_VLAN | ||
3940 | if (card->vlangrp && vlan_tx_tag_present(*skb) && | ||
3941 | ((ipv == 6) || card->options.layer2) ) { | 3928 | ((ipv == 6) || card->options.layer2) ) { |
3942 | /* | 3929 | /* |
3943 | * Move the mac addresses (6 bytes src, 6 bytes dest) | 3930 | * Move the mac addresses (6 bytes src, 6 bytes dest) |
3944 | * to the beginning of the new header. We are using three | 3931 | * to the beginning of the new header. We are using three |
3945 | * memcpys instead of one memmove to save cycles. | 3932 | * memcpys instead of one memmove to save cycles. |
3946 | */ | 3933 | */ |
3947 | skb_push(*skb, VLAN_HLEN); | 3934 | skb_push(skb, VLAN_HLEN); |
3948 | memcpy((*skb)->data, (*skb)->data + 4, 4); | 3935 | memcpy(skb->data, skb->data + 4, 4); |
3949 | memcpy((*skb)->data + 4, (*skb)->data + 8, 4); | 3936 | memcpy(skb->data + 4, skb->data + 8, 4); |
3950 | memcpy((*skb)->data + 8, (*skb)->data + 12, 4); | 3937 | memcpy(skb->data + 8, skb->data + 12, 4); |
3951 | tag = (u16 *)((*skb)->data + 12); | 3938 | tag = (u16 *)(skb->data + 12); |
3952 | /* | 3939 | /* |
3953 | * first two bytes = ETH_P_8021Q (0x8100) | 3940 | * first two bytes = ETH_P_8021Q (0x8100) |
3954 | * second two bytes = VLANID | 3941 | * second two bytes = VLANID |
3955 | */ | 3942 | */ |
3956 | *tag = __constant_htons(ETH_P_8021Q); | 3943 | *tag = __constant_htons(ETH_P_8021Q); |
3957 | *(tag + 1) = htons(vlan_tx_tag_get(*skb)); | 3944 | *(tag + 1) = htons(vlan_tx_tag_get(skb)); |
3958 | } | 3945 | } |
3959 | #endif | 3946 | #endif |
3960 | *hdr = (struct qeth_hdr *) | 3947 | return ((struct qeth_hdr *) |
3961 | qeth_push_skb(card, skb, sizeof(struct qeth_hdr)); | 3948 | qeth_push_skb(card, skb, sizeof(struct qeth_hdr))); |
3962 | if (*hdr == NULL) | 3949 | } |
3963 | return -EINVAL; | 3950 | |
3964 | return 0; | 3951 | static inline void |
3952 | __qeth_free_new_skb(struct sk_buff *orig_skb, struct sk_buff *new_skb) | ||
3953 | { | ||
3954 | if (orig_skb != new_skb) | ||
3955 | dev_kfree_skb_any(new_skb); | ||
3956 | } | ||
3957 | |||
3958 | static inline struct sk_buff * | ||
3959 | qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, | ||
3960 | struct qeth_hdr **hdr, int ipv) | ||
3961 | { | ||
3962 | struct sk_buff *new_skb; | ||
3963 | |||
3964 | QETH_DBF_TEXT(trace, 6, "prepskb"); | ||
3965 | |||
3966 | new_skb = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr)); | ||
3967 | if (new_skb == NULL) | ||
3968 | return NULL; | ||
3969 | *hdr = __qeth_prepare_skb(card, new_skb, ipv); | ||
3970 | if (*hdr == NULL) { | ||
3971 | __qeth_free_new_skb(skb, new_skb); | ||
3972 | return NULL; | ||
3973 | } | ||
3974 | return new_skb; | ||
3965 | } | 3975 | } |
3966 | 3976 | ||
3967 | static inline u8 | 3977 | static inline u8 |
@@ -4242,21 +4252,15 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4242 | * check if buffer is empty to make sure that we do not 'overtake' | 4252 | * check if buffer is empty to make sure that we do not 'overtake' |
4243 | * ourselves and try to fill a buffer that is already primed | 4253 | * ourselves and try to fill a buffer that is already primed |
4244 | */ | 4254 | */ |
4245 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { | 4255 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) |
4246 | card->stats.tx_dropped++; | 4256 | goto out; |
4247 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | ||
4248 | return -EBUSY; | ||
4249 | } | ||
4250 | if (ctx == NULL) | 4257 | if (ctx == NULL) |
4251 | queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % | 4258 | queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % |
4252 | QDIO_MAX_BUFFERS_PER_Q; | 4259 | QDIO_MAX_BUFFERS_PER_Q; |
4253 | else { | 4260 | else { |
4254 | buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx); | 4261 | buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx); |
4255 | if (buffers_needed < 0) { | 4262 | if (buffers_needed < 0) |
4256 | card->stats.tx_dropped++; | 4263 | goto out; |
4257 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | ||
4258 | return -EBUSY; | ||
4259 | } | ||
4260 | queue->next_buf_to_fill = | 4264 | queue->next_buf_to_fill = |
4261 | (queue->next_buf_to_fill + buffers_needed) % | 4265 | (queue->next_buf_to_fill + buffers_needed) % |
4262 | QDIO_MAX_BUFFERS_PER_Q; | 4266 | QDIO_MAX_BUFFERS_PER_Q; |
@@ -4271,6 +4275,9 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4271 | qeth_flush_buffers(queue, 0, index, flush_cnt); | 4275 | qeth_flush_buffers(queue, 0, index, flush_cnt); |
4272 | } | 4276 | } |
4273 | return 0; | 4277 | return 0; |
4278 | out: | ||
4279 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | ||
4280 | return -EBUSY; | ||
4274 | } | 4281 | } |
4275 | 4282 | ||
4276 | static inline int | 4283 | static inline int |
@@ -4296,8 +4303,7 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4296 | * check if buffer is empty to make sure that we do not 'overtake' | 4303 | * check if buffer is empty to make sure that we do not 'overtake' |
4297 | * ourselves and try to fill a buffer that is already primed | 4304 | * ourselves and try to fill a buffer that is already primed |
4298 | */ | 4305 | */ |
4299 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){ | 4306 | if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { |
4300 | card->stats.tx_dropped++; | ||
4301 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | 4307 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); |
4302 | return -EBUSY; | 4308 | return -EBUSY; |
4303 | } | 4309 | } |
@@ -4320,7 +4326,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4320 | * again */ | 4326 | * again */ |
4321 | if (atomic_read(&buffer->state) != | 4327 | if (atomic_read(&buffer->state) != |
4322 | QETH_QDIO_BUF_EMPTY){ | 4328 | QETH_QDIO_BUF_EMPTY){ |
4323 | card->stats.tx_dropped++; | ||
4324 | qeth_flush_buffers(queue, 0, start_index, flush_count); | 4329 | qeth_flush_buffers(queue, 0, start_index, flush_count); |
4325 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); | 4330 | atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); |
4326 | return -EBUSY; | 4331 | return -EBUSY; |
@@ -4331,7 +4336,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4331 | * free buffers) to handle eddp context */ | 4336 | * free buffers) to handle eddp context */ |
4332 | if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){ | 4337 | if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){ |
4333 | printk("eddp tx_dropped 1\n"); | 4338 | printk("eddp tx_dropped 1\n"); |
4334 | card->stats.tx_dropped++; | ||
4335 | rc = -EBUSY; | 4339 | rc = -EBUSY; |
4336 | goto out; | 4340 | goto out; |
4337 | } | 4341 | } |
@@ -4343,7 +4347,6 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, | |||
4343 | tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill); | 4347 | tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill); |
4344 | if (tmp < 0) { | 4348 | if (tmp < 0) { |
4345 | printk("eddp tx_dropped 2\n"); | 4349 | printk("eddp tx_dropped 2\n"); |
4346 | card->stats.tx_dropped++; | ||
4347 | rc = - EBUSY; | 4350 | rc = - EBUSY; |
4348 | goto out; | 4351 | goto out; |
4349 | } | 4352 | } |
@@ -4391,21 +4394,21 @@ qeth_get_elements_no(struct qeth_card *card, void *hdr, | |||
4391 | { | 4394 | { |
4392 | int elements_needed = 0; | 4395 | int elements_needed = 0; |
4393 | 4396 | ||
4394 | if (skb_shinfo(skb)->nr_frags > 0) { | 4397 | if (skb_shinfo(skb)->nr_frags > 0) |
4395 | elements_needed = (skb_shinfo(skb)->nr_frags + 1); | 4398 | elements_needed = (skb_shinfo(skb)->nr_frags + 1); |
4396 | } | 4399 | if (elements_needed == 0) |
4397 | if (elements_needed == 0 ) | ||
4398 | elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) | 4400 | elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) |
4399 | + skb->len) >> PAGE_SHIFT); | 4401 | + skb->len) >> PAGE_SHIFT); |
4400 | if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){ | 4402 | if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){ |
4401 | PRINT_ERR("qeth_do_send_packet: invalid size of " | 4403 | PRINT_ERR("Invalid size of IP packet " |
4402 | "IP packet (Number=%d / Length=%d). Discarded.\n", | 4404 | "(Number=%d / Length=%d). Discarded.\n", |
4403 | (elements_needed+elems), skb->len); | 4405 | (elements_needed+elems), skb->len); |
4404 | return 0; | 4406 | return 0; |
4405 | } | 4407 | } |
4406 | return elements_needed; | 4408 | return elements_needed; |
4407 | } | 4409 | } |
4408 | 4410 | ||
4411 | |||
4409 | static inline int | 4412 | static inline int |
4410 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | 4413 | qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) |
4411 | { | 4414 | { |
@@ -4421,108 +4424,110 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) | |||
4421 | unsigned short nr_frags = skb_shinfo(skb)->nr_frags; | 4424 | unsigned short nr_frags = skb_shinfo(skb)->nr_frags; |
4422 | unsigned short tso_size = skb_shinfo(skb)->gso_size; | 4425 | unsigned short tso_size = skb_shinfo(skb)->gso_size; |
4423 | #endif | 4426 | #endif |
4427 | struct sk_buff *new_skb, *new_skb2; | ||
4424 | int rc; | 4428 | int rc; |
4425 | 4429 | ||
4426 | QETH_DBF_TEXT(trace, 6, "sendpkt"); | 4430 | QETH_DBF_TEXT(trace, 6, "sendpkt"); |
4427 | 4431 | ||
4432 | new_skb = skb; | ||
4433 | if ((card->info.type == QETH_CARD_TYPE_OSN) && | ||
4434 | (skb->protocol == htons(ETH_P_IPV6))) | ||
4435 | return -EPERM; | ||
4436 | cast_type = qeth_get_cast_type(card, skb); | ||
4437 | if ((cast_type == RTN_BROADCAST) && | ||
4438 | (card->info.broadcast_capable == 0)) | ||
4439 | return -EPERM; | ||
4440 | queue = card->qdio.out_qs | ||
4441 | [qeth_get_priority_queue(card, skb, ipv, cast_type)]; | ||
4428 | if (!card->options.layer2) { | 4442 | if (!card->options.layer2) { |
4429 | ipv = qeth_get_ip_version(skb); | 4443 | ipv = qeth_get_ip_version(skb); |
4430 | if ((card->dev->hard_header == qeth_fake_header) && ipv) { | 4444 | if ((card->dev->hard_header == qeth_fake_header) && ipv) { |
4431 | if ((skb = qeth_pskb_unshare(skb,GFP_ATOMIC)) == NULL) { | 4445 | new_skb = qeth_pskb_unshare(skb, GFP_ATOMIC); |
4432 | card->stats.tx_dropped++; | 4446 | if (!new_skb) |
4433 | dev_kfree_skb_irq(skb); | 4447 | return -ENOMEM; |
4434 | return 0; | ||
4435 | } | ||
4436 | if(card->dev->type == ARPHRD_IEEE802_TR){ | 4448 | if(card->dev->type == ARPHRD_IEEE802_TR){ |
4437 | skb_pull(skb, QETH_FAKE_LL_LEN_TR); | 4449 | skb_pull(new_skb, QETH_FAKE_LL_LEN_TR); |
4438 | } else { | 4450 | } else { |
4439 | skb_pull(skb, QETH_FAKE_LL_LEN_ETH); | 4451 | skb_pull(new_skb, QETH_FAKE_LL_LEN_ETH); |
4440 | } | 4452 | } |
4441 | } | 4453 | } |
4442 | } | 4454 | } |
4443 | if ((card->info.type == QETH_CARD_TYPE_OSN) && | ||
4444 | (skb->protocol == htons(ETH_P_IPV6))) { | ||
4445 | dev_kfree_skb_any(skb); | ||
4446 | return 0; | ||
4447 | } | ||
4448 | cast_type = qeth_get_cast_type(card, skb); | ||
4449 | if ((cast_type == RTN_BROADCAST) && | ||
4450 | (card->info.broadcast_capable == 0)){ | ||
4451 | card->stats.tx_dropped++; | ||
4452 | card->stats.tx_errors++; | ||
4453 | dev_kfree_skb_any(skb); | ||
4454 | return NETDEV_TX_OK; | ||
4455 | } | ||
4456 | queue = card->qdio.out_qs | ||
4457 | [qeth_get_priority_queue(card, skb, ipv, cast_type)]; | ||
4458 | |||
4459 | if (skb_is_gso(skb)) | 4455 | if (skb_is_gso(skb)) |
4460 | large_send = card->options.large_send; | 4456 | large_send = card->options.large_send; |
4461 | 4457 | /* check on OSN device*/ | |
4462 | /*are we able to do TSO ? If so ,prepare and send it from here */ | 4458 | if (card->info.type == QETH_CARD_TYPE_OSN) |
4459 | hdr = (struct qeth_hdr *)new_skb->data; | ||
4460 | /*are we able to do TSO ? */ | ||
4463 | if ((large_send == QETH_LARGE_SEND_TSO) && | 4461 | if ((large_send == QETH_LARGE_SEND_TSO) && |
4464 | (cast_type == RTN_UNSPEC)) { | 4462 | (cast_type == RTN_UNSPEC)) { |
4465 | rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); | 4463 | rc = qeth_tso_prepare_packet(card, new_skb, ipv, cast_type); |
4466 | if (rc) { | 4464 | if (rc) { |
4467 | card->stats.tx_dropped++; | 4465 | __qeth_free_new_skb(skb, new_skb); |
4468 | card->stats.tx_errors++; | 4466 | return rc; |
4469 | dev_kfree_skb_any(skb); | ||
4470 | return NETDEV_TX_OK; | ||
4471 | } | 4467 | } |
4472 | elements_needed++; | 4468 | elements_needed++; |
4473 | } else { | 4469 | } else if (card->info.type != QETH_CARD_TYPE_OSN) { |
4474 | if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { | 4470 | new_skb2 = qeth_prepare_skb(card, new_skb, &hdr, ipv); |
4475 | QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); | 4471 | if (!new_skb2) { |
4476 | return rc; | 4472 | __qeth_free_new_skb(skb, new_skb); |
4473 | return -EINVAL; | ||
4477 | } | 4474 | } |
4478 | if (card->info.type != QETH_CARD_TYPE_OSN) | 4475 | if (new_skb != skb) |
4479 | qeth_fill_header(card, hdr, skb, ipv, cast_type); | 4476 | __qeth_free_new_skb(new_skb2, new_skb); |
4477 | new_skb = new_skb2; | ||
4478 | qeth_fill_header(card, hdr, new_skb, ipv, cast_type); | ||
4480 | } | 4479 | } |
4481 | |||
4482 | if (large_send == QETH_LARGE_SEND_EDDP) { | 4480 | if (large_send == QETH_LARGE_SEND_EDDP) { |
4483 | ctx = qeth_eddp_create_context(card, skb, hdr); | 4481 | ctx = qeth_eddp_create_context(card, new_skb, hdr); |
4484 | if (ctx == NULL) { | 4482 | if (ctx == NULL) { |
4483 | __qeth_free_new_skb(skb, new_skb); | ||
4485 | PRINT_WARN("could not create eddp context\n"); | 4484 | PRINT_WARN("could not create eddp context\n"); |
4486 | return -EINVAL; | 4485 | return -EINVAL; |
4487 | } | 4486 | } |
4488 | } else { | 4487 | } else { |
4489 | int elems = qeth_get_elements_no(card,(void*) hdr, skb, | 4488 | int elems = qeth_get_elements_no(card,(void*) hdr, new_skb, |
4490 | elements_needed); | 4489 | elements_needed); |
4491 | if (!elems) | 4490 | if (!elems) { |
4491 | __qeth_free_new_skb(skb, new_skb); | ||
4492 | return -EINVAL; | 4492 | return -EINVAL; |
4493 | } | ||
4493 | elements_needed += elems; | 4494 | elements_needed += elems; |
4494 | } | 4495 | } |
4495 | 4496 | ||
4496 | if (card->info.type != QETH_CARD_TYPE_IQD) | 4497 | if (card->info.type != QETH_CARD_TYPE_IQD) |
4497 | rc = qeth_do_send_packet(card, queue, skb, hdr, | 4498 | rc = qeth_do_send_packet(card, queue, new_skb, hdr, |
4498 | elements_needed, ctx); | 4499 | elements_needed, ctx); |
4499 | else | 4500 | else |
4500 | rc = qeth_do_send_packet_fast(card, queue, skb, hdr, | 4501 | rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, |
4501 | elements_needed, ctx); | 4502 | elements_needed, ctx); |
4502 | if (!rc){ | 4503 | if (!rc) { |
4503 | card->stats.tx_packets++; | 4504 | card->stats.tx_packets++; |
4504 | card->stats.tx_bytes += tx_bytes; | 4505 | card->stats.tx_bytes += tx_bytes; |
4506 | if (new_skb != skb) | ||
4507 | dev_kfree_skb_any(skb); | ||
4505 | #ifdef CONFIG_QETH_PERF_STATS | 4508 | #ifdef CONFIG_QETH_PERF_STATS |
4506 | if (tso_size && | 4509 | if (tso_size && |
4507 | !(large_send == QETH_LARGE_SEND_NO)) { | 4510 | !(large_send == QETH_LARGE_SEND_NO)) { |
4508 | card->perf_stats.large_send_bytes += tx_bytes; | 4511 | card->perf_stats.large_send_bytes += tx_bytes; |
4509 | card->perf_stats.large_send_cnt++; | 4512 | card->perf_stats.large_send_cnt++; |
4510 | } | 4513 | } |
4511 | if (nr_frags > 0){ | 4514 | if (nr_frags > 0) { |
4512 | card->perf_stats.sg_skbs_sent++; | 4515 | card->perf_stats.sg_skbs_sent++; |
4513 | /* nr_frags + skb->data */ | 4516 | /* nr_frags + skb->data */ |
4514 | card->perf_stats.sg_frags_sent += | 4517 | card->perf_stats.sg_frags_sent += |
4515 | nr_frags + 1; | 4518 | nr_frags + 1; |
4516 | } | 4519 | } |
4517 | #endif /* CONFIG_QETH_PERF_STATS */ | 4520 | #endif /* CONFIG_QETH_PERF_STATS */ |
4521 | } else { | ||
4522 | card->stats.tx_dropped++; | ||
4523 | __qeth_free_new_skb(skb, new_skb); | ||
4518 | } | 4524 | } |
4519 | if (ctx != NULL) { | 4525 | if (ctx != NULL) { |
4520 | /* drop creator's reference */ | 4526 | /* drop creator's reference */ |
4521 | qeth_eddp_put_context(ctx); | 4527 | qeth_eddp_put_context(ctx); |
4522 | /* free skb; it's not referenced by a buffer */ | 4528 | /* free skb; it's not referenced by a buffer */ |
4523 | if (rc == 0) | 4529 | if (!rc) |
4524 | dev_kfree_skb_any(skb); | 4530 | dev_kfree_skb_any(new_skb); |
4525 | |||
4526 | } | 4531 | } |
4527 | return rc; | 4532 | return rc; |
4528 | } | 4533 | } |
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h index 593f298142c1..14504afb044e 100644 --- a/drivers/s390/net/qeth_tso.h +++ b/drivers/s390/net/qeth_tso.h | |||
@@ -24,7 +24,7 @@ static inline struct qeth_hdr_tso * | |||
24 | qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) | 24 | qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) |
25 | { | 25 | { |
26 | QETH_DBF_TEXT(trace, 5, "tsoprsk"); | 26 | QETH_DBF_TEXT(trace, 5, "tsoprsk"); |
27 | return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso)); | 27 | return qeth_push_skb(card, *skb, sizeof(struct qeth_hdr_tso)); |
28 | } | 28 | } |
29 | 29 | ||
30 | /** | 30 | /** |