blob: 5498501cc63646a3a64ecfb5f68771b0c288a614 [file] [log] [blame]
// Copyright 2017 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.
#pragma once
#ifdef __Fuchsia__
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <lib/fzl/owned-vmo-mapper.h>
#include <lib/zx/vmo.h>
#endif
#include <fbl/algorithm.h>
#include <fbl/intrusive_hash_table.h>
#include <fbl/intrusive_single_list.h>
#include <fbl/macros.h>
#include <fbl/ref_ptr.h>
#include <fbl/unique_ptr.h>
#include <fs/queue.h>
#include <fs/vfs.h>
#include <minfs/allocator.h>
#include <minfs/bcache.h>
#include <minfs/block-txn.h>
#include <minfs/format.h>
#include <utility>
namespace minfs {
class VnodeMinfs;
// A wrapper around a WriteTxn, holding references to the underlying Vnodes
// corresponding to the txn, so their Vnodes (and VMOs) are not released
// while being written out to disk.
//
// Additionally, this class allows completions to be signalled when the transaction
// has successfully completed.
class WritebackWork : public WriteTxn,
public fbl::SinglyLinkedListable<fbl::unique_ptr<WritebackWork>> {
public:
WritebackWork(Bcache* bc);
// Return the WritebackWork to the default state that it was in
// after being created.
void Reset();
#ifdef __Fuchsia__
// Actually transacts the enqueued work, and resets the WritebackWork to
// its initial state.
//
// Returns the number of blocks of the writeback buffer that have been
// consumed.
size_t Complete(zx_handle_t vmo, vmoid_t vmoid);
// Adds a closure to the WritebackWork, such that it will be signalled
// when the WritebackWork is flushed to disk.
// If no closure is set, nothing will get signalled.
//
// Only one closure may be set for each WritebackWork unit.
using SyncCallback = fs::Vnode::SyncCallback;
void SetClosure(SyncCallback closure);
#else
// Flushes any pending transactions.
void Complete();
#endif
// Allow "pinning" Vnodes so they aren't destroyed while we're completing
// this writeback operation.
void PinVnode(fbl::RefPtr<VnodeMinfs> vn);
private:
#ifdef __Fuchsia__
SyncCallback closure_; // Optional.
#endif
size_t node_count_;
// May be empty. Currently '4' is the maximum number of vnodes within a
// single unit of writeback work, which occurs during a cross-directory
// rename operation.
fbl::RefPtr<VnodeMinfs> vn_[4];
};
// Tracks the current transaction, including any enqueued writes, and reserved blocks
// and inodes. Also handles allocation of previously reserved blocks/inodes.
class Transaction {
public:
Transaction(fbl::unique_ptr<WritebackWork> work,
fbl::unique_ptr<AllocatorPromise> inode_promise,
fbl::unique_ptr<AllocatorPromise> block_promise)
: work_(std::move(work)),
inode_promise_(std::move(inode_promise)),
block_promise_(std::move(block_promise)) {}
size_t AllocateInode() {
ZX_DEBUG_ASSERT(inode_promise_ != nullptr);
return inode_promise_->Allocate(work_.get());
}
size_t AllocateBlock() {
ZX_DEBUG_ASSERT(block_promise_ != nullptr);
return block_promise_->Allocate(work_.get());
}
void SetWork(fbl::unique_ptr<WritebackWork> work) {
work_ = std::move(work);
}
WritebackWork* GetWork() {
ZX_DEBUG_ASSERT(work_ != nullptr);
return work_.get();
}
fbl::unique_ptr<WritebackWork> RemoveWork() {
ZX_DEBUG_ASSERT(work_ != nullptr);
return std::move(work_);
}
private:
fbl::unique_ptr<WritebackWork> work_;
fbl::unique_ptr<AllocatorPromise> inode_promise_;
fbl::unique_ptr<AllocatorPromise> block_promise_;
};
#ifdef __Fuchsia__
// WritebackBuffer which manages a writeback buffer (and background thread,
// which flushes this buffer out to disk).
class WritebackBuffer {
public:
// Calls constructor, return an error if anything goes wrong.
static zx_status_t Create(Bcache* bc, fzl::OwnedVmoMapper mapper,
fbl::unique_ptr<WritebackBuffer>* out);
~WritebackBuffer();
// Enqueues work into the writeback buffer.
// When this function returns, the transaction blocks from |work|
// have been copied to the writeback buffer, but not necessarily written to
// disk.
//
// To avoid accessing a stale Vnode from disk before the writeback has
// completed, |work| also contains references to any Vnodes which are
// enqueued, preventing them from closing while the writeback is pending.
void Enqueue(fbl::unique_ptr<WritebackWork> work) __TA_EXCLUDES(writeback_lock_);
private:
WritebackBuffer(Bcache* bc, fzl::OwnedVmoMapper mapper);
// Blocks until |blocks| blocks of data are free for the caller.
// Returns |ZX_OK| with the lock still held in this case.
// Returns |ZX_ERR_NO_RESOURCES| if there will never be space for the
// incoming request (i.e., too many blocks requested).
//
// Doesn't actually allocate any space.
zx_status_t EnsureSpaceLocked(size_t blocks) __TA_REQUIRES(writeback_lock_);
// Copies a write transaction to the writeback buffer.
// Also updates the in-memory offsets of the WriteTxn's requests so
// they point to the correct offsets in the in-memory buffer, not their
// original VMOs.
//
// |EnsureSpaceLocked| should be called before invoking this function to
// safely guarantee that space exists within the buffer.
void CopyToBufferLocked(WriteTxn* txn) __TA_REQUIRES(writeback_lock_);
static int WritebackThread(void* arg);
// The waiter struct may be used as a stack-allocated queue for producers.
// It allows them to take turns putting data into the buffer when it is
// mostly full.
struct Waiter : public fbl::SinglyLinkedListable<Waiter*> {};
using WorkQueue = fs::Queue<fbl::unique_ptr<WritebackWork>>;
using ProducerQueue = fs::Queue<Waiter*>;
// Signalled when the writeback buffer can be consumed by the background
// thread.
cnd_t consumer_cvar_;
// Signalled when the writeback buffer has space to add txns.
cnd_t producer_cvar_;
// Work associated with the "writeback" thread, which manages work items,
// and flushes them to disk. This thread acts as a consumer of the
// writeback buffer.
thrd_t writeback_thrd_;
Bcache* bc_;
fbl::Mutex writeback_lock_;
// Ensures that if multiple producers are waiting for space to write their
// txns into the writeback buffer, they can each write in-order.
ProducerQueue producer_queue_ __TA_GUARDED(writeback_lock_){};
// Tracks all the pending Writeback Work operations which exist in the
// writeback buffer and are ready to be sent to disk.
WorkQueue work_queue_ __TA_GUARDED(writeback_lock_){};
bool unmounting_ __TA_GUARDED(writeback_lock_){false};
fzl::OwnedVmoMapper mapper_;
vmoid_t buffer_vmoid_ = VMOID_INVALID;
// The units of all the following are "MinFS blocks".
size_t start_ __TA_GUARDED(writeback_lock_){};
size_t len_ __TA_GUARDED(writeback_lock_){};
const size_t cap_ = 0;
};
#endif
} // namespace minfs