diff options
author | Prasanna S. Panchamukhi <prasannax.s.panchamukhi@intel.com> | 2010-04-08 19:24:31 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2010-05-11 17:06:36 -0400 |
commit | 85a19e07e30f67c517266cafe92b7bcd9b98966d (patch) | |
tree | e5bc9d14fecfd4dd6f8cfc297ebe689cec4eaebb /drivers/net/wimax | |
parent | 9e6e3bd52b0f77ca5cc385892c14ff8ba5ecfa67 (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>
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r-- | drivers/net/wimax/i2400m/tx.c | 65 |
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 | */ |
382 | static | 432 | static |
383 | void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) | 433 | void *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); |
514 | try_head: | 566 | try_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; |