| // Copyright 2022 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 "vm/anonymous_page_requester.h" |
| |
| #include <lib/lazy_init/lazy_init.h> |
| |
| #include <vm/pmm.h> |
| |
| namespace { |
| lazy_init::LazyInit<AnonymousPageRequester> anonymous_page_requester; |
| // Need to hold one refcount permanently so that other RefPtrs to the requester can be created and |
| // deleted. |
| lazy_init::LazyInit<fbl::RefPtr<AnonymousPageRequester>> anonymous_page_requester_ref; |
| |
| } // namespace |
| |
| zx_status_t AnonymousPageRequester::FillRequest(PageRequest* request) { |
| if (request->offset_ == UINT64_MAX) { |
| // Pretend a read request at offset 0. The only actor that should ever inspect these values is |
| // us, and we don't, so they can be anything. |
| request->Init(fbl::RefPtr<PageRequestInterface>(this), 0, page_request_type::READ, |
| VmoDebugInfo{0, 0}); |
| } else { |
| DEBUG_ASSERT(request->batch_state_ == PageRequest::BatchState::Accepting); |
| // We ignore batch requests for this requester, so should only be here if this was a batch |
| // request started against a different interface. |
| ASSERT(request->src_.get() != static_cast<PageRequestInterface*>(this)); |
| return request->FinalizeRequest(); |
| } |
| // If this is a batch request complete it immediately. There is no value in finding additional |
| // pages, since the only thing we are going to do is wait on the pmm. |
| if (request->batch_state_ == PageRequest::BatchState::Accepting) { |
| request->batch_state_ = PageRequest::BatchState::Finalized; |
| } |
| return ZX_ERR_SHOULD_WAIT; |
| } |
| |
| zx_status_t AnonymousPageRequester::WaitOnRequest(PageRequest* request) { |
| // Although the pmm_wait_till_free_pages call will unblock based on bounded kernel action, and not |
| // some unbounded user request, the kernel might need to acquire arbitrary locks to achieve this. |
| // Therefore blanket require no locks here to ensure no accidental lock dependencies. This can be |
| // relaxed in the future if necessary. |
| lockdep::AssertNoLocksHeld(); |
| |
| // This should only ever end up waiting momentarily until reclamation catches up. As such if we |
| // end up waiting for a long time then this is probably a sign of a bug in reclamation somewhere, |
| // so we want to make some noise here. |
| constexpr zx_duration_t kReportWaitTime = ZX_SEC(5); |
| |
| zx_status_t status = ZX_OK; |
| uint32_t waited = 0; |
| while ((status = pmm_wait_till_should_retry_single_alloc(Deadline::after(kReportWaitTime))) == |
| ZX_ERR_SHOULD_WAIT) { |
| waited++; |
| printf("WARNING: Waited %" PRIi64 " seconds to retry PMM allocations\n", |
| (kReportWaitTime * waited) / ZX_SEC(1)); |
| } |
| // Whether we succeeded or failed, this request is finished so clear out offset_. |
| request->offset_ = UINT64_MAX; |
| return status; |
| } |
| |
| // static |
| AnonymousPageRequester& AnonymousPageRequester::Get() { return anonymous_page_requester.Get(); } |
| |
| // static |
| void AnonymousPageRequester::Init() { |
| anonymous_page_requester.Initialize(); |
| anonymous_page_requester_ref.Initialize(fbl::AdoptRef(&anonymous_page_requester.Get())); |
| } |