blob: 51d955ede93a8c282868fd49d309c38a06460a25 [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.
#include <inttypes.h>
#include <lib/zx/status.h>
#include <memory>
#include "src/storage/minfs/allocator_reservation.h"
#include "zircon/types.h"
#ifdef __Fuchsia__
#include <lib/fzl/owned-vmo-mapper.h>
#include <lib/zx/vmo.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <fbl/vector.h>
#endif
#include <utility>
#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 <fs/vfs.h>
#include "src/storage/minfs/minfs_private.h"
#include "src/storage/minfs/writeback.h"
namespace minfs {
zx_status_t Transaction::Create(TransactionalFs* minfs, size_t reserve_inodes,
size_t reserve_blocks, InodeManager* inode_manager,
std::unique_ptr<Transaction>* out) {
auto transaction = std::make_unique<Transaction>(minfs, nullptr);
if (reserve_inodes) {
// The inode allocator is currently not accessed asynchronously.
// However, acquiring the reservation may cause the superblock to be modified via extension,
// so we still need to acquire the lock first.
zx_status_t status =
inode_manager->Reserve(transaction.get(), reserve_inodes, &transaction->inode_reservation_);
if (status != ZX_OK) {
return status;
}
}
if (reserve_blocks) {
zx_status_t status =
transaction->block_reservation_->ExtendReservation(transaction.get(), reserve_blocks);
if (status != ZX_OK) {
*out = std::move(transaction);
return status;
}
}
*out = std::move(transaction);
return ZX_OK;
}
std::unique_ptr<Transaction> Transaction::FromCachedBlockTransaction(
TransactionalFs* minfs, std::unique_ptr<CachedBlockTransaction> cached_transaction) {
auto transaction = std::make_unique<Transaction>(minfs, std::move(cached_transaction));
return transaction;
}
Transaction::Transaction(TransactionalFs* minfs,
std::unique_ptr<CachedBlockTransaction> cached_transaction)
:
#ifdef __Fuchsia__
lock_(minfs->GetLock()),
#endif
inode_reservation_(&minfs->GetInodeAllocator()),
block_reservation_(cached_transaction == nullptr
? std::make_unique<AllocatorReservation>(&minfs->GetBlockAllocator())
: cached_transaction->TakeBlockReservations()) {
}
Transaction::~Transaction() {
// Unreserve all reserved inodes/blocks while the lock is still held.
inode_reservation_.Cancel();
if (block_reservation_ != nullptr) {
block_reservation_->Cancel();
}
}
#ifdef __Fuchsia__
void Transaction::EnqueueMetadata(storage::Operation operation, storage::BlockBuffer* buffer) {
storage::UnbufferedOperation unbuffered_operation = {.vmo = zx::unowned_vmo(buffer->Vmo()),
.op = operation};
metadata_operations_.Add(std::move(unbuffered_operation));
}
void Transaction::EnqueueData(storage::Operation operation, storage::BlockBuffer* buffer) {
storage::UnbufferedOperation unbuffered_operation = {.vmo = zx::unowned_vmo(buffer->Vmo()),
.op = operation};
data_operations_.Add(std::move(unbuffered_operation));
}
void Transaction::PinVnode(fbl::RefPtr<VnodeMinfs> vnode) {
for (size_t i = 0; i < pinned_vnodes_.size(); i++) {
if (pinned_vnodes_[i].get() == vnode.get()) {
// Already pinned
return;
}
}
pinned_vnodes_.push_back(std::move(vnode));
}
std::vector<fbl::RefPtr<VnodeMinfs>> Transaction::RemovePinnedVnodes() {
return std::move(pinned_vnodes_);
}
#else
void Transaction::EnqueueMetadata(storage::Operation operation, storage::BlockBuffer* buffer) {
builder_.Add(operation, buffer);
}
void Transaction::EnqueueData(storage::Operation operation, storage::BlockBuffer* buffer) {
builder_.Add(operation, buffer);
}
// No-op - don't need to pin vnodes on host.
void Transaction::PinVnode(fbl::RefPtr<VnodeMinfs> vnode) {}
#endif
zx_status_t Transaction::ExtendBlockReservation(size_t reserve_blocks) {
return block_reservation_->ExtendReservation(this, reserve_blocks);
}
} // namespace minfs