sos-code-article10/sos/calcload.c

397 lines
12 KiB
C

/* Copyright (C) 2004 David Decotigny
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include <hwcore/irq.h>
#include <sos/kmalloc.h>
#include <sos/assert.h>
#include <sos/calcload.h>
/**
* Multiplicative factor to display digits after the decimal dot. The
* higher the value, the higher the precision, but the higher the risk
* that the value you get is incorrect (integer overflow).
*
* The CPU ratios will be correctly displayed as long as:
* 2^32 > (900 * HZ * 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
* The "900" above means 900s because the load is computed over 15mn (900s).
* HZ is the frequency of the timer tick because the load is updated
* at each timer tick.
* The "100" above is the multiplication factor to get the ratio value
* between 0 and 100 (instead of 0-1).
*
* The maximum CPU sustainable load that will be correctly displayed
* is given by the formula:
* (2^32 - 1) / (900 * HZ * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
* With HZ=100, these maximum sustainable loads are respectively
* 47.721, 477.21 and 4772.1 with
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR being respectively 1000, 100
* and 10.
*
* Hence, among these formulaes, the most limitative one is that
* concerning the CPU ratios (because of the "100" factor). Actually,
* for HZ=100, the only correct value is 10.
*/
#define SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR 10 /* 1/10 resolution */
/**
* To compute the load, at each clock tick we store the number of
* threads ready in kernel/user mode, and the kind of the thread that
* is executing (user or kernel mode): this is stored in
* current_load_entry. We then compute the sum of these numbers over 3
* periods of time: 1 minute, 5 minutes, 15 minutes. This is the role
* of the sliding windows data structures. A "sliding window" is only
* the synthetic sum of these figures, not a real sliding window of
* load_entries. At each timer tick and everytime the load is updated,
* the computations are in O(1).
*
* All the sliding windows share the main "recorded_load" array of
* load_entries for that; its role is to store the last 15mns of load
* data, which encompasses the data for the 1mn, 5mn and 15mn sliding
* windows.
*/
/* Max number of seconds that we record (ie number of entries in
recorded_loads) */
#define NB_SECS 900
/* An entry in the longest sliding window */
struct load_entry
{
sos_ui32_t nb_ticks;
sos_ui32_t nb_user_running;
sos_ui32_t nb_kernel_running;
sos_ui32_t nb_user_ready;
sos_ui32_t nb_kernel_ready;
};
struct load_entry current_load_entry;
/* The longest sliding window */
struct recorded_loads
{
sos_ui32_t most_recent;
sos_ui32_t max_entries;
struct load_entry *load_entries;
};
#define LOAD_GET_ENTRY(loads,age) \
(&((loads).load_entries[( (loads).max_entries + (loads).most_recent - (age))\
% ((loads).max_entries)]))
/* A sliding window, we manage one for each time interval */
struct sliding_window
{
sos_ui32_t max_entries;
sos_ui32_t nb_entries;
sos_ui32_t sigma_nb_ticks;
sos_ui32_t sigma_nb_user_running;
sos_ui32_t sigma_nb_kernel_running;
sos_ui32_t sigma_nb_user_ready;
sos_ui32_t sigma_nb_kernel_ready;
};
/* The main sliding window */
static struct recorded_loads recorded_loads;
/* The sliding windows for 3 tims intervals: 1min, 5min, 15min */
static struct sliding_window load_1mn, load_5mn, load_15mn;
/* Forward declaration */
static struct sos_timeout_action calcload_timeout;
static void calcload_routine(struct sos_timeout_action *a);
static void _reinit_load_subsystem(void)
{
memset(& recorded_loads, 0x0, sizeof(recorded_loads));
memset(& current_load_entry, 0x0, sizeof(struct load_entry));
memset(& load_1mn, 0x0, sizeof(load_1mn));
memset(& load_5mn, 0x0, sizeof(load_5mn));
memset(& load_15mn, 0x0, sizeof(load_15mn));
}
sos_ret_t sos_load_subsystem_setup(void)
{
struct sos_time period;
_reinit_load_subsystem();
if (recorded_loads.load_entries)
sos_kfree((sos_vaddr_t) recorded_loads.load_entries);
_reinit_load_subsystem();
/* Allocate 900 entries to store 15mn of data (because 15minutes =
900s) */
recorded_loads.max_entries = NB_SECS;
recorded_loads.load_entries
= (struct load_entry*) sos_kmalloc(NB_SECS * sizeof(struct load_entry),
0);
if (! recorded_loads.load_entries)
{
return -SOS_ENOMEM;
}
/* Compute the number of entries in each sliding window */
load_1mn.max_entries = 60;
load_5mn.max_entries = 300;
load_15mn.max_entries = 900;
/* Program the load computation action */
sos_time_init_action(& calcload_timeout);
period.sec = 1; period.nanosec = 0;
return sos_time_register_action_relative(& calcload_timeout,
& period,
calcload_routine,
NULL);
}
/* Shift the given sliding window to record the current_load_entry */
static void update_sliding_window(struct sliding_window *w)
{
/*
* Compute the value of the sum over the sliding window
*/
/* Take the new value into account */
w->sigma_nb_ticks += current_load_entry.nb_ticks;
w->sigma_nb_user_running += current_load_entry.nb_user_running;
w->sigma_nb_kernel_running += current_load_entry.nb_kernel_running;
w->sigma_nb_user_ready += current_load_entry.nb_user_ready;
w->sigma_nb_kernel_ready += current_load_entry.nb_kernel_ready;
/* Remove the oldest entry, if it is going to be popped out of the
sliding window */
if (w->nb_entries < w->max_entries)
{
w->nb_entries ++;
}
else
{
struct load_entry * oldest_entry;
oldest_entry = LOAD_GET_ENTRY(recorded_loads, w->nb_entries - 1);
w->sigma_nb_ticks -= oldest_entry->nb_ticks;
w->sigma_nb_user_running -= oldest_entry->nb_user_running;
w->sigma_nb_kernel_running -= oldest_entry->nb_kernel_running;
w->sigma_nb_user_ready -= oldest_entry->nb_user_ready;
w->sigma_nb_kernel_ready -= oldest_entry->nb_kernel_ready;
}
}
/* The timeout action responsible for computing the CPU load */
static void calcload_routine(struct sos_timeout_action *a)
{
struct load_entry * new_head;
struct sos_time delay;
if (! recorded_loads.load_entries)
return;
/* Update the sliding windows */
update_sliding_window(& load_1mn);
update_sliding_window(& load_5mn);
update_sliding_window(& load_15mn);
/* Move the head of the list forward */
recorded_loads.most_recent
= (recorded_loads.most_recent + 1) % recorded_loads.max_entries;
/* Update the new head */
new_head = & recorded_loads.load_entries[recorded_loads.most_recent];
memcpy(new_head, & current_load_entry, sizeof(current_load_entry));
/* Reset the current load entry */
memset(& current_load_entry, 0x0, sizeof(current_load_entry));
/* Program next occurence of the action */
delay.sec = 1;
delay.nanosec = 0;
sos_time_register_action_relative(a, & delay, calcload_routine, NULL);
}
sos_ret_t sos_load_do_timer_tick(sos_bool_t cur_is_user,
sos_ui32_t nb_user_ready,
sos_ui32_t nb_kernel_ready)
{
sos_ui32_t flags;
sos_disable_IRQs(flags);
current_load_entry.nb_ticks ++;
current_load_entry.nb_user_ready += nb_user_ready;
current_load_entry.nb_kernel_ready += nb_kernel_ready;
if (cur_is_user)
current_load_entry.nb_user_running ++;
else
current_load_entry.nb_kernel_running ++;
sos_restore_IRQs(flags);
return SOS_OK;
}
void sos_load_to_string(char dest[11], sos_ui32_t load_value)
{
sos_bool_t print0 = FALSE;
sos_ui32_t d;
#define PUTCH(c) ({ *dest = (c); dest ++; })
for (d = 1000000000UL ; d > 0 ; d /= 10)
{
sos_ui32_t digit = (load_value / d) % 10;
if (digit > 0)
{
PUTCH(digit + '0');
print0 = TRUE;
}
else if (print0)
PUTCH('0');
if (d == SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR)
{
if (! print0)
PUTCH('0');
PUTCH('.');
print0 = TRUE;
}
}
*dest = '\0';
}
void sos_load_get_uload(sos_ui32_t * _load_1mn,
sos_ui32_t * _load_5mn,
sos_ui32_t * _load_15mn)
{
sos_ui32_t flags;
if (load_1mn.sigma_nb_ticks < 1)
return;
sos_disable_IRQs(flags);
*_load_1mn = ( load_1mn.sigma_nb_user_ready
+ load_1mn.sigma_nb_user_running)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_1mn.sigma_nb_ticks;
*_load_5mn = ( load_5mn.sigma_nb_user_ready
+ load_5mn.sigma_nb_user_running)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_5mn.sigma_nb_ticks;
*_load_15mn = ( load_15mn.sigma_nb_user_ready
+ load_15mn.sigma_nb_user_running)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_15mn.sigma_nb_ticks;
sos_restore_IRQs(flags);
}
void sos_load_get_sload(sos_ui32_t * _load_1mn,
sos_ui32_t * _load_5mn,
sos_ui32_t * _load_15mn)
{
sos_ui32_t flags;
if (load_1mn.sigma_nb_ticks < 1)
return;
/* The "IDLE" thread is always either ready or running by definition */
SOS_ASSERT_FATAL(load_1mn.sigma_nb_kernel_ready
+ load_1mn.sigma_nb_kernel_running
>= load_1mn.sigma_nb_ticks);
/* Remove the IDLE thread from the load calculation */
sos_disable_IRQs(flags);
*_load_1mn = ( load_1mn.sigma_nb_kernel_ready
+ load_1mn.sigma_nb_kernel_running
- load_1mn.sigma_nb_ticks)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_1mn.sigma_nb_ticks;
*_load_5mn = ( load_5mn.sigma_nb_kernel_ready
+ load_5mn.sigma_nb_kernel_running
- load_5mn.sigma_nb_ticks)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_5mn.sigma_nb_ticks;
*_load_15mn = ( load_15mn.sigma_nb_kernel_ready
+ load_15mn.sigma_nb_kernel_running
- load_15mn.sigma_nb_ticks)
* SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_15mn.sigma_nb_ticks;
sos_restore_IRQs(flags);
}
void sos_load_get_uratio(sos_ui32_t * _load_1mn,
sos_ui32_t * _load_5mn,
sos_ui32_t * _load_15mn)
{
sos_ui32_t flags;
if (load_1mn.sigma_nb_ticks < 1)
return;
sos_disable_IRQs(flags);
*_load_1mn = load_1mn.sigma_nb_user_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_1mn.sigma_nb_ticks;
*_load_5mn = load_5mn.sigma_nb_user_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_5mn.sigma_nb_ticks;
*_load_15mn = load_15mn.sigma_nb_user_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_15mn.sigma_nb_ticks;
sos_restore_IRQs(flags);
}
void sos_load_get_sratio(sos_ui32_t * _load_1mn,
sos_ui32_t * _load_5mn,
sos_ui32_t * _load_15mn)
{
sos_ui32_t flags;
if (load_1mn.sigma_nb_ticks < 1)
return;
/* Don't remove the CPU occupation ration of the IDLE thread
here... */
sos_disable_IRQs(flags);
*_load_1mn = load_1mn.sigma_nb_kernel_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_1mn.sigma_nb_ticks;
*_load_5mn = load_5mn.sigma_nb_kernel_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_5mn.sigma_nb_ticks;
*_load_15mn = load_15mn.sigma_nb_kernel_running
* 100 * SOS_LOAD_DISPLAY_MULTIPLICATION_FACTOR
/ load_15mn.sigma_nb_ticks;
sos_restore_IRQs(flags);
}