| // Copyright 2018 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 "manager.h" |
| |
| #include <assert.h> |
| #include <lib/ddk/debug.h> |
| #include <zircon/threads.h> |
| |
| #include <utility> |
| |
| Manager::Manager() = default; |
| |
| Manager::~Manager() { CloseFifoServer(); } |
| |
| bool Manager::IsFifoServerRunning() { |
| { |
| std::scoped_lock lock(mutex_); |
| switch (state_) { |
| case ThreadState::Running: |
| // See if the server is about to terminate. |
| if (!server_->WillTerminate()) |
| return true; |
| // It is, so wait. |
| while (state_ != ThreadState::Joinable) |
| condition_.wait(mutex_); |
| break; |
| case ThreadState::Joinable: |
| break; |
| case ThreadState::None: |
| return false; |
| } |
| } |
| // Joining the thread here is somewhat arbitrary -- as opposed to joining in |StartServer()|. |
| JoinServer(); |
| return false; |
| } |
| |
| zx_status_t Manager::StartServer(ddk::BlockProtocolClient* protocol, zx::fifo* out_fifo) { |
| if (IsFifoServerRunning()) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| ZX_DEBUG_ASSERT(server_ == nullptr); |
| std::unique_ptr<Server> server; |
| fzl::fifo<block_fifo_request_t, block_fifo_response_t> fifo; |
| zx_status_t status = Server::Create(protocol, &fifo, &server); |
| if (status != ZX_OK) { |
| return status; |
| } |
| server_ = std::move(server); |
| SetState(ThreadState::Running); |
| if (thrd_create_with_name(&thread_, &RunServer, this, "block_server") != thrd_success) { |
| FreeServer(); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| // Set a scheduling deadline profile for the block_server thread. |
| // This is required in order to service the blobfs-pager-thread, which is on a deadline profile. |
| // This will no longer be needed once we have the ability to propagate deadlines. Until then, we |
| // need to set deadline profiles for all threads that the blobfs-pager-thread interacts with in |
| // order to service page requests. |
| // |
| // Also note that this will apply to block_server threads spawned to service each block client |
| // (in the typical case, we have two - blobfs and minfs). The capacity of 1ms is chosen so as to |
| // accommodate most cases without throttling the thread. The desired capacity was 50us, but some |
| // tests that use a large ramdisk require a larger capacity. In the average case though on a real |
| // device, the block_server thread runs for less than 50us. 1ms provides us with a generous |
| // leeway, without hurting performance in the typical case - a thread is not penalized for not |
| // using its full capacity. |
| // |
| // TODO(fxbug.dev/40858): Migrate to the role-based API when available, instead of hard |
| // coding parameters. |
| const zx_duration_t capacity = ZX_MSEC(1); |
| const zx_duration_t deadline = ZX_MSEC(2); |
| const zx_duration_t period = deadline; |
| |
| zx_handle_t profile = ZX_HANDLE_INVALID; |
| status = device_get_deadline_profile(nullptr, capacity, deadline, period, |
| "driver_host:pdev:05:00:f:block_server", &profile); |
| if (status != ZX_OK) { |
| zxlogf(WARNING, "block: Failed to get deadline profile: %d\n", status); |
| } else { |
| const zx_handle_t thread_handle = thrd_get_zx_handle(thread_); |
| status = zx_object_set_profile(thread_handle, profile, 0); |
| if (status != ZX_OK) { |
| zxlogf(WARNING, "block: Failed to set deadline profile: %d\n", status); |
| } |
| zx_handle_close(profile); |
| } |
| |
| *out_fifo = zx::fifo(fifo.release()); |
| return ZX_OK; |
| } |
| |
| zx_status_t Manager::CloseFifoServer() { |
| switch (GetState()) { |
| case ThreadState::Running: |
| server_->Shutdown(); |
| JoinServer(); |
| break; |
| case ThreadState::Joinable: |
| zxlogf(ERROR, "block: Joining un-closed FIFO server"); |
| JoinServer(); |
| break; |
| case ThreadState::None: |
| break; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t Manager::AttachVmo(zx::vmo vmo, vmoid_t* out_vmoid) { |
| if (server_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return server_->AttachVmo(std::move(vmo), out_vmoid); |
| } |
| |
| void Manager::JoinServer() { |
| thrd_join(thread_, nullptr); |
| FreeServer(); |
| } |
| |
| void Manager::FreeServer() { |
| SetState(ThreadState::None); |
| server_.reset(); |
| } |
| |
| int Manager::RunServer(void* arg) { |
| Manager* manager = reinterpret_cast<Manager*>(arg); |
| |
| // The completion of "thrd_create" synchronizes-with the beginning of this thread, so |
| // we may assume that "manager->server_" is available for our usage. |
| // |
| // The "manager->server_" pointer shall not be modified by this thread. |
| // |
| // The "manager->server_" pointer will only be nullified after thrd_join, because join |
| // synchronizes-with the completion of this thread. |
| ZX_DEBUG_ASSERT(manager->server_); |
| manager->server_->Serve(); |
| manager->SetState(ThreadState::Joinable); |
| return 0; |
| } |