1 /*-
2 * Copyright (c) 2003 Andrey Simonenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include "config.h"
28
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31 "@(#)$Id: ipa_thresholds.c,v 1.2 2011/01/23 18:42:35 simon Exp $";
32 #endif /* !lint */
33
34 #include <sys/types.h>
35
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "ipa_mod.h"
43
44 #include "queue.h"
45
46 #include "dlapi.h"
47 #include "confcommon.h"
48 #include "memfunc.h"
49 #include "parser.h"
50
51 #include "ipa_ac.h"
52 #include "ipa_db.h"
53 #include "ipa_ctl.h"
54 #include "ipa_cmd.h"
55 #include "ipa_time.h"
56
57 #include "ipa_conf.h"
58 #include "ipa_log.h"
59 #include "ipa_main.h"
60 #include "ipa_rules.h"
61 #include "ipa_autorules.h"
62
63 unsigned int nstatthresholds; /* Number of static thresholds. */
64 unsigned int ndynthresholds; /* Number of dynamic thresholds. */
65
66 #ifdef WITH_THRESHOLDS
67
68 /* Parameters in global{} section. */
69 signed char global_debug_threshold;
70 signed char global_debug_threshold_init;
71 signed char global_threshold_type;
72 signed char global_load_threshold;
73 unsigned int global_threshold_time_width;
74 const struct tevent *global_threshold_time_slice;
75
76 /* Parts of global { threshold_balance } parameter. */
77 unsigned int global_threshold_below_lim; /* X:-:- */
78 unsigned int global_threshold_equal_lim; /* -:X:- */
79 unsigned int global_threshold_above_lim; /* -:-:X */
80
81 /* Mzone for all struct threshold{}. */
82 ipa_mzone *threshold_mzone;
83
84 const char *const threshold_event_msg[] = {
85 "BELOW", /* 0 */
86 "EQUAL", /* 1 */
87 "ABOVE" /* 2 */
88 };
89
90 #define EVENT(x) IPA_THRESHOLD_EVENT_ ## x
91
92 void
set_thr_min_max(struct threshold * threshold)93 set_thr_min_max(struct threshold *threshold)
94 {
95 uint64_t thr, dev;
96
97 thr = threshold->thr;
98 dev = threshold->thr_dev_pc != 0 ?
99 uint64_per_cent(&thr, threshold->thr_dev_pc) : threshold->thr_dev;
100 threshold->thr_max = (thr > UINT64_MAX - dev) ?
101 UINT64_MAX : thr + dev;
102 threshold->thr_min = thr < dev ? 0 : thr - dev;
103 }
104
105 /*
106 * Remove statistics from the latest slice, if positive counter or
107 * negative counter overflows, then return -1, since this means
108 * incorrect configuration.
109 */
110 static int
threshold_flush_slice(const struct rule * rule,struct threshold * threshold)111 threshold_flush_slice(const struct rule *rule, struct threshold *threshold)
112 {
113 uint64_t chunk;
114 BITMAP_TYPE *bitword, bitmask;
115 unsigned int i;
116
117 i = threshold->cnt_slice_i;
118 chunk = threshold->cnt_slice[i];
119 threshold->cnt_slice[i] = 0;
120
121 bitword = BIT_WORD(threshold->cnt_slice_sign, i);
122 if (*bitword != 0) {
123 /* Have to check bit. */
124 bitmask = BIT_MASK(i);
125 if (BIT_TEST(bitword, bitmask)) {
126 BIT_CLEAR(bitword, bitmask);
127 goto add_chunk;
128 }
129 }
130
131 /* Subtract positive slice counter. */
132 if (rule->debug_threshold)
133 logdbg("rule %s, threshold %s: threshold_flush_slice: "
134 "remove chunk %"PRIu64" from slice #%u", rule->name,
135 threshold->name, chunk, i);
136 if (threshold->cnt >= chunk)
137 threshold->cnt -= chunk;
138 else {
139 chunk -= threshold->cnt;
140 if (threshold->cnt_neg > UINT64_MAX - chunk) {
141 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
142 "threshold_flush_slice: negative counter "
143 "overflowed", rule->name, threshold->name);
144 logmsgx(IPA_LOG_ERR, "this means that something "
145 "is wrong in configuration");
146 return (-1);
147 }
148 threshold->cnt_neg += chunk;
149 threshold->cnt = 0;
150 }
151
152 return (0);
153
154 add_chunk:
155 /* Add absolute value of negative slice counter. */
156 if (rule->debug_threshold)
157 logdbg("rule %s, threshold %s: threshold_flush_slice: "
158 "remove chunk -%"PRIu64" from slice #%u", rule->name,
159 threshold->name, chunk, i);
160 if (threshold->cnt_neg >= chunk)
161 threshold->cnt_neg -= chunk;
162 else {
163 chunk -= threshold->cnt_neg;
164 if (threshold->cnt > UINT64_MAX - chunk) {
165 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
166 "threshold_flush_slice: positive counter "
167 "overflowed", rule->name, threshold->name);
168 logmsgx(IPA_LOG_ERR, "this means that something is "
169 "wrong in configuration");
170 return (-1);
171 }
172 threshold->cnt += chunk;
173 threshold->cnt_neg = 0;
174 }
175
176 return (0);
177 }
178
179 /*
180 * Set threshold active or inactive in modules it uses.
181 */
182 int
mod_set_threshold_active(const struct rule * rule,struct threshold * threshold,int active)183 mod_set_threshold_active(const struct rule *rule, struct threshold *threshold,
184 int active)
185 {
186 if ((threshold->thr_flags & THRESHOLD_FLAG_ACTIVE) ==
187 (active ? THRESHOLD_FLAG_ACTIVE : 0)) {
188 logmsgx(IPA_LOG_ERR, "internal error: "
189 "mod_set_threshold_active(%s, %s, %d): threshold is "
190 "already %s", rule->name, threshold->name, active,
191 active_msg[active]);
192 return (-1);
193 }
194
195 if (debug_worktime)
196 logdbg("rule %s, threshold %s: set threshold %s",
197 rule->name, threshold->name, active_msg[active]);
198
199 if (active)
200 THRESHOLD_SET_ACTIVE(threshold);
201 else
202 THRESHOLD_SET_INACTIVE(threshold);
203
204 if (ac_set_threshold_active(rule, threshold, active) < 0)
205 goto failed;
206 if (db_set_threshold_active(rule, threshold, active) < 0)
207 goto failed;
208
209 return (0);
210
211 failed:
212 logbt("mod_set_threshold_active");
213 return (-1);
214 }
215
216 static int
set_threshold_active(const struct rule * rule,struct threshold * threshold)217 set_threshold_active(const struct rule *rule, struct threshold *threshold)
218 {
219 unsigned int i, n;
220
221 if (threshold->thr_type & THRESHOLD_JUMP_OVER_INACTIVE) {
222 i = ipa_tm_diff(&threshold->tm_updated, &threshold->tm_started);
223 threshold->tm_started = curdate;
224 ipa_tm_sub(&threshold->tm_started, i);
225 if (rule->debug_threshold)
226 logdbg("rule %s, threshold %s: set_threshold_active: "
227 "jumping over inactive time interval", rule->name,
228 threshold->name);
229 } else {
230 n = ipa_tm_diff(&curdate, &threshold->tm_updated) /
231 threshold->time_slice->event_step;
232 if (n >= threshold->cnt_slice_n)
233 n = threshold->cnt_slice_n;
234
235 if (rule->debug_threshold)
236 logdbg("rule %s, threshold %s: set_threshold_active: "
237 "flushing %u slice(s), starting from slice #%u",
238 rule->name, threshold->name, n,
239 threshold->cnt_slice_i);
240
241 for (i = 0; i < n; ++i) {
242 threshold->cnt_slice_i++;
243 if (threshold->cnt_slice_i == threshold->cnt_slice_n)
244 threshold->cnt_slice_i = 0;
245 if (threshold_flush_slice(rule, threshold) < 0) {
246 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
247 "set_threshold_active: "
248 "threshold_flush_slice failed",
249 rule->name, threshold->name);
250 return (-1);
251 }
252 }
253 }
254
255 /* Set new time when to check this threshold again. */
256 threshold->check_sec = threshold->time_slice->event_sec;
257
258 return (mod_set_threshold_active(rule, threshold, 1));
259 }
260
261 /*
262 * Add chunk to one threshold, if positive counter or slice counter
263 * overflows, then return -1, since this means incorrect configuration.
264 */
265 int
threshold_add_chunk(const struct rule * rule,struct threshold * threshold,const uint64_t * chunk_ptr)266 threshold_add_chunk(const struct rule *rule, struct threshold *threshold,
267 const uint64_t *chunk_ptr)
268 {
269 uint64_t chunk;
270 BITMAP_TYPE *bitword, bitmask;
271 unsigned int i;
272
273 chunk = *chunk_ptr;
274
275 /* Add chunk to counter. */
276 if (threshold->cnt_neg >= chunk)
277 threshold->cnt_neg -= chunk;
278 else {
279 chunk -= threshold->cnt_neg;
280 if (threshold->cnt > UINT64_MAX - chunk) {
281 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
282 "threshold_add_chunk: positive counter overflowed",
283 rule->name, threshold->name);
284 logmsgx(IPA_LOG_ERR, "this means that something is "
285 "wrong in configuration");
286 return (-1);
287 }
288 threshold->cnt += chunk;
289 threshold->cnt_neg = 0;
290 /* Restore chunk. */
291 chunk = *chunk_ptr;
292 }
293
294 /* Add chunk to slice counter. */
295 i = threshold->cnt_slice_i;
296 if (rule->debug_threshold)
297 logdbg("rule %s, threshold %s: threshold_add_chunk: add "
298 "chunk %"PRIu64" to slice #%u", rule->name,
299 threshold->name, chunk, i);
300
301 /*
302 * Usually statistics is positive, so let's make some
303 * optimization for usual situation.
304 */
305 bitword = BIT_WORD(threshold->cnt_slice_sign, i);
306 if (*bitword != 0) {
307 /* Have to check bit. */
308 bitmask = BIT_MASK(i);
309 if (BIT_TEST(bitword, bitmask)) {
310 /* Slice has negative statistics. */
311 if (threshold->cnt_slice[i] > chunk)
312 threshold->cnt_slice[i] -= chunk;
313 else {
314 threshold->cnt_slice[i] = chunk -
315 threshold->cnt_slice[i];
316 BIT_CLEAR(bitword, bitmask);
317 }
318 return (0);
319 }
320 }
321
322 /* Slice has positive statistics. */
323 if (threshold->cnt_slice[i] > UINT64_MAX - chunk) {
324 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
325 "threshold_add_chunk: positive slice counter #%u "
326 "overflowed", rule->name, threshold->name, i);
327 logmsgx(IPA_LOG_ERR, "this means that something is "
328 "wrong in configuration");
329 return (-1);
330 }
331 threshold->cnt_slice[i] += chunk;
332
333 return (0);
334 }
335
336 /*
337 * Add chunk to every rule's threshold.
338 */
339 int
thresholds_add_chunk(const struct rule * rule,const uint64_t * chunk_ptr)340 thresholds_add_chunk(const struct rule *rule, const uint64_t *chunk_ptr)
341 {
342 struct threshold *threshold;
343
344 STAILQ_FOREACH(threshold, &rule->thresholds, link) {
345 if (THRESHOLD_IS_INACTIVE(threshold))
346 continue;
347 if (threshold_add_chunk(rule, threshold, chunk_ptr) < 0) {
348 logbt("thresholds_add_chunk");
349 return (-1);
350 }
351 }
352 return (0);
353 }
354
355 /*
356 * Subtract chunk from one threshold.
357 */
358 int
threshold_sub_chunk(const struct rule * rule,struct threshold * threshold,const uint64_t * chunk_ptr)359 threshold_sub_chunk(const struct rule *rule, struct threshold *threshold,
360 const uint64_t *chunk_ptr)
361 {
362 uint64_t chunk;
363 BITMAP_TYPE *bitword, bitmask;
364 unsigned int i;
365
366 chunk = *chunk_ptr;
367
368 /* Subtract chunk from counter. */
369 if (threshold->cnt >= chunk)
370 threshold->cnt -= chunk;
371 else {
372 chunk -= threshold->cnt;
373 if (threshold->cnt_neg > UINT64_MAX - chunk) {
374 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
375 "threshold_sub_chunk: negative counter overflowed",
376 rule->name, threshold->name);
377 logmsgx(IPA_LOG_ERR, "this means that something is "
378 "wrong in configuration");
379 return (-1);
380 }
381 threshold->cnt_neg += chunk;
382 threshold->cnt = 0;
383 /* Restore chunk. */
384 chunk = *chunk_ptr;
385 }
386
387 /* Subtract chunk from slice counter. */
388 i = threshold->cnt_slice_i;
389 if (rule->debug_threshold)
390 logdbg("rule %s, threshold %s: threshold_sub_chunk: subtract "
391 "chunk %"PRIu64" from slice #%u", rule->name,
392 threshold->name, chunk, i);
393
394 bitword = BIT_WORD(threshold->cnt_slice_sign, i);
395 bitmask = BIT_MASK(i);
396 if (BIT_TEST(bitword, bitmask)) {
397 /* Slice has negative statistics. */
398 if (threshold->cnt_slice[i] > UINT64_MAX - chunk) {
399 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
400 "threshold_sub_chunk: negative slice counter "
401 "%u overflowed", rule->name, threshold->name, i);
402 logmsgx(IPA_LOG_ERR, "this means that something is "
403 "wrong in configuration");
404 return (-1);
405 }
406 threshold->cnt_slice[i] += chunk;
407 } else {
408 /* Slice has positive statistics. */
409 if (threshold->cnt_slice[i] >= chunk)
410 threshold->cnt_slice[i] -= chunk;
411 else {
412 threshold->cnt_slice[i] = chunk -
413 threshold->cnt_slice[i];
414 BIT_SET(bitword, bitmask);
415 }
416 }
417
418 return (0);
419 }
420
421 /*
422 * Subtract chunk from every rule's threshold.
423 */
424 int
thresholds_sub_chunk(const struct rule * rule,const uint64_t * chunk_ptr)425 thresholds_sub_chunk(const struct rule *rule, const uint64_t *chunk_ptr)
426 {
427 struct threshold *threshold;
428
429 STAILQ_FOREACH(threshold, &rule->thresholds, link) {
430 if (THRESHOLD_IS_INACTIVE(threshold))
431 continue;
432 if (threshold_sub_chunk(rule, threshold, chunk_ptr) < 0) {
433 logbt("thresholds_sub_chunk");
434 return (-1);
435 }
436 }
437 return (0);
438 }
439
440 static int
above_threshold(const struct rule * rule,struct threshold * threshold)441 above_threshold(const struct rule *rule, struct threshold *threshold)
442 {
443 if (rule->debug_threshold)
444 logdbg("rule %s, threshold %s: above threshold cnt %"PRIu64", "
445 "rest %u invocations", rule->name, threshold->name,
446 threshold->cnt, threshold->above_cnt);
447
448 if (threshold->above_cnt == 0)
449 return (0);
450
451 /* Adjust number of invocations. */
452 threshold->above_cnt--;
453 threshold->equal_cnt = threshold->equal_lim;
454 threshold->below_cnt = threshold->below_lim;
455
456 /* Register event in modules. */
457 if (ac_threshold_event(rule, threshold, EVENT(ABOVE)) < 0)
458 goto failed;
459
460 /* Run commands. */
461 if (threshold->above_thr.has_cmd)
462 if (run_cmds(rule, &threshold->wpid, &threshold->above_thr,
463 "rule %s { threshold %s { above_threshold {}}}",
464 rule->name, threshold->name) < 0)
465 goto failed;
466
467 return (0);
468
469 failed:
470 logbt("above_threshold");
471 return (-1);
472 }
473
474 static int
below_threshold(const struct rule * rule,struct threshold * threshold)475 below_threshold(const struct rule *rule, struct threshold *threshold)
476 {
477 if (rule->debug_threshold) {
478 uint64_t cnt;
479 const char *sign;
480
481 if (threshold->cnt_neg == 0) {
482 cnt = threshold->cnt;
483 sign = "";
484 } else {
485 cnt = threshold->cnt_neg;
486 sign = "-";
487 }
488 logdbg("rule %s, threshold %s: below threshold, "
489 "cnt %s%"PRIu64", rest %u invocations", rule->name,
490 threshold->name, sign, cnt, threshold->below_cnt);
491 }
492 if (threshold->below_cnt == 0)
493 return (0);
494
495 /* Adjust number of invocations. */
496 threshold->below_cnt--;
497 threshold->equal_cnt = threshold->equal_lim;
498 threshold->above_cnt = threshold->above_lim;
499
500 /* Register event in modules. */
501 if (ac_threshold_event(rule, threshold, EVENT(BELOW)) < 0)
502 goto failed;
503
504 /* Run commands. */
505 if (threshold->below_thr.has_cmd)
506 if (run_cmds(rule, &threshold->wpid, &threshold->below_thr,
507 "rule %s { threshold %s { below_threshold {}}}",
508 rule->name, threshold->name) < 0)
509 goto failed;
510
511 return (0);
512
513 failed:
514 logbt("below_threshold");
515 return (-1);
516 }
517
518 static int
equal_threshold(const struct rule * rule,struct threshold * threshold)519 equal_threshold(const struct rule *rule, struct threshold *threshold)
520 {
521 if (rule->debug_threshold)
522 logdbg("rule %s, threshold %s: equal threshold, "
523 "cnt %"PRIu64", rest %u invocations", rule->name,
524 threshold->name, threshold->cnt, threshold->equal_cnt);
525
526 if (threshold->equal_cnt == 0)
527 return (0);
528
529 /* Adjust number of invocations. */
530 threshold->equal_cnt--;
531 threshold->below_cnt = threshold->below_lim;
532 threshold->above_cnt = threshold->above_lim;
533
534 /* Register event in modules. */
535 if (ac_threshold_event(rule, threshold, EVENT(EQUAL)) < 0)
536 goto failed;
537
538 /* Run commands. */
539 if (threshold->equal_thr.has_cmd)
540 if (run_cmds(rule, &threshold->wpid, &threshold->equal_thr,
541 "rule %s { threshold %s { equal_threshold {}}}",
542 rule->name, threshold->name) < 0)
543 goto failed;
544
545 return (0);
546
547 failed:
548 logbt("equal_threshold");
549 return (-1);
550 }
551
552 static int
check_threshold_cnt(const struct rule * rule,struct threshold * threshold)553 check_threshold_cnt(const struct rule *rule, struct threshold *threshold)
554 {
555 if (threshold->cnt > threshold->thr_max) {
556 /* [ ] x */
557 if (above_threshold(rule, threshold) < 0)
558 goto failed;
559 } else if (threshold->cnt < threshold->thr_min) {
560 /* x [ ] */
561 if (below_threshold(rule, threshold) < 0)
562 goto failed;
563 } else {
564 /* [ x ] */
565 if (equal_threshold(rule, threshold) < 0)
566 goto failed;
567 }
568 return (0);
569
570 failed:
571 logbt("check_threshold_cnt");
572 return (-1);
573 }
574
575 /*
576 * This routine implements "below_threshold", "equal_threshold",
577 * "above_threshold" and part of "worktime" in "threshold" section.
578 */
579 int
check_thresholds(const struct rule * rule,unsigned int * check_sec_ptr)580 check_thresholds(const struct rule *rule, unsigned int *check_sec_ptr)
581 {
582 ipa_tm tm_started;
583 const struct worktime *wt;
584 struct threshold *threshold;
585 unsigned int i, check_sec;
586
587 check_sec = EVENT_NOT_SCHEDULED;
588
589 STAILQ_FOREACH(threshold, &rule->thresholds, link) {
590 wt = threshold->worktime;
591 if (THRESHOLD_IS_INACTIVE(threshold)) {
592 /* Threshold is inactive. */
593 if (wt->active_sec <= cursec) {
594 /* It's time to make threshold active. */
595 if (set_threshold_active(rule, threshold) < 0)
596 goto failed;
597 } else {
598 if (check_sec > wt->active_sec)
599 check_sec = wt->active_sec;
600 continue; /* do not check any time events. */
601 }
602 }
603
604 /* Here threshold is active. */
605 if (threshold->check_sec <= cursec) {
606 /* It's time to check threshold. */
607 if (check_threshold_cnt(rule, threshold) < 0)
608 goto failed;
609
610 /* Calculate next slice number. */
611 i = threshold->cnt_slice_i + 1;
612 if (i == threshold->cnt_slice_n)
613 i = 0;
614 threshold->cnt_slice_i = i;
615
616 /* Remove statistics from the latest slice. */
617 if (threshold_flush_slice(rule, threshold) < 0)
618 goto failed;
619
620 /* Set new time when to check this threshold again. */
621 threshold->check_sec = threshold->time_slice->event_sec;
622
623 /* Update threshold in the database. */
624 tm_started = curdate;
625 if (newday_flag)
626 fix_240000(&tm_started);
627 ipa_tm_sub(&tm_started, threshold->time_width);
628 if (threshold->shift_window)
629 threshold->tm_started = tm_started;
630 else if (cmp_ipa_tm(&tm_started,
631 &threshold->tm_started) > 0) {
632 threshold->shift_window = 1;
633 threshold->tm_started = tm_started;
634 }
635
636 threshold->tm_updated = curdate;
637 if (db_update_threshold(rule, threshold) < 0)
638 goto failed;
639 } else if (need_check_flag) {
640 if (db_update_threshold(rule, threshold) < 0)
641 goto failed;
642 }
643
644 if (WT_IS_INACTIVE(wt)) {
645 /* Threshold became inactive. */
646 if (!newday_flag) {
647 if (set_threshold_inactive(rule, threshold) < 0)
648 goto failed;
649 if (check_sec > wt->active_sec)
650 check_sec = wt->active_sec;
651 }
652 } else {
653 /* Threshold is still active. */
654 if (check_sec > wt->inactive_sec)
655 check_sec = wt->inactive_sec;
656 if (check_sec > threshold->check_sec)
657 check_sec = threshold->check_sec;
658 }
659 }
660
661 *check_sec_ptr = check_sec;
662 return (0);
663
664 failed:
665 logbt("check_thresholds");
666 return (-1);
667 }
668
669 /*
670 * Initialize one threshold.
671 */
672 static int
init_threshold(const struct rule * rule,struct threshold * threshold)673 init_threshold(const struct rule *rule, struct threshold *threshold)
674 {
675 struct ipa_threshold_state ostat, nstat;
676 uint64_t chunk, cnt;
677 size_t size;
678 const char *rule_name, *threshold_name;
679 unsigned int i, n, old_slice_n, old_time_width;
680 signed char debug_threshold_init;
681
682 /*
683 * If init_threshold() is called second, third... time,
684 * then threshold can be inactive.
685 */
686 if (THRESHOLD_IS_INACTIVE(threshold))
687 if (set_threshold_active(rule, threshold) < 0)
688 goto failed;
689
690 rule_name = rule->name;
691 threshold_name = threshold->name;
692
693 threshold->cnt_neg = 0;
694 threshold->shift_window = 0;
695 threshold->wpid.debug_exec = rule->debug_exec;
696
697 /* Get number of statistics slices. */
698 n = threshold->cnt_slice_n =
699 threshold->time_width / threshold->time_slice->event_step;
700
701 /* Initialize statistics slices. */
702 size = n * sizeof(*threshold->cnt_slice);
703 if (threshold->cnt_slice == NULL) {
704 threshold->cnt_slice = mem_malloc(size, m_anon);
705 if (threshold->cnt_slice == NULL) {
706 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
707 "init_threshold: mem_malloc for slices failed",
708 rule_name, threshold_name);
709 return (-1);
710 }
711 }
712 memset(threshold->cnt_slice, 0, size);
713
714 /* Initialize statistics slices signs. */
715 size = BITMAP_SIZE(n);
716 if (threshold->cnt_slice_sign == NULL) {
717 threshold->cnt_slice_sign = mem_malloc(size, m_anon);
718 if (threshold->cnt_slice_sign == NULL) {
719 logmsgx(IPA_LOG_ERR, "rule %s, threshold %s: "
720 "init_threshold: mem_malloc for slices signs "
721 "failed", rule_name, threshold_name);
722 return (-1);
723 }
724 }
725 memset(threshold->cnt_slice_sign, 0, size);
726
727 debug_threshold_init = rule->debug_threshold_init;
728
729 if (debug_threshold_init)
730 logdbg("rule %s, threshold %s: init_threshold: allocated %u "
731 "slices", rule_name, threshold_name, n);
732
733 /*
734 * If we cannot get current threshold state from the database,
735 * then let's use current "threshold" value.
736 */
737 nstat.thr = threshold->thr;
738
739 if (STAILQ_EMPTY(threshold->db_list)) {
740 /* "null" database is used. */
741 if (THRESHOLD_IS_INITED(threshold)) {
742 /* Imitate that old state is known. */
743 ostat.thr = threshold->thr;
744 ostat.cnt = threshold->cnt;
745 ostat.tm_from = threshold->tm_started;
746 ostat.tm_updated = threshold->tm_updated;
747 if (debug_threshold_init)
748 logdbg("rule %s, threshold %s: init_threshold: "
749 "continue to use previous threshold state",
750 rule_name, threshold_name);
751 } else {
752 if (debug_threshold_init)
753 logdbg("rule %s, threshold %s: init_threshold: "
754 "create threshold with no database",
755 rule_name, threshold_name);
756 goto new_state;
757 }
758 } else {
759 const char *db_name;
760 int rv;
761
762 /* Get threshold state from databases. */
763 rv = db_get_threshold_state(rule, threshold, &ostat, &db_name);
764 if (rv < 0)
765 goto failed;
766 if (db_name == NULL) {
767 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
768 "init_threshold: no database used for this "
769 "threshold supports db_get_threshold_state "
770 "function", rule_name, threshold_name);
771 goto new_state;
772 }
773 if (rv > 0) {
774 if (debug_threshold_init)
775 logdbg("rule %s, threshold %s: init_threshold: "
776 "got state from database %s",
777 rule_name, threshold_name, db_name);
778 } else {
779 if (debug_threshold_init)
780 logdbg("rule %s, threshold %s: init_threshold: "
781 "database %s does not have threshold state "
782 "(a new threshold)", rule_name,
783 threshold_name, db_name);
784 goto new_state;
785 }
786 }
787
788 /* Check and initialize "threshold" parameter. */
789 if (debug_threshold_init && threshold->thr != ostat.thr) {
790 logdbg("rule %s, threshold %s: init_threshold: configuration "
791 "threshold %"PRIu64", database threshold %"PRIu64" "
792 "(will use %s value)", rule_name, threshold_name,
793 threshold->thr, ostat.thr,
794 threshold->load_thr ? "database" : "configuration");
795 }
796 if (threshold->load_thr)
797 nstat.thr = threshold->thr = ostat.thr;
798
799 /* Check tm_from and tm_updated (it can be 24:00:00). */
800 if (check_ipa_tm(&ostat.tm_from) < 0) {
801 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
802 "init_threshold: wrong value of tm_from: %s",
803 rule_name, threshold_name, tm_str(&ostat.tm_from));
804 goto new_state_error;
805 }
806 fix_240000(&ostat.tm_updated);
807 if (check_ipa_tm(&ostat.tm_updated) < 0) {
808 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
809 "init_threshold: wrong value of tm_updated: %s",
810 rule_name, threshold_name, tm_str(&ostat.tm_updated));
811 goto new_state_error;
812 }
813
814 /* tm_from should be <= tm_updated. */
815 if (cmp_ipa_tm(&ostat.tm_updated, &ostat.tm_from) < 0) {
816 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
817 "init_threshold: tm_updated is less than tm_from: %s < %s",
818 rule_name, threshold_name, tm_str(&ostat.tm_updated),
819 tm_str2(&ostat.tm_from));
820 goto new_state_error;
821 }
822
823 /* Check whether time goes back. */
824 if (cmp_ipa_tm(&ostat.tm_updated, &curdate) > 0) {
825 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
826 "init_threshold: tm_updated %s is greater than "
827 "current time", rule_name, threshold_name,
828 tm_str(&ostat.tm_updated));
829 /*
830 * If already initialized threshold has update time greater
831 * than current time, then change tm_from and tm_updated.
832 */
833 if (THRESHOLD_IS_INITED(threshold)) {
834 const char *cp;
835 unsigned int d;
836
837 d = ipa_tm_diff(&ostat.tm_updated, &ostat.tm_from);
838 cp = tm_str(&ostat.tm_from);
839 ostat.tm_from = ostat.tm_updated = curdate;
840 ipa_tm_sub(&ostat.tm_from, d);
841 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: "
842 "init_threshold: tm_from time changed: %s -> %s",
843 rule_name, threshold_name, cp,
844 tm_str2(&ostat.tm_from));
845 } else
846 goto new_state_error;
847 }
848
849 old_time_width = ipa_tm_diff(&ostat.tm_updated, &ostat.tm_from);
850
851 if (old_time_width != threshold->time_width) {
852 char buf[SEC_STR_SIZE];
853
854 strncpy(buf, time_str(old_time_width), sizeof(buf) - 1);
855 buf[sizeof(buf) - 1] = '\0';
856 logmsgx(IPA_LOG_INFO, "rule %s, threshold %s: init_threshold: "
857 "it seems that threshold_time_width is changed: %s -> %s",
858 rule_name, threshold_name, buf,
859 time_str(threshold->time_width));
860 }
861
862 /* Old number of slices between tm_from and tm_updated. */
863 old_slice_n = old_time_width / threshold->time_slice->event_step;
864
865 /*
866 * Let it be one slice between tm_from and tm_updated,
867 * it is better, than simply forget about that statistics.
868 */
869 if (old_slice_n == 0)
870 old_slice_n = 1;
871
872 /* One chunk between tm_from and tm_updated. */
873 chunk = ostat.cnt / old_slice_n;
874
875 if (threshold->thr_type & THRESHOLD_JUMP_OVER_STOPPED) {
876 /* Jump over time interval when ipa was stopped. */
877 if (threshold->cnt_slice_n <= old_slice_n) {
878 threshold->cnt_slice_i = 0;
879 n = threshold->cnt_slice_n;
880 } else {
881 threshold->cnt_slice_i = old_slice_n;
882 n = old_slice_n;
883 }
884
885 /* Calculate tm_from. */
886 nstat.tm_from = curdate;
887 if (old_time_width < threshold->time_width)
888 ipa_tm_sub(&nstat.tm_from, old_time_width);
889 else
890 ipa_tm_sub(&nstat.tm_from, threshold->time_width);
891 } else {
892 /* Slide threshold's statistics over time interval. */
893 if (ipa_tm_diff(&curdate, &ostat.tm_updated) <
894 threshold->time_width) {
895 /* tm_from tm_updated curdate */
896
897 /*
898 * Get number of slices between curdate and tm_updated.
899 * n is always less than cnt_slice_n, since curdate -
900 * ostat.tm_updated is less than time_width.
901 */
902 n = ipa_tm_diff(&curdate, &ostat.tm_updated) /
903 threshold->time_slice->event_step;
904
905 /*
906 * Remember number of slices between curdate and
907 * tm_updated.
908 */
909 threshold->cnt_slice_i = n;
910
911 /*
912 * Max possible number of slices between tm_from and
913 * tm_updated.
914 */
915 n = threshold->cnt_slice_n - n;
916
917 /* n = min(n, old_slice_n). */
918 if (n > old_slice_n)
919 n = old_slice_n;
920
921 /* Get number of next slice to use. */
922 threshold->cnt_slice_i += n;
923 if (threshold->cnt_slice_i == threshold->cnt_slice_n)
924 threshold->cnt_slice_i = 0;
925
926 /* Calculate tm_from. */
927 nstat.tm_from = curdate;
928 ipa_tm_sub(&nstat.tm_from, threshold->time_width);
929 if (cmp_ipa_tm(&nstat.tm_from, &ostat.tm_from) < 0)
930 nstat.tm_from = ostat.tm_from;
931 } else {
932 /*
933 * Do not use previous threshold state, because
934 * it is stale.
935 */
936 nstat.tm_from = curdate;
937 threshold->cnt_slice_i = n = 0;
938 cnt = 0;
939 goto skip_chunk_fill;
940 }
941 }
942
943 /* Fill slices. */
944 if (chunk != 0) {
945 /* Fill equal chunks. */
946 for (i = 0; i < n; ++i)
947 threshold->cnt_slice[i] = chunk;
948 cnt = n * chunk;
949 chunk = ostat.cnt - cnt;
950 } else {
951 /* Not enough statistics for equal chunks. */
952 cnt = 0;
953 chunk = ostat.cnt;
954 }
955
956 if (chunk != 0) {
957 /*
958 * Fill the rest of statistics, this is very approximately
959 * as all threshold's initialization.
960 */
961 for (i = 0; i < n; ++i) {
962 threshold->cnt_slice[i] += 1;
963 cnt++;
964 if (--chunk == 0)
965 break;
966 }
967 }
968
969 skip_chunk_fill:
970 set_thr_min_max(threshold);
971
972 if (debug_threshold_init) {
973 logdbg("rule %s, threshold %s: init_threshold: %u slice(s), "
974 "current slice #%u, cnt %"PRIu64, rule_name,
975 threshold_name, n, threshold->cnt_slice_i, cnt);
976 logdbg("rule %s, threshold %s: init_threshold: "
977 "thr %"PRIu64", thr_min %"PRIu64", thr_max %"PRIu64,
978 rule_name, threshold_name, threshold->thr,
979 threshold->thr_min, threshold->thr_max);
980 logdbg("rule %s, threshold %s: init_threshold: set "
981 "threshold state", rule_name, threshold_name);
982 }
983
984 threshold->cnt = nstat.cnt = cnt;
985 threshold->tm_started = nstat.tm_from;
986 threshold->tm_updated = nstat.tm_updated = curdate;
987
988 /* Remove statistics from the latest slice. */
989 threshold_flush_slice(rule, threshold);
990
991 if (db_set_threshold_state(rule, threshold, &nstat) < 0)
992 goto failed;
993
994 THRESHOLD_SET_INITED(threshold);
995 return (0);
996
997 new_state_error:
998 logmsgx(IPA_LOG_WARNING, "rule %s, threshold %s: init_threshold: "
999 "set new threshold state, correcting errors found in database",
1000 rule_name, threshold_name);
1001
1002 new_state:
1003 set_thr_min_max(threshold);
1004
1005 threshold->tm_started = threshold->tm_updated =
1006 nstat.tm_from = nstat.tm_updated = curdate;
1007 threshold->cnt = nstat.cnt = 0;
1008
1009 threshold->cnt_slice_i = 0;
1010
1011 if (debug_threshold_init) {
1012 logdbg("rule %s, threshold %s: init_threshold: "
1013 "thr %"PRIu64", thr_min %"PRIu64", thr_max %"PRIu64,
1014 rule_name, threshold_name, threshold->thr,
1015 threshold->thr_min, threshold->thr_max);
1016 logdbg("rule %s, threshold %s: init_threshold: set new "
1017 "threshold state", rule_name, threshold_name);
1018 }
1019
1020 if (db_set_threshold_state(rule, threshold, &nstat) < 0)
1021 goto failed;
1022
1023 THRESHOLD_SET_INITED(threshold);
1024 return (0);
1025
1026 failed:
1027 logbt("init_threshold");
1028 return (-1);
1029 }
1030
1031 /*
1032 * Initialize all thresholds in one rule.
1033 */
1034 int
init_thresholds(const struct rule * rule)1035 init_thresholds(const struct rule *rule)
1036 {
1037 struct threshold *threshold;
1038
1039 STAILQ_FOREACH(threshold, &rule->thresholds, link)
1040 if (init_threshold(rule, threshold) < 0) {
1041 logbt("init_thresholds");
1042 return (-1);
1043 }
1044 return (0);
1045 }
1046
1047 /*
1048 * Return pointer to threshold with the give name.
1049 */
1050 struct threshold *
threshold_by_name(const struct rule * rule,const char * name)1051 threshold_by_name(const struct rule *rule, const char *name)
1052 {
1053 struct threshold *threshold;
1054
1055 STAILQ_FOREACH(threshold, &rule->thresholds, link)
1056 if (strcmp(name, threshold->name) == 0)
1057 break;
1058 return (threshold);
1059 }
1060
1061 /*
1062 * This function is called from newday().
1063 */
1064 int
thresholds_newday(struct rule * rule)1065 thresholds_newday(struct rule *rule)
1066 {
1067 struct threshold *threshold;
1068 unsigned int check_sec;
1069
1070 check_sec = EVENT_NOT_SCHEDULED;
1071
1072 STAILQ_FOREACH(threshold, &rule->thresholds, link) {
1073 if (WT_IS_INACTIVE(threshold->worktime)) {
1074 if (THRESHOLD_IS_ACTIVE(threshold))
1075 if (set_threshold_inactive(rule, threshold) < 0)
1076 goto failed;
1077 if (check_sec > threshold->worktime->active_sec)
1078 check_sec = threshold->worktime->active_sec;
1079 } else {
1080 if (THRESHOLD_IS_INACTIVE(threshold)) {
1081 if (set_threshold_active(rule, threshold) < 0)
1082 goto failed;
1083 /*
1084 * threshold->check_sec was updated in
1085 * set_threshold_active().
1086 */
1087 } else
1088 threshold->check_sec =
1089 threshold->time_slice->event_sec;
1090 if (check_sec > threshold->worktime->inactive_sec)
1091 check_sec = threshold->worktime->inactive_sec;
1092 if (check_sec > threshold->check_sec)
1093 check_sec = threshold->check_sec;
1094 }
1095 if (rule->check_sec > check_sec)
1096 rule->check_sec = check_sec;
1097 }
1098
1099 return (0);
1100
1101 failed:
1102 logbt("thresholds_newday");
1103 return (-1);
1104 }
1105
1106 /*
1107 * Copy all thresholds from the given threshold list to rule.
1108 */
1109 int
copy_thresholds(struct rule * rule,const struct thresholds_list * list)1110 copy_thresholds(struct rule *rule, const struct thresholds_list *list)
1111 {
1112 const struct threshold *tsrc;
1113 struct threshold *tdst;
1114 unsigned int count;
1115 int rv;
1116
1117 count = 0;
1118 STAILQ_FOREACH(tsrc, list, link) {
1119 tdst = mzone_alloc(threshold_mzone);
1120 if (tdst == NULL) {
1121 xlogmsgx(IPA_LOG_ERR, "copy_thresholds: "
1122 "mzone_alloc failed");
1123 return (-1);
1124 }
1125
1126 /* Copy settings from source threshold. */
1127 *tdst = *tsrc;
1128
1129 /*
1130 * Initialize fields which are pointers to memory,
1131 * which cannot be shared.
1132 */
1133 threshold_init_cmds(tdst);
1134 tdst->rule = rule;
1135 tdst->wpid.u.threshold = tdst;
1136
1137 /* Link just allocated threshold to rule. */
1138 STAILQ_INSERT_TAIL(&rule->thresholds, tdst, link);
1139
1140 /*
1141 * If something goes wrong with memory allocation,
1142 * previous settings will allow to deinitialize current rule.
1143 */
1144 rv =
1145 cmds_copy(rule, &tdst->below_thr, &tsrc->below_thr) +
1146 cmds_copy(rule, &tdst->equal_thr, &tsrc->equal_thr) +
1147 cmds_copy(rule, &tdst->above_thr, &tsrc->above_thr) +
1148 cmds_copy(rule, &tdst->rc[0], &tsrc->rc[0]) +
1149 cmds_copy(rule, &tdst->rc[1], &tsrc->rc[1]);
1150 if (rv < 0)
1151 return (-1);
1152 ++count;
1153 }
1154 if (RULE_IS_DYNAMIC(rule))
1155 ndynthresholds += count;
1156 else
1157 nstatthresholds += count;
1158 return (0);
1159 }
1160
1161 /*
1162 * Release memory used by a list of thresholds, rule_flags determines
1163 * which part of threshold{} structure to free.
1164 */
1165 void
free_thresholds(unsigned int rule_flags,struct thresholds_list * thresholds,int dyn_flag)1166 free_thresholds(unsigned int rule_flags, struct thresholds_list *thresholds,
1167 int dyn_flag)
1168 {
1169 struct threshold *threshold, *threshold_next;
1170 unsigned int count;
1171
1172 count = 0;
1173 STAILQ_FOREACH_SAFE(threshold, thresholds, link, threshold_next) {
1174 if (rule_flags & RULE_FLAG_FREE_THRESHOLDS) {
1175 mem_free(threshold->name, m_anon);
1176 mem_free(threshold->info, m_parser);
1177 }
1178 mem_free(threshold->cnt_slice, m_anon);
1179 mem_free(threshold->cnt_slice_sign, m_anon);
1180 cmds_free(&threshold->below_thr);
1181 cmds_free(&threshold->equal_thr);
1182 cmds_free(&threshold->above_thr);
1183 cmds_free(&threshold->rc[RC_STARTUP]);
1184 cmds_free(&threshold->rc[RC_SHUTDOWN]);
1185 mzone_free(threshold_mzone, threshold);
1186 ++count;
1187 }
1188 if (dyn_flag)
1189 ndynthresholds -= count;
1190 }
1191
1192 void
threshold_init_cmds(struct threshold * threshold)1193 threshold_init_cmds(struct threshold *threshold)
1194 {
1195 cmds_init(&threshold->below_thr);
1196 cmds_init(&threshold->equal_thr);
1197 cmds_init(&threshold->above_thr);
1198 cmds_init(&threshold->rc[RC_STARTUP]);
1199 cmds_init(&threshold->rc[RC_SHUTDOWN]);
1200 }
1201
1202 /*
1203 * Set default settings and inherit settings from global{} in threshold{}.
1204 */
1205 void
threshold_inherit(struct threshold * threshold)1206 threshold_inherit(struct threshold *threshold)
1207 {
1208 if (threshold->time_width == 0) {
1209 threshold->time_width = global_threshold_time_width;
1210 threshold->time_slice = global_threshold_time_slice;
1211 }
1212 if (threshold->load_thr < 0)
1213 threshold->load_thr = global_load_threshold;
1214 if (threshold->thr_type < 0)
1215 threshold->thr_type = global_threshold_type;
1216 if (threshold->below_lim == 0) {
1217 threshold->below_lim = threshold->below_cnt =
1218 global_threshold_below_lim;
1219 threshold->equal_lim = threshold->equal_cnt =
1220 global_threshold_equal_lim;
1221 threshold->above_lim = threshold->above_cnt =
1222 global_threshold_above_lim;
1223 }
1224 if (threshold->below_thr.sync < 0)
1225 threshold->below_thr.sync = 0;
1226 if (threshold->equal_thr.sync < 0)
1227 threshold->equal_thr.sync = 0;
1228 if (threshold->above_thr.sync < 0)
1229 threshold->above_thr.sync = 0;
1230 if (threshold->rc[RC_STARTUP].sync < 0)
1231 threshold->rc[RC_STARTUP].sync = 1;
1232 if (threshold->rc[RC_SHUTDOWN].sync < 0)
1233 threshold->rc[RC_SHUTDOWN].sync = 1;
1234 }
1235 #endif /* WITH_THRESHOLDS */
1236