Skip to content

Commit ce83a7c

Browse files
Johan Hedbergnashif
authored andcommitted
Bluetooth: Mesh: Fix adhering to the configured Friend Queue size
Qualification test case MESH/NODE/FRND/FN/BV-08-C requires that we do not store more messages than the reported Friend Queue size. The implementation was so far opportunistic and stored more if it could (it would later discard if necessary to make sure all queues can store the required amount). The spec also requires the queues to have new messages overwrite old ones (in the style of a circular buffer), so we have to keep track of which buffers are part of the same segmented message (so we discard all buffers belonging to the same message). To pass the test case, add APIs to check for space in the Friend queue, and track the number of buffers for each incoming segmented message. Fixes #18090 Signed-off-by: Johan Hedberg <[email protected]>
1 parent 31842cc commit ce83a7c

File tree

4 files changed

+254
-76
lines changed

4 files changed

+254
-76
lines changed

subsys/bluetooth/host/mesh/friend.c

Lines changed: 186 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -68,48 +68,6 @@ static struct bt_mesh_adv *adv_alloc(int id)
6868
return &adv_pool[id].adv;
6969
}
7070

71-
static void discard_buffer(void)
72-
{
73-
struct bt_mesh_friend *frnd = &bt_mesh.frnd[0];
74-
struct net_buf *buf;
75-
int i;
76-
77-
/* Find the Friend context with the most queued buffers */
78-
for (i = 1; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
79-
if (bt_mesh.frnd[i].queue_size > frnd->queue_size) {
80-
frnd = &bt_mesh.frnd[i];
81-
}
82-
}
83-
84-
buf = net_buf_slist_get(&frnd->queue);
85-
__ASSERT_NO_MSG(buf != NULL);
86-
BT_WARN("Discarding buffer %p for LPN 0x%04x", buf, frnd->lpn);
87-
net_buf_unref(buf);
88-
}
89-
90-
static struct net_buf *friend_buf_alloc(u16_t src)
91-
{
92-
struct net_buf *buf;
93-
94-
BT_DBG("src 0x%04x", src);
95-
96-
do {
97-
buf = bt_mesh_adv_create_from_pool(&friend_buf_pool, adv_alloc,
98-
BT_MESH_ADV_DATA,
99-
FRIEND_XMIT, K_NO_WAIT);
100-
if (!buf) {
101-
discard_buffer();
102-
}
103-
} while (!buf);
104-
105-
BT_MESH_ADV(buf)->addr = src;
106-
FRIEND_ADV(buf)->seq_auth = TRANS_SEQ_AUTH_NVAL;
107-
108-
BT_DBG("allocated buf %p", buf);
109-
110-
return buf;
111-
}
112-
11371
static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr)
11472
{
11573
if (frnd->lpn == BT_MESH_ADDR_UNASSIGNED) {
@@ -149,6 +107,20 @@ struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr,
149107
return NULL;
150108
}
151109

110+
static void purge_buffers(sys_slist_t *list)
111+
{
112+
while (!sys_slist_is_empty(list)) {
113+
struct net_buf *buf;
114+
115+
buf = (void *)sys_slist_get_not_empty(list);
116+
117+
buf->frags = NULL;
118+
buf->flags &= ~NET_BUF_FRAGS;
119+
120+
net_buf_unref(buf);
121+
}
122+
}
123+
152124
/* Intentionally start a little bit late into the ReceiveWindow when
153125
* it's large enough. This may improve reliability with some platforms,
154126
* like the PTS, where the receiver might not have sufficiently compensated
@@ -183,16 +155,13 @@ static void friend_clear(struct bt_mesh_friend *frnd)
183155
frnd->last = NULL;
184156
}
185157

186-
while (!sys_slist_is_empty(&frnd->queue)) {
187-
net_buf_unref(net_buf_slist_get(&frnd->queue));
188-
}
158+
purge_buffers(&frnd->queue);
189159

190160
for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
191161
struct bt_mesh_friend_seg *seg = &frnd->seg[i];
192162

193-
while (!sys_slist_is_empty(&seg->queue)) {
194-
net_buf_unref(net_buf_slist_get(&seg->queue));
195-
}
163+
purge_buffers(&seg->queue);
164+
seg->seg_count = 0U;
196165
}
197166

198167
frnd->valid = 0U;
@@ -334,7 +303,15 @@ static struct net_buf *create_friend_pdu(struct bt_mesh_friend *frnd,
334303
sub = bt_mesh_subnet_get(frnd->net_idx);
335304
__ASSERT_NO_MSG(sub != NULL);
336305

337-
buf = friend_buf_alloc(info->src);
306+
buf = bt_mesh_adv_create_from_pool(&friend_buf_pool, adv_alloc,
307+
BT_MESH_ADV_DATA,
308+
FRIEND_XMIT, K_NO_WAIT);
309+
if (!buf) {
310+
return NULL;
311+
}
312+
313+
BT_MESH_ADV(buf)->addr = info->src;
314+
FRIEND_ADV(buf)->seq_auth = TRANS_SEQ_AUTH_NVAL;
338315

339316
/* Friend Offer needs master security credentials */
340317
if (info->ctl && TRANS_CTL_OP(sdu->data) == TRANS_CTL_OP_FRIEND_OFFER) {
@@ -895,7 +872,8 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
895872
}
896873

897874
static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd,
898-
u16_t src, u64_t *seq_auth)
875+
u16_t src, u64_t *seq_auth,
876+
u8_t seg_count)
899877
{
900878
struct bt_mesh_friend_seg *unassigned = NULL;
901879
int i;
@@ -914,12 +892,16 @@ static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd,
914892
}
915893
}
916894

