aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrasanna S. Panchamukhi <prasannax.s.panchamukhi@intel.com>2010-04-08 19:24:31 -0400
committerInaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>2010-05-11 17:06:36 -0400
commit85a19e07e30f67c517266cafe92b7bcd9b98966d (patch)
treee5bc9d14fecfd4dd6f8cfc297ebe689cec4eaebb
parent9e6e3bd52b0f77ca5cc385892c14ff8ba5ecfa67 (diff)
wimax/i2400m: fix system freeze caused by an infinite loop [v1]
This patch fixes an infinite loop caused by i2400m_tx_fifo_push() due to a corner case where there is no tail space in the TX FIFO. Please refer the documentation in the code for details. Signed-off-by: Prasanna S. Panchamukhi <prasannax.s.panchamukhi@intel.com>
-rw-r--r--drivers/net/wimax/i2400m/tx.c65
1 files changed, 60 insertions, 5 deletions
diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c
index 101550a2f5a2..609f1ca6e9fc 100644
--- a/drivers/net/wimax/i2400m/tx.c
+++ b/drivers/net/wimax/i2400m/tx.c
@@ -341,6 +341,14 @@ size_t __i2400m_tx_tail_room(struct i2400m *i2400m)
341 * @padding: ensure that there is at least this many bytes of free 341 * @padding: ensure that there is at least this many bytes of free
342 * contiguous space in the fifo. This is needed because later on 342 * contiguous space in the fifo. This is needed because later on
343 * we might need to add padding. 343 * we might need to add padding.
344 * @try_head: specify either to allocate head room or tail room space
345 * in the TX FIFO. This boolean is required to avoids a system hang
346 * due to an infinite loop caused by i2400m_tx_fifo_push().
347 * The caller must always try to allocate tail room space first by
348 * calling this routine with try_head = 0. In case if there
349 * is not enough tail room space but there is enough head room space,
350 * (i2400m_tx_fifo_push() returns TAIL_FULL) try to allocate head
351 * room space, by calling this routine again with try_head = 1.
344 * 352 *
345 * Returns: 353 * Returns:
346 * 354 *
@@ -372,6 +380,48 @@ size_t __i2400m_tx_tail_room(struct i2400m *i2400m)
372 * fail and return TAIL_FULL and let the caller figure out if we wants to 380 * fail and return TAIL_FULL and let the caller figure out if we wants to
373 * skip the tail room and try to allocate from the head. 381 * skip the tail room and try to allocate from the head.
374 * 382 *
383 * There is a corner case, wherein i2400m_tx_new() can get into
384 * an infinite loop calling i2400m_tx_fifo_push().
385 * In certain situations, tx_in would have reached on the top of TX FIFO
386 * and i2400m_tx_tail_room() returns 0, as described below:
387 *
388 * N ___________ tail room is zero
389 * |<- IN ->|
390 * | |
391 * | |
392 * | |
393 * | data |
394 * |<- OUT ->|
395 * | |
396 * | |
397 * | head room |
398 * 0 -----------
399 * During such a time, where tail room is zero in the TX FIFO and if there
400 * is a request to add a payload to TX FIFO, which calls:
401 * i2400m_tx()
402 * ->calls i2400m_tx_close()
403 * ->calls i2400m_tx_skip_tail()
404 * goto try_new;
405 * ->calls i2400m_tx_new()
406 * |----> [try_head:]
407 * infinite loop | ->calls i2400m_tx_fifo_push()
408 * | if (tail_room < needed)
409 * | if (head_room => needed)
410 * | return TAIL_FULL;
411 * |<---- goto try_head;
412 *
413 * i2400m_tx() calls i2400m_tx_close() to close the message, since there
414 * is no tail room to accommodate the payload and calls
415 * i2400m_tx_skip_tail() to skip the tail space. Now i2400m_tx() calls
416 * i2400m_tx_new() to allocate space for new message header calling
417 * i2400m_tx_fifo_push() that returns TAIL_FULL, since there is no tail space
418 * to accommodate the message header, but there is enough head space.
419 * The i2400m_tx_new() keeps re-retrying by calling i2400m_tx_fifo_push()
420 * ending up in a loop causing system freeze.
421 *
422 * This corner case is avoided by using a try_head boolean,
423 * as an argument to i2400m_tx_fifo_push().
424 *
375 * Note: 425 * Note:
376 * 426 *
377 * Assumes i2400m->tx_lock is taken, and we use that as a barrier 427 * Assumes i2400m->tx_lock is taken, and we use that as a barrier
@@ -380,7 +430,8 @@ size_t __i2400m_tx_tail_room(struct i2400m *i2400m)
380 * pop data off the queue 430 * pop data off the queue
381 */ 431 */
382static 432static
383void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) 433void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size,
434 size_t padding, bool try_head)
384{ 435{
385 struct device *dev = i2400m_dev(i2400m); 436 struct device *dev = i2400m_dev(i2400m);
386 size_t room, tail_room, needed_size; 437 size_t room, tail_room, needed_size;
@@ -395,7 +446,7 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding)
395 } 446 }
396 /* Is there space at the tail? */ 447 /* Is there space at the tail? */
397 tail_room = __i2400m_tx_tail_room(i2400m); 448 tail_room = __i2400m_tx_tail_room(i2400m);
398 if (tail_room < needed_size) { 449 if (!try_head && tail_room < needed_size) {
399 /* 450 /*
400 * If the tail room space is not enough to push the message 451 * If the tail room space is not enough to push the message
401 * in the TX FIFO, then there are two possibilities: 452 * in the TX FIFO, then there are two possibilities:
@@ -510,14 +561,16 @@ void i2400m_tx_new(struct i2400m *i2400m)
510{ 561{
511 struct device *dev = i2400m_dev(i2400m); 562 struct device *dev = i2400m_dev(i2400m);
512 struct i2400m_msg_hdr *tx_msg; 563 struct i2400m_msg_hdr *tx_msg;
564 bool try_head = 0;
513 BUG_ON(i2400m->tx_msg != NULL); 565 BUG_ON(i2400m->tx_msg != NULL);
514try_head: 566try_head:
515 tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, 0); 567 tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, 0, try_head);
516 if (tx_msg == NULL) 568 if (tx_msg == NULL)
517 goto out; 569 goto out;
518 else if (tx_msg == TAIL_FULL) { 570 else if (tx_msg == TAIL_FULL) {
519 i2400m_tx_skip_tail(i2400m); 571 i2400m_tx_skip_tail(i2400m);
520 d_printf(2, dev, "new TX message: tail full, trying head\n"); 572 d_printf(2, dev, "new TX message: tail full, trying head\n");
573 try_head = 1;
521 goto try_head; 574 goto try_head;
522 } 575 }
523 memset(tx_msg, 0, I2400M_TX_PLD_SIZE); 576 memset(tx_msg, 0, I2400M_TX_PLD_SIZE);
@@ -591,7 +644,7 @@ void i2400m_tx_close(struct i2400m *i2400m)
591 aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size); 644 aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size);
592 padding = aligned_size - tx_msg_moved->size; 645 padding = aligned_size - tx_msg_moved->size;
593 if (padding > 0) { 646 if (padding > 0) {
594 pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0); 647 pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0, 0);
595 if (unlikely(WARN_ON(pad_buf == NULL 648 if (unlikely(WARN_ON(pad_buf == NULL
596 || pad_buf == TAIL_FULL))) { 649 || pad_buf == TAIL_FULL))) {
597 /* This should not happen -- append should verify 650 /* This should not happen -- append should verify
@@ -657,6 +710,7 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len,
657 unsigned long flags; 710 unsigned long flags;
658 size_t padded_len; 711 size_t padded_len;
659 void *ptr; 712 void *ptr;
713 bool try_head = 0;
660 unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM 714 unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM
661 || pl_type == I2400M_PT_RESET_COLD; 715 || pl_type == I2400M_PT_RESET_COLD;
662 716
@@ -702,11 +756,12 @@ try_new:
702 /* So we have a current message header; now append space for 756 /* So we have a current message header; now append space for
703 * the message -- if there is not enough, try the head */ 757 * the message -- if there is not enough, try the head */
704 ptr = i2400m_tx_fifo_push(i2400m, padded_len, 758 ptr = i2400m_tx_fifo_push(i2400m, padded_len,
705 i2400m->bus_tx_block_size); 759 i2400m->bus_tx_block_size, try_head);
706 if (ptr == TAIL_FULL) { /* Tail is full, try head */ 760 if (ptr == TAIL_FULL) { /* Tail is full, try head */
707 d_printf(2, dev, "pl append: tail full\n"); 761 d_printf(2, dev, "pl append: tail full\n");
708 i2400m_tx_close(i2400m); 762 i2400m_tx_close(i2400m);
709 i2400m_tx_skip_tail(i2400m); 763 i2400m_tx_skip_tail(i2400m);
764 try_head = 1;
710 goto try_new; 765 goto try_new;
711 } else if (ptr == NULL) { /* All full */ 766 } else if (ptr == NULL) { /* All full */
712 result = -ENOSPC; 767 result = -ENOSPC;