/* 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 #include #include #include /** * 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() { 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); }