Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Frank Pavlic and committed by
Jeff Garzik
f7b65d70 1fda1a12

+114 -116
+13 -20
drivers/s390/net/qeth.h
··· 859 859 } 860 860 } 861 861 862 - static inline int 863 - qeth_realloc_headroom(struct qeth_card *card, struct sk_buff **skb, int size) 862 + static inline struct sk_buff * 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){ 868 - new_skb = skb_realloc_headroom(*skb, size); 869 - if (!new_skb) { 870 - PRINT_ERR("qeth_prepare_skb: could " 871 - "not realloc headroom for qeth_hdr " 872 - "on interface %s", QETH_CARD_IFNAME(card)); 873 - return -ENOMEM; 874 - } 875 - kfree_skb(*skb); 876 - *skb = new_skb; 877 - } 878 - return 0; 867 + if (skb_headroom(skb) >= size) 868 + return skb; 869 + new_skb = skb_realloc_headroom(skb, size); 870 + if (!new_skb) 871 + PRINT_ERR("Could not realloc headroom for qeth_hdr " 872 + "on interface %s", QETH_CARD_IFNAME(card)); 873 + return new_skb; 879 874 } 880 875 881 876 static inline struct sk_buff * ··· 880 885 if (!skb_cloned(skb)) 881 886 return skb; 882 887 nskb = skb_copy(skb, pri); 883 - kfree_skb(skb); /* free our shared copy */ 884 888 return nskb; 885 889 } 886 890 887 891 static inline void * 888 - qeth_push_skb(struct qeth_card *card, struct sk_buff **skb, int size) 892 + qeth_push_skb(struct qeth_card *card, struct sk_buff *skb, int size) 889 893 { 890 894 void *hdr; 891 895 892 - hdr = (void *) skb_push(*skb, size); 896 + hdr = (void *) skb_push(skb, size); 893 897 /* 894 898 * sanity check, the Linux memory allocation scheme should 895 899 * never present us cases like this one (the qdio header size plus ··· 897 903 if ((((unsigned long) hdr) & (~(PAGE_SIZE - 1))) != 898 904 (((unsigned long) hdr + size + 899 905 QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) { 900 - PRINT_ERR("qeth_prepare_skb: misaligned " 901 - "packet on interface %s. Discarded.", 906 + PRINT_ERR("Misaligned packet on interface %s. Discarded.", 902 907 QETH_CARD_IFNAME(card)); 903 908 return NULL; 904 909 }
+100 -95
drivers/s390/net/qeth_main.c
··· 3919 3919 } 3920 3920 } 3921 3921 3922 - static inline int 3923 - qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb, 3924 - struct qeth_hdr **hdr, int ipv) 3922 + static inline struct qeth_hdr * 3923 + __qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv) 3925 3924 { 3926 - int rc = 0; 3927 3925 #ifdef CONFIG_QETH_VLAN 3928 3926 u16 *tag; 3929 - #endif 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) && 3927 + if (card->vlangrp && vlan_tx_tag_present(skb) && 3941 3928 ((ipv == 6) || card->options.layer2) ) { 3942 3929 /* 3943 3930 * Move the mac addresses (6 bytes src, 6 bytes dest) 3944 3931 * to the beginning of the new header. We are using three 3945 3932 * memcpys instead of one memmove to save cycles. 3946 3933 */ 3947 - skb_push(*skb, VLAN_HLEN); 3948 - memcpy((*skb)->data, (*skb)->data + 4, 4); 3949 - memcpy((*skb)->data + 4, (*skb)->data + 8, 4); 3950 - memcpy((*skb)->data + 8, (*skb)->data + 12, 4); 3951 - tag = (u16 *)((*skb)->data + 12); 3934 + skb_push(skb, VLAN_HLEN); 3935 + memcpy(skb->data, skb->data + 4, 4); 3936 + memcpy(skb->data + 4, skb->data + 8, 4); 3937 + memcpy(skb->data + 8, skb->data + 12, 4); 3938 + tag = (u16 *)(skb->data + 12); 3952 3939 /* 3953 3940 * first two bytes = ETH_P_8021Q (0x8100) 3954 3941 * second two bytes = VLANID 3955 3942 */ 3956 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 3946 #endif 3960 - *hdr = (struct qeth_hdr *) 3961 - qeth_push_skb(card, skb, sizeof(struct qeth_hdr)); 3962 - if (*hdr == NULL) 3963 - return -EINVAL; 3964 - return 0; 3947 + return ((struct qeth_hdr *) 3948 + qeth_push_skb(card, skb, sizeof(struct qeth_hdr))); 3949 + } 3950 + 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 3977 static inline u8 ··· 4252 4242 * check if buffer is empty to make sure that we do not 'overtake' 4253 4243 * ourselves and try to fill a buffer that is already primed 4254 4244 */ 4255 - if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { 4256 - card->stats.tx_dropped++; 4257 - atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 4258 - return -EBUSY; 4259 - } 4245 + if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) 4246 + goto out; 4260 4247 if (ctx == NULL) 4261 4248 queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) % 4262 4249 QDIO_MAX_BUFFERS_PER_Q; 4263 4250 else { 4264 4251 buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx); 4265 - if (buffers_needed < 0) { 4266 - card->stats.tx_dropped++; 4267 - atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 4268 - return -EBUSY; 4269 - } 4252 + if (buffers_needed < 0) 4253 + goto out; 4270 4254 queue->next_buf_to_fill = 4271 4255 (queue->next_buf_to_fill + buffers_needed) % 4272 4256 QDIO_MAX_BUFFERS_PER_Q; ··· 4275 4271 qeth_flush_buffers(queue, 0, index, flush_cnt); 4276 4272 } 4277 4273 return 0; 4274 + out: 4275 + atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 4276 + return -EBUSY; 4278 4277 } 4279 4278 4280 4279 static inline int ··· 4303 4296 * check if buffer is empty to make sure that we do not 'overtake' 4304 4297 * ourselves and try to fill a buffer that is already primed 4305 4298 */ 4306 - if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){ 4307 - card->stats.tx_dropped++; 4299 + if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) { 4308 4300 atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 4309 4301 return -EBUSY; 4310 4302 } ··· 4326 4320 * again */ 4327 4321 if (atomic_read(&buffer->state) != 4328 4322 QETH_QDIO_BUF_EMPTY){ 4329 - card->stats.tx_dropped++; 4330 4323 qeth_flush_buffers(queue, 0, start_index, flush_count); 4331 4324 atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); 4332 4325 return -EBUSY; ··· 4336 4331 * free buffers) to handle eddp context */ 4337 4332 if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){ 4338 4333 printk("eddp tx_dropped 1\n"); 4339 - card->stats.tx_dropped++; 4340 4334 rc = -EBUSY; 4341 4335 goto out; 4342 4336 } ··· 4347 4343 tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill); 4348 4344 if (tmp < 0) { 4349 4345 printk("eddp tx_dropped 2\n"); 4350 - card->stats.tx_dropped++; 4351 4346 rc = - EBUSY; 4352 4347 goto out; 4353 4348 } ··· 4394 4391 { 4395 4392 int elements_needed = 0; 4396 4393 4397 - if (skb_shinfo(skb)->nr_frags > 0) { 4394 + if (skb_shinfo(skb)->nr_frags > 0) 4398 4395 elements_needed = (skb_shinfo(skb)->nr_frags + 1); 4399 - } 4400 - if (elements_needed == 0 ) 4396 + if (elements_needed == 0) 4401 4397 elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) 4402 4398 + skb->len) >> PAGE_SHIFT); 4403 4399 if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){ 4404 - PRINT_ERR("qeth_do_send_packet: invalid size of " 4405 - "IP packet (Number=%d / Length=%d). Discarded.\n", 4400 + PRINT_ERR("Invalid size of IP packet " 4401 + "(Number=%d / Length=%d). Discarded.\n", 4406 4402 (elements_needed+elems), skb->len); 4407 4403 return 0; 4408 4404 } 4409 4405 return elements_needed; 4410 4406 } 4407 + 4411 4408 4412 4409 static inline int 4413 4410 qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) ··· 4424 4421 unsigned short nr_frags = skb_shinfo(skb)->nr_frags; 4425 4422 unsigned short tso_size = skb_shinfo(skb)->gso_size; 4426 4423 #endif 4424 + struct sk_buff *new_skb, *new_skb2; 4427 4425 int rc; 4428 4426 4429 4427 QETH_DBF_TEXT(trace, 6, "sendpkt"); 4430 4428 4429 + new_skb = skb; 4430 + if ((card->info.type == QETH_CARD_TYPE_OSN) && 4431 + (skb->protocol == htons(ETH_P_IPV6))) 4432 + return -EPERM; 4433 + cast_type = qeth_get_cast_type(card, skb); 4434 + if ((cast_type == RTN_BROADCAST) && 4435 + (card->info.broadcast_capable == 0)) 4436 + return -EPERM; 4437 + queue = card->qdio.out_qs 4438 + [qeth_get_priority_queue(card, skb, ipv, cast_type)]; 4431 4439 if (!card->options.layer2) { 4432 4440 ipv = qeth_get_ip_version(skb); 4433 4441 if ((card->dev->hard_header == qeth_fake_header) && ipv) { 4434 - if ((skb = qeth_pskb_unshare(skb,GFP_ATOMIC)) == NULL) { 4435 - card->stats.tx_dropped++; 4436 - dev_kfree_skb_irq(skb); 4437 - return 0; 4438 - } 4442 + new_skb = qeth_pskb_unshare(skb, GFP_ATOMIC); 4443 + if (!new_skb) 4444 + return -ENOMEM; 4439 4445 if(card->dev->type == ARPHRD_IEEE802_TR){ 4440 - skb_pull(skb, QETH_FAKE_LL_LEN_TR); 4446 + skb_pull(new_skb, QETH_FAKE_LL_LEN_TR); 4441 4447 } else { 4442 - skb_pull(skb, QETH_FAKE_LL_LEN_ETH); 4448 + skb_pull(new_skb, QETH_FAKE_LL_LEN_ETH); 4443 4449 } 4444 4450 } 4445 4451 } 4446 - if ((card->info.type == QETH_CARD_TYPE_OSN) && 4447 - (skb->protocol == htons(ETH_P_IPV6))) { 4448 - dev_kfree_skb_any(skb); 4449 - return 0; 4450 - } 4451 - cast_type = qeth_get_cast_type(card, skb); 4452 - if ((cast_type == RTN_BROADCAST) && 4453 - (card->info.broadcast_capable == 0)){ 4454 - card->stats.tx_dropped++; 4455 - card->stats.tx_errors++; 4456 - dev_kfree_skb_any(skb); 4457 - return NETDEV_TX_OK; 4458 - } 4459 - queue = card->qdio.out_qs 4460 - [qeth_get_priority_queue(card, skb, ipv, cast_type)]; 4461 - 4462 4452 if (skb_is_gso(skb)) 4463 4453 large_send = card->options.large_send; 4464 - 4465 - /*are we able to do TSO ? If so ,prepare and send it from here */ 4454 + /* check on OSN device*/ 4455 + if (card->info.type == QETH_CARD_TYPE_OSN) 4456 + hdr = (struct qeth_hdr *)new_skb->data; 4457 + /*are we able to do TSO ? */ 4466 4458 if ((large_send == QETH_LARGE_SEND_TSO) && 4467 4459 (cast_type == RTN_UNSPEC)) { 4468 - rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type); 4460 + rc = qeth_tso_prepare_packet(card, new_skb, ipv, cast_type); 4469 4461 if (rc) { 4470 - card->stats.tx_dropped++; 4471 - card->stats.tx_errors++; 4472 - dev_kfree_skb_any(skb); 4473 - return NETDEV_TX_OK; 4474 - } 4475 - elements_needed++; 4476 - } else { 4477 - if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) { 4478 - QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc); 4462 + __qeth_free_new_skb(skb, new_skb); 4479 4463 return rc; 4480 4464 } 4481 - if (card->info.type != QETH_CARD_TYPE_OSN) 4482 - qeth_fill_header(card, hdr, skb, ipv, cast_type); 4465 + elements_needed++; 4466 + } else if (card->info.type != QETH_CARD_TYPE_OSN) { 4467 + new_skb2 = qeth_prepare_skb(card, new_skb, &hdr, ipv); 4468 + if (!new_skb2) { 4469 + __qeth_free_new_skb(skb, new_skb); 4470 + return -EINVAL; 4471 + } 4472 + if (new_skb != skb) 4473 + __qeth_free_new_skb(new_skb2, new_skb); 4474 + new_skb = new_skb2; 4475 + qeth_fill_header(card, hdr, new_skb, ipv, cast_type); 4483 4476 } 4484 - 4485 4477 if (large_send == QETH_LARGE_SEND_EDDP) { 4486 - ctx = qeth_eddp_create_context(card, skb, hdr); 4478 + ctx = qeth_eddp_create_context(card, new_skb, hdr); 4487 4479 if (ctx == NULL) { 4480 + __qeth_free_new_skb(skb, new_skb); 4488 4481 PRINT_WARN("could not create eddp context\n"); 4489 4482 return -EINVAL; 4490 4483 } 4491 4484 } else { 4492 - int elems = qeth_get_elements_no(card,(void*) hdr, skb, 4485 + int elems = qeth_get_elements_no(card,(void*) hdr, new_skb, 4493 4486 elements_needed); 4494 - if (!elems) 4487 + if (!elems) { 4488 + __qeth_free_new_skb(skb, new_skb); 4495 4489 return -EINVAL; 4490 + } 4496 4491 elements_needed += elems; 4497 4492 } 4498 4493 4499 4494 if (card->info.type != QETH_CARD_TYPE_IQD) 4500 - rc = qeth_do_send_packet(card, queue, skb, hdr, 4495 + rc = qeth_do_send_packet(card, queue, new_skb, hdr, 4501 4496 elements_needed, ctx); 4502 4497 else 4503 - rc = qeth_do_send_packet_fast(card, queue, skb, hdr, 4498 + rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, 4504 4499 elements_needed, ctx); 4505 - if (!rc){ 4500 + if (!rc) { 4506 4501 card->stats.tx_packets++; 4507 4502 card->stats.tx_bytes += tx_bytes; 4503 + if (new_skb != skb) 4504 + dev_kfree_skb_any(skb); 4508 4505 #ifdef CONFIG_QETH_PERF_STATS 4509 4506 if (tso_size && 4510 4507 !(large_send == QETH_LARGE_SEND_NO)) { 4511 4508 card->perf_stats.large_send_bytes += tx_bytes; 4512 4509 card->perf_stats.large_send_cnt++; 4513 4510 } 4514 - if (nr_frags > 0){ 4511 + if (nr_frags > 0) { 4515 4512 card->perf_stats.sg_skbs_sent++; 4516 4513 /* nr_frags + skb->data */ 4517 4514 card->perf_stats.sg_frags_sent += 4518 4515 nr_frags + 1; 4519 4516 } 4520 4517 #endif /* CONFIG_QETH_PERF_STATS */ 4518 + } else { 4519 + card->stats.tx_dropped++; 4520 + __qeth_free_new_skb(skb, new_skb); 4521 4521 } 4522 4522 if (ctx != NULL) { 4523 4523 /* drop creator's reference */ 4524 4524 qeth_eddp_put_context(ctx); 4525 4525 /* free skb; it's not referenced by a buffer */ 4526 - if (rc == 0) 4527 - dev_kfree_skb_any(skb); 4528 - 4526 + if (!rc) 4527 + dev_kfree_skb_any(new_skb); 4529 4528 } 4530 4529 return rc; 4531 4530 }
+1 -1
drivers/s390/net/qeth_tso.h
··· 24 24 qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb) 25 25 { 26 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 /**