1// Copyright 2016 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7// This file defines:
8// * Initialization code for kernel/object module
9// * Singleton instances and global locks
10// * Helper functions
11//
12// TODO(dbort): Split this file into self-consistent pieces.
13
14#include <inttypes.h>
15
16#include <trace.h>
17
18#include <kernel/cmdline.h>
19
20#include <lk/init.h>
21
22#include <lib/oom.h>
23
24#include <object/diagnostics.h>
25#include <object/excp_port.h>
26#include <object/job_dispatcher.h>
27#include <object/policy_manager.h>
28#include <object/port_dispatcher.h>
29#include <object/process_dispatcher.h>
30
31#include <fbl/function.h>
32
33#include <zircon/types.h>
34
35#define LOCAL_TRACE 0
36
37// All jobs and processes are rooted at the |root_job|.
38static fbl::RefPtr<JobDispatcher> root_job;
39
40// The singleton policy manager, for jobs and processes. This is
41// not a Dispatcher, just a plain class.
42static PolicyManager* policy_manager;
43
44fbl::RefPtr<JobDispatcher> GetRootJobDispatcher() {
45    return root_job;
46}
47
48PolicyManager* GetSystemPolicyManager() {
49    return policy_manager;
50}
51
52// Counts and optionally prints all job/process descendants of a job.
53namespace {
54class OomJobEnumerator final : public JobEnumerator {
55public:
56    // If |prefix| is non-null, also prints each job/process.
57    OomJobEnumerator(const char* prefix)
58        : prefix_(prefix) { reset_counts(); }
59    void reset_counts() {
60        num_jobs_ = 0;
61        num_processes_ = 0;
62        num_running_processes_ = 0;
63    }
64    size_t num_jobs() const { return num_jobs_; }
65    size_t num_processes() const { return num_processes_; }
66    size_t num_running_processes() const { return num_running_processes_; }
67
68private:
69    bool OnJob(JobDispatcher* job) final {
70        if (prefix_ != nullptr) {
71            char name[ZX_MAX_NAME_LEN];
72            job->get_name(name);
73            printf("%sjob %6" PRIu64 " '%s'\n", prefix_, job->get_koid(), name);
74        }
75        num_jobs_++;
76        return true;
77    }
78    bool OnProcess(ProcessDispatcher* process) final {
79        // Since we want to free memory by actually killing something, only
80        // count running processes that aren't attached to a debugger.
81        // It's a race, but will stop us from re-killing a job that only has
82        // blocked-by-debugger processes.
83        zx_info_process_t info = {};
84        process->GetInfo(&info);
85        if (info.started && !info.exited && !info.debugger_attached) {
86            num_running_processes_++;
87        }
88        if (prefix_ != nullptr) {
89            const char* tag = "new";
90            if (info.started) {
91                tag = "run";
92            }
93            if (info.exited) {
94                tag = "dead";
95            }
96            if (info.debugger_attached) {
97                tag = "dbg";
98            }
99            char name[ZX_MAX_NAME_LEN];
100            process->get_name(name);
101            printf("%sproc %5" PRIu64 " %4s '%s'\n",
102                   prefix_, process->get_koid(), tag, name);
103        }
104        num_processes_++;
105        return true;
106    }
107
108    const char* prefix_;
109    size_t num_jobs_;
110    size_t num_processes_;
111    size_t num_running_processes_;
112};
113} // namespace
114
115// Called from a dedicated kernel thread when the system is low on memory.
116static void oom_lowmem(size_t shortfall_bytes) {
117    printf("OOM: oom_lowmem(shortfall_bytes=%zu) called\n", shortfall_bytes);
118    printf("OOM: Process mapped committed bytes:\n");
119    DumpProcessMemoryUsage("OOM:   ", /*min_pages=*/8 * MB / PAGE_SIZE);
120    printf("OOM: Finding a job to kill...\n");
121
122    OomJobEnumerator job_counter(nullptr);
123    OomJobEnumerator job_printer("OOM:        + ");
124
125    bool killed = false;
126    int next = 3; // Used to print a few "up next" jobs.
127    JobDispatcher::ForEachJob([&](JobDispatcher* job) {
128        // TODO(dbort): Consider adding an "immortal" bit on jobs and skip them
129        // here if they (and and all of their ancestors) have it set.
130        bool kill = false;
131        if (!killed) {
132            // We want to kill a job that will actually free memory by dying, so
133            // look for one with running process descendants (i.e., started,
134            // non-exited, not blocked in a debugger).
135            job_counter.reset_counts();
136            job->EnumerateChildren(&job_counter, /*recurse=*/true);
137            kill = job_counter.num_running_processes() > 0;
138        }
139
140        const char* tag;
141        if (kill) {
142            tag = "*KILL*";
143        } else if (!killed) {
144            tag = "(skip)";
145        } else {
146            tag = "(next)";
147        }
148        char name[ZX_MAX_NAME_LEN];
149        job->get_name(name);
150        printf("OOM:   %s job %6" PRIu64 " '%s'\n", tag, job->get_koid(), name);
151        if (kill) {
152            // Print the descendants of the job we're about to kill.
153            job_printer.reset_counts();
154            job->EnumerateChildren(&job_printer, /*recurse=*/true);
155            printf("OOM:        = %zu running procs (%zu total), %zu jobs\n",
156                   job_printer.num_running_processes(),
157                   job_printer.num_processes(), job_printer.num_jobs());
158            // TODO(dbort): Join on dying processes/jobs to make sure we've
159            // freed memory before returning control to the OOM thread?
160            // TODO(ZX-961): 'kill -9' these processes (which will require new
161            // ProcessDispatcher features) so we can reclaim the memory of
162            // processes that are stuck in a debugger or in the crashlogger.
163            job->Kill();
164            killed = true;
165        } else if (killed) {
166            if (--next == 0) {
167                return ZX_ERR_STOP;
168            }
169        }
170        return ZX_OK;
171    });
172}
173
174static void object_glue_init(uint level) TA_NO_THREAD_SAFETY_ANALYSIS {
175    Handle::Init();
176    root_job = JobDispatcher::CreateRootJob();
177    policy_manager = PolicyManager::Create();
178    PortDispatcher::Init();
179    // Be sure to update kernel_cmdline.md if any of these defaults change.
180    oom_init(cmdline_get_bool("kernel.oom.enable", true),
181             ZX_SEC(cmdline_get_uint64("kernel.oom.sleep-sec", 1)),
182             cmdline_get_uint64("kernel.oom.redline-mb", 50) * MB,
183             oom_lowmem);
184}
185
186LK_INIT_HOOK(libobject, object_glue_init, LK_INIT_LEVEL_THREADING);
187