| // Copyright 2018 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 |
| |
| #pragma once |
| |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/intrusive_wavl_tree.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <kernel/event.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/mutex.h> |
| #include <vm/page.h> |
| #include <vm/vm.h> |
| #include <zircon/types.h> |
| |
| // A page source has two parts - the PageSource and the PageSourceCallback. The |
| // PageSource is responsible for generic functionality, mostly around managing |
| // the lifecycle of page requests. The PageSourceCallback is responsible for |
| // actually providing the pages. |
| // |
| // The synchronous fulfillment of requests is fairly straightforward, with |
| // direct calls from the vm object to the PageSource to the PageSourceCallback. |
| // |
| // For asynchronous requests, the lifecycle is as follows: |
| // 1) A vm object requests a page with PageSource::GetPage |
| // 2) PageSource starts tracking the request's PageRequest and then |
| // forwards the request to PageSourceCallback::GetPageAsync. |
| // 3) The caller waits for the request with PageRequest::Wait |
| // 4) At some point, whatever is backing the callback provides pages |
| // to the vm object (e.g. with VmObjectPaged::SupplyPages). |
| // 5) The vm object calls PageSource::OnPagesSupplied, which signals |
| // any PageRequests that have been fulfilled. |
| // 6) The caller wakes up and queries the vm object again, by which |
| // point the request page will be present. |
| |
| class PageRequest; |
| |
| // A simple page_request struct for use by the PageSourceCallback implementation. |
| typedef struct page_request { |
| list_node_t node; |
| uint64_t offset; |
| uint64_t length; |
| } page_request_t; |
| |
| // Object which provides pages to a vm_object. |
| class PageSource : public fbl::RefCounted<PageSource> { |
| public: |
| PageSource(); |
| virtual ~PageSource(); |
| |
| // Sends a request to the backing source to provide the requested page. |
| // |
| // Returns ZX_OK if the request was synchronously fulfilled. |
| // Returns ZX_ERR_SHOULD_WAIT if the request will be asynchronously |
| // fulfilled. The caller should wait on |req|. |
| // Returns ZX_ERR_NOT_FOUND if the request cannot be fulfilled. |
| zx_status_t GetPage(uint64_t offset, PageRequest* req, |
| vm_page_t** const page_out, paddr_t* const pa_out); |
| |
| // Updates the request tracking metadata to account for pages [offset, len) having |
| // been supplied to the owning vmo. |
| void OnPagesSupplied(uint64_t offset, uint64_t len); |
| |
| // Detaches the source. All future calls into the page source will fail. All |
| // pending read transactions are aborted. Pending flush transactions will still |
| // be serviced. |
| void Detach(); |
| |
| // Closes the source. All pending transactions will be aborted and all future |
| // calls will fail. |
| void Close(); |
| |
| protected: |
| // Synchronously gets a page from the backing source. |
| virtual bool GetPage(uint64_t offset, |
| vm_page_t** const page_out, paddr_t* const pa_out) = 0; |
| // Informs the backing source of a page request. The callback has ownership |
| // of |request| until the async request is cancelled. |
| virtual void GetPageAsync(page_request_t* request) = 0; |
| // Informs the backing source that a page request has been fulfilled. This |
| // must be called for all requests that are raised. |
| virtual void ClearAsyncRequest(page_request_t* request) = 0; |
| // Swaps the backing memory for a request. Assumes that |old| |
| // and |new_request| have the same type, offset, and length. |
| virtual void SwapRequest(page_request_t* old, page_request_t* new_req) = 0; |
| |
| // OnDetach is called once no more calls to GetPage/GetPageAsync will be made. It |
| // will be called before OnClose and will only be called once. |
| virtual void OnDetach() = 0; |
| // After OnClose is called, no more calls will be made except for ::WaitOnEvent. |
| virtual void OnClose() = 0; |
| |
| virtual zx_status_t WaitOnEvent(event_t* event) = 0; |
| |
| private: |
| fbl::Canary<fbl::magic("VMPS")> canary_; |
| |
| mutable DECLARE_MUTEX(PageSource) page_source_mtx_; |
| bool detached_ TA_GUARDED(page_source_mtx_) = false; |
| bool closed_ TA_GUARDED(page_source_mtx_) = false; |
| |
| // Tree of pending_request structs which have been sent to the callback. |
| fbl::WAVLTree<uint64_t, PageRequest*> outstanding_requests_ TA_GUARDED(page_source_mtx_); |
| |
| // Wakes up the given PageRequest and all overlapping requests. |
| void CompleteRequestLocked(PageRequest* head) TA_REQ(page_source_mtx_); |
| |
| // Removes |request| from any internal tracking. Called by a PageRequest if |
| // it needs to abort itself. |
| void CancelRequest(PageRequest* request) TA_EXCL(page_source_mtx_); |
| |
| friend PageRequest; |
| }; |
| |
| // Object which is used to make delayed page requests to a PageSource |
| class PageRequest : public fbl::WAVLTreeContainable<PageRequest*>, |
| public fbl::DoublyLinkedListable<PageRequest*> { |
| public: |
| explicit PageRequest() {} |
| ~PageRequest(); |
| |
| // Returns ZX_OK on success or ZX_ERR_INTERNAL_INTR_KILLED if the thread was killed. |
| zx_status_t Wait(); |
| |
| uint64_t GetKey() const { return offset_; } |
| |
| DISALLOW_COPY_ASSIGN_AND_MOVE(PageRequest); |
| |
| private: |
| void Init(fbl::RefPtr<PageSource> src, uint64_t offset); |
| |
| // The page source this request is currently associated with. |
| fbl::RefPtr<PageSource> src_; |
| // Event signaled when the request is fulfilled. |
| event_t event_; |
| // PageRequests are active if offset_ is not UINT64_MAX. |
| uint64_t offset_ = UINT64_MAX; |
| |
| // List node for overlapping requests. |
| fbl::DoublyLinkedList<PageRequest*> overlap_; |
| |
| // Request struct for the PageSourceCallback |
| page_request_t read_request_; |
| |
| friend PageSource; |
| }; |