/* Get the index of a neighbor from its link-layer address */ static int index_from_lladdr(const linkaddr_t *lladdr) { nbr_table_key_t *key; /* Allow lladdr-free insertion, useful e.g. for IPv6 ND. * Only one such entry is possible at a time, indexed by linkaddr_null. */ if(lladdr == NULL) { lladdr = &linkaddr_null; } key = list_head(nbr_table_keys); while(key != NULL) { if(lladdr && linkaddr_cmp(lladdr, &key->lladdr)) { return index_from_key(key); } key = list_item_next(key); } return -1; }
/*---------------------------------------------------------------------------*/ static void remove_key(nbr_table_key_t *least_used_key) { int i; for(i = 0; i < MAX_NUM_TABLES; i++) { if(all_tables[i] != NULL && all_tables[i]->callback != NULL) { /* Call table callback for each table that uses this item */ nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key); if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) { all_tables[i]->callback(removed_item); } } } /* Empty used map */ used_map[index_from_key(least_used_key)] = 0; /* Remove neighbor from list */ list_remove(nbr_table_keys, least_used_key); }
/* Get an item from its key */ static nbr_table_item_t * item_from_key(nbr_table_t *table, nbr_table_key_t *key) { return item_from_index(table, index_from_key(key)); }
/*---------------------------------------------------------------------------*/ static nbr_table_key_t * nbr_table_allocate(void) { nbr_table_key_t *key; int least_used_count = 0; nbr_table_key_t *least_used_key = NULL; key = memb_alloc(&neighbor_addr_mem); if(key != NULL) { return key; } else { /* No more space, try to free a neighbor. * The replacement policy is the following: remove neighbor that is: * (1) not locked * (2) used by fewest tables * (3) oldest (the list is ordered by insertion time) * */ /* Get item from first key */ key = list_head(nbr_table_keys); while(key != NULL) { int item_index = index_from_key(key); int locked = locked_map[item_index]; /* Never delete a locked item */ if(!locked) { int used = used_map[item_index]; int used_count = 0; /* Count how many tables are using this item */ while(used != 0) { if((used & 1) == 1) { used_count++; } used >>= 1; } /* Find least used item */ if(least_used_key == NULL || used_count < least_used_count) { least_used_key = key; least_used_count = used_count; if(used_count == 0) { /* We won't find any least used item */ break; } } } key = list_item_next(key); } if(least_used_key == NULL) { /* We haven't found any unlocked item, allocation fails */ return NULL; } else { /* Reuse least used item */ int i; for(i = 0; i<MAX_NUM_TABLES; i++) { if(all_tables[i] != NULL && all_tables[i]->callback != NULL) { /* Call table callback for each table that uses this item */ nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key); if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) { all_tables[i]->callback(removed_item); } } } /* Empty used map */ used_map[index_from_key(least_used_key)] = 0; /* Remove neighbor from list */ list_remove(nbr_table_keys, least_used_key); /* Return associated key */ return least_used_key; } }
/*---------------------------------------------------------------------------*/ static nbr_table_key_t * nbr_table_allocate(nbr_table_reason_t reason, void *data) { nbr_table_key_t *key; int least_used_count = 0; nbr_table_key_t *least_used_key = NULL; key = memb_alloc(&neighbor_addr_mem); if(key != NULL) { return key; } else { #ifdef NBR_TABLE_FIND_REMOVABLE const linkaddr_t *lladdr; lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data); if(lladdr == NULL) { /* Nothing found that can be deleted - return NULL to indicate failure */ PRINTF("*** Not removing entry to allocate new\n"); return NULL; } else { /* used least_used_key to indicate what is the least useful entry */ int index; int locked = 0; if((index = index_from_lladdr(lladdr)) != -1) { least_used_key = key_from_index(index); locked = locked_map[index]; } /* Allow delete of locked item? */ if(least_used_key != NULL && locked) { PRINTF("Deleting locked item!\n"); locked_map[index] = 0; } } #endif /* NBR_TABLE_FIND_REMOVABLE */ if(least_used_key == NULL) { /* No more space, try to free a neighbor. * The replacement policy is the following: remove neighbor that is: * (1) not locked * (2) used by fewest tables * (3) oldest (the list is ordered by insertion time) * */ /* Get item from first key */ key = list_head(nbr_table_keys); while(key != NULL) { int item_index = index_from_key(key); int locked = locked_map[item_index]; /* Never delete a locked item */ if(!locked) { int used = used_map[item_index]; int used_count = 0; /* Count how many tables are using this item */ while(used != 0) { if((used & 1) == 1) { used_count++; } used >>= 1; } /* Find least used item */ if(least_used_key == NULL || used_count < least_used_count) { least_used_key = key; least_used_count = used_count; if(used_count == 0) { /* We won't find any least used item */ break; } } } key = list_item_next(key); } }