Exemplo n.º 1
0
static
void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size,
			  size_t padding, bool try_head)
{
	struct device *dev = i2400m_dev(i2400m);
	size_t room, tail_room, needed_size;
	void *ptr;

	needed_size = size + padding;
	room = I2400M_TX_BUF_SIZE - (i2400m->tx_in - i2400m->tx_out);
	if (room < needed_size)	{ 
		d_printf(2, dev, "fifo push %zu/%zu: no space\n",
			 size, padding);
		return NULL;
	}
	
	tail_room = __i2400m_tx_tail_room(i2400m);
	if (!try_head && tail_room < needed_size) {
		if (room - tail_room >= needed_size) {
			d_printf(2, dev, "fifo push %zu/%zu: tail full\n",
				 size, padding);
			return TAIL_FULL;	
		} else {
			d_printf(2, dev, "fifo push %zu/%zu: no head space\n",
				 size, padding);
			return NULL;	
		}
	}
	ptr = i2400m->tx_buf + i2400m->tx_in % I2400M_TX_BUF_SIZE;
	d_printf(2, dev, "fifo push %zu/%zu: at @%zu\n", size, padding,
		 i2400m->tx_in % I2400M_TX_BUF_SIZE);
	i2400m->tx_in += size;
	return ptr;
}
Exemplo n.º 2
0
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_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",
		 tail_room, tx_in);
	i2400m->tx_in += tail_room;
}
Exemplo n.º 3
0
Arquivo: tx.c Projeto: avagin/linux
/*
 * Allocate @size bytes in the TX fifo, return a pointer to it
 *
 * @i2400m: device descriptor
 * @size: size of the buffer we need to allocate
 * @padding: ensure that there is at least this many bytes of free
 *     contiguous space in the fifo. This is needed because later on
 *     we might need to add padding.
 * @try_head: specify either to allocate head room or tail room space
 *     in the TX FIFO. This boolean is required to avoids a system hang
 *     due to an infinite loop caused by i2400m_tx_fifo_push().
 *     The caller must always try to allocate tail room space first by
 *     calling this routine with try_head = 0. In case if there
 *     is not enough tail room space but there is enough head room space,
 *     (i2400m_tx_fifo_push() returns TAIL_FULL) try to allocate head
 *     room space, by calling this routine again with try_head = 1.
 *
 * Returns:
 *
 *     Pointer to the allocated space. NULL if there is no
 *     space. TAIL_FULL if there is no space at the tail but there is at
 *     the head (Case B below).
 *
 * These are the two basic cases we need to keep an eye for -- it is
 * much better explained in linux/kernel/kfifo.c, but this code
 * basically does the same. No rocket science here.
 *
 *       Case A               Case B
 * N  ___________          ___________
 *   | tail room |        |   data    |
 *   |           |        |           |
 *   |<-  IN   ->|        |<-  OUT  ->|
 *   |           |        |           |
 *   |   data    |        |   room    |
 *   |           |        |           |
 *   |<-  OUT  ->|        |<-  IN   ->|
 *   |           |        |           |
 *   | head room |        |   data    |
 * 0  -----------          -----------
 *
 * We allocate only *contiguous* space.
 *
 * We can allocate only from 'room'. In Case B, it is simple; in case
 * A, we only try from the tail room; if it is not enough, we just
 * fail and return TAIL_FULL and let the caller figure out if we wants to
 * skip the tail room and try to allocate from the head.
 *
 * There is a corner case, wherein i2400m_tx_new() can get into
 * an infinite loop calling i2400m_tx_fifo_push().
 * In certain situations, tx_in would have reached on the top of TX FIFO
 * and i2400m_tx_tail_room() returns 0, as described below:
 *
 * N  ___________ tail room is zero
 *   |<-  IN   ->|
 *   |           |
 *   |           |
 *   |           |
 *   |   data    |
 *   |<-  OUT  ->|
 *   |           |
 *   |           |
 *   | head room |
 * 0  -----------
 * During such a time, where tail room is zero in the TX FIFO and if there
 * is a request to add a payload to TX FIFO, which calls:
 * i2400m_tx()
 *         ->calls i2400m_tx_close()
 *         ->calls i2400m_tx_skip_tail()
 *         goto try_new;
 *         ->calls i2400m_tx_new()
 *                    |----> [try_head:]
 *     infinite loop  |     ->calls i2400m_tx_fifo_push()
 *                    |                if (tail_room < needed)
 *                    |                   if (head_room => needed)
 *                    |                       return TAIL_FULL;
 *                    |<----  goto try_head;
 *
 * i2400m_tx() calls i2400m_tx_close() to close the message, since there
 * is no tail room to accommodate the payload and calls
 * i2400m_tx_skip_tail() to skip the tail space. Now i2400m_tx() calls
 * i2400m_tx_new() to allocate space for new message header calling
 * i2400m_tx_fifo_push() that returns TAIL_FULL, since there is no tail space
 * to accommodate the message header, but there is enough head space.
 * The i2400m_tx_new() keeps re-retrying by calling i2400m_tx_fifo_push()
 * ending up in a loop causing system freeze.
 *
 * This corner case is avoided by using a try_head boolean,
 * as an argument to i2400m_tx_fifo_push().
 *
 * Note:
 *
 *     Assumes i2400m->tx_lock is taken, and we use that as a barrier
 *
 *     The indexes keep increasing and we reset them to zero when we
 *     pop data off the queue
 */
static
void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size,
			  size_t padding, bool try_head)
{
	struct device *dev = i2400m_dev(i2400m);
	size_t room, tail_room, needed_size;
	void *ptr;

	needed_size = size + padding;
	room = I2400M_TX_BUF_SIZE - (i2400m->tx_in - i2400m->tx_out);
	if (room < needed_size)	{ /* this takes care of Case B */
		d_printf(2, dev, "fifo push %zu/%zu: no space\n",
			 size, padding);
		return NULL;
	}
	/* Is there space at the tail? */
	tail_room = __i2400m_tx_tail_room(i2400m);
	if (!try_head && tail_room < needed_size) {
		/*
		 * If the tail room space is not enough to push the message
		 * in the TX FIFO, then there are two possibilities:
		 * 1. There is enough head room space to accommodate
		 * this message in the TX FIFO.
		 * 2. There is not enough space in the head room and
		 * in tail room of the TX FIFO to accommodate the message.
		 * In the case (1), return TAIL_FULL so that the caller
		 * can figure out, if the caller wants to push the message
		 * into the head room space.
		 * In the case (2), return NULL, indicating that the TX FIFO
		 * cannot accommodate the message.
		 */
		if (room - tail_room >= needed_size) {
			d_printf(2, dev, "fifo push %zu/%zu: tail full\n",
				 size, padding);
			return TAIL_FULL;	/* There might be head space */
		} else {
			d_printf(2, dev, "fifo push %zu/%zu: no head space\n",
				 size, padding);
			return NULL;	/* There is no space */
		}
	}
	ptr = i2400m->tx_buf + i2400m->tx_in % I2400M_TX_BUF_SIZE;
	d_printf(2, dev, "fifo push %zu/%zu: at @%zu\n", size, padding,
		 i2400m->tx_in % I2400M_TX_BUF_SIZE);
	i2400m->tx_in += size;
	return ptr;
}