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