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