397 lines
12 KiB
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);
|
|
}
|