| // Copyright 2021 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 "src/storage/lib/vfs/cpp/pager_thread_pool.h" |
| |
| #include <zircon/assert.h> |
| #include <zircon/syscalls-next.h> |
| #include <zircon/syscalls/port.h> |
| #include <zircon/syscalls/types.h> |
| #include <zircon/threads.h> |
| |
| #include <fbl/auto_lock.h> |
| |
| #include "src/storage/lib/vfs/cpp/paged_vfs.h" |
| |
| namespace fs { |
| |
| PagerThreadPool::PagerThreadPool(PagedVfs& vfs, int num_threads) |
| : vfs_(vfs), num_threads_(num_threads) {} |
| |
| PagerThreadPool::~PagerThreadPool() { |
| // The loop will treat a USER packet as the quit event so we can synchronize with it. |
| zx_port_packet_t quit_packet{}; |
| quit_packet.type = ZX_PKT_TYPE_USER; |
| |
| // Each thread will quit as soon as it reads one quit packet, so post that many packets. |
| for (int i = 0; i < num_threads_; i++) |
| port_.queue(&quit_packet); |
| |
| for (auto& thread : threads_) { |
| thread.join(); |
| } |
| threads_.clear(); |
| } |
| |
| zx::result<> PagerThreadPool::Init() { |
| if (zx_status_t status = zx::port::create(0, &port_); status != ZX_OK) |
| return zx::error(status); |
| |
| // Start all the threads. |
| for (int i = 0; i < num_threads_; i++) |
| threads_.push_back(std::thread([self = this]() { self->ThreadProc(); })); |
| |
| return zx::ok(); |
| } |
| |
| std::vector<zx::unowned_thread> PagerThreadPool::GetPagerThreads() const { |
| std::vector<zx::unowned_thread> result; |
| result.reserve(num_threads_); |
| |
| for (const auto& thread : threads_) { |
| result.emplace_back( |
| native_thread_get_zx_handle(const_cast<std::thread&>(thread).native_handle())); |
| } |
| return result; |
| } |
| |
| void PagerThreadPool::ThreadProc() { |
| while (true) { |
| zx_port_packet_t packet; |
| if (zx_status_t status = port_.wait(zx::time::infinite(), &packet); status != ZX_OK) { |
| // TODO(brettw) it would be nice to log from here but some drivers that depend on this |
| // library aren't allowed to log. |
| // FX_LOGST(ERROR, "pager") << "Pager port wait failed, stopping. The system will probably go |
| // down."; |
| return; |
| } |
| |
| if (packet.type == ZX_PKT_TYPE_USER) |
| break; // USER packets tell us to quit. |
| |
| // Should only be getting pager requests on this port. |
| ZX_ASSERT(packet.type == ZX_PKT_TYPE_PAGE_REQUEST); |
| |
| switch (packet.page_request.command) { |
| case ZX_PAGER_VMO_READ: |
| vfs_.PagerVmoRead(packet.key, packet.page_request.offset, packet.page_request.length); |
| break; |
| case ZX_PAGER_VMO_DIRTY: |
| vfs_.PagerVmoDirty(packet.key, packet.page_request.offset, packet.page_request.length); |
| break; |
| case ZX_PAGER_VMO_COMPLETE: |
| // We don't currently do anything on "complete" requests. This is issued by the kernel in |
| // response to a "detach vmo" call. But with multiple pager threads in the thread pool, |
| // we have no guarantee that we'll process the complete message after the read requests |
| // that were already pending, so the "complete" message doesn't tell us anything. |
| // |
| // We rely on the fact that by the time the kernel returns from the "detach" request that |
| // no more valid page requests are issued, and that any in-flight ones (which might be |
| // pending in our port queue or are being handled in the filesystem) are internally |
| // cancelled by the kernel. As such, as long as we can tolerate pager requests for detached |
| // vmos (which we do by using unique identifiers into a map), there is no need to handle |
| // the COMPLETE message. |
| break; |
| default: |
| // Unexpected request. |
| ZX_ASSERT(false); |
| break; |
| } |
| } |
| } |
| |
| } // namespace fs |