blob: 457260154d3c7744ba5209a8ed327c9285f51ccd [file] [log] [blame]
// 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
#pragma once
#include <stdint.h>
#include <object/dispatcher.h>
#include <object/excp_port.h>
#include <object/policy_manager.h>
#include <object/process_dispatcher.h>
#include <zircon/types.h>
#include <fbl/array.h>
#include <fbl/auto_lock.h>
#include <fbl/canary.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include <fbl/name.h>
#include <fbl/ref_counted.h>
class JobNode;
// Interface for walking a job/process tree.
class JobEnumerator {
public:
// Visits a job. If OnJob returns false, the enumeration stops.
virtual bool OnJob(JobDispatcher* job) { return true; }
// Visits a process. If OnProcess returns false, the enumeration stops.
virtual bool OnProcess(ProcessDispatcher* proc) { return true; }
protected:
virtual ~JobEnumerator() = default;
};
// This class implements the Job object kernel interface. Each Job has a parent
// Job and zero or more child Jobs and zero or more Child processes. This
// creates a DAG (tree) that connects every living task in the system.
// This is critically important because of the bottoms up refcount nature of
// the system in which the scheduler keeps alive the thread and the thread keeeps
// alive the process, so without the Job it would not be possible to enumerate
// or control the tasks in the system for which there are no outstanding handles.
//
// The second important job of the Job is to apply policies that cannot otherwise
// be easily enforced by capabilities, for example kernel object creation.
//
// The third one is to support exception propagation from the leaf tasks to
// the root tasks.
//
// Obviously there is a special case for the 'root' Job which its parent is null
// and in the current implementation will call platform_halt() when its process
// and job count reaches zero. The root job is not exposed to user mode, instead
// the single child Job of the root job is given to the userboot process.
class JobDispatcher final : public SoloDispatcher {
public:
// Traits to belong to the parent's raw job list.
struct ListTraitsRaw {
static fbl::DoublyLinkedListNodeState<JobDispatcher*>& node_state(
JobDispatcher& obj) {
return obj.dll_job_raw_;
}
};
// Traits to belong to the parent's job list.
struct ListTraits {
static fbl::SinglyLinkedListNodeState<fbl::RefPtr<JobDispatcher>>& node_state(
JobDispatcher& obj) {
return obj.dll_job_;
}
};
static fbl::RefPtr<JobDispatcher> CreateRootJob();
static zx_status_t Create(uint32_t flags,
fbl::RefPtr<JobDispatcher> parent,
fbl::RefPtr<Dispatcher>* dispatcher,
zx_rights_t* rights);
~JobDispatcher() final;
// Dispatcher implementation.
zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_JOB; }
bool has_state_tracker() const final { return true; }
zx_koid_t get_related_koid() const final;
fbl::RefPtr<JobDispatcher> parent() { return fbl::RefPtr<JobDispatcher>(parent_); }
// Job methods.
void get_name(char out_name[ZX_MAX_NAME_LEN]) const final;
zx_status_t set_name(const char* name, size_t len) final;
uint32_t max_height() const { return max_height_; }
bool AddChildProcess(const fbl::RefPtr<ProcessDispatcher>& process);
void RemoveChildProcess(ProcessDispatcher* process);
void Kill();
// Set policy. |mode| is is either ZX_JOB_POL_RELATIVE or ZX_JOB_POL_ABSOLUTE and
// in_policy is an array of |count| elements.
zx_status_t SetPolicy(uint32_t mode, const zx_policy_basic* in_policy, size_t policy_count);
pol_cookie_t GetPolicy();
// Calls the provided |zx_status_t func(JobDispatcher*)| on every
// JobDispatcher in the system. Stops if |func| returns an error,
// returning the error value.
template <typename T>
static zx_status_t ForEachJob(T func) {
fbl::AutoLock lock(&all_jobs_lock_);
for (auto &job : all_jobs_list_) {
zx_status_t s = func(&job);
if (s != ZX_OK)
return s;
}
return ZX_OK;
}
// Walks the job/process tree and invokes |je| methods on each node. If
// |recurse| is false, only visits direct children of this job. Returns
// false if any methods of |je| return false; returns true otherwise.
bool EnumerateChildren(JobEnumerator* je, bool recurse);
fbl::RefPtr<ProcessDispatcher> LookupProcessById(zx_koid_t koid);
fbl::RefPtr<JobDispatcher> LookupJobById(zx_koid_t koid);
// exception handling support
zx_status_t SetExceptionPort(fbl::RefPtr<ExceptionPort> eport);
// Returns true if a port had been set.
bool ResetExceptionPort(bool quietly);
fbl::RefPtr<ExceptionPort> exception_port();
private:
enum class State {
READY,
KILLING,
DEAD
};
using LiveRefsArray = fbl::Array<fbl::RefPtr<Dispatcher>>;
JobDispatcher(uint32_t flags, fbl::RefPtr<JobDispatcher> parent, pol_cookie_t policy);
bool AddChildJob(const fbl::RefPtr<JobDispatcher>& job);
void RemoveChildJob(JobDispatcher* job);
void UpdateSignalsIncrementLocked() TA_REQ(get_lock());
void UpdateSignalsDecrementLocked() TA_REQ(get_lock());
template <typename T, typename Fn>
__attribute__((warn_unused_result)) LiveRefsArray ForEachChildInLocked(
T& children, zx_status_t* status, Fn func) TA_REQ(get_lock());
template <typename T>
uint32_t ChildCountLocked() const TA_REQ(get_lock());
fbl::Canary<fbl::magic("JOBD")> canary_;
const fbl::RefPtr<JobDispatcher> parent_;
const uint32_t max_height_;
fbl::DoublyLinkedListNodeState<JobDispatcher*> dll_job_raw_;
fbl::SinglyLinkedListNodeState<fbl::RefPtr<JobDispatcher>> dll_job_;
// The user-friendly job name. For debug purposes only. That
// is, there is no mechanism to mint a handle to a job via this name.
fbl::Name<ZX_MAX_NAME_LEN> name_;
// The common |get_lock()| protects all members below.
State state_ TA_GUARDED(get_lock());
uint32_t process_count_ TA_GUARDED(get_lock());
uint32_t job_count_ TA_GUARDED(get_lock());
using RawJobList =
fbl::DoublyLinkedList<JobDispatcher*, ListTraitsRaw>;
using RawProcessList =
fbl::DoublyLinkedList<ProcessDispatcher*, ProcessDispatcher::JobListTraitsRaw>;
using ProcessList =
fbl::SinglyLinkedList<fbl::RefPtr<ProcessDispatcher>, ProcessDispatcher::JobListTraits>;
using JobList =
fbl::SinglyLinkedList<fbl::RefPtr<JobDispatcher>, ListTraits>;
// Access to the pointers in these lists, especially any promotions to
// RefPtr, must be handled very carefully, because the children can die
// even when |lock_| is held. See ForEachChildInLocked() for more details
// and for a safe way to enumerate them.
RawJobList jobs_ TA_GUARDED(get_lock());
RawProcessList procs_ TA_GUARDED(get_lock());
pol_cookie_t policy_ TA_GUARDED(get_lock());
fbl::RefPtr<ExceptionPort> exception_port_ TA_GUARDED(get_lock());
// Global list of JobDispatchers, ordered by hierarchy and
// creation order. Used to find victims in low-resource
// situations.
fbl::DoublyLinkedListNodeState<JobDispatcher*> dll_all_jobs_;
struct ListTraitsAllJobs {
static fbl::DoublyLinkedListNodeState<JobDispatcher*>& node_state(
JobDispatcher& obj) {
return obj.dll_all_jobs_;
}
};
using AllJobsList =
fbl::DoublyLinkedList<JobDispatcher*, ListTraitsAllJobs>;
static fbl::Mutex all_jobs_lock_;
// All jobs in the system.
static AllJobsList all_jobs_list_ TA_GUARDED(all_jobs_lock_);
};
// Returns the job that is the ancestor of all other tasks.
fbl::RefPtr<JobDispatcher> GetRootJobDispatcher();