1 /*
2  * history.c		History Management
3  *
4  * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
5  * Copyright (c) 2013 Red Hat, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <bmon/bmon.h>
27 #include <bmon/conf.h>
28 #include <bmon/history.h>
29 #include <bmon/utils.h>
30 
31 static LIST_HEAD(def_list);
32 
33 static struct history_def *current_history;
34 
history_def_lookup(const char * name)35 struct history_def *history_def_lookup(const char *name)
36 {
37 	struct history_def *def;
38 
39 	list_for_each_entry(def, &def_list, hd_list)
40 		if (!strcmp(def->hd_name, name))
41 		    return def;
42 
43 	return NULL;
44 }
45 
history_def_alloc(const char * name)46 struct history_def *history_def_alloc(const char *name)
47 {
48 	struct history_def *def;
49 
50 	if ((def = history_def_lookup(name)))
51 		return def;
52 
53 	def = xcalloc(1, sizeof(*def));
54 	def->hd_name = strdup(name);
55 
56 	list_add_tail(&def->hd_list, &def_list);
57 
58 	DBG("New history definition %s", name);
59 
60 	return def;
61 }
62 
history_def_free(struct history_def * def)63 static void history_def_free(struct history_def *def)
64 {
65 	if (!def)
66 		return;
67 
68 	xfree(def->hd_name);
69 	xfree(def);
70 }
71 
history_alloc_data(struct history_def * def)72 static void *history_alloc_data(struct history_def *def)
73 {
74 	return xcalloc(def->hd_size, def->hd_type);
75 }
76 
history_store_data(struct history * h,struct history_store * hs,uint64_t total,float diff)77 static void history_store_data(struct history *h, struct history_store *hs,
78 			       uint64_t total, float diff)
79 {
80 	uint64_t delta;
81 
82 	if (!hs->hs_data) {
83 		if (total == HISTORY_UNKNOWN)
84 			return;
85 
86 		hs->hs_data = history_alloc_data(h->h_definition);
87 	}
88 
89 	if (total == HISTORY_UNKNOWN)
90 		delta = HISTORY_UNKNOWN;
91 	else {
92 		delta = (total - hs->hs_prev_total);
93 
94 		if (delta > 0)
95 			delta /= diff;
96 		hs->hs_prev_total = total;
97 	}
98 
99 	switch (h->h_definition->hd_type) {
100 	case HISTORY_TYPE_8:
101 		((uint8_t *) hs->hs_data)[h->h_index] = (uint8_t) delta;
102 		break;
103 
104 	case HISTORY_TYPE_16:
105 		((uint16_t *) hs->hs_data)[h->h_index] = (uint16_t) delta;
106 		break;
107 
108 	case HISTORY_TYPE_32:
109 		((uint32_t *) hs->hs_data)[h->h_index] = (uint32_t) delta;
110 		break;
111 
112 	case HISTORY_TYPE_64:
113 		((uint64_t *) hs->hs_data)[h->h_index] = (uint64_t) delta;
114 		break;
115 
116 	default:
117 		BUG();
118 	}
119 }
120 
inc_history_index(struct history * h)121 static inline void inc_history_index(struct history *h)
122 {
123 	if (h->h_index < (h->h_definition->hd_size - 1))
124 		h->h_index++;
125 	else
126 		h->h_index = 0;
127 }
128 
history_data(struct history * h,struct history_store * hs,int index)129 uint64_t history_data(struct history *h, struct history_store *hs, int index)
130 {
131 	switch (h->h_definition->hd_type) {
132 	case HISTORY_TYPE_8: {
133 		uint8_t v = ((uint8_t *) hs->hs_data)[index];
134 		return (v == (uint8_t) -1) ? HISTORY_UNKNOWN : v;
135 	}
136 
137 	case HISTORY_TYPE_16: {
138 		uint16_t v = ((uint16_t *) hs->hs_data)[index];
139 		return (v == (uint16_t) -1) ? HISTORY_UNKNOWN : v;
140 	}
141 
142 	case HISTORY_TYPE_32: {
143 		uint32_t v = ((uint32_t *) hs->hs_data)[index];
144 		return (v == (uint32_t) -1) ? HISTORY_UNKNOWN : v;
145 	}
146 
147 	case HISTORY_TYPE_64: {
148 		uint64_t v = ((uint64_t *) hs->hs_data)[index];
149 		return (v == (uint64_t) -1) ? HISTORY_UNKNOWN : v;
150 	}
151 
152 	default:
153 		BUG();
154 	}
155 }
156 
history_update(struct attr * a,struct history * h,timestamp_t * ts)157 void history_update(struct attr *a, struct history *h, timestamp_t *ts)
158 {
159 	struct history_def *def = h->h_definition;
160 	float timediff;
161 
162 	if (h->h_last_update.tv_sec)
163 		timediff = timestamp_diff(&h->h_last_update, ts);
164 	else {
165 		timediff = 0.0f; /* initial history update */
166 
167 		/* Need a delta when working with counters */
168 		if (a->a_def->ad_type == ATTR_TYPE_COUNTER)
169 			goto update_prev_total;
170 	}
171 
172 	/*
173 	 * A read interval greater than the desired history interval
174 	 * can't possibly result in anything useful. Discard it and
175 	 * mark history data as invalid. The user has to adjust the
176 	 * read interval.
177 	 */
178 	if (cfg_read_interval > def->hd_interval)
179 		goto discard;
180 
181 	/*
182 	 * If the history interval matches the read interval it makes
183 	 * sense to update upon every read. The reader timing already
184 	 * took care of being as close as possible to the desired
185 	 * interval.
186 	 */
187 	if (cfg_read_interval == def->hd_interval)
188 		goto update;
189 
190 	if (timediff > h->h_max_interval)
191 		goto discard;
192 
193 	if (timediff < h->h_min_interval)
194 		return;
195 
196 update:
197 	history_store_data(h, &h->h_rx, a->a_rx_rate.r_total, timediff);
198 	history_store_data(h, &h->h_tx, a->a_tx_rate.r_total, timediff);
199 	inc_history_index(h);
200 
201 	goto update_ts;
202 
203 discard:
204 	while(timediff >= (def->hd_interval / 2)) {
205 		history_store_data(h, &h->h_rx, HISTORY_UNKNOWN, 0.0f);
206 		history_store_data(h, &h->h_tx, HISTORY_UNKNOWN, 0.0f);
207 
208 		inc_history_index(h);
209 		timediff -= def->hd_interval;
210 	}
211 
212 update_prev_total:
213 	h->h_rx.hs_prev_total = a->a_rx_rate.r_total;
214 	h->h_tx.hs_prev_total = a->a_tx_rate.r_total;
215 
216 update_ts:
217 	copy_timestamp(&h->h_last_update, ts);
218 }
219 
history_alloc(struct history_def * def)220 struct history *history_alloc(struct history_def *def)
221 {
222 	struct history *h;
223 
224 	h  = xcalloc(1, sizeof(*h));
225 
226 	init_list_head(&h->h_list);
227 
228 	h->h_definition = def;
229 
230 	h->h_min_interval = (def->hd_interval - (cfg_read_interval / 2.0f));
231 	h->h_max_interval = (def->hd_interval / cfg_history_variance);
232 
233 	return h;
234 }
235 
history_free(struct history * h)236 void history_free(struct history *h)
237 {
238 	if (!h)
239 		return;
240 
241 	xfree(h->h_rx.hs_data);
242 	xfree(h->h_tx.hs_data);
243 
244 	list_del(&h->h_list);
245 
246 	xfree(h);
247 }
248 
history_attach(struct attr * attr)249 void history_attach(struct attr *attr)
250 {
251 	struct history_def *def;
252 	struct history *h;
253 
254 	list_for_each_entry(def, &def_list, hd_list) {
255 		h = history_alloc(def);
256 		list_add_tail(&h->h_list, &attr->a_history_list);
257 	}
258 }
259 
history_select_first(void)260 struct history_def *history_select_first(void)
261 {
262 	if (list_empty(&def_list))
263 		current_history = NULL;
264 	else
265 		current_history = list_first_entry(&def_list,
266 					 struct history_def, hd_list);
267 
268 	return current_history;
269 }
270 
history_select_last(void)271 struct history_def *history_select_last(void)
272 {
273 	if (list_empty(&def_list))
274 		current_history = NULL;
275 	else
276 		current_history = list_entry(def_list.prev,
277 					struct history_def, hd_list);
278 
279 	return current_history;
280 }
281 
history_select_next(void)282 struct history_def *history_select_next(void)
283 {
284 	if (current_history && current_history->hd_list.next != &def_list) {
285 		current_history = list_entry(current_history->hd_list.next,
286 					struct history_def, hd_list);
287 		return current_history;
288 	}
289 
290 	return history_select_first();
291 }
292 
history_select_prev(void)293 struct history_def *history_select_prev(void)
294 {
295 	if (current_history && current_history->hd_list.prev != &def_list) {
296 		current_history = list_entry(current_history->hd_list.prev,
297 					struct history_def, hd_list);
298 		return current_history;
299 	}
300 
301 	return history_select_last();
302 }
303 
history_current(void)304 struct history_def *history_current(void)
305 {
306 	if (!current_history)
307 		current_history = history_select_first();
308 
309 	return current_history;
310 }
311 
history_exit(void)312 static void __exit history_exit(void)
313 {
314 	struct history_def *def, *n;
315 
316 	list_for_each_entry_safe(def, n, &def_list, hd_list)
317 		history_def_free(def);
318 }
319