ip_tree.c

Go to the documentation of this file.
00001 /* 
00002  * $Id: ip_tree.c 4518 2008-07-28 15:39:28Z henningw $
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  * History:
00023  * --------
00024  *  2004-11-05: adaptiv init lock (bogdan)
00025  *  2008-04-17  the leaf nodes memorize (via flags) if they are in RED state
00026  *               (detected) or not -> better logging and MI (bogdan)
00027  */
00028 
00029 
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <unistd.h>
00034 #include <assert.h>
00035 
00036 #include "../../dprint.h"
00037 #include "../../mem/shm_mem.h"
00038 #include "ip_tree.h"
00039 
00040 
00041 
00042 static struct ip_tree*  root = 0;
00043 
00044 
00045 static inline struct ip_node* prv_get_tree_branch(unsigned char b)
00046 {
00047    return root->entries[b].node;
00048 }
00049 
00050 
00051 /* locks a tree branch */
00052 static inline void prv_lock_tree_branch(unsigned char b)
00053 {
00054    lock_set_get( root->entry_lock_set, root->entries[b].lock_idx);
00055 }
00056 
00057 
00058 
00059 /* unlocks a tree branch */
00060 static inline void prv_unlock_tree_branch(unsigned char b)
00061 {
00062    lock_set_release( root->entry_lock_set, root->entries[b].lock_idx);
00063 }
00064 
00065 
00066 /* wrapper functions */
00067 struct ip_node* get_tree_branch(unsigned char b)
00068 {
00069    return prv_get_tree_branch(b);
00070 }
00071 void lock_tree_branch(unsigned char b)
00072 {
00073    prv_lock_tree_branch(b);
00074 }
00075 void unlock_tree_branch(unsigned char b)
00076 {
00077    prv_unlock_tree_branch(b);
00078 }
00079 
00080 
00081 /* size must be a power of 2  */
00082 static gen_lock_set_t* init_lock_set(int *size)
00083 {
00084    gen_lock_set_t *lset;
00085 
00086    lset=0; /* kill warnings */
00087    for( ; *size ; *size=((*size)>>1) ) {
00088       LM_INFO("probing %d set size\n", *size);
00089       /* create a lock set */
00090       lset = lock_set_alloc( *size );
00091       if (lset==0) {
00092          LM_INFO("cannot get %d locks\n", *size);
00093          continue;
00094       }
00095       /* init lock set */
00096       if (lock_set_init(lset)==0) {
00097          LM_INFO("cannot init %d locks\n", *size);
00098          lock_set_dealloc( lset );
00099          lset = 0;
00100          continue;
00101       }
00102       /* alloc and init succesfull */
00103       break;
00104    }
00105 
00106    if (*size==0) {
00107       LM_ERR("cannot get a lock set\n");
00108       return 0;
00109    }
00110    return lset;
00111 }
00112 
00113 
00114 
00115 /* Builds and Inits a new IP tree */
00116 int init_ip_tree(int maximum_hits)
00117 {
00118    int size;
00119    int i;
00120 
00121    /* create the root */
00122    root = (struct ip_tree*)shm_malloc(sizeof(struct ip_tree));
00123    if (root==0) {
00124       LM_ERR("shm malloc failed\n");
00125       goto error;
00126    }
00127    memset( root, 0, sizeof(struct ip_tree));
00128 
00129    /* init lock set */
00130    size = MAX_IP_BRANCHES;
00131    root->entry_lock_set = init_lock_set( &size );
00132    if (root->entry_lock_set==0) {
00133       LM_ERR("failed to create locks\n");
00134       goto error;
00135    }
00136    /* assign to each branch a lock */
00137    for(i=0;i<MAX_IP_BRANCHES;i++) {
00138       root->entries[i].node = 0;
00139       root->entries[i].lock_idx = i % size;
00140    }
00141 
00142    root->max_hits = maximum_hits;
00143 
00144    return 0;
00145 error:
00146    if (root)
00147       shm_free(root);
00148    return -1;
00149 }
00150 
00151 
00152 
00153 /* destroy an ip_node and all nodes under it; the nodes must be first removed
00154  * from any other lists/timers */
00155 static inline void destroy_ip_node(struct ip_node *node)
00156 {
00157    struct ip_node *foo, *bar;
00158 
00159    foo = node->kids;
00160    while (foo){
00161       bar = foo;
00162       foo = foo->next;
00163       destroy_ip_node(bar);
00164    }
00165 
00166    shm_free(node);
00167 }
00168 
00169 
00170 
00171 /* destroy and free the IP tree */
00172 void destroy_ip_tree(void)
00173 {
00174    int i;
00175 
00176    if (root==0)
00177       return;
00178 
00179    /* destroy and free the lock set */
00180    if (root->entry_lock_set) {
00181       lock_set_destroy(root->entry_lock_set);
00182       lock_set_dealloc(root->entry_lock_set);
00183    }
00184 
00185    /* destroy all the nodes */
00186    for(i=0;i<MAX_IP_BRANCHES;i++)
00187       if (root->entries[i].node)
00188          destroy_ip_node(root->entries[i].node);
00189 
00190    shm_free( root );
00191    root = 0;
00192 
00193    return;
00194 }
00195 
00196 
00197 
00198 /* builds a new ip_node corresponding to a byte value */
00199 static inline struct ip_node *new_ip_node(unsigned char byte)
00200 {
00201    struct ip_node *new_node;
00202 
00203    new_node = (struct ip_node*)shm_malloc(sizeof(struct ip_node));
00204    if (!new_node) {
00205       LM_ERR("no more shm mem\n");
00206       return 0;
00207    }
00208    memset( new_node, 0, sizeof(struct ip_node));
00209    new_node->byte = byte;
00210    return new_node;
00211 }
00212 
00213 
00214 
00215 /* splits from the current node (dad) a new child */
00216 struct ip_node *split_node(struct ip_node* dad, unsigned char byte)
00217 {
00218    struct ip_node *new_node;
00219 
00220    /* create a new node */
00221    if ( (new_node=new_ip_node(byte))==0 )
00222       return 0;
00223    /* the child node inherits a part of his father hits */
00224    if (dad->hits[CURR_POS]>=1)
00225       new_node->hits[CURR_POS] = (dad->hits[CURR_POS])-1;
00226    if (dad->leaf_hits[CURR_POS]>=1)
00227       new_node->leaf_hits[PREV_POS] = (dad->leaf_hits[PREV_POS])-1;
00228    /* link the child into father's kids list -> insert it at the beginning,
00229     * is much faster */
00230    if (dad->kids) {
00231       dad->kids->prev = new_node;
00232       new_node->next = dad->kids;
00233    }
00234    dad->kids = new_node;
00235    new_node->branch = dad->branch;
00236    new_node->prev = dad;
00237 
00238    return new_node;
00239 }
00240 
00241 
00242 #define is_hot_non_leaf(_node) \
00243    ( (_node)->hits[PREV_POS]>=root->max_hits>>2 ||\
00244      (_node)->hits[CURR_POS]>=root->max_hits>>2 ||\
00245      (((_node)->hits[PREV_POS]+(_node)->hits[CURR_POS])>>1)>=\
00246        root->max_hits>>2 )
00247 
00248 #define is_hot_leaf(_node) \
00249    ( (_node)->leaf_hits[PREV_POS]>=root->max_hits ||\
00250      (_node)->leaf_hits[CURR_POS]>=root->max_hits ||\
00251      (((_node)->leaf_hits[PREV_POS]+(_node)->leaf_hits[CURR_POS])>>1)>=\
00252        root->max_hits )
00253 
00254 #define is_warm_leaf(_node) \
00255    ( (_node)->hits[CURR_POS]>=root->max_hits>>2 )
00256 
00257 #define MAX_TYPE_VAL(_x) \
00258    (( (1<<(8*sizeof(_x)-1))-1 )|( (1<<(8*sizeof(_x)-1)) ))
00259 
00260 
00261 int is_node_hot_leaf(struct ip_node *node)
00262 {
00263    return is_hot_leaf(node);
00264 }
00265 
00266 
00267 /* mark with one more hit the given IP address - */
00268 struct ip_node* mark_node(unsigned char *ip,int ip_len,
00269                      struct ip_node **father,unsigned char *flag)
00270 {
00271    struct ip_node *node;
00272    struct ip_node *kid;
00273    int    byte_pos;
00274 
00275    kid = root->entries[ ip[0] ].node;
00276    node = 0;
00277    byte_pos = 0;
00278 
00279    LM_DBG("search on branch %d (top=%p)\n", ip[0],kid);
00280    /* search into the ip tree the longest prefix matching the given IP */
00281    while (kid && byte_pos<ip_len) {
00282       while (kid && kid->byte!=(unsigned char)ip[byte_pos]) {
00283             kid = kid->next;
00284       }
00285       if (kid) {
00286          node = kid;
00287          kid = kid->kids;
00288          byte_pos++;
00289       }
00290    }
00291 
00292    LM_DBG("only first %d were matched!\n",byte_pos);
00293    *flag = 0;
00294    *father = 0;
00295 
00296    /* what have we found? */
00297    if (byte_pos==ip_len) {
00298       /* we found the entire address */
00299       node->flags |= NODE_IPLEAF_FLAG;
00300       /* increment it, but be careful not to overflow the value */
00301       if(node->leaf_hits[CURR_POS]<MAX_TYPE_VAL(node->leaf_hits[CURR_POS])-1)
00302          node->leaf_hits[CURR_POS]++;
00303       /* becoming red node? */
00304       if ( (node->flags&NODE_ISRED_FLAG)==0 ) {
00305          if (is_hot_leaf(node) ) {
00306             *flag |= RED_NODE|NEWRED_NODE;
00307             node->flags |= NODE_ISRED_FLAG;
00308          }
00309       } else {
00310          *flag |= RED_NODE;
00311       }
00312    } else if (byte_pos==0) {
00313       /* we hit an empty branch in the IP tree */
00314       assert(node==0);
00315       /* add a new node containing the start byte of the IP address */
00316       if ( (node=new_ip_node(ip[0]))==0)
00317          return 0;
00318       node->hits[CURR_POS] = 1;
00319       node->branch = ip[0];
00320       *flag = NEW_NODE ;
00321       /* set this node as root of the branch starting with first byte of IP*/
00322       root->entries[ ip[0] ].node = node;
00323    } else{
00324       /* only a non-empty prefix of the IP was found */
00325       if ( node->hits[CURR_POS]<MAX_TYPE_VAL(node->hits[CURR_POS])-1 )
00326          node->hits[CURR_POS]++;
00327       if ( is_hot_non_leaf(node) ) {
00328          /* we have to split the node */
00329          *flag = NEW_NODE ;
00330          LM_DBG("splitting node %p [%d]\n",node,node->byte);
00331          *father = node;
00332          node = split_node(node,ip[byte_pos]);
00333       } else {
00334          /* to reduce memory usage, force to expire non-leaf nodes if they
00335           * have just a few hits -> basically, don't update the timer for
00336           * them the nr of hits is small */
00337          if ( !is_warm_leaf(node) )
00338             *flag = NO_UPDATE;
00339       }
00340    }
00341 
00342    return node;
00343 }
00344 
00345 
00346 
00347 /* remove and destroy a IP node along with its subtree */
00348 void remove_node(struct ip_node *node)
00349 {
00350    LM_DBG("destroying node %p\n",node);
00351    /* is it a branch root node? (these nodes have no prev (father)) */
00352    if (node->prev==0) {
00353       assert(root->entries[node->byte].node==node);
00354       root->entries[node->byte].node = 0;
00355    } else {
00356       /* unlink it from kids list */
00357       if (node->prev->kids==node)
00358          /* it's the head of the list! */
00359          node->prev->kids = node->next;
00360       else
00361          /* it's somewhere in the list */
00362          node->prev->next = node->next;
00363       if (node->next)
00364          node->next->prev = node->prev;
00365    }
00366 
00367    /* destroy the node */
00368    node->next = node->prev = 0;
00369    destroy_ip_node(node);
00370 }
00371 

Generated on Wed May 23 08:00:57 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6