lp_flush(p->rte_withdrawal_pool);
}
+/*
+ * Change size of trie node so that @id fits into node's bitmap, allocate
+ * new slab configured with this size and initialize root node there.
+ */
+static void
+aggregator_resize_slab(struct aggregator_proto *p, u32 id)
+{
+ ASSERT_DIE(p->trie_slab != NULL);
+
+ /* Calculate sufficient bitmap size */
+ while (id >= (p->bitmap_size * sizeof(p->root->potential_buckets[0]) * 8))
+ p->bitmap_size *= 2;
+
+ /* Save bucket with default route */
+ struct aggregator_bucket *default_rte_bucket = p->root->original_bucket;
+
+ /* Calculate size of trie node with bigger bitmap */
+ const size_t node_size = sizeof(*p->root) + sizeof(p->root->potential_buckets[0]) * p->bitmap_size;
+
+ /* Free old slab and create new */
+ rfree(p->trie_slab);
+ p->trie_slab = sl_new(p->p.pool, node_size);
+
+ /* Initialize root node in new slab */
+ p->root = aggregator_root_init(default_rte_bucket, p->trie_slab);
+}
+
static void
aggregator_aggregate_on_feed_end(struct channel *C)
{
hmap_set(&p->bucket_id_map, new_bucket->id);
aggregator_add_bucket(p, new_bucket);
+
+ if (p->aggr_mode == PREFIX_AGGR)
+ {
+ /*
+ * If ID of new bucket doesn't fit into bitmap in trie nodes, we have to
+ * create new slab and build whole trie all over again.
+ */
+ if (new_bucket->id >= (p->bitmap_size * sizeof(p->root->potential_buckets[0]) * 8))
+ {
+ aggregator_resize_slab(p, new_bucket->id);
+
+ /* Trie already exists after initial feed, build it again in the new slab */
+ if (!p->initial_feed)
+ aggregator_aggregate(p);
+ }
+ }
}
/* Store the route attributes */
if (p->aggr_mode == PREFIX_AGGR)
{
ASSERT_DIE(p->trie_slab == NULL);
- p->trie_slab = sl_new(P->pool, sizeof(struct trie_node));
-
ASSERT_DIE(p->bucket_list == NULL);
ASSERT_DIE(p->bucket_list_size == 0);
+ p->bitmap_size = POTENTIAL_BUCKETS_BITMAP_INIT_SIZE;
+
+ const size_t node_size = sizeof(*p->root) + sizeof(p->root->potential_buckets[0]) * p->bitmap_size;
+ p->trie_slab = sl_new(P->pool, node_size);
+
p->bucket_list_size = BUCKET_LIST_INIT_SIZE;
p->bucket_list = mb_allocz(P->pool, sizeof(p->bucket_list[0]) * p->bucket_list_size);
p->bucket_id_map = (struct hmap) { 0 };
p->initial_feed = true;
+
+ p->bitmap_size = 0;
}
static int
static inline void
aggregator_node_add_potential_bucket(struct trie_node *node, u32 bucket_id)
{
- ASSERT_DIE(node->potential_buckets_count < MAX_POTENTIAL_BUCKETS_COUNT);
-
if (BIT32R_TEST(node->potential_buckets, bucket_id))
return;
* Check if @bucket is one of potential buckets of @node
*/
static inline int
-aggregator_is_bucket_potential(const struct trie_node *node, u32 id)
+aggregator_is_bucket_potential(const struct trie_node *node, u32 id, int bitmap_size)
{
ASSERT_DIE(node != NULL);
- ASSERT_DIE(id < MAX_POTENTIAL_BUCKETS_COUNT);
+ ASSERT_DIE(id < (sizeof(node->potential_buckets[0]) * bitmap_size * 8));
return BIT32R_TEST(node->potential_buckets, id);
}
ASSERT_DIE(p != NULL);
ASSERT_DIE(node != NULL);
- for (int i = 0; i < POTENTIAL_BUCKETS_BITMAP_SIZE; i++)
+ for (int i = 0; i < p->bitmap_size; i++)
{
if (node->potential_buckets[i] == 0)
continue;
* Returns: whether the set of potential buckets in the target node has changed.
*/
static bool
-aggregator_merge_potential_buckets(struct trie_node *target, const struct trie_node *left, const struct trie_node *right)
+aggregator_merge_potential_buckets(struct trie_node *target, const struct trie_node *left, const struct trie_node *right, int bitmap_size)
{
ASSERT_DIE(target != NULL);
ASSERT_DIE(left != NULL);
bool has_intersection = false;
bool has_changed = false;
- u32 before[ARRAY_SIZE(target->potential_buckets)] = { 0 };
+ u32 *before = allocz(sizeof(*before) * bitmap_size);
target->potential_buckets_count = 0;
/* First we try to compute intersection. If it exists, we want to keep it. */
- for (int i = 0; i < POTENTIAL_BUCKETS_BITMAP_SIZE; i++)
+ for (int i = 0; i < bitmap_size; i++)
{
/* Save current bitmap values */
before[i] = target->potential_buckets[i];
target->potential_buckets_count = 0;
has_changed = false;
- for (int i = 0; i < POTENTIAL_BUCKETS_BITMAP_SIZE; i++)
+ for (int i = 0; i < bitmap_size; i++)
{
target->potential_buckets[i] = left->potential_buckets[i] | right->potential_buckets[i];
target->potential_buckets_count += u32_popcount(target->potential_buckets[i]);
buffer_print(buf, "{");
- for (int i = 0, j = 0; i < POTENTIAL_BUCKETS_BITMAP_SIZE; i++)
+ for (int i = 0, j = 0; i < p->bitmap_size; i++)
{
if (node->potential_buckets[i] == 0)
continue;
node->ancestor = NULL;
node->original_bucket = NULL;
node->potential_buckets_count = 0;
- memset(node->potential_buckets, 0, sizeof(node->potential_buckets));
+ memset(node->potential_buckets, 0, sizeof(node->potential_buckets[0]) * p->bitmap_size);
return node;
}
* @node: node from which to descend
*/
static void
-aggregator_propagate_and_merge(struct trie_node *node)
+aggregator_propagate_and_merge(struct trie_node *node, int bitmap_size)
{
ASSERT_DIE(node != NULL);
ASSERT_DIE(node->status != UNASSIGNED_FIB);
- ASSERT_DIE(node->potential_buckets_count <= MAX_POTENTIAL_BUCKETS_COUNT);
+ ASSERT_DIE(node->potential_buckets_count <= (int)(bitmap_size * sizeof(node->potential_buckets[0]) * 8));
if (node->px_origin == ORIGINAL)
ASSERT_DIE(node->original_bucket != NULL);
{
/* Reset the bucket bitmap to cleanup possible old bucket information */
node->potential_buckets_count = 0;
- memset(node->potential_buckets, 0, sizeof(node->potential_buckets));
+ memset(node->potential_buckets, 0, sizeof(node->potential_buckets[0]) * bitmap_size);
/*
* For the leaf node, by definition, the only bucket in the bitmap is the
* original bucket.
*/
+ ASSERT_DIE(node->original_bucket->id < (bitmap_size * sizeof(node->potential_buckets[0]) * 8));
aggregator_node_add_potential_bucket(node, node->original_bucket->id);
/* No children, no further work. Done! */
* This fixes the (kinda) missing first pass when comparing our algorithm
* to the original one.
*/
- struct trie_node imaginary_node = { 0 };
- aggregator_node_add_potential_bucket(&imaginary_node, node->original_bucket->id);
+ struct trie_node *imaginary_node = allocz(sizeof(*imaginary_node) + sizeof(imaginary_node->potential_buckets[0]) * bitmap_size);
+
+ ASSERT_DIE(node->original_bucket->id < (bitmap_size * sizeof(imaginary_node->potential_buckets[0]) * 8));
+ aggregator_node_add_potential_bucket(imaginary_node, node->original_bucket->id);
/* Process children */
if (left)
- aggregator_propagate_and_merge(left);
+ aggregator_propagate_and_merge(left, bitmap_size);
else
- left = &imaginary_node;
+ left = imaginary_node;
if (right)
- aggregator_propagate_and_merge(right);
+ aggregator_propagate_and_merge(right, bitmap_size);
else
- right = &imaginary_node;
+ right = imaginary_node;
/* Merge sets of potential buckets */
- aggregator_merge_potential_buckets(node, left, right);
+ aggregator_merge_potential_buckets(node, left, right, bitmap_size);
}
/*
* the trie.
*/
static void
-aggregator_process_one_child_nodes(struct trie_node *node, const struct aggregator_bucket *inherited_bucket, struct slab *trie_slab)
+aggregator_process_one_child_nodes(struct trie_node *node, const struct aggregator_bucket *inherited_bucket, struct slab *trie_slab, int bitmap_size)
{
ASSERT_DIE(node != NULL);
/* Imaginary node that would have been added during normalization of the trie */
- struct trie_node imaginary_node = {
+ const size_t node_size = sizeof(*node) + sizeof(node->potential_buckets[0]) * bitmap_size;
+
+ struct trie_node *imaginary_node = allocz(node_size);
+
+ *imaginary_node = (struct trie_node) {
.parent = node,
.original_bucket = node->original_bucket,
.status = NON_FIB,
};
/* Imaginary node inherits bucket from its parent - current node */
- aggregator_node_add_potential_bucket(&imaginary_node, node->original_bucket->id);
+ ASSERT_DIE(node->original_bucket->id < (bitmap_size * sizeof(node->potential_buckets[0]) * 8));
+ aggregator_node_add_potential_bucket(imaginary_node, node->original_bucket->id);
/*
* If the current node (parent of the imaginary node) has a bucket, then
* imaginary node needs to be added to the trie because it's not covered
* by its ancestor.
*/
- if (!aggregator_is_bucket_potential(&imaginary_node, imaginary_node_inherited_bucket->id))
+ if (!aggregator_is_bucket_potential(imaginary_node, imaginary_node_inherited_bucket->id, bitmap_size))
{
/* Allocate new node and copy imaginary node into it */
struct trie_node *new = sl_allocz(trie_slab);
- *new = imaginary_node;
+ memcpy(new, imaginary_node, node_size);
const struct trie_node * const left = node->child[0];
const struct trie_node * const right = node->child[1];
{
ASSERT_DIE(node != NULL);
ASSERT_DIE(node->status != UNASSIGNED_FIB);
- ASSERT_DIE(node->potential_buckets_count <= MAX_POTENTIAL_BUCKETS_COUNT);
+ ASSERT_DIE(node->potential_buckets_count <= (int)(p->bitmap_size * sizeof(node->potential_buckets[0]) * 8));
ASSERT_DIE(node->original_bucket != NULL);
ASSERT_DIE(node->parent->ancestor != NULL);
* of the current node, then this node doesn't need a bucket because it
* inherits one, and its prefix is thus not needed in FIB.
*/
- if (aggregator_is_bucket_potential(node, inherited_bucket->id))
+ if (aggregator_is_bucket_potential(node, inherited_bucket->id, p->bitmap_size))
aggregator_remove_node_prefix(p, node, *prefix, pxlen);
else
aggregator_export_node_prefix(p, node, *prefix, pxlen);
/* Process nodes with only one child */
if ((left && !right) || (!left && right))
- aggregator_process_one_child_nodes(node, inherited_bucket, p->trie_slab);
+ aggregator_process_one_child_nodes(node, inherited_bucket, p->trie_slab, p->bitmap_size);
/* Preorder traversal */
if (node->child[0]) /* TODO: nestačí tady left a right? Takhle to vypadá, že se left a right pod rukama můžou přepsat. */
aggregator_group_prefixes(struct aggregator_proto *p, struct trie_node *node)
{
ASSERT_DIE(node != NULL);
- ASSERT_DIE(node->potential_buckets_count <= MAX_POTENTIAL_BUCKETS_COUNT);
ASSERT_DIE(node->potential_buckets_count > 0);
+ ASSERT_DIE(node->potential_buckets_count <= (int)(p->bitmap_size * sizeof(node->potential_buckets[0]) * 8));
ip_addr prefix = (p->addr_type == NET_IP4) ? ipa_from_ip4(IP4_NONE) : ipa_from_ip6(IP6_NONE);
u32 pxlen = 0;
* Stop when the node's set doesn't change and return the last updated node.
*/
static struct trie_node *
-aggregator_merge_buckets_above(struct trie_node *node)
+aggregator_merge_buckets_above(struct trie_node *node, int bitmap_size)
{
ASSERT_DIE(node != NULL);
const struct trie_node *right = parent->child[1];
ASSERT_DIE(left == node || right == node);
- struct trie_node imaginary_node = { 0 };
- aggregator_node_add_potential_bucket(&imaginary_node, parent->original_bucket->id);
+ struct trie_node *imaginary_node = allocz(sizeof(*imaginary_node) + sizeof(imaginary_node->potential_buckets[0]) * bitmap_size);
+
+ ASSERT_DIE(parent->original_bucket->id < (bitmap_size * sizeof(imaginary_node->potential_buckets[0]) * 8));
+ aggregator_node_add_potential_bucket(imaginary_node, parent->original_bucket->id);
/* Nodes with only one child */
if (left && !right)
- right = &imaginary_node;
+ right = imaginary_node;
else if (!left && right)
- left = &imaginary_node;
+ left = imaginary_node;
ASSERT_DIE(left != NULL && right != NULL);
/* The parent's set didn't change by merging, stop here */
- if (!aggregator_merge_potential_buckets(parent, left, right))
+ if (!aggregator_merge_potential_buckets(parent, left, right, bitmap_size))
return node;
node = parent;
{
ASSERT_DIE(p->addr_type == NET_IP4 || p->addr_type == NET_IP6);
- aggregator_propagate_and_merge(p->root);
+ aggregator_propagate_and_merge(p->root, p->bitmap_size);
aggregator_group_prefixes(p, p->root);
check_trie_after_aggregation(p->root);
ASSERT_DIE(ancestor->status == IN_FIB);
/* Reaggregate trie with incorporated update */
- aggregator_propagate_and_merge(ancestor);
+ aggregator_propagate_and_merge(ancestor, p->bitmap_size);
/* Merge buckets upwards until they change, return last updated node */
- struct trie_node *highest_node = aggregator_merge_buckets_above(ancestor);
+ struct trie_node *highest_node = aggregator_merge_buckets_above(ancestor, p->bitmap_size);
ASSERT_DIE(highest_node != NULL);
aggregator_group_prefixes(p, highest_node);