contiki-ng: Problem with tsch_link: the first KA packet after association will never be sent if given true mac address to tsch_schedule_add_link()

When I was writing my own TSCH scheduler, I set the address parameter in the tsch_schedule_add_link() function to the real MAC address of the neighbor instead of using a tsch_broadcast_address like in orchestra. Because I wanted the unicast link to be dedicated. So the code is like:

// in tsch-schedule.[hc]
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
struct tsch_link *
tsch_schedule_add_link(struct tsch_slotframe *slotframe,
                       uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
                       uint16_t timeslot, uint16_t channel_offset, uint8_t do_remove)

// my schedule plan
static void new_time_source(old, new) // this is called by the call back: TSCH_CALLBACK_NEW_TIME_SOURCE()
{
    ...
    tsch_schedule_add_link(sf_unicast, LINK_OPTION_TX, LINK_TYPE_NORMAL, new_addr,
                                             timeslot_tx, channel_offset, 0);
    tsch_schedule_add_link(sf_unicast, LINK_OPTION_RX, LINK_TYPE_NORMAL, new_addr,
                                             timeslot_rx, channel_offset, 0);
    ...
}

The new_time_source() function is called immediately after receiving EB from the time source. Therefore, before the parent knows this node, the links are already added. In the tsch_schedule_add_link() function, there is a tx_links_count attribute increasing:

if(l->link_options & LINK_OPTION_TX) {
  n = tsch_queue_add_nbr(&l->addr);
  /* We have a tx link to this neighbor, update counters */
  if(n != NULL) {
    n->tx_links_count++;
    if(!(l->link_options & LINK_OPTION_SHARED)) {
      n->dedicated_tx_links_count++;
    }
  }
}

Since the real MAC address is put to l->addr, n will be a real neighbor (coordinator for this case). Then the tx_links_count increases and is no longer 0. This causes the KA packet from the node never to be sent. And the reason is follows.

  1. The default common rule will select the KA packet, of course.
  2. When the default common link is active, it gets a packet and a neighbor in tsch_slot_operation, it goes
/* Protothread for slot operation, called from rtimer interrupt
 * and scheduled from tsch_schedule_slot_operation */
static
PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
{
    ....
    current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
    ....
}
  1. For the KA packet, the get_packet_and_neighbor_for_link() function finally falls into the case to pick any unicast packet in tsch_queue_get_unicast_packet_for_any()
  2. In tsch_queue_get_unicast_packet_for_any(), it goes
/* Returns the head packet of any neighbor queue with zero backoff counter.
 * Writes pointer to the neighbor in *n */
struct tsch_packet *
tsch_queue_get_unicast_packet_for_any(struct tsch_neighbor **n, struct tsch_link *link)
{
  if(!tsch_is_locked()) {
    struct tsch_neighbor *curr_nbr = (struct tsch_neighbor *)nbr_table_head(tsch_neighbors);
    struct tsch_packet *p = NULL;
    while(curr_nbr != NULL) {
      if(!curr_nbr->is_broadcast && curr_nbr->tx_links_count == 0) {
        /* Only look up for non-broadcast neighbors we do not have a tx link to */
        p = tsch_queue_get_packet_for_nbr(curr_nbr, link);
        if(p != NULL) {
          if(n != NULL) {
            *n = curr_nbr;
          }
          return p;
        }
      }
      curr_nbr = (struct tsch_neighbor *)nbr_table_next(tsch_neighbors, curr_nbr);
    }
  }
  return NULL;
}

So eventually, the condition curr_nbr->tx_links_count == 0 stops the KA packet to be selected since tx_links_count is not zero for the tsch_neighbor as mentioned above. In my experiments, the KA packets are enqueued one after another, but can never be actually sent🤕.

I hope that I’ve explained myself well. And I wonder if it is my wrong usage or a problem in the current implementation?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 17 (10 by maintainers)

Most upvoted comments

For example, you could change the condition code to

Thank you guys very much for helping me to fix this. Now I have changed the condition code as you said and it works pretty well.😄