| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| // This file defines: |
| // * Initialization code for kernel/object module |
| // * Singleton instances and global locks |
| // * Helper functions |
| |
| #include <inttypes.h> |
| #include <lib/cmdline.h> |
| #include <lib/crashlog.h> |
| #include <zircon/syscalls/object.h> |
| #include <zircon/types.h> |
| |
| #include <lk/init.h> |
| #include <object/diagnostics.h> |
| #include <object/event_dispatcher.h> |
| #include <object/excp_port.h> |
| #include <object/job_dispatcher.h> |
| #include <object/port_dispatcher.h> |
| #include <platform/halt_helper.h> |
| |
| // All jobs and processes are rooted at the |root_job|. |
| static fbl::RefPtr<JobDispatcher> root_job; |
| |
| fbl::RefPtr<JobDispatcher> GetRootJobDispatcher() { return root_job; } |
| |
| // Kernel-owned event that is used to signal userspace before taking action in OOM situation. |
| static fbl::RefPtr<EventDispatcher> low_mem_event; |
| // Event used for communicating lowmem state between the lowmem callback and the oom thread. |
| static Event mem_state_signal(EVENT_FLAG_AUTOUNSIGNAL); |
| static ktl::atomic<uint8_t> mem_event_idx = 1; |
| |
| fbl::RefPtr<EventDispatcher> GetLowMemEvent() { return low_mem_event; } |
| |
| // Callback used with |pmm_init_reclamation|. |
| // This is a very minimal save idx and signal an event as we are called under the pmm lock and must |
| // avoid causing any additional allocations. |
| static void mem_avail_state_updated_cb(uint8_t idx) { |
| mem_event_idx = idx; |
| mem_state_signal.Signal(); |
| } |
| |
| // Helper called by the oom thread when low memory mode is entered. |
| static void on_lowmem() { |
| #if defined(ENABLE_KERNEL_DEBUGGING_FEATURES) |
| // See ZX-3637 for the product details on when this path vs. the reboot |
| // should be used. |
| |
| if (!root_job->KillJobWithKillOnOOM()) { |
| printf("OOM: no alive job has a kill bit\n"); |
| } |
| |
| // Since killing is asynchronous, sleep for a short period for the system to quiesce. This |
| // prevents us from rapidly killing more jobs than necessary. And if we don't find a |
| // killable job, don't just spin since the next iteration probably won't find a one either. |
| thread_sleep_relative(ZX_MSEC(500)); |
| #else |
| const int kSleepSeconds = 8; |
| printf("OOM: pausing for %ds after low mem signal\n", kSleepSeconds); |
| zx_status_t status = thread_sleep_relative(ZX_SEC(kSleepSeconds)); |
| if (status != ZX_OK) { |
| printf("OOM: sleep failed: %d\n", status); |
| } |
| printf("OOM: rebooting\n"); |
| static char buf[1024]; |
| size_t len = crashlog_to_string(buf, sizeof(buf), CrashlogType::OOM); |
| platform_stow_crashlog(buf, len); |
| platform_graceful_halt_helper(HALT_ACTION_REBOOT); |
| #endif |
| } |
| |
| static int oom_thread(void* unused) { |
| while (true) { |
| // Check if the current index is 0. After observing this we know that if it should change to |
| // zero that the event will get signaled and we would immediately wake back up. |
| if (mem_event_idx != 0) { |
| zx_status_t status = low_mem_event->user_signal_self(ZX_EVENT_SIGNALED, 0); |
| if (status != ZX_OK) { |
| printf("OOM: unsignal low mem failed: %d\n", status); |
| } |
| mem_state_signal.Wait(Deadline::infinite()); |
| } |
| // get local copy of the atomic. It's possible by the time we read this that we've already |
| // exited low mem mode, but that's fine as we're happy to not have to invoke the oom killer. |
| uint8_t idx = mem_event_idx; |
| printf("OOM: memory availability state %u\n", idx); |
| |
| if (idx == 0) { |
| // Tell the user we're in low memory mode and then run our oom handler |
| zx_status_t status = low_mem_event->user_signal_self(0, ZX_EVENT_SIGNALED); |
| if (status != ZX_OK) { |
| printf("OOM: signal low mem failed: %d\n", status); |
| } |
| on_lowmem(); |
| } |
| } |
| } |
| |
| static void object_glue_init(uint level) TA_NO_THREAD_SAFETY_ANALYSIS { |
| Handle::Init(); |
| root_job = JobDispatcher::CreateRootJob(); |
| PortDispatcher::Init(); |
| |
| KernelHandle<EventDispatcher> event; |
| zx_rights_t rights; |
| zx_status_t status = EventDispatcher::Create(0, &event, &rights); |
| if (status != ZX_OK) { |
| panic("low mem event create: %d\n", status); |
| } |
| low_mem_event = event.release(); |
| |
| if (gCmdline.GetBool("kernel.oom.enable", true)) { |
| auto redline = gCmdline.GetUInt64("kernel.oom.redline-mb", 50) * MB; |
| zx_status_t status = pmm_init_reclamation(&redline, 1, MB, mem_avail_state_updated_cb); |
| if (status != ZX_OK) { |
| panic("failed to initialize pmm reclamation: %d\n", status); |
| } |
| |
| auto thread = thread_create("oom-thread", oom_thread, nullptr, HIGH_PRIORITY); |
| DEBUG_ASSERT(thread); |
| thread_detach(thread); |
| thread_resume(thread); |
| } |
| } |
| |
| LK_INIT_HOOK(libobject, object_glue_init, LK_INIT_LEVEL_THREADING) |