895+
if (unassigned) {
896+
unassigned->seg_count = seg_count;
897+
}
898+
917899
return unassigned;
918900
}
919901

920902
static void enqueue_friend_pdu(struct bt_mesh_friend *frnd,
921903
enum bt_mesh_friend_pdu_type type,
922-
struct net_buf *buf)
904+
u8_t seg_count, struct net_buf *buf)
923905
{
924906
struct bt_mesh_friend_seg *seg;
925907
struct friend_adv *adv;
@@ -936,7 +918,7 @@ static void enqueue_friend_pdu(struct bt_mesh_friend *frnd,
936918
}
937919

938920
adv = FRIEND_ADV(buf);
939-
seg = get_seg(frnd, BT_MESH_ADV(buf)->addr, &adv->seq_auth);
921+
seg = get_seg(frnd, BT_MESH_ADV(buf)->addr, &adv->seq_auth, seg_count);
940922
if (!seg) {
941923
BT_ERR("No free friend segment RX contexts for 0x%04x",
942924
BT_MESH_ADV(buf)->addr);
@@ -961,6 +943,10 @@ static void enqueue_friend_pdu(struct bt_mesh_friend *frnd,
961943
}
962944

963945
sys_slist_merge_slist(&frnd->queue, &seg->queue);
946+
seg->seg_count = 0U;
947+
} else {
948+
/* Mark the buffer as having more to come after it */
949+
buf->flags |= NET_BUF_FRAGS;
964950
}
965951
}
966952

@@ -1026,13 +1012,17 @@ static void friend_timeout(struct k_work *work)
10261012
return;
10271013
}
10281014

1029-
frnd->last = net_buf_slist_get(&frnd->queue);
1015+
frnd->last = (void *)sys_slist_get(&frnd->queue);
10301016
if (!frnd->last) {
10311017
BT_WARN("Friendship not established with 0x%04x", frnd->lpn);
10321018
friend_clear(frnd);
10331019
return;
10341020
}
10351021

1022+
/* Clear the flag we use for segment tracking */
1023+
frnd->last->flags &= ~NET_BUF_FRAGS;
1024+
frnd->last->frags = NULL;
1025+
10361026
BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x",
10371027
frnd->last, frnd->lpn);
10381028
frnd->queue_size--;
@@ -1095,7 +1085,8 @@ static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth,
10951085
static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd,
10961086
struct bt_mesh_net_rx *rx,
10971087
enum bt_mesh_friend_pdu_type type,
1098-
u64_t *seq_auth, struct net_buf_simple *sbuf)
1088+
u64_t *seq_auth, u8_t seg_count,
1089+
struct net_buf_simple *sbuf)
10991090
{
11001091
struct friend_pdu_info info;
11011092
struct net_buf *buf;
@@ -1133,7 +1124,7 @@ static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd,
11331124
FRIEND_ADV(buf)->seq_auth = *seq_auth;
11341125
}
11351126

