📄 fib_trie.c
字号:
printk(KERN_WARNING "Fix halve_threshold. Now=%d size=%d bits\n", halve_threshold, tn->bits); } /* Only one child remains */ if (tn->empty_children == tnode_child_length(tn) - 1) for (i = 0; i < tnode_child_length(tn); i++) { struct node *n; n = tn->child[i]; if (!n) continue; /* compress one level */ node_set_parent(n, NULL); tnode_free(tn); return n; } return (struct node *) tn;}static struct tnode *inflate(struct trie *t, struct tnode *tn){ struct tnode *inode; struct tnode *oldtnode = tn; int olen = tnode_child_length(tn); int i; pr_debug("In inflate\n"); tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits + 1); if (!tn) return ERR_PTR(-ENOMEM); /* * Preallocate and store tnodes before the actual work so we * don't get into an inconsistent state if memory allocation * fails. In case of failure we return the oldnode and inflate * of tnode is ignored. */ for (i = 0; i < olen; i++) { struct tnode *inode = (struct tnode *) tnode_get_child(oldtnode, i); if (inode && IS_TNODE(inode) && inode->pos == oldtnode->pos + oldtnode->bits && inode->bits > 1) { struct tnode *left, *right; t_key m = ~0U << (KEYLENGTH - 1) >> inode->pos; left = tnode_new(inode->key&(~m), inode->pos + 1, inode->bits - 1); if (!left) goto nomem; right = tnode_new(inode->key|m, inode->pos + 1, inode->bits - 1); if (!right) { tnode_free(left); goto nomem; } put_child(t, tn, 2*i, (struct node *) left); put_child(t, tn, 2*i+1, (struct node *) right); } } for (i = 0; i < olen; i++) { struct node *node = tnode_get_child(oldtnode, i); struct tnode *left, *right; int size, j; /* An empty child */ if (node == NULL) continue; /* A leaf or an internal node with skipped bits */ if (IS_LEAF(node) || ((struct tnode *) node)->pos > tn->pos + tn->bits - 1) { if (tkey_extract_bits(node->key, oldtnode->pos + oldtnode->bits, 1) == 0) put_child(t, tn, 2*i, node); else put_child(t, tn, 2*i+1, node); continue; } /* An internal node with two children */ inode = (struct tnode *) node; if (inode->bits == 1) { put_child(t, tn, 2*i, inode->child[0]); put_child(t, tn, 2*i+1, inode->child[1]); tnode_free(inode); continue; } /* An internal node with more than two children */ /* We will replace this node 'inode' with two new * ones, 'left' and 'right', each with half of the * original children. The two new nodes will have * a position one bit further down the key and this * means that the "significant" part of their keys * (see the discussion near the top of this file) * will differ by one bit, which will be "0" in * left's key and "1" in right's key. Since we are * moving the key position by one step, the bit that * we are moving away from - the bit at position * (inode->pos) - is the one that will differ between * left and right. So... we synthesize that bit in the * two new keys. * The mask 'm' below will be a single "one" bit at * the position (inode->pos) */ /* Use the old key, but set the new significant * bit to zero. */ left = (struct tnode *) tnode_get_child(tn, 2*i); put_child(t, tn, 2*i, NULL); BUG_ON(!left); right = (struct tnode *) tnode_get_child(tn, 2*i+1); put_child(t, tn, 2*i+1, NULL); BUG_ON(!right); size = tnode_child_length(left); for (j = 0; j < size; j++) { put_child(t, left, j, inode->child[j]); put_child(t, right, j, inode->child[j + size]); } put_child(t, tn, 2*i, resize(t, left)); put_child(t, tn, 2*i+1, resize(t, right)); tnode_free(inode); } tnode_free(oldtnode); return tn;nomem: { int size = tnode_child_length(tn); int j; for (j = 0; j < size; j++) if (tn->child[j]) tnode_free((struct tnode *)tn->child[j]); tnode_free(tn); return ERR_PTR(-ENOMEM); }}static struct tnode *halve(struct trie *t, struct tnode *tn){ struct tnode *oldtnode = tn; struct node *left, *right; int i; int olen = tnode_child_length(tn); pr_debug("In halve\n"); tn = tnode_new(oldtnode->key, oldtnode->pos, oldtnode->bits - 1); if (!tn) return ERR_PTR(-ENOMEM); /* * Preallocate and store tnodes before the actual work so we * don't get into an inconsistent state if memory allocation * fails. In case of failure we return the oldnode and halve * of tnode is ignored. */ for (i = 0; i < olen; i += 2) { left = tnode_get_child(oldtnode, i); right = tnode_get_child(oldtnode, i+1); /* Two nonempty children */ if (left && right) { struct tnode *newn; newn = tnode_new(left->key, tn->pos + tn->bits, 1); if (!newn) goto nomem; put_child(t, tn, i/2, (struct node *)newn); } } for (i = 0; i < olen; i += 2) { struct tnode *newBinNode; left = tnode_get_child(oldtnode, i); right = tnode_get_child(oldtnode, i+1); /* At least one of the children is empty */ if (left == NULL) { if (right == NULL) /* Both are empty */ continue; put_child(t, tn, i/2, right); continue; } if (right == NULL) { put_child(t, tn, i/2, left); continue; } /* Two nonempty children */ newBinNode = (struct tnode *) tnode_get_child(tn, i/2); put_child(t, tn, i/2, NULL); put_child(t, newBinNode, 0, left); put_child(t, newBinNode, 1, right); put_child(t, tn, i/2, resize(t, newBinNode)); } tnode_free(oldtnode); return tn;nomem: { int size = tnode_child_length(tn); int j; for (j = 0; j < size; j++) if (tn->child[j]) tnode_free((struct tnode *)tn->child[j]); tnode_free(tn); return ERR_PTR(-ENOMEM); }}static void trie_init(struct trie *t){ if (!t) return; t->size = 0; rcu_assign_pointer(t->trie, NULL); t->revision = 0;#ifdef CONFIG_IP_FIB_TRIE_STATS memset(&t->stats, 0, sizeof(struct trie_use_stats));#endif}/* readside must use rcu_read_lock currently dump routines via get_fa_head and dump */static struct leaf_info *find_leaf_info(struct leaf *l, int plen){ struct hlist_head *head = &l->list; struct hlist_node *node; struct leaf_info *li; hlist_for_each_entry_rcu(li, node, head, hlist) if (li->plen == plen) return li; return NULL;}static inline struct list_head * get_fa_head(struct leaf *l, int plen){ struct leaf_info *li = find_leaf_info(l, plen); if (!li) return NULL; return &li->falh;}static void insert_leaf_info(struct hlist_head *head, struct leaf_info *new){ struct leaf_info *li = NULL, *last = NULL; struct hlist_node *node; if (hlist_empty(head)) { hlist_add_head_rcu(&new->hlist, head); } else { hlist_for_each_entry(li, node, head, hlist) { if (new->plen > li->plen) break; last = li; } if (last) hlist_add_after_rcu(&last->hlist, &new->hlist); else hlist_add_before_rcu(&new->hlist, &li->hlist); }}/* rcu_read_lock needs to be hold by caller from readside */static struct leaf *fib_find_node(struct trie *t, u32 key){ int pos; struct tnode *tn; struct node *n; pos = 0; n = rcu_dereference(t->trie); while (n != NULL && NODE_TYPE(n) == T_TNODE) { tn = (struct tnode *) n; check_tnode(tn); if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { pos = tn->pos + tn->bits; n = tnode_get_child(tn, tkey_extract_bits(key, tn->pos, tn->bits)); } else break; } /* Case we have found a leaf. Compare prefixes */ if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) return (struct leaf *)n; return NULL;}static struct node *trie_rebalance(struct trie *t, struct tnode *tn){ int wasfull; t_key cindex, key = tn->key; struct tnode *tp; while (tn != NULL && (tp = node_parent((struct node *)tn)) != NULL) { cindex = tkey_extract_bits(key, tp->pos, tp->bits); wasfull = tnode_full(tp, tnode_get_child(tp, cindex)); tn = (struct tnode *) resize (t, (struct tnode *)tn); tnode_put_child_reorg((struct tnode *)tp, cindex,(struct node*)tn, wasfull); tp = node_parent((struct node *) tn); if (!tp) break; tn = tp; } /* Handle last (top) tnode */ if (IS_TNODE(tn)) tn = (struct tnode*) resize(t, (struct tnode *)tn); return (struct node*) tn;}/* only used from updater-side */static struct list_head *fib_insert_node(struct trie *t, int *err, u32 key, int plen){ int pos, newpos; struct tnode *tp = NULL, *tn = NULL; struct node *n; struct leaf *l; int missbit; struct list_head *fa_head = NULL; struct leaf_info *li; t_key cindex; pos = 0; n = t->trie; /* If we point to NULL, stop. Either the tree is empty and we should * just put a new leaf in if, or we have reached an empty child slot, * and we should just put our new leaf in that. * If we point to a T_TNODE, check if it matches our key. Note that * a T_TNODE might be skipping any number of bits - its 'pos' need * not be the parent's 'pos'+'bits'! * * If it does match the current key, get pos/bits from it, extract * the index from our key, push the T_TNODE and walk the tree. * * If it doesn't, we have to replace it with a new T_TNODE. * * If we point to a T_LEAF, it might or might not have the same key * as we do. If it does, just change the value, update the T_LEAF's * value, and return it. * If it doesn't, we need to replace it with a T_TNODE. */ while (n != NULL && NODE_TYPE(n) == T_TNODE) { tn = (struct tnode *) n; check_tnode(tn); if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) { tp = tn; pos = tn->pos + tn->bits; n = tnode_get_child(tn, tkey_extract_bits(key, tn->pos, tn->bits)); BUG_ON(n && node_parent(n) != tn); } else break; } /* * n ----> NULL, LEAF or TNODE * * tp is n's (parent) ----> NULL or TNODE */ BUG_ON(tp && IS_LEAF(tp)); /* Case 1: n is a leaf. Compare prefixes */ if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) { struct leaf *l = (struct leaf *) n; li = leaf_info_new(plen); if (!li) { *err = -ENOMEM; goto err; } fa_head = &li->falh; insert_leaf_info(&l->list, li); goto done; } t->size++; l = leaf_new(); if (!l) { *err = -ENOMEM; goto err; } l->key = key; li = leaf_info_new(plen); if (!li) { tnode_free((struct tnode *) l); *err = -ENOMEM; goto err; } fa_head = &li->falh; insert_leaf_info(&l->list, li); if (t->trie && n == NULL) { /* Case 2: n is NULL, and will just insert a new leaf */ node_set_parent((struct node *)l, tp); cindex = tkey_extract_bits(key, tp->pos, tp->bits); put_child(t, (struct tnode *)tp, cindex, (struct node *)l); } else { /* Case 3: n is a LEAF or a TNODE and the key doesn't match. */ /* * Add a new tnode here * first tnode need some special handling */ if (tp) pos = tp->pos+tp->bits; else pos = 0; if (n) { newpos = tkey_mismatch(key, pos, n->key); tn = tnode_new(n->key, newpos, 1); } else { newpos = 0; tn = tnode_new(key, newpos, 1); /* First tnode */ } if (!tn) { free_leaf_info(li); tnode_free((struct tnode *) l); *err = -ENOMEM; goto err; } node_set_parent((struct node *)tn, tp); missbit = tkey_extract_bits(key, newpos, 1); put_child(t, tn, missbit, (struct node *)l); put_child(t, tn, 1-missbit, n); if (tp) { cindex = tkey_extract_bits(key, tp->pos, tp->bits); put_child(t, (struct tnode *)tp, cindex, (struct node *)tn); } else { rcu_assign_pointer(t->trie, (struct node *)tn); /* First tnode */ tp = tn; } } if (tp && tp->pos + tp->bits > 32) printk(KERN_WARNING "fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n", tp, tp->pos, tp->bits, key, plen); /* Rebalance the trie */ rcu_assign_pointer(t->trie, trie_rebalance(t, tp));done: t->revision++;err: return fa_head;}/* * Caller must hold RTNL. */static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg){ struct trie *t = (struct trie *) tb->tb_data; struct fib_alias *fa, *new_fa; struct list_head *fa_head = NULL; struct fib_info *fi; int plen = cfg->fc_dst_len; u8 tos = cfg->fc_tos; u32 key, mask; int err; struct leaf *l; if (plen > 32) return -EINVAL; key = ntohl(cfg->fc_dst); pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); mask = ntohl(inet_make_mask(plen)); if (key & ~mask) return -EINVAL; key = key & mask; fi = fib_create_info(cfg); if (IS_ERR(fi)) { err = PTR_ERR(fi); goto err; } l = fib_find_node(t, key); fa = NULL; if (l) { fa_head = get_fa_head(l, plen); fa = fib_find_alias(fa_head, tos, fi->fib_priority); } /* Now fa, if non-NULL, points to the first fib alias * with the same keys [prefix,tos,priority], if such key already * exists or to the node before which we will insert new one. * * If fa is NULL, we will need to allocate a new one and * insert to the head of f. * * If f is NULL, no fib node matched the destination key * and we need to allocate a new one of those as well. */ if (fa && fa->fa_info->fib_priority == fi->fib_priority) { struct fib_alias *fa_orig; err = -EEXIST; if (cfg->fc_nlflags & NLM_F_EXCL) goto out; if (cfg->fc_nlflags & NLM_F_REPLACE) { struct fib_info *fi_drop; u8 state; if (fi->fib_treeref > 1) goto out; err = -ENOBUFS; new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); if (new_fa == NULL) goto out; fi_drop = fa->fa_info; new_fa->fa_tos = fa->fa_tos; new_fa->fa_info = fi; new_fa->fa_type = cfg->fc_type; new_fa->fa_scope = cfg->fc_scope; state = fa->fa_state; new_fa->fa_state &= ~FA_S_ACCESSED; list_replace_rcu(&fa->fa_list, &new_fa->fa_list); alias_free_mem_rcu(fa); fib_release_info(fi_drop); if (state & FA_S_ACCESSED) rt_cache_flush(-1); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE); goto succeeded; } /* Error if we find a perfect match which * uses the same scope, type, and nexthop * information. */ fa_orig = fa; list_for_each_entry(fa, fa_orig->fa_list.prev, fa_list) { if (fa->fa_tos != tos) break; if (fa->fa_info->fib_priority != fi->fib_priority) break; if (fa->fa_type == cfg->fc_type && fa->fa_scope == cfg->fc_scope && fa->fa_info == fi) { goto out; } } if (!(cfg->fc_nlflags & NLM_F_APPEND)) fa = fa_orig; } err = -ENOENT; if (!(cfg->fc_nlflags & NLM_F_CREATE)) goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -