| // 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 |
| |
| #include "object/profile_dispatcher.h" |
| |
| #include <bits.h> |
| #include <lib/counters.h> |
| #include <zircon/errors.h> |
| #include <zircon/rights.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/alloc_checker.h> |
| #include <fbl/ref_ptr.h> |
| #include <ktl/bit.h> |
| #include <object/thread_dispatcher.h> |
| |
| KCOUNTER(dispatcher_profile_create_count, "dispatcher.profile.create") |
| KCOUNTER(dispatcher_profile_destroy_count, "dispatcher.profile.destroy") |
| |
| static zx_status_t parse_cpu_mask(const zx_cpu_set_t& set, cpu_mask_t* result) { |
| // The code below only supports reading up to 1 word in the mask. |
| static_assert(SMP_MAX_CPUS <= sizeof(set.mask[0]) * CHAR_BIT); |
| static_assert(SMP_MAX_CPUS <= sizeof(cpu_mask_t) * CHAR_BIT); |
| static_assert(SMP_MAX_CPUS <= ZX_CPU_SET_MAX_CPUS); |
| |
| // We throw away any bits beyond SMP_MAX_CPUs. |
| *result = static_cast<cpu_mask_t>(set.mask[0] & BIT_MASK(SMP_MAX_CPUS)); |
| return ZX_OK; |
| } |
| |
| zx_status_t validate_profile(const zx_profile_info_t& info) { |
| uint32_t flags = info.flags; |
| |
| // Ensure at least one flag has been set. |
| if (flags == 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // Ensure only zero or one of the mutually exclusive flags is set. |
| const uint32_t kMutuallyExclusiveFlags = |
| ZX_PROFILE_INFO_FLAG_PRIORITY | ZX_PROFILE_INFO_FLAG_DEADLINE; |
| if (ktl::popcount(flags & kMutuallyExclusiveFlags) > 1) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // Ensure priority is valid. |
| if ((flags & ZX_PROFILE_INFO_FLAG_PRIORITY) != 0) { |
| if ((info.priority < LOWEST_PRIORITY) || (info.priority > HIGHEST_PRIORITY)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| flags &= ~ZX_PROFILE_INFO_FLAG_PRIORITY; |
| } |
| |
| if ((flags & ZX_PROFILE_INFO_FLAG_DEADLINE) != 0) { |
| // TODO(eieio): Add additional admission criteria to prevent values that are |
| // too large or too small. These values are mediated by a privileged service |
| // so the risk of abuse is low, but it still might be good to implement some |
| // sort of failsafe check to prevent mistakes. |
| const bool admissible = |
| info.deadline_params.capacity > 0 && |
| info.deadline_params.capacity <= info.deadline_params.relative_deadline && |
| info.deadline_params.relative_deadline <= info.deadline_params.period; |
| if (!admissible) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| flags &= ~ZX_PROFILE_INFO_FLAG_DEADLINE; |
| } |
| |
| // Ensure affinity mask is valid. |
| if ((flags & ZX_PROFILE_INFO_FLAG_CPU_MASK) != 0) { |
| cpu_mask_t unused_mask; |
| zx_status_t result = parse_cpu_mask(info.cpu_affinity_mask, &unused_mask); |
| if (result != ZX_OK) { |
| return result; |
| } |
| flags &= ~ZX_PROFILE_INFO_FLAG_CPU_MASK; |
| } |
| |
| // Ensure no other flags have been set. |
| if (flags != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t ProfileDispatcher::Create(const zx_profile_info_t& info, |
| KernelHandle<ProfileDispatcher>* handle, |
| zx_rights_t* rights) { |
| auto status = validate_profile(info); |
| if (status != ZX_OK) |
| return status; |
| |
| fbl::AllocChecker ac; |
| KernelHandle new_handle(fbl::AdoptRef(new (&ac) ProfileDispatcher(info))); |
| if (!ac.check()) |
| return ZX_ERR_NO_MEMORY; |
| |
| *rights = default_rights(); |
| *handle = ktl::move(new_handle); |
| return ZX_OK; |
| } |
| |
| ProfileDispatcher::ProfileDispatcher(const zx_profile_info_t& info) : info_(info) { |
| kcounter_add(dispatcher_profile_create_count, 1); |
| } |
| |
| ProfileDispatcher::~ProfileDispatcher() { kcounter_add(dispatcher_profile_destroy_count, 1); } |
| |
| zx_status_t ProfileDispatcher::ApplyProfile(fbl::RefPtr<ThreadDispatcher> thread) { |
| // Set priority. |
| if ((info_.flags & ZX_PROFILE_INFO_FLAG_PRIORITY) != 0) { |
| zx_status_t result = thread->SetPriority(info_.priority); |
| if (result != ZX_OK) { |
| return result; |
| } |
| } |
| |
| // Set deadline. |
| if ((info_.flags & ZX_PROFILE_INFO_FLAG_DEADLINE) != 0) { |
| zx_status_t result = thread->SetDeadline(info_.deadline_params); |
| if (result != ZX_OK) { |
| return result; |
| } |
| } |
| |
| // Set affinity. |
| if ((info_.flags & ZX_PROFILE_INFO_FLAG_CPU_MASK) != 0) { |
| cpu_mask_t mask; |
| zx_status_t result = parse_cpu_mask(info_.cpu_affinity_mask, &mask); |
| if (result != ZX_OK) { |
| return result; |
| } |
| return thread->SetSoftAffinity(mask); |
| } |
| |
| return ZX_OK; |
| } |