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