mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
13 KiB
595 lines
13 KiB
/*
|
|
* $Id$
|
|
*
|
|
* pipelimit module
|
|
*
|
|
* Copyright (C) 2006 Hendrik Scholz <hscholz@raisdorf.net>
|
|
* Copyright (C) 2008 Ovidiu Sas <osas@voipembedded.com>
|
|
* Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*! \file
|
|
* \ingroup pipelimit
|
|
* \brief pipelimit :: pl_ht
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "../../dprint.h"
|
|
#include "../../ut.h"
|
|
#include "../../str.h"
|
|
#include "../../hashes.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../lib/kmi/mi.h"
|
|
#include "../../lib/kcore/km_ut.h"
|
|
|
|
#include "pl_ht.h"
|
|
|
|
static rlp_htable_t *_pl_pipes_ht = NULL;
|
|
|
|
str_map_t algo_names[] = {
|
|
{str_init("NOP"), PIPE_ALGO_NOP},
|
|
{str_init("RED"), PIPE_ALGO_RED},
|
|
{str_init("TAILDROP"), PIPE_ALGO_TAILDROP},
|
|
{str_init("FEEDBACK"), PIPE_ALGO_FEEDBACK},
|
|
{str_init("NETWORK"), PIPE_ALGO_NETWORK},
|
|
{{0, 0}, 0},
|
|
};
|
|
|
|
|
|
int pl_init_htable(unsigned int hsize)
|
|
{
|
|
int i;
|
|
|
|
if(_pl_pipes_ht!=NULL)
|
|
return -1;
|
|
|
|
_pl_pipes_ht = (rlp_htable_t*)shm_malloc(sizeof(rlp_htable_t));
|
|
if(_pl_pipes_ht==NULL)
|
|
{
|
|
LM_ERR("no more shm\n");
|
|
return -1;
|
|
}
|
|
memset(_pl_pipes_ht, 0, sizeof(rlp_htable_t));
|
|
_pl_pipes_ht->htsize = hsize;
|
|
|
|
_pl_pipes_ht->slots =
|
|
(rlp_slot_t*)shm_malloc(_pl_pipes_ht->htsize*sizeof(rlp_slot_t));
|
|
if(_pl_pipes_ht->slots==NULL)
|
|
{
|
|
LM_ERR("no more shm.\n");
|
|
shm_free(_pl_pipes_ht);
|
|
return -1;
|
|
}
|
|
memset(_pl_pipes_ht->slots, 0, _pl_pipes_ht->htsize*sizeof(rlp_slot_t));
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
if(lock_init(&_pl_pipes_ht->slots[i].lock)==0)
|
|
{
|
|
LM_ERR("cannot initalize lock[%d]\n", i);
|
|
i--;
|
|
while(i>=0)
|
|
{
|
|
lock_destroy(&_pl_pipes_ht->slots[i].lock);
|
|
i--;
|
|
}
|
|
shm_free(_pl_pipes_ht->slots);
|
|
shm_free(_pl_pipes_ht);
|
|
return -1;
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void pl_pipe_free(pl_pipe_t *it)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int pl_destroy_htable(void)
|
|
{
|
|
int i;
|
|
pl_pipe_t *it;
|
|
pl_pipe_t *it0;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return -1;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
/* free entries */
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
it0 = it;
|
|
it = it->next;
|
|
pl_pipe_free(it0);
|
|
}
|
|
/* free locks */
|
|
lock_destroy(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
shm_free(_pl_pipes_ht->slots);
|
|
shm_free(_pl_pipes_ht);
|
|
_pl_pipes_ht = NULL;
|
|
return 0;
|
|
}
|
|
|
|
#define pl_compute_hash(_s) get_hash1_raw((_s)->s,(_s)->len)
|
|
#define pl_get_entry(_h,_size) (_h)&((_size)-1)
|
|
|
|
int pl_pipe_add(str *pipeid, str *algorithm, int limit)
|
|
{
|
|
unsigned int cellid;
|
|
unsigned int idx;
|
|
pl_pipe_t *it, *prev, *cell;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return -1;
|
|
|
|
cellid = pl_compute_hash(pipeid);
|
|
idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
|
|
|
|
lock_get(&_pl_pipes_ht->slots[idx].lock);
|
|
it = _pl_pipes_ht->slots[idx].first;
|
|
prev = NULL;
|
|
while(it!=NULL && it->cellid < cellid)
|
|
{
|
|
prev = it;
|
|
it = it->next;
|
|
}
|
|
while(it!=NULL && it->cellid == cellid)
|
|
{
|
|
if(pipeid->len==it->name.len
|
|
&& strncmp(pipeid->s, it->name.s, pipeid->len)==0)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
return 1;
|
|
}
|
|
prev = it;
|
|
it = it->next;
|
|
}
|
|
|
|
cell =
|
|
(pl_pipe_t*)shm_malloc(sizeof(pl_pipe_t)+(1+pipeid->len)*sizeof(char));
|
|
if(cell == NULL)
|
|
{
|
|
LM_ERR("cannot create new cell.\n");
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
return -1;
|
|
}
|
|
memset(cell, 0, sizeof(pl_pipe_t)+(1+pipeid->len)*sizeof(char));
|
|
|
|
cell->name.s = (char*)cell + sizeof(pl_pipe_t);
|
|
strncpy(cell->name.s, pipeid->s, pipeid->len);
|
|
cell->name.len = pipeid->len;
|
|
cell->name.s[cell->name.len] = '\0';
|
|
cell->cellid = cellid;
|
|
cell->limit = limit;
|
|
if (str_map_str(algo_names, algorithm, &cell->algo))
|
|
{
|
|
LM_ERR("cannot find algorithm [%.*s].\n", algorithm->len,
|
|
algorithm->s);
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
return -1;
|
|
}
|
|
|
|
if(prev==NULL)
|
|
{
|
|
if(_pl_pipes_ht->slots[idx].first!=NULL)
|
|
{
|
|
cell->next = _pl_pipes_ht->slots[idx].first;
|
|
_pl_pipes_ht->slots[idx].first->prev = cell;
|
|
}
|
|
_pl_pipes_ht->slots[idx].first = cell;
|
|
} else {
|
|
cell->next = prev->next;
|
|
cell->prev = prev;
|
|
if(prev->next)
|
|
prev->next->prev = cell;
|
|
prev->next = cell;
|
|
}
|
|
_pl_pipes_ht->slots[idx].ssize++;
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
pl_pipe_t* pl_pipe_get(str *pipeid, int mode)
|
|
{
|
|
unsigned int cellid;
|
|
unsigned int idx;
|
|
pl_pipe_t *it;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return NULL;
|
|
|
|
cellid = pl_compute_hash(pipeid);
|
|
idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
|
|
|
|
lock_get(&_pl_pipes_ht->slots[idx].lock);
|
|
it = _pl_pipes_ht->slots[idx].first;
|
|
while(it!=NULL && it->cellid < cellid)
|
|
{
|
|
it = it->next;
|
|
}
|
|
while(it!=NULL && it->cellid == cellid)
|
|
{
|
|
if(pipeid->len==it->name.len
|
|
&& strncmp(pipeid->s, it->name.s, pipeid->len)==0)
|
|
{
|
|
/* lock_release(&_pl_pipes_ht->slots[idx].lock);*/
|
|
return it;
|
|
}
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
return NULL;
|
|
}
|
|
|
|
void pl_pipe_release(str *pipeid)
|
|
{
|
|
unsigned int cellid;
|
|
unsigned int idx;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return;
|
|
|
|
cellid = pl_compute_hash(pipeid);
|
|
idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
|
|
|
|
lock_release(&_pl_pipes_ht->slots[idx].lock);
|
|
}
|
|
|
|
|
|
int pl_print_pipes(void)
|
|
{
|
|
int i;
|
|
pl_pipe_t *it;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return -1;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[i].lock);
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
LM_DBG("+++ pipe: %.*s [%u/%d]\n", it->name.len, it->name.s,
|
|
it->cellid, i);
|
|
LM_DBG("+++ ++++ algo: %d\n", it->algo);
|
|
LM_DBG("+++ ++++ limit: %d\n", it->limit);
|
|
LM_DBG("+++ ++++ counter: %d\n", it->counter);
|
|
LM_DBG("+++ ++++ last_counter: %d\n", it->last_counter);
|
|
LM_DBG("+++ ++++ load: %d\n", it->load);
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int pl_pipe_check_feedback_setpoints(int *cfgsp)
|
|
{
|
|
int i, sp;
|
|
pl_pipe_t *it;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return -1;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[i].lock);
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
if (it->algo == PIPE_ALGO_FEEDBACK) {
|
|
sp = it->limit;
|
|
|
|
if (sp < 0 || sp > 100) {
|
|
LM_ERR("FEEDBACK cpu load must be >=0 and <= 100 [%.*s]\n",
|
|
it->name.len, it->name.s);
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
return -1;
|
|
} else if (*cfgsp == -1) {
|
|
*cfgsp = sp;
|
|
} else if (sp != *cfgsp) {
|
|
LM_ERR("pipe %.*s: FEEDBACK cpu load values must "
|
|
"be equal for all pipes\n", it->name.len, it->name.s);
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
return -1;
|
|
}
|
|
}
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pl_pipe_timer_update(int interval, int netload)
|
|
{
|
|
int i;
|
|
pl_pipe_t *it;
|
|
|
|
if(_pl_pipes_ht==NULL)
|
|
return;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[i].lock);
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
if (it->algo != PIPE_ALGO_NOP) {
|
|
if( it->algo == PIPE_ALGO_NETWORK ) {
|
|
it->load = ( netload > it->limit ) ? 1 : -1;
|
|
} else if (it->limit && interval) {
|
|
it->load = it->counter / (it->limit * interval);
|
|
}
|
|
it->last_counter = it->counter;
|
|
it->counter = 0;
|
|
}
|
|
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
}
|
|
|
|
extern int _pl_cfg_setpoint;
|
|
extern double *_pl_pid_setpoint;
|
|
|
|
/**
|
|
* checks that all FEEDBACK pipes use the same setpoint
|
|
* cpu load. also sets (common) cfg_setpoint value
|
|
* \param modparam 1 to check modparam (static) fields, 0 to use shm ones
|
|
*
|
|
* \return 0 if ok, -1 on error
|
|
*/
|
|
static int check_feedback_setpoints(int modparam)
|
|
{
|
|
_pl_cfg_setpoint = -1;
|
|
return pl_pipe_check_feedback_setpoints(&_pl_cfg_setpoint);
|
|
}
|
|
|
|
|
|
/*
|
|
* MI functions
|
|
*
|
|
* mi_stats() dumps the current config/statistics
|
|
* mi_{invite|register|subscribe}() set the limits
|
|
*/
|
|
|
|
/* mi function implementations */
|
|
struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
struct mi_root *rpl_tree;
|
|
struct mi_node *node=NULL, *rpl=NULL;
|
|
struct mi_attr* attr;
|
|
char* p;
|
|
int i, len;
|
|
pl_pipe_t *it;
|
|
|
|
rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
|
if (rpl_tree==0)
|
|
return 0;
|
|
rpl = &rpl_tree->node;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[i].lock);
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
if (it->algo != PIPE_ALGO_NOP) {
|
|
node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0);
|
|
if(node == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "id", 2, it->name.s,
|
|
it->name.len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
p = int2str((unsigned long)(it->load), &len);
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "load", 4, p, len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
p = int2str((unsigned long)(it->last_counter), &len);
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
}
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
|
|
#if 0
|
|
p = int2str((unsigned long)(*drop_rate), &len);
|
|
node = add_mi_node_child(rpl, MI_DUP_VALUE, "DROP_RATE", 9, p, len);
|
|
#endif
|
|
|
|
return rpl_tree;
|
|
error:
|
|
LM_ERR("Unable to create reply\n");
|
|
free_mi_tree(rpl_tree);
|
|
return 0;
|
|
}
|
|
|
|
struct mi_root* mi_get_pipes(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
struct mi_root *rpl_tree;
|
|
struct mi_node *node=NULL, *rpl=NULL;
|
|
struct mi_attr* attr;
|
|
str algo;
|
|
char* p;
|
|
int i, len;
|
|
pl_pipe_t *it;
|
|
|
|
rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
|
if (rpl_tree==0)
|
|
return 0;
|
|
rpl = &rpl_tree->node;
|
|
|
|
for(i=0; i<_pl_pipes_ht->htsize; i++)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[i].lock);
|
|
it = _pl_pipes_ht->slots[i].first;
|
|
while(it)
|
|
{
|
|
if (it->algo != PIPE_ALGO_NOP) {
|
|
node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0);
|
|
if(node == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "id" , 2, it->name.s,
|
|
it->name.len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
if (str_map_int(algo_names, it->algo, &algo))
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
attr = add_mi_attr(node, 0, "algorithm", 9, algo.s, algo.len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
p = int2str((unsigned long)(it->limit), &len);
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "limit", 5, p, len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
|
|
p = int2str((unsigned long)(it->counter), &len);
|
|
attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len);
|
|
if(attr == NULL)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
goto error;
|
|
}
|
|
}
|
|
it = it->next;
|
|
}
|
|
lock_release(&_pl_pipes_ht->slots[i].lock);
|
|
}
|
|
return rpl_tree;
|
|
error:
|
|
LM_ERR("Unable to create reply\n");
|
|
free_mi_tree(rpl_tree);
|
|
return 0;
|
|
}
|
|
|
|
struct mi_root* mi_set_pipe(struct mi_root* cmd_tree, void* param)
|
|
{
|
|
struct mi_node *node;
|
|
unsigned int algo_id, limit = 0;
|
|
pl_pipe_t *it;
|
|
str pipeid;
|
|
|
|
node = cmd_tree->node.kids;
|
|
if (node == NULL)
|
|
return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
|
|
if ( !node->value.s || !node->value.len)
|
|
goto error;
|
|
pipeid = node->value;
|
|
|
|
node = node->next;
|
|
if ( !node->value.s || !node->value.len)
|
|
goto error;
|
|
if (str_map_str(algo_names, &(node->value), (int*)&algo_id)) {
|
|
LM_ERR("unknown algorithm: '%.*s'\n", node->value.len, node->value.s);
|
|
goto error;
|
|
}
|
|
|
|
node = node->next;
|
|
if ( !node->value.s || !node->value.len || strno2int(&node->value,&limit)<0)
|
|
goto error;
|
|
|
|
LM_DBG("set_pipe: %.*s:%d:%d\n", pipeid.len, pipeid.s, algo_id, limit);
|
|
|
|
it = pl_pipe_get(&pipeid, 1);
|
|
if (it==NULL) {
|
|
LM_ERR("no pipe: %.*s\n", pipeid.len, pipeid.s);
|
|
goto error;
|
|
}
|
|
|
|
it->algo = algo_id;
|
|
it->limit = limit;
|
|
|
|
if (check_feedback_setpoints(0)) {
|
|
pl_pipe_release(&pipeid);
|
|
LM_ERR("feedback limits don't match\n");
|
|
goto error;
|
|
} else {
|
|
*_pl_pid_setpoint = 0.01 * (double)_pl_cfg_setpoint;
|
|
}
|
|
|
|
pl_pipe_release(&pipeid);
|
|
|
|
return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
|
|
error:
|
|
return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
|
|
}
|
|
|
|
void rpl_pipe_lock(int slot)
|
|
{
|
|
lock_get(&_pl_pipes_ht->slots[slot].lock);
|
|
}
|
|
|
|
void rpl_pipe_release(int slot)
|
|
{
|
|
lock_release(&_pl_pipes_ht->slots[slot].lock);
|
|
}
|
|
|
|
|