1136-
enqueue_friend_pdu(frnd, type, buf);
1127+
enqueue_friend_pdu(frnd, type, seg_count, buf);
11371128

11381129
BT_DBG("Queued message for LPN 0x%04x, queue_size %u",
11391130
frnd->lpn, frnd->queue_size);
@@ -1142,7 +1133,8 @@ static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd,
11421133
static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd,
11431134
struct bt_mesh_net_tx *tx,
11441135
enum bt_mesh_friend_pdu_type type,
1145-
u64_t *seq_auth, struct net_buf_simple *sbuf)
1136+
u64_t *seq_auth, u8_t seg_count,
1137+
struct net_buf_simple *sbuf)
11461138
{
11471139
struct friend_pdu_info info;
11481140
struct net_buf *buf;
@@ -1177,7 +1169,7 @@ static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd,
11771169
FRIEND_ADV(buf)->seq_auth = *seq_auth;
11781170
}
11791171

1180-
enqueue_friend_pdu(frnd, type, buf);
1172+
enqueue_friend_pdu(frnd, type, seg_count, buf);
11811173

11821174
BT_DBG("Queued message for LPN 0x%04x", frnd->lpn);
11831175
}
@@ -1227,9 +1219,118 @@ bool bt_mesh_friend_match(u16_t net_idx, u16_t addr)
12271219
return false;
12281220
}
12291221

