| // Copyright 2018 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 |
| #ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_SCHEDULER_H_ |
| #define ZIRCON_KERNEL_INCLUDE_KERNEL_SCHEDULER_H_ |
| |
| #include <lib/fit/function.h> |
| #include <lib/ktrace/string_ref.h> |
| #include <lib/relaxed_atomic.h> |
| #include <platform.h> |
| #include <stdint.h> |
| #include <zircon/syscalls/scheduler.h> |
| #include <zircon/syscalls/system.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/intrusive_pointer_traits.h> |
| #include <fbl/intrusive_wavl_tree.h> |
| #include <fbl/wavl_tree_best_node_observer.h> |
| #include <ffl/fixed.h> |
| #include <kernel/scheduler_state.h> |
| #include <kernel/thread.h> |
| #include <kernel/wait.h> |
| |
| // Forward declaration. |
| struct percpu; |
| |
| // Ensure this define has a value when not defined globally by the build system. |
| #ifndef SCHEDULER_TRACING_LEVEL |
| #define SCHEDULER_TRACING_LEVEL 0 |
| #endif |
| |
| // Ensure this define has a value when not defined globally by the build system. |
| #ifndef SCHEDULER_QUEUE_TRACING_ENABLED |
| #define SCHEDULER_QUEUE_TRACING_ENABLED false |
| #endif |
| |
| // Performance scale of a CPU relative to the highest performance CPU in the |
| // system. |
| using SchedPerformanceScale = ffl::Fixed<int64_t, 31>; |
| |
| // Converts a userspace CPU performance scale to a SchedPerformanceScale value. |
| constexpr SchedPerformanceScale ToSchedPerformanceScale(zx_cpu_performance_scale_t value) { |
| const size_t FractionaBits = sizeof(value.fractional_part) * 8; |
| return ffl::FromRaw<FractionaBits>(uint64_t{value.integral_part} << FractionaBits | |
| value.fractional_part); |
| } |
| |
| // Converts a SchedPerformanceScale value to a userspace CPU performance scale. |
| constexpr zx_cpu_performance_scale_t ToUserPerformanceScale(SchedPerformanceScale value) { |
| using UserScale = ffl::Fixed<uint64_t, 32>; |
| const UserScale user_scale{value}; |
| const uint64_t integral = user_scale.Integral().raw_value() >> UserScale::Format::FractionalBits; |
| const uint64_t fractional = user_scale.Fraction().raw_value(); |
| return {.integral_part = uint32_t(integral), .fractional_part = uint32_t(fractional)}; |
| } |
| |
| // Implements fair and deadline scheduling algorithms and manages the associated |
| // per-CPU state. |
| class Scheduler { |
| public: |
| // Default minimum granularity of time slices. |
| static constexpr SchedDuration kDefaultMinimumGranularity = SchedMs(1); |
| |
| // Default target latency for a scheduling period. |
| static constexpr SchedDuration kDefaultTargetLatency = SchedMs(8); |
| |
| // The threshold for cross-cluster work stealing. Queues with an estimated |
| // runtime below this value are not stolen from if the target and destination |
| // CPUs are in different logical clusters. In a performance-balanced system, |
| // this tunable value approximates the cost of cross-cluster migration due to |
| // cache misses, assuming a task has high cache affinity in its current |
| // cluster. This tunable may be increased to limit cross-cluster spill over. |
| static constexpr SchedDuration kInterClusterThreshold = SchedMs(2); |
| |
| // The threshold for early termination when searching for a CPU to place a |
| // task. Queues with an estimated runtime below this value are sufficiently |
| // unloaded. In a performance-balanced system, this tunable value approximates |
| // the cost of intra-cluster migration due to cache misses, assuming a task |
| // has high cache affinity with the last CPU it ran on. This tunable may be |
| // increased to limit intra-cluster spill over. |
| static constexpr SchedDuration kIntraClusterThreshold = SchedUs(25); |
| |
| // The per-CPU deadline utilization limit to attempt to honor when selecting a |
| // CPU to place a task. It is up to userspace to ensure that the total set of |
| // deadline tasks can honor this limit. Even if userspace ensures the total |
| // set of deadline utilizations is within the total available processor |
| // resources, when total utilization is high enough it may not be possible to |
| // honor this limit due to the bin packing problem. |
| static constexpr SchedUtilization kCpuUtilizationLimit{1}; |
| |
| // The maximum deadline utilization permitted for a single thread. This limit |
| // is applied when scaling the utilization of a deadline task to the relative |
| // performance of a candidate target processor -- placing a task on a |
| // processor that would cause the scaled thread utilization to exceed this |
| // value is avoided if possible. |
| static constexpr SchedUtilization kThreadUtilizationMax{1}; |
| |
| // The adjustment rates of the exponential moving averages tracking the |
| // expected runtimes of each thread. |
| static constexpr ffl::Fixed<int32_t, 2> kExpectedRuntimeAlpha = ffl::FromRatio(1, 4); |
| static constexpr ffl::Fixed<int32_t, 0> kExpectedRuntimeBeta = ffl::FromRatio(1, 1); |
| |
| Scheduler() = default; |
| ~Scheduler() = default; |
| |
| Scheduler(const Scheduler&) = delete; |
| Scheduler& operator=(const Scheduler&) = delete; |
| |
| // Accessors for total weight and number of runnable tasks. |
| SchedWeight GetTotalWeight() const TA_EXCL(thread_lock); |
| size_t GetRunnableTasks() const TA_EXCL(thread_lock); |
| |
| // Dumps the state of the run queue to the specified output target. |
| void Dump(FILE* output_target = stdout) TA_REQ(thread_lock); |
| |
| // Returns the number of the CPU this scheduler instance is associated with. |
| cpu_num_t this_cpu() const { return this_cpu_; } |
| |
| // Returns the index of the logical cluster of the CPU this scheduler instance |
| // is associated with. |
| size_t cluster() const { return cluster_; } |
| |
| // Returns the lock-free value of the predicted queue time for the CPU this |
| // scheduler instance is associated with. |
| SchedDuration predicted_queue_time_ns() const { |
| return exported_total_expected_runtime_ns_.load(); |
| } |
| |
| // Returns the lock-free value of the predicted deadline utilization for the |
| // CPU this scheduler instance is associated with. |
| SchedUtilization predicted_deadline_utilization() const { |
| return exported_total_deadline_utilization_.load(); |
| } |
| |
| // Returns the performance scale of the CPU this scheduler instance is |
| // associated with. |
| SchedPerformanceScale performance_scale() const { return performance_scale_; } |
| |
| // Returns the reciprocal performance scale of the CPU this scheduler instance |
| // is associated with. |
| SchedPerformanceScale performance_scale_reciprocal() const { |
| return performance_scale_reciprocal_; |
| } |
| |
| // Returns a pointer to the currently running thread, if any. |
| Thread* active_thread() const TA_REQ(thread_lock) { return active_thread_; } |
| |
| // Public entry points. |
| |
| static void InitializeThread(Thread* thread, int priority); |
| static void InitializeThread(Thread* thread, const zx_sched_deadline_params_t& params); |
| static void Block() TA_REQ(thread_lock); |
| static void Yield() TA_REQ(thread_lock); |
| static void Preempt() TA_REQ(thread_lock); |
| static void Reschedule() TA_REQ(thread_lock); |
| static void RescheduleInternal() TA_REQ(thread_lock); |
| static void Unblock(Thread* thread) TA_REQ(thread_lock); |
| static void Unblock(Thread::UnblockList thread_list) TA_REQ(thread_lock); |
| static void UnblockIdle(Thread* idle_thread) TA_REQ(thread_lock); |
| |
| static void Migrate(Thread* thread) TA_REQ(thread_lock); |
| static void MigrateUnpinnedThreads() TA_REQ(thread_lock); |
| |
| // TimerTick is called when the preemption timer for a CPU has fired. |
| // |
| // This function is logically private and should only be called by timer.cc. |
| static void TimerTick(SchedTime now); |
| |
| // Set the inherited priority of a thread. |
| static void InheritPriority(Thread* t, int priority) TA_REQ(thread_lock, preempt_disabled_token); |
| |
| // Set the priority of a thread and reset the boost value. This function might reschedule. |
| // pri should be 0 <= to <= MAX_PRIORITY. |
| static void ChangePriority(Thread* t, int pri) TA_REQ(thread_lock, preempt_disabled_token); |
| |
| // Set the deadline of a thread. This function might reschedule. |
| // This requires: 0 < capacity <= relative_deadline <= period. |
| static void ChangeDeadline(Thread* t, const zx_sched_deadline_params_t& params) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| |
| // Return the time at which the current thread should be preempted. |
| // |
| // May only be called with preemption disabled. |
| static zx_time_t GetTargetPreemptionTime() TA_EXCL(thread_lock); |
| |
| // Updates the performance scales of the requested CPUs and returns the |
| // effective values in place, which may be different than the requested values |
| // if they are below the minimum safe values for the respective CPUs. |
| // |
| // Requires |count| <= num CPUs. |
| static void UpdatePerformanceScales(zx_cpu_performance_info_t* info, size_t count) |
| TA_EXCL(thread_lock); |
| |
| // Gets the performance scales of up to count CPUs. Returns the last values |
| // requested by userspace, even if they have not yet taken effect. |
| // |
| // Requires |count| <= num CPUs. |
| static void GetPerformanceScales(zx_cpu_performance_info_t* info, size_t count) |
| TA_EXCL(thread_lock); |
| |
| // Gets the default performance scales of up to count CPUs. Returns the |
| // initial values determined by the system topology, or 1.0 when no topology |
| // is available. |
| // |
| // Requires |count| <= num CPUs. |
| static void GetDefaultPerformanceScales(zx_cpu_performance_info_t* info, size_t count) |
| TA_EXCL(thread_lock); |
| |
| private: |
| // Allow percpu to init our cpu number and performance scale. |
| friend struct percpu; |
| // Load balancer test. |
| friend struct LoadBalancerTestAccess; |
| // Allow tests to modify our state. |
| friend class LoadBalancerTest; |
| |
| // Sets the initial values of the CPU performance scales for this Scheduler |
| // instance. |
| void InitializePerformanceScale(SchedPerformanceScale scale); |
| |
| static inline void RescheduleMask(cpu_mask_t cpus_to_reschedule_mask) TA_REQ(thread_lock); |
| |
| static void ChangeWeight(Thread* thread, int priority, cpu_mask_t* cpus_to_reschedule_mask) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| static void ChangeDeadline(Thread* thread, const SchedDeadlineParams& params, |
| cpu_mask_t* cpus_to_reschedule_mask) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| static void InheritWeight(Thread* thread, int priority, cpu_mask_t* cpus_to_reschedule_mask) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| |
| // Specifies how to associate a thread with a Scheduler instance, update |
| // metadata, and whether/where to place the thread in a run queue. |
| enum class Placement { |
| // Selects a place in the queue based on the current insertion time and |
| // thread weight or deadline. |
| Insertion, |
| |
| // Selects a place in the queue based on the original insertion time and |
| // the updated (inherited or changed) weight or deadline on the same CPU. |
| Adjustment, |
| |
| // Selects a place in the queue based on the original insertion time and |
| // the updated time slice due to being preempted by another thread. |
| Preemption, |
| |
| // Selects a place in the queue based on the insertion time in the original |
| // queue adjusted for the new queue. |
| Migration, |
| |
| // Updates the metadata to account for a stolen thread that was just taken |
| // from a different queue. This is distinct from Migration in that the |
| // thread is not also enqueued, it is run immediately by the new CPU. |
| Association, |
| }; |
| |
| // Returns the current system time as a SchedTime value. |
| static SchedTime CurrentTime() { return SchedTime{current_time()}; } |
| |
| // Returns the Scheduler instance for the current CPU. |
| static Scheduler* Get(); |
| |
| // Returns the Scheduler instance for the given CPU. |
| static Scheduler* Get(cpu_num_t cpu); |
| |
| // Returns a CPU to run the given thread on. |
| static cpu_num_t FindTargetCpu(Thread* thread) TA_REQ(thread_lock); |
| |
| // Updates the system load metrics. |
| void UpdateCounters(SchedDuration queue_time_ns) TA_REQ(thread_lock); |
| |
| // Updates the thread's weight and updates state-dependent bookkeeping. |
| static void UpdateWeightCommon(Thread* thread, int original_priority, SchedWeight weight, |
| cpu_mask_t* cpus_to_reschedule_mask, PropagatePI propagate) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| |
| // Updates the thread's deadline and updates state-dependent bookkeeping. |
| static void UpdateDeadlineCommon(Thread* thread, int original_priority, |
| const SchedDeadlineParams& params, |
| cpu_mask_t* cpus_to_reschedule_mask, PropagatePI propagate) |
| TA_REQ(thread_lock, preempt_disabled_token); |
| |
| using EndTraceCallback = fit::inline_function<void(), sizeof(void*)>; |
| |
| // Common logic for reschedule API. |
| void RescheduleCommon(SchedTime now, EndTraceCallback end_outer_trace = nullptr) |
| TA_REQ(thread_lock); |
| |
| // Evaluates the schedule and returns the thread that should execute, |
| // updating the run queue as necessary. |
| Thread* EvaluateNextThread(SchedTime now, Thread* current_thread, bool timeslice_expired, |
| SchedDuration total_runtime_ns) TA_REQ(thread_lock); |
| |
| // Adds a thread to the run queue tree. The thread must be active on this |
| // CPU. |
| void QueueThread(Thread* thread, Placement placement, SchedTime now = SchedTime{0}, |
| SchedDuration total_runtime_ns = SchedDuration{0}) TA_REQ(thread_lock); |
| |
| // Removes the thread at the head of the first eligible run queue. |
| Thread* DequeueThread(SchedTime now) TA_REQ(thread_lock); |
| |
| // Removes the thread at the head of the fair run queue and returns it. |
| Thread* DequeueFairThread() TA_REQ(thread_lock); |
| |
| // Removes the eligible thread with the earliest deadline in the deadline run |
| // queue and returns it. |
| Thread* DequeueDeadlineThread(SchedTime eligible_time) TA_REQ(thread_lock); |
| |
| // Returns the eligible thread in the run queue with a deadline earlier than |
| // the given deadline, or nullptr if one does not exist. |
| Thread* FindEarlierDeadlineThread(SchedTime eligible_time, SchedTime finish_time) |
| TA_REQ(thread_lock); |
| |
| // Removes the eligible thread with a deadline earlier than the given deadline |
| // and returns it or nullptr if one does not exist. |
| Thread* DequeueEarlierDeadlineThread(SchedTime eligible_time, SchedTime finish_time) |
| TA_REQ(thread_lock); |
| |
| // Attempts to steal work from other busy CPUs. Returns nullptr if no work was |
| // stolen, otherwise returns a pointer to the stolen thread that is now |
| // associated with the local Scheduler instance. |
| Thread* StealWork(SchedTime now) TA_REQ(thread_lock); |
| |
| // Returns the time that the next deadline task will become eligible or infinite |
| // if there are no ready deadline tasks. |
| SchedTime GetNextEligibleTime() TA_REQ(thread_lock); |
| |
| // Calculates the timeslice of the thread based on the current run queue |
| // state. |
| SchedDuration CalculateTimeslice(Thread* thread) TA_REQ(thread_lock); |
| |
| // Returns the completion time clamped to the start of the earliest deadline |
| // thread that will become eligible in that time frame. |
| SchedTime ClampToDeadline(SchedTime completion_time) TA_REQ(thread_lock); |
| |
| // Returns the completion time clamped to the start of the earliest deadline |
| // thread that will become eligible in that time frame and also has an earlier |
| // deadline than the given finish time. |
| SchedTime ClampToEarlierDeadline(SchedTime completion_time, SchedTime finish_time) |
| TA_REQ(thread_lock); |
| |
| // Updates the timeslice of the thread based on the current run queue state. |
| // Returns the absolute deadline for the next time slice, which may be earlier |
| // than the completion of the time slice if other threads could preempt the |
| // given thread before the time slice is exhausted. |
| SchedTime NextThreadTimeslice(Thread* thread, SchedTime now) TA_REQ(thread_lock); |
| |
| // Updates the scheduling period based on the number of active threads. |
| void UpdatePeriod() TA_REQ(thread_lock); |
| |
| // Updates the global virtual timeline. |
| void UpdateTimeline(SchedTime now) TA_REQ(thread_lock); |
| |
| // Makes a thread active on this CPU's scheduler and inserts it into the |
| // run queue tree. |
| void Insert(SchedTime now, Thread* thread, Placement placement = Placement::Insertion) |
| TA_REQ(thread_lock); |
| |
| // Removes the thread from this CPU's scheduler. The thread must not be in |
| // the run queue tree. |
| void Remove(Thread* thread) TA_REQ(thread_lock); |
| |
| // Returns true if there is at least one eligible deadline thread in the |
| // run queue. |
| inline bool IsDeadlineThreadEligible(SchedTime eligible_time) TA_REQ(thread_lock) { |
| return !deadline_run_queue_.is_empty() && |
| deadline_run_queue_.front().scheduler_state().start_time_ <= eligible_time; |
| } |
| |
| // Updates the total expected runtime estimator and exports the atomic shadow |
| // variable for cross-CPU readers. |
| inline void UpdateTotalExpectedRuntime(SchedDuration delta_ns) TA_REQ(thread_lock); |
| |
| // Updates to total deadline utilization estimator and exports the atomic |
| // shadow variable for cross-CPU readers. |
| inline void UpdateTotalDeadlineUtilization(SchedUtilization delta_ns) TA_REQ(thread_lock); |
| |
| // Utilities to scale up or down the given value by the performace scale of the CPU. |
| template <typename T> |
| inline T ScaleUp(T value) const; |
| template <typename T> |
| inline T ScaleDown(T value) const; |
| |
| // Update trace counters which track the total number of runnable threads for a CPU |
| inline void TraceTotalRunnableThreads() const TA_REQ(thread_lock); |
| |
| // Returns a new flow id when flow tracing is enabled, zero otherwise. |
| inline static uint64_t NextFlowId(); |
| |
| // Traits type to adapt the WAVLTree to Thread with node state in the |
| // scheduler_state member. |
| struct TaskTraits { |
| using KeyType = SchedulerState::KeyType; |
| static KeyType GetKey(const Thread& thread) { return thread.scheduler_state().key(); } |
| static bool LessThan(KeyType a, KeyType b) { return a < b; } |
| static bool EqualTo(KeyType a, KeyType b) { return a == b; } |
| static auto& node_state(Thread& thread) { return thread.scheduler_state().run_queue_node_; } |
| }; |
| |
| // Observer that maintains the subtree invariant min_finish_time as nodes are |
| // added to and removed from the run queue. |
| struct SubtreeMinTraits { |
| static SchedTime GetValue(const Thread& node) { return node.scheduler_state().finish_time_; } |
| static SchedTime GetSubtreeBest(const Thread& node) { |
| return node.scheduler_state().min_finish_time_; |
| } |
| static bool Compare(SchedTime a, SchedTime b) { return a < b; } |
| static void AssignBest(Thread& node, SchedTime val) { |
| node.scheduler_state().min_finish_time_ = val; |
| } |
| static void ResetBest(Thread& target) {} |
| }; |
| |
| using SubtreeMinObserver = fbl::WAVLTreeBestNodeObserver<SubtreeMinTraits>; |
| |
| // Alias of the WAVLTree type for the run queue. |
| using RunQueue = fbl::WAVLTree<TaskTraits::KeyType, Thread*, TaskTraits, fbl::DefaultObjectTag, |
| TaskTraits, SubtreeMinObserver>; |
| |
| // Finds the next eligible thread in the given run queue. |
| static Thread* FindEarliestEligibleThread(RunQueue* run_queue, SchedTime eligible_time) |
| TA_REQ(thread_lock); |
| |
| // Finds the next eligible thread in the given run queue that also passes the |
| // given predicate. |
| template <typename Predicate> |
| static Thread* FindEarliestEligibleThread(RunQueue* run_queue, SchedTime eligible_time, |
| Predicate&& predicate) TA_REQ(thread_lock); |
| |
| // Returns the run queue for the given thread's scheduling discipline. |
| inline RunQueue& GetRunQueue(Thread* thread) { |
| return thread->scheduler_state().discipline() == SchedDiscipline::Fair ? fair_run_queue_ |
| : deadline_run_queue_; |
| } |
| |
| // Emits queue event tracers for trace-based scheduler performance analysis. |
| inline void TraceThreadQueueEvent(StringRef* name, Thread* thread) TA_REQ(thread_lock); |
| |
| // The run queue of fair scheduled threads ready to run, but not currently running. |
| TA_GUARDED(thread_lock) |
| RunQueue fair_run_queue_; |
| |
| // The run queue of deadline scheduled threads ready to run, but not currently running. |
| TA_GUARDED(thread_lock) |
| RunQueue deadline_run_queue_; |
| |
| // Pointer to the thread actively running on this CPU. |
| TA_GUARDED(thread_lock) |
| Thread* active_thread_{nullptr}; |
| |
| // Monotonically increasing counter to break ties when queuing tasks with |
| // the same key. This has the effect of placing newly queued tasks behind |
| // already queued tasks with the same key. This is also necessary to |
| // guarantee uniqueness of the key as required by the WAVLTree container. |
| TA_GUARDED(thread_lock) |
| uint64_t generation_count_{0}; |
| |
| // Count of the fair threads running on this CPU, including threads in the run |
| // queue and the currently running thread. Does not include the idle thread. |
| TA_GUARDED(thread_lock) |
| int32_t runnable_fair_task_count_{0}; |
| |
| // Count of the deadline threads running on this CPU, including threads in the |
| // run queue and the currently running thread. Does not include the idle |
| // thread. |
| TA_GUARDED(thread_lock) |
| int32_t runnable_deadline_task_count_{0}; |
| |
| // Total weights of threads running on this CPU, including threads in the |
| // run queue and the currently running thread. Does not include the idle |
| // thread. |
| TA_GUARDED(thread_lock) |
| SchedWeight weight_total_{0}; |
| |
| // The value of |weight_total_| when the current thread was scheduled. |
| // Provides a reference for determining whether the total weights changed |
| // since the last reschedule. |
| TA_GUARDED(thread_lock) |
| SchedWeight scheduled_weight_total_{0}; |
| |
| // The global virtual time of this run queue. |
| TA_GUARDED(thread_lock) |
| SchedTime virtual_time_{0}; |
| |
| // The system time since the last update to the global virtual time. |
| TA_GUARDED(thread_lock) |
| SchedTime last_update_time_ns_{0}; |
| |
| // The system time that the current time slice started. |
| TA_GUARDED(thread_lock) |
| SchedTime start_of_current_time_slice_ns_{0}; |
| |
| // The system time that the current thread should be preempted. Initialized to |
| // ZX_TIME_INFINITE to pass the assertion now < target_preemption_time_ns_ (or |
| // else the current time slice is expired) on the first entry into the |
| // scheduler. |
| TA_GUARDED(thread_lock) |
| SchedTime target_preemption_time_ns_{ZX_TIME_INFINITE}; |
| |
| // The sum of the expected runtimes of all active threads on this CPU. This |
| // value is an estimate of the average queuimg time for this CPU, given the |
| // current set of active threads. |
| TA_GUARDED(thread_lock) |
| SchedDuration total_expected_runtime_ns_{0}; |
| |
| // The sum of the worst case utilization of all active deadline threads on |
| // this CPU. |
| TA_GUARDED(thread_lock) |
| SchedUtilization total_deadline_utilization_{0}; |
| |
| // Scheduling period in which every runnable task executes once in units of |
| // minimum granularity. |
| TA_GUARDED(thread_lock) |
| SchedDuration scheduling_period_grans_{kDefaultTargetLatency / kDefaultMinimumGranularity}; |
| |
| // The smallest timeslice a thread is allocated in a single round. |
| TA_GUARDED(thread_lock) |
| SchedDuration minimum_granularity_ns_{kDefaultMinimumGranularity}; |
| |
| // The target scheduling period. The scheduling period is set to this value |
| // when the number of tasks is low enough for the sum of all timeslices to |
| // fit within this duration. This has the effect of increasing the size of |
| // the timeslices under nominal load to reduce scheduling overhead. |
| TA_GUARDED(thread_lock) |
| SchedDuration target_latency_grans_{kDefaultTargetLatency / kDefaultMinimumGranularity}; |
| |
| // Performance scale of this CPU relative to the highest performance CPU. This |
| // value is initially determined from the system topology, when available, and |
| // by userspace performance/thermal management at runtime. |
| SchedPerformanceScale performance_scale_{1}; |
| SchedPerformanceScale performance_scale_reciprocal_{1}; |
| |
| // Performance scale requested by userspace. The operational performance scale |
| // is updated to this value (possibly adjusted for the minimum allowed value) |
| // on the next reschedule, after the current thread's accounting is updated. |
| SchedPerformanceScale pending_user_performance_scale_{1}; |
| |
| // Default performance scale, determined from the system topology, when |
| // available. |
| SchedPerformanceScale default_performance_scale_{1}; |
| |
| // The CPU this scheduler instance is associated with. |
| // NOTE: This member is not initialized to prevent clobbering the value set |
| // by sched_early_init(), which is called before the global ctors that |
| // initialize the rest of the members of this class. |
| // TODO(eieio): Figure out a better long-term solution to determine which |
| // CPU is associated with each instance of this class. This is needed by |
| // non-static methods that are called from arbitrary CPUs, namely Insert(). |
| cpu_num_t this_cpu_; |
| |
| // The index of the logical cluster this CPU belongs to. CPUs with the same |
| // logical cluster index have the best chance of good cache affinity with |
| // respect to load distribution decisions. |
| size_t cluster_{0}; |
| |
| // Values exported for lock-free access across CPUs. These are mirrors of the |
| // members of the same name without the exported_ prefix. This avoids |
| // unnecessary atomic loads when updating the values using arithmetic |
| // operations on the local CPU. These values are atomically readonly to other |
| // CPUs. |
| // TODO(eieio): Look at cache line alignment for these members to optimize |
| // cache performance. |
| RelaxedAtomic<SchedDuration> exported_total_expected_runtime_ns_{SchedNs(0)}; |
| RelaxedAtomic<SchedUtilization> exported_total_deadline_utilization_{SchedUtilization{0}}; |
| |
| // Flow id counter for sched_latency flow events. |
| inline static RelaxedAtomic<uint64_t> next_flow_id_{1}; |
| }; |
| |
| #endif // ZIRCON_KERNEL_INCLUDE_KERNEL_SCHEDULER_H_ |