net: buf: Introduce support for fragmentation

Introduce support buffer fragment chains that are linked together.
This is done with the help of a flag while the buffer is inside a FIFO
(indicating that the next fragment follows it in the same FIFO) and
with the help of a "next" pointer while the buffer is outside of a
FIFO.

In order to do proper "marshaling" a new net_buf_put() API needs to be
always used when inserting a buffer into a FIFO. Respectively, the
net_buf_get() and net_buf_get_timeout() functions are extended to
support getting buffers from arbitrary FIFOs and reassemble the
fragment chain based on the flags that the received buffers contain.

The insertion of a fragment chain using net_buf_put() into a FIFO is
done atomically with the help of irq_lock/unlock since FIFOs support
multiple writers, however since there's ever only a single reader per
FIFO similar locking is not necessary there.

Change-Id: I0ec579f63ea8d063f50e3f1f4c2e80ec399622d7
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2016-06-05 16:55:47 +03:00
commit 7720f6ed1c
2 changed files with 94 additions and 15 deletions

View file

@ -33,9 +33,23 @@ extern "C" {
/* Alignment needed for various parts of the buffer definition */
#define __net_buf_align __aligned(sizeof(int))
/** Flag indicating that the buffer has associated fragments. Only used
* internally by the buffer handling code while the buffer is inside a
* FIFO, meaning this never needs to be explicitly set or unset by the
* net_buf API user. As long as the buffer is outside of a FIFO, i.e.
* in practice always for the user for this API, the buf->frags pointer
* should be used instead.
*/
#define NET_BUF_FRAGS BIT(0)
struct net_buf {
/** FIFO uses first 4 bytes itself, reserve space */
int _unused;
union {
/** FIFO uses first 4 bytes itself, reserve space */
int _unused;
/** Fragments associated with this buffer. */
struct net_buf *frags;
};
/** Size of the user data associated with this buffer. */
const uint16_t user_data_size;
@ -43,6 +57,9 @@ struct net_buf {
/** Reference count. */
uint8_t ref;
/** Bit-field of buffer flags. */
uint8_t flags;
/** Pointer to the start of data in the buffer. */
uint8_t *data;
@ -115,10 +132,12 @@ struct net_buf {
} \
} while (0)
/** @brief Get a new buffer from the pool.
/** @brief Get a new buffer from a FIFO.
*
* Get buffer from the available buffers pool with specified type and
* reserved headroom.
* Get buffer from a FIFO. The reserve_head parameter is only relevant
* if the FIFO in question is a free buffers pool, i.e. the buffer will
* end up being initialized upon return. If called for any other FIFO
* the reserve_head parameter will be ignored and should be set to 0.
*
* @param fifo Which FIFO to take the buffer from.
* @param reserve_head How much headroom to reserve.
@ -127,19 +146,21 @@ struct net_buf {
*
* @warning If there are no available buffers and the function is
* called from a task or fiber the call will block until a buffer
* becomes available in the pool. If you want to make sure no blocking
* becomes available in the FIFO. If you want to make sure no blocking
* happens use net_buf_get_timeout() instead with TICKS_NONE.
*/
struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head);
/** @brief Get a new buffer from the pool.
/** @brief Get a new buffer from a FIFO.
*
* Get buffer from the available buffers pool with specified type and
* reserved headroom.
* Get buffer from a FIFO. The reserve_head parameter is only relevant
* if the FIFO in question is a free buffers pool, i.e. the buffer will
* end up being initialized upon return. If called for any other FIFO
* the reserve_head parameter will be ignored and should be set to 0.
*
* @param fifo Which FIFO to take the buffer from.
* @param reserve_head How much headroom to reserve.
* @param timeout Affects the action taken should the pool (FIFO) be empty.
* @param timeout Affects the action taken should the FIFO be empty.
* If TICKS_NONE, then return immediately. If TICKS_UNLIMITED, then
* wait as long as necessary. Otherwise, wait up to the specified
* number of ticks before timing out.
@ -149,6 +170,17 @@ struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head);
struct net_buf *net_buf_get_timeout(struct nano_fifo *fifo,
size_t reserve_head, int32_t timeout);
/** @brief Put a buffer into a FIFO
*
* Put a buffer to the end of a FIFO. If the buffer contains follow-up
* fragments this function will take care of inserting them as well
* into the FIFO.
*
* @param fifo Which FIFO to put the buffer to.
* @param buf Buffer.
*/
void net_buf_put(struct nano_fifo *fifo, struct net_buf *buf);
/** @brief Decrements the reference count of a buffer.
*
* Decrements the reference count of a buffer and puts it back into the

View file

@ -46,7 +46,7 @@
struct net_buf *net_buf_get_timeout(struct nano_fifo *fifo,
size_t reserve_head, int32_t timeout)
{
struct net_buf *buf;
struct net_buf *buf, *frag;
NET_BUF_DBG("fifo %p reserve %u timeout %d\n", fifo, reserve_head,
timeout);
@ -57,12 +57,34 @@ struct net_buf *net_buf_get_timeout(struct nano_fifo *fifo,
return NULL;
}
buf->ref = 1;
buf->data = buf->__buf + reserve_head;
buf->len = 0;
NET_BUF_DBG("buf %p fifo %p reserve %u\n", buf, fifo, reserve_head);
/* If this buffer is from the free buffers FIFO there wont be
* any fragments and we can directly proceed with initializing
* and returning it.
*/
if (buf->free == fifo) {
buf->ref = 1;
buf->data = buf->__buf + reserve_head;
buf->len = 0;
buf->flags = 0;
buf->frags = NULL;
return buf;
}
/* Get any fragments belonging to this buffer */
for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) {
frag->frags = nano_fifo_get(fifo, TICKS_NONE);
NET_BUF_ASSERT(frag->frags);
/* The fragments flag is only for FIFO-internal usage */
frag->flags &= ~NET_BUF_FRAGS;
}
/* Mark the end of the fragment list */
frag->frags = NULL;
return buf;
}
@ -82,6 +104,26 @@ struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head)
return net_buf_get_timeout(fifo, reserve_head, TICKS_UNLIMITED);
}
void net_buf_put(struct nano_fifo *fifo, struct net_buf *buf)
{
int mask;
mask = irq_lock();
while (buf) {
struct net_buf *frag = buf->frags;
if (frag) {
buf->flags |= NET_BUF_FRAGS;
}
nano_fifo_put(fifo, buf);
buf = frag;
}
irq_unlock(mask);
}
void net_buf_unref(struct net_buf *buf)
{
NET_BUF_DBG("buf %p ref %u fifo %p\n", buf, buf->ref, buf->free);
@ -91,6 +133,11 @@ void net_buf_unref(struct net_buf *buf)
return;
}
if (buf->frags) {
net_buf_unref(buf->frags);
buf->frags = NULL;
}
if (buf->destroy) {
buf->destroy(buf);
} else {