Skip to content

Commit

Permalink
mesh: Segmented TX to groups
Browse files Browse the repository at this point in the history
Implements several changes to the transport layer segmented tx to
improve group message performance:
- Moves retransmit counter to tx context instead of per packet. As every
  unacked packet is sent every retransmit, the retransmit counters would
  be the same in each segment. This makes it easier to control progress.
- Delays the scheduling of the retransmit until the completion of the
  last segment by adding a seg_pending counter. This is essentially the
  same as the old behavior, except that the old behavior might retrigger
  the sending before all segments are finished if the advertising is
  slow.
- Allows the group transmits to stop as soon as all retransmits have
  been exhausted, instead of timing out waiting for acks that won't
  come. This allows group tx to finish without error.
- Fixes a bug where a failed TX would block IV update.
- Cancels any pending transmissions of acked segments.
- Reduces log level for several common group tx scenarios that aren't
  erronous.

this is port of zephyrproject-rtos/zephyr#23102
  • Loading branch information
KKopyscinski committed Oct 13, 2020
1 parent 091dce0 commit bcad736
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 40 deletions.
7 changes: 1 addition & 6 deletions nimble/host/mesh/src/adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,9 @@ struct bt_mesh_adv {
void *cb_data;

u8_t type:2,
busy:1;
busy:1;
u8_t xmit;

/* For transport layer segment sending */
struct {
u8_t attempts;
} seg;

u8_t flags;

int ref_cnt;
Expand Down
104 changes: 70 additions & 34 deletions nimble/host/mesh/src/transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,17 @@ BUILD_ASSERT(CONFIG_BT_MESH_ADV_BUF_COUNT >= (CONFIG_BT_MESH_TX_SEG_MAX + 3));
* We use 400 since 300 is a common send duration for standard HCI, and we
* need to have a timeout that's bigger than that.
*/
#define SEG_RETRANSMIT_TIMEOUT(tx) (K_MSEC(400) + 50 * (tx)->ttl)
#define SEG_RETRANSMIT_TIMEOUT_UNICAST(tx) (K_MSEC(400) + 50 * (tx)->ttl)
/* When sending to a group, the messages are not acknowledged, and there's no
* reason to delay the repetitions significantly. Delaying by more than 0 ms
* to avoid flooding the network.
*/
#define SEG_RETRANSMIT_TIMEOUT_GROUP K_MSEC(50)

#define SEG_RETRANSMIT_TIMEOUT(tx) \
(BT_MESH_ADDR_IS_UNICAST(tx->dst) ? \
SEG_RETRANSMIT_TIMEOUT_UNICAST(tx) : \
SEG_RETRANSMIT_TIMEOUT_GROUP)
/* How long to wait for available buffers before giving up */
#define BUF_TIMEOUT K_NO_WAIT

Expand All @@ -67,9 +76,11 @@ static struct seg_tx {
u64_t seq_auth;
u16_t dst;
u8_t seg_n:5, /* Last segment index */
new_key:1; /* New/old key */
new_key:1; /* New/old key */
u8_t nack_count; /* Number of unacked segs */
u8_t ttl;
u8_t seg_pending:5, /* Number of segments pending */
attempts:3;
const struct bt_mesh_send_cb *cb;
void *cb_data;
struct k_delayed_work retransmit; /* Retransmit timer */
Expand Down Expand Up @@ -168,6 +179,14 @@ bool bt_mesh_tx_in_progress(void)
return false;
}

static void seg_tx_done(struct seg_tx *tx, u8_t seg_idx)
{
BT_MESH_ADV(tx->seg[seg_idx])->busy = 0U;
net_buf_unref(tx->seg[seg_idx]);
tx->seg[seg_idx] = NULL;
tx->nack_count--;
}

static void seg_tx_reset(struct seg_tx *tx)
{
int i;
Expand All @@ -180,23 +199,15 @@ static void seg_tx_reset(struct seg_tx *tx)
tx->sub = NULL;
tx->dst = BT_MESH_ADDR_UNASSIGNED;

if (!tx->nack_count) {
return;
}

for (i = 0; i <= tx->seg_n; i++) {
for (i = 0; i <= tx->seg_n && tx->nack_count; i++) {
if (!tx->seg[i]) {
continue;
}

BT_MESH_ADV(tx->seg[i])->busy = 0U;
net_buf_unref(tx->seg[i]);
tx->seg[i] = NULL;
seg_tx_done(tx, i);
}

tx->nack_count = 0U;

if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IVU_PENDING)) {
if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IVU_PENDING)) {
BT_DBG("Proceding with pending IV Update");

/* bt_mesh_net_iv_update() will re-enable the flag if this
Expand All @@ -210,11 +221,29 @@ static void seg_tx_reset(struct seg_tx *tx)

static inline void seg_tx_complete(struct seg_tx *tx, int err)
{
if (tx->cb && tx->cb->end) {
tx->cb->end(err, tx->cb_data);
}
const struct bt_mesh_send_cb *cb = tx->cb;
void *cb_data = tx->cb_data;

seg_tx_reset(tx);

if (cb && cb->end) {
cb->end(err, cb_data);
}
}

static void schedule_retransmit(struct seg_tx *tx)
{
if (--tx->seg_pending) {
return;
}

if (!BT_MESH_ADDR_IS_UNICAST(tx->dst) && !tx->attempts) {
BT_DBG("SDU TX complete");
seg_tx_complete(tx, 0);
return;
}

k_delayed_work_submit(&tx->retransmit, SEG_RETRANSMIT_TIMEOUT(tx));
}

static void seg_first_send_start(u16_t duration, int err, void *user_data)
Expand All @@ -235,17 +264,15 @@ static void seg_send_start(u16_t duration, int err, void *user_data)
* case since otherwise we risk the transmission of becoming stale.
*/
if (err) {
k_delayed_work_submit(&tx->retransmit,
SEG_RETRANSMIT_TIMEOUT(tx));
schedule_retransmit(tx);
}
}

