1 /*
2  * pipelimit module
3  *
4  * Copyright (C) 2006 Hendrik Scholz <hscholz@raisdorf.net>
5  * Copyright (C) 2008 Ovidiu Sas <osas@voipembedded.com>
6  * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
7  *
8  * This file is part of Kamailio, a free SIP server.
9  *
10  * Kamailio is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * Kamailio is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24 
25 /*! \file
26  * \ingroup pipelimit
27  * \brief pipelimit :: pl_ht
28  */
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 
34 #include "../../core/dprint.h"
35 #include "../../core/ut.h"
36 #include "../../core/str.h"
37 #include "../../core/hashes.h"
38 #include "../../core/mem/shm_mem.h"
39 #include "../../core/rpc_lookup.h"
40 
41 #include "pl_ht.h"
42 
43 static rlp_htable_t *_pl_pipes_ht = NULL;
44 extern int pl_clean_unused;
45 
46 str_map_t algo_names[] = {
47 	{str_init("NOP"),	PIPE_ALGO_NOP},
48 	{str_init("RED"),	PIPE_ALGO_RED},
49 	{str_init("TAILDROP"),	PIPE_ALGO_TAILDROP},
50 	{str_init("FEEDBACK"),	PIPE_ALGO_FEEDBACK},
51 	{str_init("NETWORK"),	PIPE_ALGO_NETWORK},
52 	{{0, 0},		0},
53 };
54 
55 
pl_init_htable(unsigned int hsize)56 int pl_init_htable(unsigned int hsize)
57 {
58 	int i;
59 
60 	if(_pl_pipes_ht!=NULL)
61 		return -1;
62 
63 	_pl_pipes_ht = (rlp_htable_t*)shm_malloc(sizeof(rlp_htable_t));
64 	if(_pl_pipes_ht==NULL)
65 	{
66 		LM_ERR("no more shm\n");
67 		return -1;
68 	}
69 	memset(_pl_pipes_ht, 0, sizeof(rlp_htable_t));
70 	_pl_pipes_ht->htsize = hsize;
71 
72 	_pl_pipes_ht->slots =
73 			(rlp_slot_t*)shm_malloc(_pl_pipes_ht->htsize*sizeof(rlp_slot_t));
74 	if(_pl_pipes_ht->slots==NULL)
75 	{
76 		LM_ERR("no more shm.\n");
77 		shm_free(_pl_pipes_ht);
78 		return -1;
79 	}
80 	memset(_pl_pipes_ht->slots, 0, _pl_pipes_ht->htsize*sizeof(rlp_slot_t));
81 
82 	for(i=0; i<_pl_pipes_ht->htsize; i++)
83 	{
84 		if(lock_init(&_pl_pipes_ht->slots[i].lock)==0)
85 		{
86 			LM_ERR("cannot initialize lock[%d]\n", i);
87 			i--;
88 			while(i>=0)
89 			{
90 				lock_destroy(&_pl_pipes_ht->slots[i].lock);
91 				i--;
92 			}
93 			shm_free(_pl_pipes_ht->slots);
94 			shm_free(_pl_pipes_ht);
95 			return -1;
96 
97 		}
98 	}
99 
100 	return 0;
101 }
102 
103 
pl_pipe_free(pl_pipe_t * it)104 void pl_pipe_free(pl_pipe_t *it)
105 {
106 	shm_free(it);
107 	return;
108 }
109 
pl_destroy_htable(void)110 int pl_destroy_htable(void)
111 {
112 	int i;
113 	pl_pipe_t *it;
114 	pl_pipe_t *it0;
115 
116 	if(_pl_pipes_ht==NULL)
117 		return -1;
118 
119 	for(i=0; i<_pl_pipes_ht->htsize; i++)
120 	{
121 		/* free entries */
122 		it = _pl_pipes_ht->slots[i].first;
123 		while(it)
124 		{
125 			it0 = it;
126 			it = it->next;
127 			pl_pipe_free(it0);
128 		}
129 		/* free locks */
130 		lock_destroy(&_pl_pipes_ht->slots[i].lock);
131 	}
132 	shm_free(_pl_pipes_ht->slots);
133 	shm_free(_pl_pipes_ht);
134 	_pl_pipes_ht = NULL;
135 	return 0;
136 }
137 
138 #define pl_compute_hash(_s)        get_hash1_raw((_s)->s,(_s)->len)
139 #define pl_get_entry(_h,_size)    (_h)&((_size)-1)
140 
pl_pipe_add(str * pipeid,str * algorithm,int limit)141 int pl_pipe_add(str *pipeid, str *algorithm, int limit)
142 {
143 	unsigned int cellid;
144 	unsigned int idx;
145 	pl_pipe_t *it, *prev, *cell;
146 
147 	if(_pl_pipes_ht==NULL)
148 		return -1;
149 
150 	cellid = pl_compute_hash(pipeid);
151 	idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
152 
153 	lock_get(&_pl_pipes_ht->slots[idx].lock);
154 	it = _pl_pipes_ht->slots[idx].first;
155 	prev = NULL;
156 	while(it!=NULL && it->cellid < cellid)
157 	{
158 		prev = it;
159 		it = it->next;
160 	}
161 	while(it!=NULL && it->cellid == cellid)
162 	{
163 		if(pipeid->len==it->name.len
164 				&& strncmp(pipeid->s, it->name.s, pipeid->len)==0)
165 		{
166 			 lock_release(&_pl_pipes_ht->slots[idx].lock);
167 			 return 1;
168 		}
169 		prev = it;
170 		it = it->next;
171 	}
172 
173 	cell =
174 		(pl_pipe_t*)shm_malloc(sizeof(pl_pipe_t)+(1+pipeid->len)*sizeof(char));
175 	if(cell == NULL)
176 	{
177 		lock_release(&_pl_pipes_ht->slots[idx].lock);
178 		LM_ERR("cannot create new cell.\n");
179 		return -1;
180 	}
181 	memset(cell, 0, sizeof(pl_pipe_t)+(1+pipeid->len)*sizeof(char));
182 
183 	cell->name.s = (char*)cell + sizeof(pl_pipe_t);
184 	strncpy(cell->name.s, pipeid->s, pipeid->len);
185 	cell->name.len = pipeid->len;
186 	cell->name.s[cell->name.len] = '\0';
187 	cell->cellid = cellid;
188 	cell->limit = limit;
189 	if (str_map_str(algo_names, algorithm, &cell->algo))
190 	{
191 		lock_release(&_pl_pipes_ht->slots[idx].lock);
192 		shm_free(cell);
193 		LM_ERR("cannot find algorithm [%.*s].\n", algorithm->len,
194 				algorithm->s);
195 		return -1;
196 	}
197 
198 	if(prev==NULL)
199 	{
200 		if(_pl_pipes_ht->slots[idx].first!=NULL)
201 		{
202 			cell->next = _pl_pipes_ht->slots[idx].first;
203 			_pl_pipes_ht->slots[idx].first->prev = cell;
204 		}
205 		_pl_pipes_ht->slots[idx].first = cell;
206 	} else {
207 		cell->next = prev->next;
208 		cell->prev = prev;
209 		if(prev->next)
210 			prev->next->prev = cell;
211 		prev->next = cell;
212 	}
213 	_pl_pipes_ht->slots[idx].ssize++;
214 	lock_release(&_pl_pipes_ht->slots[idx].lock);
215 
216 	return 0;
217 }
218 
pl_pipe_get(str * pipeid,int mode)219 pl_pipe_t* pl_pipe_get(str *pipeid, int mode)
220 {
221 	unsigned int cellid;
222 	unsigned int idx;
223 	pl_pipe_t *it;
224 
225 	if(_pl_pipes_ht==NULL)
226 		return NULL;
227 
228 	cellid = pl_compute_hash(pipeid);
229 	idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
230 
231 	lock_get(&_pl_pipes_ht->slots[idx].lock);
232 	it = _pl_pipes_ht->slots[idx].first;
233 	while(it!=NULL && it->cellid < cellid)
234 	{
235 		it = it->next;
236 	}
237 	while(it!=NULL && it->cellid == cellid)
238 	{
239 		if(pipeid->len==it->name.len
240 				&& strncmp(pipeid->s, it->name.s, pipeid->len)==0)
241 		{
242 			 if(mode==0) lock_release(&_pl_pipes_ht->slots[idx].lock);
243 			 return it;
244 		}
245 		it = it->next;
246 	}
247 	lock_release(&_pl_pipes_ht->slots[idx].lock);
248 	return NULL;
249 }
250 
pl_pipe_release(str * pipeid)251 void pl_pipe_release(str *pipeid)
252 {
253 	unsigned int cellid;
254 	unsigned int idx;
255 
256 	if(_pl_pipes_ht==NULL)
257 		return;
258 
259 	cellid = pl_compute_hash(pipeid);
260 	idx = pl_get_entry(cellid, _pl_pipes_ht->htsize);
261 
262 	lock_release(&_pl_pipes_ht->slots[idx].lock);
263 }
264 
265 
pl_print_pipes(void)266 int pl_print_pipes(void)
267 {
268 	int i;
269 	pl_pipe_t *it;
270 
271 	if(_pl_pipes_ht==NULL)
272 		return -1;
273 
274 	for(i=0; i<_pl_pipes_ht->htsize; i++)
275 	{
276 		lock_get(&_pl_pipes_ht->slots[i].lock);
277 		it = _pl_pipes_ht->slots[i].first;
278 		while(it)
279 		{
280 			LM_DBG("+++ pipe: %.*s [%u/%d]\n", it->name.len, it->name.s,
281 					it->cellid, i);
282 			LM_DBG("+++ ++++ algo: %d\n", it->algo);
283 			LM_DBG("+++ ++++ limit: %d\n", it->limit);
284 			LM_DBG("+++ ++++ counter: %d\n", it->counter);
285 			LM_DBG("+++ ++++ last_counter: %d\n", it->last_counter);
286 			LM_DBG("+++ ++++ load: %d\n", it->load);
287 			LM_DBG("+++ ++++ unused intervals: %d\n", it->unused_intervals);
288 			it = it->next;
289 		}
290 		lock_release(&_pl_pipes_ht->slots[i].lock);
291 	}
292 	return 0;
293 }
294 
pl_pipe_check_feedback_setpoints(int * cfgsp)295 int pl_pipe_check_feedback_setpoints(int *cfgsp)
296 {
297 	int i, sp;
298 	pl_pipe_t *it;
299 
300 	if(_pl_pipes_ht==NULL)
301 		return -1;
302 
303 	for(i=0; i<_pl_pipes_ht->htsize; i++)
304 	{
305 		lock_get(&_pl_pipes_ht->slots[i].lock);
306 		it = _pl_pipes_ht->slots[i].first;
307 		while(it)
308 		{
309 			if (it->algo == PIPE_ALGO_FEEDBACK) {
310 				sp = it->limit;
311 
312 				if (sp < 0 || sp > 100) {
313 					LM_ERR("FEEDBACK cpu load must be >=0 and <= 100 [%.*s]\n",
314 							 it->name.len, it->name.s);
315 					lock_release(&_pl_pipes_ht->slots[i].lock);
316 					return -1;
317 				} else if (*cfgsp == -1) {
318 					*cfgsp = sp;
319 				} else if (sp != *cfgsp) {
320 					LM_ERR("pipe %.*s: FEEDBACK cpu load values must "
321 						"be equal for all pipes\n",  it->name.len, it->name.s);
322 					lock_release(&_pl_pipes_ht->slots[i].lock);
323 					return -1;
324 				}
325 			}
326 			it = it->next;
327 		}
328 		lock_release(&_pl_pipes_ht->slots[i].lock);
329 	}
330 	return 0;
331 }
332 
pl_pipe_timer_update(int interval,int netload)333 void pl_pipe_timer_update(int interval, int netload)
334 {
335 	int i;
336 	pl_pipe_t *it, *it0;
337 
338 	if(_pl_pipes_ht==NULL)
339 		return;
340 
341 	for(i=0; i<_pl_pipes_ht->htsize; i++)
342 	{
343 		lock_get(&_pl_pipes_ht->slots[i].lock);
344 		it = _pl_pipes_ht->slots[i].first;
345 		while(it)
346 		{
347 			if (pl_clean_unused) {
348 				if (it->counter > 0) {
349 					// used, reset unused intervals counter
350 					it->unused_intervals = 0;
351 				} else {
352 					if (it->unused_intervals >= pl_clean_unused) {
353 						// unused for n intervals, delete
354 						it0 = it;
355 						it = it->next;
356 						if(it0->prev==NULL) {
357 							_pl_pipes_ht->slots[i].first = it;
358 						} else {
359 							it0->prev->next = it;
360 						}
361 						if(it) {
362 							it->prev = it0->prev;
363 						}
364 						_pl_pipes_ht->slots[i].ssize--;
365 						pl_pipe_free(it0);
366 						continue;
367 					} else {
368 						it->unused_intervals++;
369 					}
370 				}
371 			}
372 			if (it->algo != PIPE_ALGO_NOP) {
373 				if( it->algo == PIPE_ALGO_NETWORK ) {
374 					it->load = ( netload > it->limit ) ? 1 : -1;
375 				} else if (it->limit && interval) {
376 					it->load = it->counter / it->limit;
377 				}
378 				it->last_counter = it->counter;
379 				it->counter = 0;
380 			}
381 
382 			it = it->next;
383 		}
384 		lock_release(&_pl_pipes_ht->slots[i].lock);
385 	}
386 }
387 
388 extern int _pl_cfg_setpoint;
389 extern double *_pl_pid_setpoint;
390 
391 /**
392  * checks that all FEEDBACK pipes use the same setpoint
393  * cpu load. also sets (common) cfg_setpoint value
394  * \param	modparam 1 to check modparam (static) fields, 0 to use shm ones
395  *
396  * \return	0 if ok, -1 on error
397  */
check_feedback_setpoints(int modparam)398 static int check_feedback_setpoints(int modparam)
399 {
400 	_pl_cfg_setpoint = -1;
401 	return pl_pipe_check_feedback_setpoints(&_pl_cfg_setpoint);
402 }
403 
404 
rpl_pipe_lock(int slot)405 void rpl_pipe_lock(int slot)
406 {
407 	lock_get(&_pl_pipes_ht->slots[slot].lock);
408 }
409 
rpl_pipe_release(int slot)410 void rpl_pipe_release(int slot)
411 {
412 	lock_release(&_pl_pipes_ht->slots[slot].lock);
413 }
414 
415 
416 /* rpc function implementations */
rpc_pl_stats(rpc_t * rpc,void * c)417 void rpc_pl_stats(rpc_t *rpc, void *c)
418 {
419 	int i;
420 	pl_pipe_t *it;
421 
422 	for(i=0; i<_pl_pipes_ht->htsize; i++)
423 	{
424 		lock_get(&_pl_pipes_ht->slots[i].lock);
425 		it = _pl_pipes_ht->slots[i].first;
426 		while(it)
427 		{
428 			if (it->algo != PIPE_ALGO_NOP) {
429 				if (rpc->rpl_printf(c, "PIPE: id=%.*s load=%d counter=%d",
430 					it->name.len, it->name.s,
431 					it->load, it->last_counter) < 0)
432 				{
433 					lock_release(&_pl_pipes_ht->slots[i].lock);
434 					return;
435 				}
436 			}
437 			it = it->next;
438 		}
439 		lock_release(&_pl_pipes_ht->slots[i].lock);
440 	}
441 }
442 
rpc_pl_get_pipes(rpc_t * rpc,void * c)443 void rpc_pl_get_pipes(rpc_t *rpc, void *c)
444 {
445 	int i;
446 	str algo;
447 	pl_pipe_t *it;
448 
449 	for(i=0; i<_pl_pipes_ht->htsize; i++)
450 	{
451 		lock_get(&_pl_pipes_ht->slots[i].lock);
452 		it = _pl_pipes_ht->slots[i].first;
453 		while(it)
454 		{
455 			if (it->algo != PIPE_ALGO_NOP) {
456 				if (str_map_int(algo_names, it->algo, &algo))
457 				{
458 					lock_release(&_pl_pipes_ht->slots[i].lock);
459 					return;
460 				}
461 				if (rpc->rpl_printf(c, "PIPE: id=%.*s algorithm=%.*s limit=%d counter=%d",
462 					it->name.len, it->name.s, algo.len, algo.s,
463 					it->limit, it->counter) < 0)
464 				{
465 					lock_release(&_pl_pipes_ht->slots[i].lock);
466 					return;
467 				}
468 			}
469 			it = it->next;
470 		}
471 		lock_release(&_pl_pipes_ht->slots[i].lock);
472 	}
473 }
474 
rpc_pl_list_pipe(rpc_t * rpc,void * c,pl_pipe_t * it)475 int rpc_pl_list_pipe(rpc_t *rpc, void *c, pl_pipe_t *it)
476 {
477 	str algo;
478 	void* th;
479 
480 	if (it->algo == PIPE_ALGO_NOP) {
481 		return 0;
482 	}
483 
484 	if (str_map_int(algo_names, it->algo, &algo)) {
485 		return -1;
486 	}
487 	/* add structure node */
488 	if (rpc->add(c, "{", &th) < 0) {
489 		rpc->fault(c, 500, "Internal pipe structure");
490 		return -1;
491 	}
492 	if(rpc->struct_add(th, "ssdddd",
493 				"name",	it->name.s,
494 				"algorithm", algo.s,
495 				"limit", it->limit,
496 				"counter",  it->counter,
497 				"last_counter", it->last_counter,
498 				"unused_intervals", it->unused_intervals)<0) {
499 		rpc->fault(c, 500, "Internal error address list structure");
500 		return -1;
501 	}
502 	return 0;
503 }
504 
rpc_pl_list(rpc_t * rpc,void * c)505 void rpc_pl_list(rpc_t *rpc, void *c)
506 {
507 	int i;
508 	pl_pipe_t *it;
509 	str pipeid = STR_NULL;
510 
511 	if (rpc->scan(c, "*S", &pipeid) < 1) {
512 		pipeid.s = NULL;
513 		pipeid.len = 0;
514 	}
515 
516 	if(pipeid.len>0) {
517 		it = pl_pipe_get(&pipeid, 1);
518 		if (it==NULL) {
519 			LM_DBG("no pipe: %.*s\n", pipeid.len, pipeid.s);
520 			rpc->fault(c, 400, "Unknown pipe id %.*s", pipeid.len, pipeid.s);
521 			return;
522 		}
523 		if(rpc_pl_list_pipe(rpc, c, it)<0) {
524 			LM_DBG("failed to list pipe: %.*s\n", it->name.len, it->name.s);
525 		}
526 		pl_pipe_release(&pipeid);
527 		return;
528 	}
529 
530 	for(i=0; i<_pl_pipes_ht->htsize; i++)
531 	{
532 		lock_get(&_pl_pipes_ht->slots[i].lock);
533 		it = _pl_pipes_ht->slots[i].first;
534 		while(it) {
535 			if(rpc_pl_list_pipe(rpc, c, it)<0) {
536 				LM_DBG("failed to list pipe: %.*s\n", it->name.len, it->name.s);
537 				lock_release(&_pl_pipes_ht->slots[i].lock);
538 				return;
539 			}
540 			it = it->next;
541 		}
542 		lock_release(&_pl_pipes_ht->slots[i].lock);
543 	}
544 }
545 
rpc_pl_set_pipe(rpc_t * rpc,void * c)546 void rpc_pl_set_pipe(rpc_t *rpc, void *c)
547 {
548 	unsigned int algo_id, limit = 0;
549 	pl_pipe_t *it;
550 	str pipeid, algo_str;
551 
552 	if (rpc->scan(c, "SSd", &pipeid, &algo_str, &limit) < 3) return;
553 
554 	if (str_map_str(algo_names, &algo_str, (int*)&algo_id)) {
555 		LM_ERR("unknown algorithm: '%.*s'\n", algo_str.len, algo_str.s);
556 		rpc->fault(c, 400, "Unknown algorithm");
557 		return;
558 	}
559 
560 	LM_DBG("set_pipe: %.*s:%d:%d\n", pipeid.len, pipeid.s, algo_id, limit);
561 
562 	it = pl_pipe_get(&pipeid, 1);
563 	if (it==NULL) {
564 		LM_ERR("no pipe: %.*s\n", pipeid.len, pipeid.s);
565 		rpc->fault(c, 400, "Unknown pipe id %.*s", pipeid.len, pipeid.s);
566 		return;
567 	}
568 
569 	it->algo = algo_id;
570 	it->limit = limit;
571 	pl_pipe_release(&pipeid);
572 
573 	if (check_feedback_setpoints(0)) {
574 		LM_ERR("feedback limits don't match\n");
575 		rpc->fault(c, 400, "Feedback limits don't match");
576 		return;
577 	} else {
578 		*_pl_pid_setpoint = 0.01 * (double)_pl_cfg_setpoint;
579 	}
580 }
581 
582