1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* Copyright (c) [2023] [Syswonder Community]
 *   [Rukos] is licensed under Mulan PSL v2.
 *   You can use this software according to the terms and conditions of the Mulan PSL v2.
 *   You may obtain a copy of Mulan PSL v2 at:
 *               http://license.coscl.org.cn/MulanPSL2
 *   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *   See the Mulan PSL v2 for more details.
 */

use core::sync::atomic::{AtomicU64, Ordering};

use crate::AVENRUN;

/// bits to shift fixed point
const FSHIFT: u64 = 16;
/// fixed point
const FIXED_1: u64 = 1 << FSHIFT;
/// update AVENRUN per 5 seconds
const LOAD_FREQ: u64 = 5 * axhal::time::NANOS_PER_SEC + 1;

/* 1/exp(5sec/1min) as fixed-point */
/* 1/exp(5sec/5min) */
/* 1/exp(5sec/15min) */
const EXP: [u64; 3] = [1884, 2014, 2037];

/// count of idle ticks
static mut IDLE_CNT: AtomicU64 = AtomicU64::new(0);
/// count of all ticks
static mut ALL_CNT: AtomicU64 = AtomicU64::new(0);
/// last update time
static mut LAST_UPDATE: AtomicU64 = AtomicU64::new(0);

/*
 * a1 = a0 * e + a * (1 - e)
 */
fn calc_load(load: u64, exp: u64, active: u64) -> u64 {
    let mut newload: u64 = load * exp + active * (FIXED_1 - exp);
    if active >= load {
        newload += FIXED_1 - 1;
    }
    newload / FIXED_1
}

/*
 * calc_load_tick - update the avenrun load
 *
 * Called from the scheduler_timer_tick.
 */
pub(crate) fn calc_load_tick(is_idle: bool) {
    if is_idle {
        unsafe {
            IDLE_CNT.fetch_add(1, Ordering::Relaxed);
        }
    }
    unsafe {
        ALL_CNT.fetch_add(1, Ordering::Relaxed);
    }

    let curr = axhal::time::current_time_nanos();

    if curr - unsafe { LAST_UPDATE.load(Ordering::Relaxed) } < LOAD_FREQ {
        return;
    }
    let idle_cnt;
    let all_cnt;
    unsafe {
        LAST_UPDATE.store(curr, Ordering::Relaxed);
        idle_cnt = IDLE_CNT.load(Ordering::Relaxed);
        IDLE_CNT.store(0, Ordering::Relaxed);
        all_cnt = ALL_CNT.load(Ordering::Relaxed);
        ALL_CNT.store(0, Ordering::Relaxed);
    }
    for i in 0..3 {
        unsafe {
            AVENRUN[i] = calc_load(AVENRUN[i], EXP[i], (all_cnt - idle_cnt) * FIXED_1 / all_cnt);
        }
    }
}