@@ -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-
11371static 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
897874static 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
920902static 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,
10951085static 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,
11421133static 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+
12301330void 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
12561365bool 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