Commit c3e08c8f authored by Johan Hedberg's avatar Johan Hedberg Committed by Anas Nashif
Browse files

net: buf: Redesigned pool & buffer allocation API



Until now it has been necessary to separately define a k_fifo and
an array of buffers when creating net_buf pools. This has been a bit
of an inconvenience as well as blurred the line of what exactly
constitutes the "pool".

This patch removes the NET_BUF_POOL() macro and replaces it with a
NET_BUF_POOL_DEFINE() macro that internally expands into the buffer
array and new net_buf_pool struct with a given name:

	NET_BUF_POOL_DEFINE(pool_name, ...);

Having a dedicated context struct for the pool has the added benefit
that we can start moving there net_buf members that have the same
value for all buffers from the same pool. The first such member that
gets moved is the destroy callback, thus shrinking net_buf by four
bytes. Another potential candidate is the user_data_size, however
right not that's left out since it would just leave 2 bytes of padding
in net_buf (i.e. not influence its size). Another common value is
buf->size, however that one is also used by net_buf_simple and can
therefore not be moved.

This patch also splits getting buffers from a FIFO and allocating a
new buffer from a pool into two separate APIs: net_buf_get and
net_buf_alloc, thus simplifying the APIs and their usage. There is no
separate 'reserve_head' parameter anymore when allocating, rather the
user is expected to call net_buf_reserve() afterwards if something
else than 0 headroom is desired.

Change-Id: Id91b1e5c2be2deb1274dde47f5edebfe29af383a
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 1e2e05c7
Loading
Loading
Loading
Loading
+19 −10
Original line number Diff line number Diff line
@@ -8,36 +8,45 @@ defined in ``include/net/buf.h``.
Creating buffers
================

Network buffers are created by first declaring a pool of them:
Network buffers are created by first defining a pool of them:

.. code-block:: c

   static struct nano_fifo free_fifo;
   static NET_BUF_POOL(pool_name, buf_count, buf_size, &free_fifo, NULL,
                       user_data_size);
   NET_BUF_POOL_DEFINE(pool_name, buf_count, buf_size, user_data_size, NULL);

Before operating on the pool it also needs to be initialized at runtime:

.. code-block:: c

   net_buf_pool_init(pool_name);
   net_buf_pool_init(&pool_name);

Once the pool has been initialized the available buffers are managed
with the help of a nano_fifo object and can be acquired with:

.. code-block:: c

   buf = net_buf_get(&free_fifo, reserve_headroom);
   buf = net_buf_alloc(&pool_name);

If there is a need to reserve space in the buffer for protocol headers
to be prependend later, it's possible to reserve this headroom with:

.. code-block:: c

   net_buf_reserve(buf, headroom);

In addition to actual protocol data and generic parsing context, network
buffers may also contain protocol-specific context, known as user data.
Both the maximum data and user data capacity of the buffers is
compile-time defined when declaring the buffer pool.

Since the free buffers are managed with the help of a nano_fifo it means
the buffers have native support for being passed through other nano_fifos
Since the free buffers are managed with the help of a k_fifo it means
the buffers have native support for being passed through other FIFOs
as well. This is a very practical feature when the buffers need to be
passed from one fiber to another.
passed from one thread to another. However, since a net_buf may have a
fragment chain attached to it, instead of using the :c:func:`k_fifo_put`
and :c:func:`k_fifo_get` APIs, special :c:func:`net_buf_put` and
:c:func:`net_buf_get` APIs must be used when passing buffers through
FIFOs. These APIs ensure that the buffer chains stay intact.

Common Operations
=================
@@ -87,7 +96,7 @@ Reference Counting
==================

Each network buffer is reference counted. The buffer is initially
acquired from a free buffers pool by calling :c:func:`net_buf_get()`,
acquired from a free buffers pool by calling :c:func:`net_buf_alloc()`,
resulting in a buffer with reference count 1. The reference count can be
incremented with :c:func:`net_buf_ref()` or decremented with
:c:func:`net_buf_unref()`. When the count drops to zero the buffer is
+10 −15
Original line number Diff line number Diff line
@@ -134,9 +134,8 @@ static const uint8_t conf_rsp[] = { 0x04, 0x7b };
#define CONFIG_BLUETOOTH_SIGNAL_COUNT	2
#define SIG_BUF_SIZE (CONFIG_BLUETOOTH_HCI_RECV_RESERVE + \
		      CONFIG_BLUETOOTH_MAX_SIG_LEN)
