blob: 82e68f543ec247fd9590b2462990d0ce7603f6e2 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "zircon/syscalls/profile.h"
#include <fuchsia/scheduler/c/fidl.h>
#include <inttypes.h>
#include <lib/fidl-async/bind.h>
#include <lib/fitx/result.h>
#include <lib/profile/profile.h>
#include <lib/syslog/global.h>
#include <lib/zx/profile.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <algorithm>
#include <iterator>
#include <string>
#include "zircon/system/ulib/profile/config.h"
namespace {
constexpr char kConfigPath[] = "/config/profiles";
using zircon_profile::ProfileMap;
struct Context {
const zx_handle_t root_job;
ProfileMap profiles;
static Context* Get(void* ctx) { return static_cast<Context*>(ctx); }
};
zx_status_t GetProfileSimple(void* ctx, uint32_t priority, const char* name_data, size_t name_size,
fidl_txn_t* txn) {
Context* const context = Context::Get(ctx);
const std::string name{name_data, name_size};
FX_LOGF(INFO, "ProfileProvider", "\"%s\" requested priority %u", name.c_str(), priority);
zx_profile_info_t info = {};
info.flags = ZX_PROFILE_INFO_FLAG_PRIORITY;
info.priority =
std::min<uint32_t>(std::max<uint32_t>(priority, ZX_PRIORITY_LOWEST), ZX_PRIORITY_HIGHEST);
zx::profile profile;
zx_status_t status =
zx_profile_create(context->root_job, 0u, &info, profile.reset_and_get_address());
return fuchsia_scheduler_ProfileProviderGetProfile_reply(
txn, status, status == ZX_OK ? profile.release() : ZX_HANDLE_INVALID);
}
zx_status_t GetDeadlineProfileSimple(void* ctx, uint64_t capacity, uint64_t relative_deadline,
uint64_t period, const char* name_data, size_t name_size,
fidl_txn_t* txn) {
Context* const context = Context::Get(ctx);
const std::string name{name_data, name_size};
const double utilization = static_cast<double>(capacity) / static_cast<double>(relative_deadline);
FX_LOGF(INFO, "ProfileProvider",
"\"%s\" requested capacity %" PRIu64 " deadline %" PRIu64 " period %" PRIu64
" utilization %f",
name.c_str(), capacity, relative_deadline, period, utilization);
zx_profile_info_t info = {};
info.flags = ZX_PROFILE_INFO_FLAG_DEADLINE;
info.deadline_params.capacity = capacity;
info.deadline_params.relative_deadline = relative_deadline;
info.deadline_params.period = period;
zx::profile profile;
zx_status_t status =
zx_profile_create(context->root_job, 0u, &info, profile.reset_and_get_address());
return fuchsia_scheduler_ProfileProviderGetDeadlineProfile_reply(
txn, status, status == ZX_OK ? profile.release() : ZX_HANDLE_INVALID);
}
zx_status_t GetCpuAffinityProfileSimple(void* ctx, const fuchsia_scheduler_CpuSet* cpu_mask,
fidl_txn_t* txn) {
Context* const context = Context::Get(ctx);
zx_profile_info_t info = {};
info.flags = ZX_PROFILE_INFO_FLAG_CPU_MASK;
static_assert(sizeof(info.cpu_affinity_mask.mask) == sizeof(cpu_mask->mask));
static_assert(std::size(info.cpu_affinity_mask.mask) == std::size(decltype(cpu_mask->mask){}));
memcpy(info.cpu_affinity_mask.mask, cpu_mask->mask, sizeof(cpu_mask->mask));
zx::profile profile;
zx_status_t status =
zx_profile_create(context->root_job, 0u, &info, profile.reset_and_get_address());
return fuchsia_scheduler_ProfileProviderGetCpuAffinityProfile_reply(
txn, status, status == ZX_OK ? profile.release() : ZX_HANDLE_INVALID);
}
zx_status_t SetProfileByRoleSimple(void* ctx, zx_handle_t thread, const char* role_data,
size_t role_size, fidl_txn_t* txn) {
Context* const context = Context::Get(ctx);
// Log the requested role and PID:TID of the thread being assigned.
zx_info_handle_basic_t handle_info{};
zx_status_t status = zx_object_get_info(thread, ZX_INFO_HANDLE_BASIC, &handle_info,
sizeof(handle_info), nullptr, nullptr);
if (status != ZX_OK) {
FX_LOGF(WARNING, "ProfileProvider", "Failed to get info for thread handle: %s",
zx_status_get_string(status));
handle_info.koid = ZX_KOID_INVALID;
handle_info.related_koid = ZX_KOID_INVALID;
}
const std::string role{role_data, role_size};
FX_LOGF(INFO, "ProfileProvider", "Role \"%s\" requested by %" PRId64 ":%" PRId64, role.c_str(),
handle_info.related_koid, handle_info.koid);
// Select the profile parameters based on the requested role. The builtin roles cannot be
// overridden.
zx_profile_info_t info = {};
if (role == "fuchsia.default") {
info.flags = ZX_PROFILE_INFO_FLAG_PRIORITY;
info.priority = ZX_PRIORITY_DEFAULT;
} else if (role == "fuchsia.test-role:not-found") {
return fuchsia_scheduler_ProfileProviderSetProfileByRole_reply(txn, ZX_ERR_NOT_FOUND);
} else if (role == "fuchsia.test-role:ok") {
return fuchsia_scheduler_ProfileProviderSetProfileByRole_reply(txn, ZX_OK);
} else if (auto search = context->profiles.find(role); search != context->profiles.cend()) {
info = search->second.info;
} else {
FX_LOGF(WARNING, "ProfileProvider", "Requested role \"%s\" not found!", role.c_str());
return fuchsia_scheduler_ProfileProviderSetProfileByRole_reply(txn, ZX_ERR_NOT_FOUND);
}
zx::profile profile;
status = zx_profile_create(context->root_job, 0u, &info, profile.reset_and_get_address());
if (status != ZX_OK) {
// Failing to create a profile is likely due to a programming error in
// this handler. The most likely cause is invalid profile parameters.
return fuchsia_scheduler_ProfileProviderSetProfileByRole_reply(txn, ZX_ERR_INTERNAL);
}
status = zx_object_set_profile(thread, profile.get(), 0);
return fuchsia_scheduler_ProfileProviderSetProfileByRole_reply(txn, status);
}
fuchsia_scheduler_ProfileProvider_ops ops = {
.GetProfile = GetProfileSimple,
.GetDeadlineProfile = GetDeadlineProfileSimple,
.GetCpuAffinityProfile = GetCpuAffinityProfileSimple,
.SetProfileByRole = SetProfileByRoleSimple,
};
constexpr const char* profile_svc_names[] = {
fuchsia_scheduler_ProfileProvider_Name,
nullptr,
};
zx_status_t init(void** out_ctx) {
auto result = zircon_profile::LoadConfigs(kConfigPath);
if (result.is_error()) {
FX_LOGF(ERROR, "ProfileProvider", "Failed to load configs: %s", result.error_value().c_str());
return ZX_ERR_INTERNAL;
}
auto root_job = static_cast<zx_handle_t>(reinterpret_cast<uintptr_t>(*out_ctx));
*out_ctx = new Context{root_job, std::move(result.value())};
return ZX_OK;
}
zx_status_t connect(void* ctx, async_dispatcher_t* dispatcher, const char* service_name,
zx_handle_t request) {
if (strcmp(service_name, fuchsia_scheduler_ProfileProvider_Name) == 0) {
auto callback_adapter = [](void* ctx, fidl_txn* txn, fidl_incoming_msg* msg,
const void* ops) -> int {
const auto* provider_ops = static_cast<const fuchsia_scheduler_ProfileProvider_ops_t*>(ops);
return fuchsia_scheduler_ProfileProvider_dispatch(ctx, txn, msg, provider_ops);
};
return fidl_bind(dispatcher, request, callback_adapter, ctx, &ops);
}
zx_handle_close(request);
return ZX_ERR_NOT_SUPPORTED;
}
constexpr zx_service_ops_t service_ops = {
.init = init,
.connect = connect,
.release = nullptr,
};
constexpr zx_service_provider_t profile_service_provider = {
.version = SERVICE_PROVIDER_VERSION,
.services = profile_svc_names,
.ops = &service_ops,
};
} // namespace
const zx_service_provider_t* profile_get_service_provider() { return &profile_service_provider; }