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