diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c index 7c46c05a5866718c72be7849bc0122945df08de6..4295dcf96ee2b1ec09e862e7e8e9bdf3b8e22f27 100644 --- a/drivers/net/wimax/i2400m/tx.c +++ b/drivers/net/wimax/i2400m/tx.c @@ -277,6 +277,48 @@ enum { #define TAIL_FULL ((void *)~(unsigned long)NULL) +/* + * Calculate how much tail room is available + * + * Note the trick here. This path is ONLY caleed for Case A (see + * i2400m_tx_fifo_push() below), where we have: + * + * Case A + * N ___________ + * | tail room | + * | | + * |<- IN ->| + * | | + * | data | + * | | + * |<- OUT ->| + * | | + * | head room | + * 0 ----------- + * + * When calculating the tail_room, tx_in might get to be zero if + * i2400m->tx_in is right at the end of the buffer (really full + * buffer) if there is no head room. In this case, tail_room would be + * I2400M_TX_BUF_SIZE, although it is actually zero. Hence the final + * mod (%) operation. However, when doing this kind of optimization, + * i2400m->tx_in being zero would fail, so we treat is an a special + * case. + */ +static inline +size_t __i2400m_tx_tail_room(struct i2400m *i2400m) +{ + size_t tail_room; + size_t tx_in; + + if (unlikely(i2400m->tx_in) == 0) + return I2400M_TX_BUF_SIZE; + tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; + tail_room = I2400M_TX_BUF_SIZE - tx_in; + tail_room %= I2400M_TX_BUF_SIZE; + return tail_room; +} + + /* * Allocate @size bytes in the TX fifo, return a pointer to it * @@ -338,7 +380,7 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) return NULL; } /* Is there space at the tail? */ - tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE; + tail_room = __i2400m_tx_tail_room(i2400m); if (tail_room < needed_size) { if (i2400m->tx_out % I2400M_TX_BUF_SIZE < i2400m->tx_in % I2400M_TX_BUF_SIZE) { @@ -367,17 +409,29 @@ void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the * header). * + * Tail room can get to be zero if a message was opened when there was + * space only for a header. _tx_close() will mark it as to-skip (as it + * will have no payloads) and there will be no more space to flush, so + * nothing has to be done here. This is probably cheaper than ensuring + * in _tx_new() that there is some space for payloads...as we could + * always possibly hit the same problem if the payload wouldn't fit. + * * Note: * * Assumes i2400m->tx_lock is taken, and we use that as a barrier + * + * This path is only taken for Case A FIFO situations [see + * i2400m_tx_fifo_push()] */ static void i2400m_tx_skip_tail(struct i2400m *i2400m) { struct device *dev = i2400m_dev(i2400m); size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; - size_t tail_room = I2400M_TX_BUF_SIZE - tx_in; + size_t tail_room = __i2400m_tx_tail_room(i2400m); struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; + if (unlikely(tail_room == 0)) + return; BUG_ON(tail_room < sizeof(*msg)); msg->size = tail_room | I2400M_TX_SKIP; d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n",