1 // Copyright 2017 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #ifdef __Fuchsia__ 8 #include <fbl/auto_lock.h> 9 #include <fbl/mutex.h> 10 #include <lib/fzl/owned-vmo-mapper.h> 11 #include <lib/zx/vmo.h> 12 #endif 13 14 #include <fbl/algorithm.h> 15 #include <fbl/intrusive_hash_table.h> 16 #include <fbl/intrusive_single_list.h> 17 #include <fbl/macros.h> 18 #include <fbl/ref_ptr.h> 19 #include <fbl/unique_ptr.h> 20 21 #include <fs/queue.h> 22 #include <fs/vfs.h> 23 24 #include <minfs/allocator.h> 25 #include <minfs/bcache.h> 26 #include <minfs/block-txn.h> 27 #include <minfs/format.h> 28 29 #include <utility> 30 31 namespace minfs { 32 33 class VnodeMinfs; 34 35 // A wrapper around a WriteTxn, holding references to the underlying Vnodes 36 // corresponding to the txn, so their Vnodes (and VMOs) are not released 37 // while being written out to disk. 38 // 39 // Additionally, this class allows completions to be signalled when the transaction 40 // has successfully completed. 41 class WritebackWork : public WriteTxn, 42 public fbl::SinglyLinkedListable<fbl::unique_ptr<WritebackWork>> { 43 public: 44 WritebackWork(Bcache* bc); 45 46 // Return the WritebackWork to the default state that it was in 47 // after being created. 48 void Reset(); 49 50 #ifdef __Fuchsia__ 51 // Actually transacts the enqueued work, and resets the WritebackWork to 52 // its initial state. 53 // 54 // Returns the number of blocks of the writeback buffer that have been 55 // consumed. 56 size_t Complete(zx_handle_t vmo, vmoid_t vmoid); 57 58 // Adds a closure to the WritebackWork, such that it will be signalled 59 // when the WritebackWork is flushed to disk. 60 // If no closure is set, nothing will get signalled. 61 // 62 // Only one closure may be set for each WritebackWork unit. 63 using SyncCallback = fs::Vnode::SyncCallback; 64 void SetClosure(SyncCallback closure); 65 #else 66 // Flushes any pending transactions. 67 void Complete(); 68 #endif 69 70 // Allow "pinning" Vnodes so they aren't destroyed while we're completing 71 // this writeback operation. 72 void PinVnode(fbl::RefPtr<VnodeMinfs> vn); 73 74 private: 75 #ifdef __Fuchsia__ 76 SyncCallback closure_; // Optional. 77 #endif 78 size_t node_count_; 79 // May be empty. Currently '4' is the maximum number of vnodes within a 80 // single unit of writeback work, which occurs during a cross-directory 81 // rename operation. 82 fbl::RefPtr<VnodeMinfs> vn_[4]; 83 }; 84 85 // Tracks the current transaction, including any enqueued writes, and reserved blocks 86 // and inodes. Also handles allocation of previously reserved blocks/inodes. 87 class Transaction { 88 public: Transaction(fbl::unique_ptr<WritebackWork> work,fbl::unique_ptr<AllocatorPromise> inode_promise,fbl::unique_ptr<AllocatorPromise> block_promise)89 Transaction(fbl::unique_ptr<WritebackWork> work, 90 fbl::unique_ptr<AllocatorPromise> inode_promise, 91 fbl::unique_ptr<AllocatorPromise> block_promise) 92 : work_(std::move(work)), 93 inode_promise_(std::move(inode_promise)), 94 block_promise_(std::move(block_promise)) {} 95 AllocateInode()96 size_t AllocateInode() { 97 ZX_DEBUG_ASSERT(inode_promise_ != nullptr); 98 return inode_promise_->Allocate(work_.get()); 99 } 100 AllocateBlock()101 size_t AllocateBlock() { 102 ZX_DEBUG_ASSERT(block_promise_ != nullptr); 103 return block_promise_->Allocate(work_.get()); 104 } 105 SetWork(fbl::unique_ptr<WritebackWork> work)106 void SetWork(fbl::unique_ptr<WritebackWork> work) { 107 work_ = std::move(work); 108 } 109 GetWork()110 WritebackWork* GetWork() { 111 ZX_DEBUG_ASSERT(work_ != nullptr); 112 return work_.get(); 113 } 114 RemoveWork()115 fbl::unique_ptr<WritebackWork> RemoveWork() { 116 ZX_DEBUG_ASSERT(work_ != nullptr); 117 return std::move(work_); 118 } 119 120 private: 121 fbl::unique_ptr<WritebackWork> work_; 122 fbl::unique_ptr<AllocatorPromise> inode_promise_; 123 fbl::unique_ptr<AllocatorPromise> block_promise_; 124 }; 125 126 #ifdef __Fuchsia__ 127 128 // WritebackBuffer which manages a writeback buffer (and background thread, 129 // which flushes this buffer out to disk). 130 class WritebackBuffer { 131 public: 132 // Calls constructor, return an error if anything goes wrong. 133 static zx_status_t Create(Bcache* bc, fzl::OwnedVmoMapper mapper, 134 fbl::unique_ptr<WritebackBuffer>* out); 135 ~WritebackBuffer(); 136 137 // Enqueues work into the writeback buffer. 138 // When this function returns, the transaction blocks from |work| 139 // have been copied to the writeback buffer, but not necessarily written to 140 // disk. 141 // 142 // To avoid accessing a stale Vnode from disk before the writeback has 143 // completed, |work| also contains references to any Vnodes which are 144 // enqueued, preventing them from closing while the writeback is pending. 145 void Enqueue(fbl::unique_ptr<WritebackWork> work) __TA_EXCLUDES(writeback_lock_); 146 147 private: 148 WritebackBuffer(Bcache* bc, fzl::OwnedVmoMapper mapper); 149 150 // Blocks until |blocks| blocks of data are free for the caller. 151 // Returns |ZX_OK| with the lock still held in this case. 152 // Returns |ZX_ERR_NO_RESOURCES| if there will never be space for the 153 // incoming request (i.e., too many blocks requested). 154 // 155 // Doesn't actually allocate any space. 156 zx_status_t EnsureSpaceLocked(size_t blocks) __TA_REQUIRES(writeback_lock_); 157 158 // Copies a write transaction to the writeback buffer. 159 // Also updates the in-memory offsets of the WriteTxn's requests so 160 // they point to the correct offsets in the in-memory buffer, not their 161 // original VMOs. 162 // 163 // |EnsureSpaceLocked| should be called before invoking this function to 164 // safely guarantee that space exists within the buffer. 165 void CopyToBufferLocked(WriteTxn* txn) __TA_REQUIRES(writeback_lock_); 166 167 static int WritebackThread(void* arg); 168 169 // The waiter struct may be used as a stack-allocated queue for producers. 170 // It allows them to take turns putting data into the buffer when it is 171 // mostly full. 172 struct Waiter : public fbl::SinglyLinkedListable<Waiter*> {}; 173 using WorkQueue = fs::Queue<fbl::unique_ptr<WritebackWork>>; 174 using ProducerQueue = fs::Queue<Waiter*>; 175 176 // Signalled when the writeback buffer can be consumed by the background 177 // thread. 178 cnd_t consumer_cvar_; 179 // Signalled when the writeback buffer has space to add txns. 180 cnd_t producer_cvar_; 181 182 // Work associated with the "writeback" thread, which manages work items, 183 // and flushes them to disk. This thread acts as a consumer of the 184 // writeback buffer. 185 thrd_t writeback_thrd_; 186 Bcache* bc_; 187 fbl::Mutex writeback_lock_; 188 189 // Ensures that if multiple producers are waiting for space to write their 190 // txns into the writeback buffer, they can each write in-order. __TA_GUARDED(writeback_lock_)191 ProducerQueue producer_queue_ __TA_GUARDED(writeback_lock_){}; 192 // Tracks all the pending Writeback Work operations which exist in the 193 // writeback buffer and are ready to be sent to disk. __TA_GUARDED(writeback_lock_)194 WorkQueue work_queue_ __TA_GUARDED(writeback_lock_){}; __TA_GUARDED(writeback_lock_)195 bool unmounting_ __TA_GUARDED(writeback_lock_){false}; 196 fzl::OwnedVmoMapper mapper_; 197 vmoid_t buffer_vmoid_ = VMOID_INVALID; 198 // The units of all the following are "MinFS blocks". __TA_GUARDED(writeback_lock_)199 size_t start_ __TA_GUARDED(writeback_lock_){}; __TA_GUARDED(writeback_lock_)200 size_t len_ __TA_GUARDED(writeback_lock_){}; 201 const size_t cap_ = 0; 202 }; 203 204 #endif 205 206 } // namespace minfs 207