summaryrefslogtreecommitdiff
path: root/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c
blob: d38d9a9c928047838a8cb79303c4f92f2ffc66d9 (plain)
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 */

#include "proc.h"
#include "procfs.h"

#include <rthw.h>
#include <rtdbg.h>

#include <fcntl.h>
#include <errno.h>

#include <dfs_dentry.h>

#ifdef RT_USING_SMART
#include "lwp.h"
#include "lwp_pid.h"
#endif

#ifdef RT_USING_SMART
struct loadavg_count
{
    int total;
    int runnable;
};

/* Fixed-point load averages, 11 bits of fraction like Linux FSHIFT. */
#define PROC_LOAD_FSHIFT  11
#define PROC_LOAD_FIXED_1 (1UL << PROC_LOAD_FSHIFT)
#define PROC_LOAD_EXP_1   1884UL /* 1 / exp(5s / 60s)  * 2048 */
#define PROC_LOAD_EXP_5   2014UL /* 1 / exp(5s / 300s) * 2048 */
#define PROC_LOAD_EXP_15  2037UL /* 1 / exp(5s / 900s) * 2048 */

static unsigned long load_1 = 0;
static unsigned long load_5 = 0;
static unsigned long load_15 = 0;
static rt_tick_t load_last_tick = 0;

static int loadavg_count_pid(pid_t pid, void *arg)
{
    struct loadavg_count *count = (struct loadavg_count *)arg;
    struct rt_lwp *lwp = lwp_from_pid_locked(pid);

    if (!lwp)
    {
        return 0;
    }
    count->total++;
    {
        rt_list_t *node;

        node = lwp->t_grp.next;
        while (node != &lwp->t_grp)
        {
            rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling);
            int stat = RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK;

            if (stat == RT_THREAD_RUNNING || stat == RT_THREAD_READY)
            {
                count->runnable++;
                break;
            }
            node = node->next;
        }
    }
    return 0;
}

static void loadavg_fold(unsigned long active)
{
    load_1 = load_1 * PROC_LOAD_EXP_1 / PROC_LOAD_FIXED_1 +
             active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_1) / PROC_LOAD_FIXED_1;
    load_5 = load_5 * PROC_LOAD_EXP_5 / PROC_LOAD_FIXED_1 +
             active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_5) / PROC_LOAD_FIXED_1;
    load_15 = load_15 * PROC_LOAD_EXP_15 / PROC_LOAD_FIXED_1 +
              active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_15) / PROC_LOAD_FIXED_1;
}

static void loadavg_update(unsigned long active)
{
    rt_tick_t now = rt_tick_get();
    rt_tick_t period = RT_TICK_PER_SECOND * 5;

    if (load_last_tick == 0)
    {
        load_1 = load_5 = load_15 = active;
        load_last_tick = now;
        return;
    }

    while ((rt_tick_t)(now - load_last_tick) >= period)
    {
        loadavg_fold(active);
        load_last_tick += period;
    }
}

static void loadavg_print(struct dfs_seq_file *seq, unsigned long load)
{
    unsigned long integer = load >> PROC_LOAD_FSHIFT;
    unsigned long frac = ((load & (PROC_LOAD_FIXED_1 - 1)) * 100) >> PROC_LOAD_FSHIFT;

    dfs_seq_printf(seq, "%lu.%02lu", integer, frac);
}
#endif

static int single_show(struct dfs_seq_file *seq, void *data)
{
    RT_UNUSED(data);
#ifdef RT_USING_SMART
    struct loadavg_count count = { 0, 0 };

    lwp_pid_for_each(loadavg_count_pid, &count);
    loadavg_update(((unsigned long)count.runnable) << PROC_LOAD_FSHIFT);
    loadavg_print(seq, load_1);
    dfs_seq_puts(seq, " ");
    loadavg_print(seq, load_5);
    dfs_seq_puts(seq, " ");
    loadavg_print(seq, load_15);
    dfs_seq_printf(seq, " %d/%d %d\n", count.runnable, count.total,
                   (int)lwp_pid_get_last());
#else
    dfs_seq_puts(seq, "0.00 0.00 0.00 0/0 0\n");
#endif

    return 0;
}

int proc_loadavg_init(void)
{
    struct proc_dentry *dentry = proc_create_single_data("loadavg", 0, NULL, single_show, NULL);
    proc_release(dentry);

    return 0;
}
INIT_ENV_EXPORT(proc_loadavg_init);