source: mainline/kernel/generic/src/sysinfo/stats.c@ 70e2b2d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 70e2b2d was 70e2b2d, checked in by Martin Decky <martin@…>, 15 years ago

avoid costly allocation and generation of data when it is actually not necessary

  • Property mode set to 100644
File size: 12.1 KB
RevLine 
[9dae191e]1/*
2 * Copyright (c) 2010 Stanislav Kozina
3 * Copyright (c) 2010 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup generic
31 * @{
32 */
33/** @file
34 */
35
36#include <typedefs.h>
37#include <sysinfo/abi.h>
38#include <sysinfo/stats.h>
39#include <sysinfo/sysinfo.h>
40#include <time/clock.h>
41#include <mm/frame.h>
42#include <proc/thread.h>
43#include <str.h>
44#include <errno.h>
45#include <cpu.h>
46#include <arch.h>
47
48/** Bits of fixed-point precision for load */
49#define LOAD_FIXED_SHIFT 11
50
51/** 1.0 as fixed-point for load */
52#define LOAD_FIXED_1 (1 << LOAD_FIXED_SHIFT)
53
54/** Compute load in 5 second intervals */
55#define LOAD_INTERVAL 5
56
[80bfb601]57/** Fixed-point representation of
[9dae191e]58 *
59 * 1 / exp(5 sec / 1 min)
60 * 1 / exp(5 sec / 5 min)
61 * 1 / exp(5 sec / 15 min)
62 *
63 */
64static load_t load_exp[LOAD_STEPS] = {1884, 2014, 2037};
[80bfb601]65
66/** Running average of the number of ready threads */
[9dae191e]67static load_t avenrdy[LOAD_STEPS] = {0, 0, 0};
[80bfb601]68
69/** Load calculation spinlock */
[9dae191e]70SPINLOCK_STATIC_INITIALIZE_NAME(load_lock, "load_lock");
71
[80bfb601]72/** Get system uptime
73 *
74 * @param item Sysinfo item (unused).
75 *
76 * @return System uptime (in secords).
77 *
78 */
[9dae191e]79static unative_t get_stats_uptime(struct sysinfo_item *item)
80{
81 /* This doesn't have to be very accurate */
82 return uptime->seconds1;
83}
84
[80bfb601]85/** Get statistics of all CPUs
86 *
[70e2b2d]87 * @param item Sysinfo item (unused).
88 * @param size Size of the returned data.
89 * @param dry_run Do not get the data, just calculate the size.
[80bfb601]90 *
91 * @return Data containing several stats_cpu_t structures.
92 * If the return value is not NULL, it should be freed
93 * in the context of the sysinfo request.
94 */
[70e2b2d]95static void *get_stats_cpus(struct sysinfo_item *item, size_t *size,
96 bool dry_run)
[9dae191e]97{
[70e2b2d]98 *size = sizeof(stats_cpu_t) * config.cpu_count;
99 if (dry_run)
100 return NULL;
101
[80bfb601]102 /* Assumption: config.cpu_count is constant */
[70e2b2d]103 stats_cpu_t *stats_cpus = (stats_cpu_t *) malloc(*size, FRAME_ATOMIC);
[9dae191e]104 if (stats_cpus == NULL) {
105 *size = 0;
106 return NULL;
107 }
108
[80bfb601]109 /* Each CPU structure is locked separatelly */
[9dae191e]110 ipl_t ipl = interrupts_disable();
111
112 size_t i;
113 for (i = 0; i < config.cpu_count; i++) {
114 spinlock_lock(&cpus[i].lock);
115
116 stats_cpus[i].id = cpus[i].id;
117 stats_cpus[i].frequency_mhz = cpus[i].frequency_mhz;
118 stats_cpus[i].busy_ticks = cpus[i].busy_ticks;
119 stats_cpus[i].idle_ticks = cpus[i].idle_ticks;
120
121 spinlock_unlock(&cpus[i].lock);
122 }
123
124 interrupts_restore(ipl);
125
126 return ((void *) stats_cpus);
127}
128
[80bfb601]129/** Count number of tasks
130 *
131 * AVL task tree walker for counting tasks.
132 *
133 * @param node AVL task tree node (unused).
134 * @param arg Pointer to the counter.
135 *
136 * @param Always true (continue the walk).
137 *
138 */
[9dae191e]139static bool task_count_walker(avltree_node_t *node, void *arg)
140{
141 size_t *count = (size_t *) arg;
142 (*count)++;
143
144 return true;
145}
146
[80bfb601]147/** Gather tasks
148 *
149 * AVL task tree walker for gathering task IDs. Interrupts should
150 * be already disabled while walking the tree.
151 *
152 * @param node AVL task tree node.
153 * @param arg Pointer to the iterator into the array of task IDs.
154 *
155 * @param Always true (continue the walk).
156 *
157 */
[9dae191e]158static bool task_serialize_walker(avltree_node_t *node, void *arg)
159{
160 task_id_t **ids = (task_id_t **) arg;
161 task_t *task = avltree_get_instance(node, task_t, tasks_tree_node);
162
[80bfb601]163 /* Interrupts are already disabled */
[9dae191e]164 spinlock_lock(&(task->lock));
165
[80bfb601]166 /* Record the ID and increment the iterator */
[9dae191e]167 **ids = task->taskid;
168 (*ids)++;
169
170 spinlock_unlock(&(task->lock));
171
172 return true;
173}
174
[80bfb601]175/** Get task IDs
176 *
[70e2b2d]177 * @param item Sysinfo item (unused).
178 * @param size Size of the returned data.
179 * @param dry_run Do not get the data, just calculate the size.
[80bfb601]180 *
181 * @return Data containing task IDs of all tasks.
182 * If the return value is not NULL, it should be freed
183 * in the context of the sysinfo request.
184 */
[70e2b2d]185static void *get_stats_tasks(struct sysinfo_item *item, size_t *size,
186 bool dry_run)
[9dae191e]187{
188 /* Messing with task structures, avoid deadlock */
189 ipl_t ipl = interrupts_disable();
190 spinlock_lock(&tasks_lock);
191
[80bfb601]192 /* First walk the task tree to count the tasks */
[9dae191e]193 size_t count = 0;
194 avltree_walk(&tasks_tree, task_count_walker, (void *) &count);
195
196 if (count == 0) {
[80bfb601]197 /* No tasks found (strange) */
[9dae191e]198 spinlock_unlock(&tasks_lock);
199 interrupts_restore(ipl);
200
201 *size = 0;
202 return NULL;
203 }
204
[70e2b2d]205 *size = sizeof(task_id_t) * count;
206 if (dry_run) {
207 spinlock_unlock(&tasks_lock);
208 interrupts_restore(ipl);
209 return NULL;
210 }
211
212 task_id_t *task_ids = (task_id_t *) malloc(*size, FRAME_ATOMIC);
[9dae191e]213 if (task_ids == NULL) {
[80bfb601]214 /* No free space for allocation */
[9dae191e]215 spinlock_unlock(&tasks_lock);
216 interrupts_restore(ipl);
217
218 *size = 0;
219 return NULL;
220 }
221
[80bfb601]222 /* Walk tha task tree again to gather the IDs */
[9dae191e]223 task_id_t *iterator = task_ids;
224 avltree_walk(&tasks_tree, task_serialize_walker, (void *) &iterator);
225
226 spinlock_unlock(&tasks_lock);
227 interrupts_restore(ipl);
228
229 return ((void *) task_ids);
230}
231
[80bfb601]232/** Get the size of a virtual address space
233 *
234 * @param as Address space.
235 *
236 * @return Size of the mapped virtual address space (bytes).
237 *
238 */
[9dae191e]239static size_t get_task_virtmem(as_t *as)
240{
241 mutex_lock(&as->lock);
242
243 size_t result = 0;
244
[80bfb601]245 /* Walk the B+ tree and count pages */
[9dae191e]246 link_t *cur;
247 for (cur = as->as_area_btree.leaf_head.next;
248 cur != &as->as_area_btree.leaf_head; cur = cur->next) {
249 btree_node_t *node =
250 list_get_instance(cur, btree_node_t, leaf_link);
251
252 unsigned int i;
253 for (i = 0; i < node->keys; i++) {
254 as_area_t *area = node->value[i];
255
256 mutex_lock(&area->lock);
257 result += area->pages;
258 mutex_unlock(&area->lock);
259 }
260 }
261
262 mutex_unlock(&as->lock);
263
264 return result * PAGE_SIZE;
265}
266
[80bfb601]267/** Get task statistics
268 *
269 * Get statistics of a given task. The task ID is passed
270 * as a string (current limitation of the sysinfo interface,
271 * but it is still reasonable for the given purpose).
272 *
273 * @param name Task ID (string-encoded number).
274 *
275 * @return Sysinfo return holder. The type of the returned
276 * data is either SYSINFO_VAL_UNDEFINED (unknown
277 * task ID or memory allocation error) or
278 * SYSINFO_VAL_FUNCTION_DATA (in that case the
279 * generated data should be freed within the
280 * sysinfo request context).
281 *
282 */
[9dae191e]283static sysinfo_return_t get_stats_task(const char *name)
284{
[80bfb601]285 /* Initially no return value */
[9dae191e]286 sysinfo_return_t ret;
287 ret.tag = SYSINFO_VAL_UNDEFINED;
288
[80bfb601]289 /* Parse the task ID */
[9dae191e]290 task_id_t task_id;
291 if (str_uint64(name, NULL, 0, true, &task_id) != EOK)
292 return ret;
293
[80bfb601]294 /* Allocate stats_task_t structure */
[9dae191e]295 stats_task_t *stats_task =
296 (stats_task_t *) malloc(sizeof(stats_task_t), FRAME_ATOMIC);
297 if (stats_task == NULL)
298 return ret;
299
[80bfb601]300 /* Messing with task structures, avoid deadlock */
[9dae191e]301 ipl_t ipl = interrupts_disable();
302 spinlock_lock(&tasks_lock);
303
304 task_t *task = task_find_by_id(task_id);
305 if (task == NULL) {
[80bfb601]306 /* No task with this ID */
[9dae191e]307 spinlock_unlock(&tasks_lock);
308 interrupts_restore(ipl);
309 free(stats_task);
310 return ret;
311 }
312
[80bfb601]313 /* Hand-over-hand locking */
[9dae191e]314 spinlock_lock(&task->lock);
315 spinlock_unlock(&tasks_lock);
316
[80bfb601]317 /* Copy task's statistics */
[9dae191e]318 str_cpy(stats_task->name, TASK_NAME_BUFLEN, task->name);
319 stats_task->virtmem = get_task_virtmem(task->as);
320 stats_task->threads = atomic_get(&task->refcount);
321 task_get_accounting(task, &(stats_task->ucycles),
322 &(stats_task->kcycles));
323 stats_task->ipc_info = task->ipc_info;
324
325 spinlock_unlock(&task->lock);
326 interrupts_restore(ipl);
327
[80bfb601]328 /* Correct return value */
[9dae191e]329 ret.tag = SYSINFO_VAL_FUNCTION_DATA;
330 ret.data.data = (void *) stats_task;
331 ret.data.size = sizeof(stats_task_t);
332
333 return ret;
334}
335
[80bfb601]336/** Get physical memory statistics
337 *
[70e2b2d]338 * @param item Sysinfo item (unused).
339 * @param size Size of the returned data.
340 * @param dry_run Do not get the data, just calculate the size.
[80bfb601]341 *
342 * @return Data containing stats_physmem_t.
343 * If the return value is not NULL, it should be freed
344 * in the context of the sysinfo request.
345 */
[70e2b2d]346static void *get_stats_physmem(struct sysinfo_item *item, size_t *size,
347 bool dry_run)
[9dae191e]348{
[70e2b2d]349 *size = sizeof(stats_physmem_t);
350 if (dry_run)
351 return NULL;
352
[9dae191e]353 stats_physmem_t *stats_physmem =
[70e2b2d]354 (stats_physmem_t *) malloc(*size, FRAME_ATOMIC);
[9dae191e]355 if (stats_physmem == NULL) {
356 *size = 0;
357 return NULL;
358 }
359
360 zones_stats(&(stats_physmem->total), &(stats_physmem->unavail),
361 &(stats_physmem->used), &(stats_physmem->free));
362
363 return ((void *) stats_physmem);
364}
365
[80bfb601]366/** Get system load
367 *
[70e2b2d]368 * @param item Sysinfo item (unused).
369 * @param size Size of the returned data.
370 * @param dry_run Do not get the data, just calculate the size.
[80bfb601]371 *
372 * @return Data several load_t values.
373 * If the return value is not NULL, it should be freed
374 * in the context of the sysinfo request.
375 */
[70e2b2d]376static void *get_stats_load(struct sysinfo_item *item, size_t *size,
377 bool dry_run)
[9dae191e]378{
[70e2b2d]379 *size = sizeof(load_t) * LOAD_STEPS;
380 if (dry_run)
381 return NULL;
382
383 load_t *stats_load = (load_t *) malloc(*size, FRAME_ATOMIC);
[9dae191e]384 if (stats_load == NULL) {
385 *size = 0;
386 return NULL;
387 }
388
[80bfb601]389 /* To always get consistent values acquire the spinlock */
[9dae191e]390 ipl_t ipl = interrupts_disable();
391 spinlock_lock(&load_lock);
392
393 unsigned int i;
394 for (i = 0; i < LOAD_STEPS; i++)
395 stats_load[i] = avenrdy[i] << LOAD_FIXED_SHIFT;
396
397 spinlock_unlock(&load_lock);
398 interrupts_restore(ipl);
399
400 return ((void *) stats_load);
401}
402
403/** Calculate load
404 *
405 */
406static inline load_t load_calc(load_t load, load_t exp, size_t ready)
407{
408 load *= exp;
409 load += ready * (LOAD_FIXED_1 - exp);
410
411 return (load >> LOAD_FIXED_SHIFT);
412}
413
414/** Count threads in ready queues
415 *
416 * Should be called with interrupts disabled.
417 *
418 */
419static inline size_t get_ready_count(void)
420{
421 size_t i;
422 size_t count = 0;
423
424 for (i = 0; i < config.cpu_count; i++) {
425 spinlock_lock(&cpus[i].lock);
426
427 size_t j;
428 for (j = 0; j < RQ_COUNT; j++) {
429 spinlock_lock(&cpus[i].rq[j].lock);
430 count += cpus[i].rq[j].n;
431 spinlock_unlock(&cpus[i].rq[j].lock);
432 }
433
434 spinlock_unlock(&cpus[i].lock);
435 }
436
437 return count;
438}
439
440/** Load computation thread.
441 *
442 * Compute system load every few seconds.
443 *
444 * @param arg Unused.
445 *
446 */
447void kload(void *arg)
448{
449 thread_detach(THREAD);
450
451 while (true) {
[80bfb601]452 /* Mutually exclude with get_stats_load() */
[9dae191e]453 ipl_t ipl = interrupts_disable();
454 spinlock_lock(&load_lock);
455
456 size_t ready = get_ready_count() * LOAD_FIXED_1;
457
458 unsigned int i;
459 for (i = 0; i < LOAD_STEPS; i++)
460 avenrdy[i] = load_calc(avenrdy[i], load_exp[i], ready);
461
462 spinlock_unlock(&load_lock);
463 interrupts_restore(ipl);
464
465 thread_sleep(LOAD_INTERVAL);
466 }
467}
468
[80bfb601]469/** Register sysinfo statistical items
470 *
471 */
[9dae191e]472void stats_init(void)
473{
474 sysinfo_set_item_fn_val("system.uptime", NULL, get_stats_uptime);
475 sysinfo_set_item_fn_data("system.cpus", NULL, get_stats_cpus);
476 sysinfo_set_item_fn_data("system.physmem", NULL, get_stats_physmem);
477 sysinfo_set_item_fn_data("system.load", NULL, get_stats_load);
478 sysinfo_set_item_fn_data("system.tasks", NULL, get_stats_tasks);
479 sysinfo_set_subtree_fn("system.tasks", NULL, get_stats_task);
480}
481
482/** @}
483 */
Note: See TracBrowser for help on using the repository browser.