blob: cdb7ce224d1dbcdb084809db027d96148de302d9 [file] [log] [blame]
// Copyright 2020 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 <fuchsia/scheduler/cpp/fidl.h>
#include <lib/fdio/spawn.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/eventpair.h>
#include <lib/zx/handle.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/time.h>
#include <pthread.h>
#include <zircon/assert.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/threads.h>
#include <thread>
namespace {
struct State {
pthread_barrier_t start_barrier;
pthread_barrier_t stop_barrier;
const size_t number_of_switches;
State(uint32_t thread_count, size_t number_of_switches) : number_of_switches(number_of_switches) {
FX_CHECK(0 == pthread_barrier_init(&start_barrier, nullptr,
thread_count + 1)); // additional thread for main
FX_CHECK(0 == pthread_barrier_init(&stop_barrier, nullptr,
thread_count + 1)); // additional thread for main
}
};
class ProfileService {
public:
bool Init() {
std::shared_ptr<sys::ServiceDirectory> svc = sys::ServiceDirectory::CreateFromNamespace();
if (svc == nullptr) {
FX_LOGS(ERROR) << "No service directory";
return false;
}
zx_status_t status = svc->Connect(provider_.NewRequest());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Couldn't connect to service: " << zx_status_get_string(status);
return false;
}
return true;
}
zx_status_t ApplyAffinityProfile(size_t cpu_num, std::thread* thread_a, std::thread* thread_b) {
FX_CHECK(cpu_num < 31);
uint32_t mask = 1 << cpu_num;
zx::profile profile;
zx_status_t server_status;
zx_status_t status = provider_->GetCpuAffinityProfile({mask}, &server_status, &profile);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to contact profile service: " << zx_status_get_string(status);
return status;
}
if (server_status != ZX_OK) {
FX_LOGS(ERROR) << "Profile service failure: " << zx_status_get_string(server_status);
return server_status;
}
status = HandleFromThread(thread_a)->set_profile(profile, /*options=*/0);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set profile: " << zx_status_get_string(status);
return status;
}
status = HandleFromThread(thread_b)->set_profile(profile, /*options=*/0);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set profile: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
}
private:
zx::unowned<zx::thread> HandleFromThread(std::thread* thread) {
zx_handle_t handle = native_thread_get_zx_handle(thread->native_handle());
return zx::unowned<zx::thread>(handle);
}
fuchsia::scheduler::ProfileProviderSyncPtr provider_;
};
void ThreadPair(size_t cpu_num, State* state, ProfileService* profiles) {
auto thread_action = [state](zx::eventpair event, bool first) {
auto wait_val = pthread_barrier_wait(&state->start_barrier);
FX_CHECK(wait_val == PTHREAD_BARRIER_SERIAL_THREAD || wait_val == 0);
size_t to_receive = state->number_of_switches;
if (first) {
FX_CHECK(ZX_OK == event.signal_peer(0, ZX_USER_SIGNAL_0));
}
while (to_receive > 0) {
FX_CHECK(ZX_OK == event.wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr));
to_receive--;
FX_CHECK(ZX_OK == event.signal(ZX_USER_SIGNAL_0, 0));
FX_CHECK(ZX_OK == event.signal_peer(0, ZX_USER_SIGNAL_0));
}
wait_val = pthread_barrier_wait(&state->stop_barrier);
FX_CHECK(wait_val == PTHREAD_BARRIER_SERIAL_THREAD || wait_val == 0);
};
zx::eventpair e1, e2;
FX_CHECK(ZX_OK == zx::eventpair::create(0, &e1, &e2));
std::thread thread_a(thread_action, std::move(e1), true);
std::thread thread_b(thread_action, std::move(e2), false);
FX_CHECK(ZX_OK == profiles->ApplyAffinityProfile(cpu_num, &thread_a, &thread_b));
thread_a.detach();
thread_b.detach();
}
} // namespace
const char kMessage[] = "ping";
const size_t kMessageSize = 4;
int main(int argc, char** argv) {
zx::channel incoming(zx_take_startup_handle(PA_USER0));
if (!incoming) {
printf("ERROR: Invalid incoming handle\n");
return 1;
}
uint32_t cpus = zx_system_get_num_cpus();
uint64_t number_of_switches = 0;
// Signal that this process is ready to accept instructions.
FX_CHECK(ZX_OK == incoming.write(0, kMessage, kMessageSize, nullptr, 0));
ProfileService profiles;
FX_CHECK(profiles.Init());
while (true) {
// Read the number of context switches to perform.
FX_CHECK(ZX_OK == incoming.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
zx::time::infinite(), nullptr));
if (ZX_OK != incoming.read(0, &number_of_switches, nullptr, sizeof(number_of_switches), 0,
nullptr, nullptr)) {
break;
}
State state(cpus * 2, number_of_switches);
// Initialize all thread pairs with the state.
for (size_t i = 0; i < cpus; i++) {
ThreadPair(i, &state, &profiles);
}
// Wait until all threads are ready to start, then signal to the test that we have started.
auto wait_val = pthread_barrier_wait(&state.start_barrier);
FX_CHECK(wait_val == PTHREAD_BARRIER_SERIAL_THREAD || wait_val == 0);
FX_CHECK(ZX_OK == incoming.write(0, kMessage, kMessageSize, nullptr, 0));
// Wait until all threads have completed, then signal to the test that we have finished.
wait_val = pthread_barrier_wait(&state.stop_barrier);
FX_CHECK(wait_val == PTHREAD_BARRIER_SERIAL_THREAD || wait_val == 0);
FX_CHECK(ZX_OK == incoming.write(0, kMessage, kMessageSize, nullptr, 0));
}
return 0;
}