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