static void seg_sent(int err, void *user_data)
{
struct seg_tx *tx = user_data;

k_delayed_work_submit(&tx->retransmit,
SEG_RETRANSMIT_TIMEOUT(tx));
schedule_retransmit(tx);
}

static const struct bt_mesh_send_cb first_sent_cb = {
Expand All @@ -262,6 +289,14 @@ static void seg_tx_send_unacked(struct seg_tx *tx)
{
int i, err;

if (!(tx->attempts--)) {
BT_ERR("Ran out of retransmit attempts");
seg_tx_complete(tx, -ETIMEDOUT);
return;
}

BT_DBG("Attempts: %u", tx->attempts);

for (i = 0; i <= tx->seg_n; i++) {
struct os_mbuf *seg = tx->seg[i];

Expand All @@ -274,11 +309,7 @@ static void seg_tx_send_unacked(struct seg_tx *tx)
continue;
}

if (!(BT_MESH_ADV(seg)->seg.attempts--)) {
BT_ERR("Ran out of retransmit attempts");
seg_tx_complete(tx, -ETIMEDOUT);
return;
}
tx->seg_pending++;

BT_DBG("resending %u/%u", i, tx->seg_n);

Expand Down Expand Up @@ -347,6 +378,8 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu,
tx->new_key = net_tx->sub->kr_flag;
tx->cb = cb;
tx->cb_data = cb_data;
tx->attempts = SEG_RETRANSMIT_ATTEMPTS;
tx->seg_pending = 0;

if (net_tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) {
tx->ttl = bt_mesh_default_ttl_get();
Expand Down Expand Up @@ -382,8 +415,6 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu,
return -ENOBUFS;
}

BT_MESH_ADV(seg)->seg.attempts = SEG_RETRANSMIT_ATTEMPTS;

net_buf_reserve(seg, BT_MESH_NET_HDR_LEN);

net_buf_add_u8(seg, seg_hdr);
Expand Down Expand Up @@ -421,6 +452,7 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu,
tx->seg[seg_o] = net_buf_ref(seg);

BT_DBG("Sending %u/%u", seg_o, tx->seg_n);
tx->seg_pending++;

err = bt_mesh_net_send(net_tx, seg,
seg_o ? &seg_sent_cb : &first_sent_cb,
Expand Down Expand Up @@ -765,6 +797,11 @@ static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr,
return -EINVAL;
}

if (!BT_MESH_ADDR_IS_UNICAST(tx->dst)) {
BT_ERR("Received ack for group seg");
return -EINVAL;
}

seq_zero = net_buf_simple_pull_be16(buf);
obo = seq_zero >> 15;
seq_zero = (seq_zero >> 2) & TRANS_SEQ_ZERO_MASK;
Expand Down Expand Up @@ -805,9 +842,7 @@ static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr,
while ((bit = find_lsb_set(ack))) {
if (tx->seg[bit - 1]) {
BT_DBG("seg %u/%u acked", bit - 1, tx->seg_n);
net_buf_unref(tx->seg[bit - 1]);
tx->seg[bit - 1] = NULL;
tx->nack_count--;
seg_tx_done(tx, bit - 1);
}

ack &= ~BIT(bit - 1);
Expand Down Expand Up @@ -1037,6 +1072,8 @@ static int ctl_send_seg(struct bt_mesh_net_tx *tx, u8_t ctl_op,
tx_seg->new_key = tx->sub->kr_flag;
tx_seg->cb = cb;
tx_seg->cb_data = cb_data;
tx_seg->attempts = SEG_RETRANSMIT_ATTEMPTS;
tx_seg->seg_pending = 0;

if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) {
tx_seg->ttl = bt_mesh_default_ttl_get();
Expand All @@ -1059,8 +1096,6 @@ static int ctl_send_seg(struct bt_mesh_net_tx *tx, u8_t ctl_op,
return -ENOBUFS;
}

BT_MESH_ADV(seg)->seg.attempts = SEG_RETRANSMIT_ATTEMPTS;

net_buf_reserve(seg, BT_MESH_NET_HDR_LEN);
net_buf_add_u8(seg, TRANS_CTL_HDR(ctl_op, 1));
net_buf_add_u8(seg, (tx->aszmic << 7) | seq_zero >> 6);
Expand All @@ -1072,6 +1107,7 @@ static int ctl_send_seg(struct bt_mesh_net_tx *tx, u8_t ctl_op,
tx_seg->seg[seg_o] = net_buf_ref(seg);

BT_DBG("Sending %u/%u", seg_o, tx_seg->seg_n);
tx_seg->seg_pending++;

err = bt_mesh_net_send(tx, seg,
seg_o ? &seg_sent_cb : &first_sent_cb,
Expand Down Expand Up @@ -1134,7 +1170,7 @@ static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst,
* or virtual address.
*/
if (!BT_MESH_ADDR_IS_UNICAST(src)) {
BT_WARN("Not sending ack for non-unicast address");
BT_DBG("Not sending ack for non-unicast address");
return 0;
}

Expand Down Expand Up @@ -1382,7 +1418,7 @@ static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
}

if (rx->block == BLOCK_COMPLETE(rx->seg_n)) {
BT_WARN("Got segment for already complete SDU");
BT_DBG("Got segment for already complete SDU");

send_ack(net_rx->sub, net_rx->ctx.recv_dst,
net_rx->ctx.addr, net_rx->ctx.send_ttl,
Expand Down

0 comments on commit bcad736

Please sign in to comment.