static struct k_fifo h5_sig;
static NET_BUF_POOL(signal_pool, CONFIG_BLUETOOTH_SIGNAL_COUNT, SIG_BUF_SIZE,
		    &h5_sig, NULL, 0);
NET_BUF_POOL_DEFINE(h5_pool, CONFIG_BLUETOOTH_SIGNAL_COUNT, SIG_BUF_SIZE, 0,
		    NULL);

static struct device *h5_dev;

@@ -210,8 +209,7 @@ static void process_unack(void)
	BT_DBG("Need to remove %u packet from the queue", number_removed);

	while (number_removed) {
		struct net_buf *buf = net_buf_get_timeout(&h5.unack_queue, 0,
							  K_NO_WAIT);
		struct net_buf *buf = net_buf_get(&h5.unack_queue, K_NO_WAIT);

		if (!buf) {
			BT_ERR("Unack queue is empty");
@@ -347,14 +345,12 @@ static void retx_timeout(struct k_work *work)
		k_fifo_init(&tmp_queue);

		/* Queue to temperary queue */
		while ((buf = net_buf_get_timeout(&h5.tx_queue, 0,
						  K_NO_WAIT))) {
		while ((buf = net_buf_get(&h5.tx_queue, K_NO_WAIT))) {
			net_buf_put(&tmp_queue, buf);
		}

		/* Queue unack packets to the beginning of the queue */
		while ((buf = net_buf_get_timeout(&h5.unack_queue, 0,
						  K_NO_WAIT))) {
		while ((buf = net_buf_get(&h5.unack_queue, K_NO_WAIT))) {
			/* include also packet type */
			net_buf_push(buf, sizeof(uint8_t));
			net_buf_put(&h5.tx_queue, buf);
@@ -363,7 +359,7 @@ static void retx_timeout(struct k_work *work)
		}

		/* Queue saved packets from temp queue */
		while ((buf = net_buf_get_timeout(&tmp_queue, 0, K_NO_WAIT))) {
		while ((buf = net_buf_get(&tmp_queue, K_NO_WAIT))) {
			net_buf_put(&h5.tx_queue, buf);
		}
	}
@@ -500,8 +496,7 @@ static void bt_uart_isr(struct device *unused)
				break;
			case HCI_3WIRE_LINK_PKT:
			case HCI_3WIRE_ACK_PKT:
				h5.rx_buf = net_buf_get_timeout(&h5_sig, 0,
								K_NO_WAIT);
				h5.rx_buf = net_buf_alloc(&h5_pool, K_NO_WAIT);
				if (!h5.rx_buf) {
					BT_WARN("No available signal buffers");
					h5_reset_rx();
@@ -612,7 +607,7 @@ static void tx_thread(void)
			k_sleep(100);
			break;
		case ACTIVE:
			buf = net_buf_get_timeout(&h5.tx_queue, 0, K_FOREVER);
			buf = net_buf_get(&h5.tx_queue, K_FOREVER);
			type = h5_get_type(buf);

			h5_send(buf->data, type, buf->len);
@@ -642,7 +637,7 @@ static void rx_thread(void)
	while (true) {
		struct net_buf *buf;

		buf = net_buf_get_timeout(&h5.rx_queue, 0, K_FOREVER);
		buf = net_buf_get(&h5.rx_queue, K_FOREVER);

		hexdump("=> ", buf->data, buf->len);

@@ -707,7 +702,7 @@ static void h5_init(void)
		       NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);

	/* RX thread */
	net_buf_pool_init(signal_pool);
	net_buf_pool_init(&h5_pool);

	k_fifo_init(&h5.rx_queue);
	k_thread_spawn(rx_stack, sizeof(rx_stack), (k_thread_entry_t)rx_thread,
+5 −6
Original line number Diff line number Diff line
@@ -39,10 +39,9 @@

#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
/* Pool for incoming ATT packets */
static struct k_fifo prep_data;
static NET_BUF_POOL(prep_pool, CONFIG_BLUETOOTH_ATT_PREPARE_COUNT,
		    BLE_GATT_MTU_SIZE, &prep_data, NULL,
		    sizeof(struct nble_gatts_write_evt));
NET_BUF_POOL_DEFINE(prep_pool, CONFIG_BLUETOOTH_ATT_PREPARE_COUNT,
		    BLE_GATT_MTU_SIZE, sizeof(struct nble_gatts_write_evt),
		    NULL);

static struct k_fifo queue;
#endif
@@ -1422,7 +1421,7 @@ static int32_t prep_write_evt(const struct nble_gatts_write_evt *ev,
		return ret;
	}

	buf = net_buf_get_timeout(&prep_data, 0, K_NO_WAIT);
	buf = net_buf_alloc(&prep_pool, K_NO_WAIT);
	if (!buf) {
		BT_ERR("No more buffers for prepare write");
		return BT_GATT_ERR(BT_ATT_ERR_PREPARE_QUEUE_FULL);
@@ -1585,7 +1584,7 @@ void bt_gatt_init(void)

#if CONFIG_BLUETOOTH_ATT_PREPARE_COUNT > 0
	k_fifo_init(&queue);
	net_buf_pool_init(prep_pool);
	net_buf_pool_init(&prep_pool);
#endif
}

+9 −10
Original line number Diff line number Diff line
@@ -57,11 +57,8 @@ struct ipc_uart_header {
#define NBLE_RX_BUF_COUNT	10
#define NBLE_BUF_SIZE		384

static struct k_fifo rx;
static NET_BUF_POOL(rx_pool, NBLE_RX_BUF_COUNT, NBLE_BUF_SIZE, &rx, NULL, 0);

static struct k_fifo tx;
static NET_BUF_POOL(tx_pool, NBLE_TX_BUF_COUNT, NBLE_BUF_SIZE, &tx, NULL, 0);
NET_BUF_POOL_DEFINE(rx_pool, NBLE_RX_BUF_COUNT, NBLE_BUF_SIZE, 0, NULL);
NET_BUF_POOL_DEFINE(tx_pool, NBLE_TX_BUF_COUNT, NBLE_BUF_SIZE, 0, NULL);

static BT_STACK_NOINIT(rx_thread_stack, CONFIG_BLUETOOTH_RX_STACK_SIZE);

@@ -76,7 +73,7 @@ static void rx_thread(void)
	while (true) {
		struct net_buf *buf;

		buf = net_buf_get_timeout(&rx_queue, 0, K_FOREVER);
		buf = net_buf_get(&rx_queue, K_FOREVER);
		BT_DBG("Got buf %p", buf);

		rpc_deserialize(buf);
@@ -96,12 +93,14 @@ struct net_buf *rpc_alloc_cb(uint16_t length)

	BT_DBG("length %u", length);

	buf = net_buf_get(&tx, sizeof(struct ipc_uart_header));
	buf = net_buf_alloc(&tx_pool, K_FOREVER);
	if (!buf) {
		BT_ERR("Unable to get tx buffer");
		return NULL;
	}

	net_buf_reserve(buf, sizeof(struct ipc_uart_header));

	if (length > net_buf_tailroom(buf)) {
		BT_ERR("Too big tx buffer requested");
		net_buf_unref(buf);
@@ -185,7 +184,7 @@ static void bt_uart_isr(struct device *unused)
				BT_ERR("Too much data to fit buffer");
				buf = NULL;
			} else {
				buf = net_buf_get_timeout(&rx, 0, K_NO_WAIT);
				buf = net_buf_alloc(&rx_pool, K_NO_WAIT);
				if (!buf) {
					BT_ERR("No available IPC buffers");
				}
@@ -251,8 +250,8 @@ static int _bt_nble_init(struct device *unused)
		return -EINVAL;
	}

	net_buf_pool_init(rx_pool);
	net_buf_pool_init(tx_pool);
	net_buf_pool_init(&rx_pool);
	net_buf_pool_init(&tx_pool);

	return 0;
}
+4 −4
Original line number Diff line number Diff line
@@ -175,14 +175,14 @@ int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf);
 */
int bt_rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc);

/** @brief Get the buffer from fifo after reserving head room for RFCOMM, L2CAP
 *  and ACL headers.
/** @brief Allocate the buffer from pool after reserving head room for RFCOMM,
 *  L2CAP and ACL headers.
 *
 *  @param fifo Which FIFO to take the buffer from.
 *  @param pool Which pool to take the buffer from.
 *
 *  @return New buffer.
 */
struct net_buf *bt_rfcomm_create_pdu(struct k_fifo *fifo);
struct net_buf *bt_rfcomm_create_pdu(struct net_buf_pool *pool);

#ifdef __cplusplus
}
Loading