Contiki 3.x
tsch-queue.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, SICS Swedish ICT.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * Per-neighbor packet queues for TSCH MAC.
36  * The list of neighbors uses the TSCH lock, but per-neighbor packet array are lock-free.
37  * Read-only operation on neighbor and packets are allowed from interrupts and outside of them.
38  * *Other operations are allowed outside of interrupt only.*
39  * \author
40  * Simon Duquennoy <simonduq@sics.se>
41  * Beshr Al Nahas <beshr@sics.se>
42  * Domenico De Guglielmo <d.deguglielmo@iet.unipi.it >
43  */
44 
45 #include "contiki.h"
46 #include "lib/list.h"
47 #include "lib/memb.h"
48 #include "lib/random.h"
49 #include "net/queuebuf.h"
50 #include "net/mac/rdc.h"
51 #include "net/mac/tsch/tsch.h"
53 #include "net/mac/tsch/tsch-queue.h"
54 #include "net/mac/tsch/tsch-schedule.h"
55 #include "net/mac/tsch/tsch-slot-operation.h"
56 #include "net/mac/tsch/tsch-log.h"
57 #include <string.h>
58 
59 #if TSCH_LOG_LEVEL >= 1
60 #define DEBUG DEBUG_PRINT
61 #else /* TSCH_LOG_LEVEL */
62 #define DEBUG DEBUG_NONE
63 #endif /* TSCH_LOG_LEVEL */
64 #include "net/net-debug.h"
65 
66 /* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
67 #if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0
68 #error TSCH_QUEUE_NUM_PER_NEIGHBOR must be power of two
69 #endif
70 
71 /* We have as many packets are there are queuebuf in the system */
72 MEMB(packet_memb, struct tsch_packet, QUEUEBUF_NUM);
73 MEMB(neighbor_memb, struct tsch_neighbor, TSCH_QUEUE_MAX_NEIGHBOR_QUEUES);
74 LIST(neighbor_list);
75 
76 /* Broadcast and EB virtual neighbors */
77 struct tsch_neighbor *n_broadcast;
78 struct tsch_neighbor *n_eb;
79 
80 /*---------------------------------------------------------------------------*/
81 /* Add a TSCH neighbor */
82 struct tsch_neighbor *
83 tsch_queue_add_nbr(const linkaddr_t *addr)
84 {
85  struct tsch_neighbor *n = NULL;
86  /* If we have an entry for this neighbor already, we simply update it */
87  n = tsch_queue_get_nbr(addr);
88  if(n == NULL) {
89  if(tsch_get_lock()) {
90  /* Allocate a neighbor */
91  n = memb_alloc(&neighbor_memb);
92  if(n != NULL) {
93  /* Initialize neighbor entry */
94  memset(n, 0, sizeof(struct tsch_neighbor));
95  ringbufindex_init(&n->tx_ringbuf, TSCH_QUEUE_NUM_PER_NEIGHBOR);
96  linkaddr_copy(&n->addr, addr);
97  n->is_broadcast = linkaddr_cmp(addr, &tsch_eb_address)
98  || linkaddr_cmp(addr, &tsch_broadcast_address);
99  tsch_queue_backoff_reset(n);
100  /* Add neighbor to the list */
101  list_add(neighbor_list, n);
102  }
103  tsch_release_lock();
104  }
105  }
106  return n;
107 }
108 /*---------------------------------------------------------------------------*/
109 /* Get a TSCH neighbor */
110 struct tsch_neighbor *
111 tsch_queue_get_nbr(const linkaddr_t *addr)
112 {
113  if(!tsch_is_locked()) {
114  struct tsch_neighbor *n = list_head(neighbor_list);
115  while(n != NULL) {
116  if(linkaddr_cmp(&n->addr, addr)) {
117  return n;
118  }
119  n = list_item_next(n);
120  }
121  }
122  return NULL;
123 }
124 /*---------------------------------------------------------------------------*/
125 /* Get a TSCH time source (we currently assume there is only one) */
126 struct tsch_neighbor *
127 tsch_queue_get_time_source(void)
128 {
129  if(!tsch_is_locked()) {
130  struct tsch_neighbor *curr_nbr = list_head(neighbor_list);
131  while(curr_nbr != NULL) {
132  if(curr_nbr->is_time_source) {
133  return curr_nbr;
134  }
135  curr_nbr = list_item_next(curr_nbr);
136  }
137  }
138  return NULL;
139 }
140 /*---------------------------------------------------------------------------*/
141 /* Update TSCH time source */
142 int
143 tsch_queue_update_time_source(const linkaddr_t *new_addr)
144 {
145  if(!tsch_is_locked()) {
146  if(!tsch_is_coordinator) {
147  struct tsch_neighbor *old_time_src = tsch_queue_get_time_source();
148  struct tsch_neighbor *new_time_src = NULL;
149 
150  if(new_addr != NULL) {
151  /* Get/add neighbor, return 0 in case of failure */
152  new_time_src = tsch_queue_add_nbr(new_addr);
153  if(new_time_src == NULL) {
154  return 0;
155  }
156  }
157 
158  if(new_time_src != old_time_src) {
159  PRINTF("TSCH: update time source: %u -> %u\n",
160  TSCH_LOG_ID_FROM_LINKADDR(old_time_src ? &old_time_src->addr : NULL),
161  TSCH_LOG_ID_FROM_LINKADDR(new_time_src ? &new_time_src->addr : NULL));
162 
163  /* Update time source */
164  if(new_time_src != NULL) {
165  new_time_src->is_time_source = 1;
166  }
167 
168  if(old_time_src != NULL) {
169  old_time_src->is_time_source = 0;
170  }
171 
172 #ifdef TSCH_CALLBACK_NEW_TIME_SOURCE
173  TSCH_CALLBACK_NEW_TIME_SOURCE(old_time_src, new_time_src);
174 #endif
175  }
176 
177  return 1;
178  }
179  }
180  return 0;
181 }
182 /*---------------------------------------------------------------------------*/
183 /* Flush a neighbor queue */
184 static void
185 tsch_queue_flush_nbr_queue(struct tsch_neighbor *n)
186 {
187  while(!tsch_queue_is_empty(n)) {
188  struct tsch_packet *p = tsch_queue_remove_packet_from_queue(n);
189  if(p != NULL) {
190  /* Set return status for packet_sent callback */
191  p->ret = MAC_TX_ERR;
192  PRINTF("TSCH-queue:! flushing packet\n");
193  /* Call packet_sent callback */
194  mac_call_sent_callback(p->sent, p->ptr, p->ret, p->transmissions);
195  /* Free packet queuebuf */
196  tsch_queue_free_packet(p);
197  }
198  }
199 }
200 /*---------------------------------------------------------------------------*/
201 /* Remove TSCH neighbor queue */
202 static void
203 tsch_queue_remove_nbr(struct tsch_neighbor *n)
204 {
205  if(n != NULL) {
206  if(tsch_get_lock()) {
207 
208  /* Remove neighbor from list */
209  list_remove(neighbor_list, n);
210 
211  tsch_release_lock();
212 
213  /* Flush queue */
214  tsch_queue_flush_nbr_queue(n);
215 
216  /* Free neighbor */
217  memb_free(&neighbor_memb, n);
218  }
219  }
220 }
221 /*---------------------------------------------------------------------------*/
222 /* Add packet to neighbor queue. Use same lockfree implementation as ringbuf.c (put is atomic) */
223 struct tsch_packet *
224 tsch_queue_add_packet(const linkaddr_t *addr, mac_callback_t sent, void *ptr)
225 {
226  struct tsch_neighbor *n = NULL;
227  int16_t put_index = -1;
228  struct tsch_packet *p = NULL;
229  if(!tsch_is_locked()) {
230  n = tsch_queue_add_nbr(addr);
231  if(n != NULL) {
232  put_index = ringbufindex_peek_put(&n->tx_ringbuf);
233  if(put_index != -1) {
234  p = memb_alloc(&packet_memb);
235  if(p != NULL) {
236  /* Enqueue packet */
237 #ifdef TSCH_CALLBACK_PACKET_READY
238  TSCH_CALLBACK_PACKET_READY();
239 #endif
240  p->qb = queuebuf_new_from_packetbuf();
241  if(p->qb != NULL) {
242  p->sent = sent;
243  p->ptr = ptr;
244  p->ret = MAC_TX_DEFERRED;
245  p->transmissions = 0;
246  /* Add to ringbuf (actual add committed through atomic operation) */
247  n->tx_array[put_index] = p;
248  ringbufindex_put(&n->tx_ringbuf);
249  return p;
250  } else {
251  memb_free(&packet_memb, p);
252  }
253  }
254  }
255  }
256  }
257  PRINTF("TSCH-queue:! add packet failed: %u %p %d %p %p\n", tsch_is_locked(), n, put_index, p, p ? p->qb : NULL);
258  return 0;
259 }
260 /*---------------------------------------------------------------------------*/
261 /* Returns the number of packets currently in the queue */
262 int
263 tsch_queue_packet_count(const linkaddr_t *addr)
264 {
265  struct tsch_neighbor *n = NULL;
266  if(!tsch_is_locked()) {
267  n = tsch_queue_add_nbr(addr);
268  if(n != NULL) {
269  return ringbufindex_elements(&n->tx_ringbuf);
270  }
271  }
272  return -1;
273 }
274 /*---------------------------------------------------------------------------*/
275 /* Remove first packet from a neighbor queue */
276 struct tsch_packet *
277 tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n)
278 {
279  if(!tsch_is_locked()) {
280  if(n != NULL) {
281  /* Get and remove packet from ringbuf (remove committed through an atomic operation */
282  int16_t get_index = ringbufindex_get(&n->tx_ringbuf);
283  if(get_index != -1) {
284  return n->tx_array[get_index];
285  } else {
286  return NULL;
287  }
288  }
289  }
290  return NULL;
291 }
292 /*---------------------------------------------------------------------------*/
293 /* Free a packet */
294 void
295 tsch_queue_free_packet(struct tsch_packet *p)
296 {
297  if(p != NULL) {
298  queuebuf_free(p->qb);
299  memb_free(&packet_memb, p);
300  }
301 }
302 /*---------------------------------------------------------------------------*/
303 /* Flush all neighbor queues */
304 void
305 tsch_queue_reset(void)
306 {
307  /* Deallocate unneeded neighbors */
308  if(!tsch_is_locked()) {
309  struct tsch_neighbor *n = list_head(neighbor_list);
310  while(n != NULL) {
311  struct tsch_neighbor *next_n = list_item_next(n);
312  /* Flush queue */
313  tsch_queue_flush_nbr_queue(n);
314  /* Reset backoff exponent */
315  tsch_queue_backoff_reset(n);
316  n = next_n;
317  }
318  }
319 }
320 /*---------------------------------------------------------------------------*/
321 /* Deallocate neighbors with empty queue */
322 void
323 tsch_queue_free_unused_neighbors(void)
324 {
325  /* Deallocate unneeded neighbors */
326  if(!tsch_is_locked()) {
327  struct tsch_neighbor *n = list_head(neighbor_list);
328  while(n != NULL) {
329  struct tsch_neighbor *next_n = list_item_next(n);
330  /* Queue is empty, no tx link to this neighbor: deallocate.
331  * Always keep time source and virtual broadcast neighbors. */
332  if(!n->is_broadcast && !n->is_time_source && !n->tx_links_count
333  && tsch_queue_is_empty(n)) {
334  tsch_queue_remove_nbr(n);
335  }
336  n = next_n;
337  }
338  }
339 }
340 /*---------------------------------------------------------------------------*/
341 /* Is the neighbor queue empty? */
342 int
343 tsch_queue_is_empty(const struct tsch_neighbor *n)
344 {
345  return !tsch_is_locked() && n != NULL && ringbufindex_empty(&n->tx_ringbuf);
346 }
347 /*---------------------------------------------------------------------------*/
348 /* Returns the first packet from a neighbor queue */
349 struct tsch_packet *
350 tsch_queue_get_packet_for_nbr(const struct tsch_neighbor *n, struct tsch_link *link)
351 {
352  if(!tsch_is_locked()) {
353  int is_shared_link = link != NULL && link->link_options & LINK_OPTION_SHARED;
354  if(n != NULL) {
355  int16_t get_index = ringbufindex_peek_get(&n->tx_ringbuf);
356  if(get_index != -1 &&
357  !(is_shared_link && !tsch_queue_backoff_expired(n))) { /* If this is a shared link,
358  make sure the backoff has expired */
359 #if TSCH_WITH_LINK_SELECTOR
360  int packet_attr_slotframe = queuebuf_attr(n->tx_array[get_index]->qb, PACKETBUF_ATTR_TSCH_SLOTFRAME);
361  int packet_attr_timeslot = queuebuf_attr(n->tx_array[get_index]->qb, PACKETBUF_ATTR_TSCH_TIMESLOT);
362  if(packet_attr_slotframe != 0xffff && packet_attr_slotframe != link->slotframe_handle) {
363  return NULL;
364  }
365  if(packet_attr_timeslot != 0xffff && packet_attr_timeslot != link->timeslot) {
366  return NULL;
367  }
368 #endif
369  return n->tx_array[get_index];
370  }
371  }
372  }
373  return NULL;
374 }
375 /*---------------------------------------------------------------------------*/
376 /* Returns the head packet from a neighbor queue (from neighbor address) */
377 struct tsch_packet *
378 tsch_queue_get_packet_for_dest_addr(const linkaddr_t *addr, struct tsch_link *link)
379 {
380  if(!tsch_is_locked()) {
381  return tsch_queue_get_packet_for_nbr(tsch_queue_get_nbr(addr), link);
382  }
383  return NULL;
384 }
385 /*---------------------------------------------------------------------------*/
386 /* Returns the head packet of any neighbor queue with zero backoff counter.
387  * Writes pointer to the neighbor in *n */
388 struct tsch_packet *
389 tsch_queue_get_unicast_packet_for_any(struct tsch_neighbor **n, struct tsch_link *link)
390 {
391  if(!tsch_is_locked()) {
392  struct tsch_neighbor *curr_nbr = list_head(neighbor_list);
393  struct tsch_packet *p = NULL;
394  while(curr_nbr != NULL) {
395  if(!curr_nbr->is_broadcast && curr_nbr->tx_links_count == 0) {
396  /* Only look up for non-broadcast neighbors we do not have a tx link to */
397  p = tsch_queue_get_packet_for_nbr(curr_nbr, link);
398  if(p != NULL) {
399  if(n != NULL) {
400  *n = curr_nbr;
401  }
402  return p;
403  }
404  }
405  curr_nbr = list_item_next(curr_nbr);
406  }
407  }
408  return NULL;
409 }
410 /*---------------------------------------------------------------------------*/
411 /* May the neighbor transmit over a shared link? */
412 int
413 tsch_queue_backoff_expired(const struct tsch_neighbor *n)
414 {
415  return n->backoff_window == 0;
416 }
417 /*---------------------------------------------------------------------------*/
418 /* Reset neighbor backoff */
419 void
420 tsch_queue_backoff_reset(struct tsch_neighbor *n)
421 {
422  n->backoff_window = 0;
423  n->backoff_exponent = TSCH_MAC_MIN_BE;
424 }
425 /*---------------------------------------------------------------------------*/
426 /* Increment backoff exponent, pick a new window */
427 void
428 tsch_queue_backoff_inc(struct tsch_neighbor *n)
429 {
430  /* Increment exponent */
431  n->backoff_exponent = MIN(n->backoff_exponent + 1, TSCH_MAC_MAX_BE);
432  /* Pick a window (number of shared slots to skip). Ignore least significant
433  * few bits, which, on some embedded implementations of rand (e.g. msp430-libc),
434  * are known to have poor pseudo-random properties. */
435  n->backoff_window = (random_rand() >> 6) % (1 << n->backoff_exponent);
436  /* Add one to the window as we will decrement it at the end of the current slot
437  * through tsch_queue_update_all_backoff_windows */
438  n->backoff_window++;
439 }
440 /*---------------------------------------------------------------------------*/
441 /* Decrement backoff window for all queues directed at dest_addr */
442 void
443 tsch_queue_update_all_backoff_windows(const linkaddr_t *dest_addr)
444 {
445  if(!tsch_is_locked()) {
446  int is_broadcast = linkaddr_cmp(dest_addr, &tsch_broadcast_address);
447  struct tsch_neighbor *n = list_head(neighbor_list);
448  while(n != NULL) {
449  if(n->backoff_window != 0 /* Is the queue in backoff state? */
450  && ((n->tx_links_count == 0 && is_broadcast)
451  || (n->tx_links_count > 0 && linkaddr_cmp(dest_addr, &n->addr)))) {
452  n->backoff_window--;
453  }
454  n = list_item_next(n);
455  }
456  }
457 }
458 /*---------------------------------------------------------------------------*/
459 /* Initialize TSCH queue module */
460 void
461 tsch_queue_init(void)
462 {
463  list_init(neighbor_list);
464  memb_init(&neighbor_memb);
465  memb_init(&packet_memb);
466  /* Add virtual EB and the broadcast neighbors */
467  n_eb = tsch_queue_add_nbr(&tsch_eb_address);
468  n_broadcast = tsch_queue_add_nbr(&tsch_broadcast_address);
469 }
470 /*---------------------------------------------------------------------------*/
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
#define LIST(name)
Declare a linked list.
Definition: list.h:86
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
Private TSCH definitions (meant for use by TSCH implementation files only) ...
The MAC layer transmission could not be performed because of a fatal error.
Definition: mac.h:93
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
The MAC layer transmission could not be performed because of an error.
Definition: mac.h:89
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
A set of debugging macros for the netstack
#define NULL
The null pointer.
unsigned short random_rand(void)
Generates a new random number using the cc2538 RNG.
Definition: random.c:47
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
RDC driver header file
Linked list manipulation routines.
Memory block allocation routines.
Header file for the Rime queue buffer management
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two Rime addresses.
Definition: linkaddr.c:66
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a Rime address.
Definition: linkaddr.c:60
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79