diff options
| -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; |
