summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c36
1 files changed, 24 insertions, 12 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 011167c22da8..12c9c0030da6 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -65,18 +65,30 @@
***************************************************/
static int iwl_queue_space(const struct iwl_queue *q)
{
- int s = q->read_ptr - q->write_ptr;
-
- if (q->read_ptr > q->write_ptr)
- s -= q->n_bd;
-
- if (s <= 0)
- s += q->n_window;
- /* keep some reserve to not confuse empty and full situations */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
+ unsigned int max;
+ unsigned int used;
+
+ /*
+ * To avoid ambiguity between empty and completely full queues, there
+ * should always be less than q->n_bd elements in the queue.
+ * If q->n_window is smaller than q->n_bd, there is no need to reserve
+ * any queue entries for this purpose.
+ */
+ if (q->n_window < q->n_bd)
+ max = q->n_window;
+ else
+ max = q->n_bd - 1;
+
+ /*
+ * q->n_bd is a power of 2, so the following is equivalent to modulo by
+ * q->n_bd and is well defined for negative dividends.
+ */
+ used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
+
+ if (WARN_ON(used > max))
+ return 0;
+
+ return max - used;
}
/*