1222+
static bool friend_queue_has_space(struct bt_mesh_friend *frnd, u16_t addr,
1223+
u64_t *seq_auth, u8_t seg_count)
1224+
{
1225+
u32_t total = 0;
1226+
int i;
1227+
1228+
for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
1229+
struct bt_mesh_friend_seg *seg = &frnd->seg[i];
1230+
1231+
if (seq_auth) {
1232+
struct net_buf *buf;
1233+
1234+
/* If there's a segment queue for this message then the
1235+
* space verification has already happened.
1236+
*/
1237+
buf = (void *)sys_slist_peek_head(&seg->queue);
1238+
if (buf && BT_MESH_ADV(buf)->addr == addr &&
1239+
FRIEND_ADV(buf)->seq_auth == *seq_auth) {
1240+
return true;
1241+
}
1242+
}
1243+
1244+
total += seg->seg_count;
1245+
}
1246+
1247+
/* If currently pending segments combined with this segmented message
1248+
* are more than the Friend Queue Size, then there's no space. This
1249+
* is because we don't have a mechanism of aborting already pending
1250+
* segmented messages to free up buffers.
1251+
*/
1252+
return (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - total) > seg_count;
1253+
}
1254+
1255+
bool bt_mesh_friend_queue_has_space(u16_t net_idx, u16_t src, u16_t dst,
1256+
u64_t *seq_auth, u8_t seg_count)
1257+
{
1258+
bool someone_has_space = false, friend_match = false;
1259+
int i;
1260+
1261+
if (seg_count > CONFIG_BT_MESH_FRIEND_QUEUE_SIZE) {
1262+
return false;
1263+
}
1264+
1265+
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
1266+
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
1267+
1268+
if (!friend_lpn_matches(frnd, net_idx, dst)) {
1269+
continue;
1270+
}
1271+
1272+
friend_match = true;
1273+
1274+
if (friend_queue_has_space(frnd, src, seq_auth, seg_count)) {
1275+
someone_has_space = true;
1276+
}
1277+
}
1278+
1279+
/* If there were no matched LPNs treat this as success, so the
1280+
* transport layer can continue its work.
1281+
*/
1282+
if (!friend_match) {
1283+
return true;
1284+
}
1285+
1286+
/* From the transport layers perspective it's good enough that at
1287+
* least one Friend Queue has space. If there were multiple Friend
1288+
* matches then the destination must be a group address, in which
1289+
* case e.g. segment acks are not sent.
1290+
*/
1291+
return someone_has_space;
1292+
}
1293+
1294+
static bool friend_queue_prepare_space(struct bt_mesh_friend *frnd, u16_t addr,
1295+
u64_t *seq_auth, u8_t seg_count)
1296+
{
1297+
bool pending_segments;
1298+
u8_t avail_space;
1299+
1300+
if (!friend_queue_has_space(frnd, addr, seq_auth, seg_count)) {
1301+
return false;
1302+
}
1303+
1304+
avail_space = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - frnd->queue_size;
1305+
pending_segments = false;
1306+
1307+
while (pending_segments || avail_space < seg_count) {
1308+
struct net_buf *buf = (void *)sys_slist_get(&frnd->queue);
1309+
1310+
if (!buf) {
1311+
BT_ERR("Unable to free up enough buffers");
1312+
return false;
1313+
}
1314+
1315+
frnd->queue_size--;
1316+
avail_space++;
1317+
1318+
pending_segments = (buf->flags & NET_BUF_FRAGS);
1319+
1320+
/* Make sure old slist entry state doesn't remain */
1321+
buf->frags = NULL;
1322+
buf->flags &= ~NET_BUF_FRAGS;
1323+
1324+
net_buf_unref(buf);
1325+
}
1326+
1327+
return true;
1328+
}
1329+
12301330
void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx,
12311331
enum bt_mesh_friend_pdu_type type,
1232-
u64_t *seq_auth, struct net_buf_simple *sbuf)
1332+
u64_t *seq_auth, u8_t seg_count,
1333+
struct net_buf_simple *sbuf)
12331334
{
12341335
int i;
12351336

@@ -1246,16 +1347,25 @@ void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx,
12461347
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
12471348
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
12481349

1249-
if (friend_lpn_matches(frnd, rx->sub->net_idx,
1250-
rx->ctx.recv_dst)) {
1251-
friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, sbuf);
1350+
if (!friend_lpn_matches(frnd, rx->sub->net_idx,
1351+
rx->ctx.recv_dst)) {
1352+
continue;
12521353
}
1354+
1355+
if (!friend_queue_prepare_space(frnd, rx->ctx.addr, seq_auth,
1356+
seg_count)) {
1357+
continue;
1358+
}
1359+
1360+
friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, seg_count,
1361+
sbuf);
12531362
}
12541363
}
12551364

12561365
bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx,
12571366
enum bt_mesh_friend_pdu_type type,
1258-
u64_t *seq_auth, struct net_buf_simple *sbuf)
1367+
u64_t *seq_auth, u8_t seg_count,
1368+
struct net_buf_simple *sbuf)
12591369
{
12601370
bool matched = false;
12611371
int i;
@@ -1271,10 +1381,19 @@ bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx,
12711381
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
12721382
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
12731383

1274-
if (friend_lpn_matches(frnd, tx->sub->net_idx, tx->ctx->addr)) {
1275-
friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, sbuf);
1276-
matched = true;
1384+
if (!friend_lpn_matches(frnd, tx->sub->net_idx,
1385+
tx->ctx->addr)) {
1386+
continue;
1387+
}
1388+
1389+
if (!friend_queue_prepare_space(frnd, tx->src, seq_auth,
1390+
seg_count)) {
1391+
continue;
12771392
}
1393+
1394+
friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, seg_count,
1395+
sbuf);
1396+
matched = true;
12781397
}
12791398

12801399
return matched;
@@ -1314,9 +1433,8 @@ void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src,
13141433

13151434
BT_WARN("Clearing incomplete segments for 0x%04x", src);
13161435

1317-
while (!sys_slist_is_empty(&seg->queue)) {
1318-
net_buf_unref(net_buf_slist_get(&seg->queue));
1319-
}
1436+
purge_buffers(&seg->queue);
1437+
seg->seg_count = 0U;
13201438
}
13211439
}
13221440
}

0 commit comments

Comments
 (0)