blob: 33b06fbe3d4edd8f239812175cdd794166e4df3b [file] [log] [blame]
// Copyright 2018 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 <stdlib.h>
#include <string.h>
#include <limits>
#include <memory>
#include <utility>
#include <bitmap/raw-bitmap.h>
#include "allocator.h"
namespace minfs {
// Static.
zx_status_t Allocator::Create(fs::BufferedOperationsBuilder* builder,
std::unique_ptr<AllocatorStorage> storage,
std::unique_ptr<Allocator>* out) FS_TA_NO_THREAD_SAFETY_ANALYSIS {
// Ignore thread-safety analysis on the |allocator| object; no one has an
// external reference to it yet.
zx_status_t status;
std::unique_ptr<Allocator> allocator(new Allocator(std::move(storage)));
blk_t total_blocks = allocator->storage_->PoolTotal();
blk_t pool_blocks = allocator->storage_->PoolBlocks();
if ((status = allocator->map_.Reset(pool_blocks * kMinfsBlockBits)) != ZX_OK) {
return status;
}
if ((status = allocator->map_.Shrink(total_blocks)) != ZX_OK) {
return status;
}
status = allocator->LoadStorage(builder);
if (status != ZX_OK) {
return status;
}
*out = std::move(allocator);
return ZX_OK;
}
size_t Allocator::GetAvailable() const {
AutoLock lock(&lock_);
return GetAvailableLocked();
}
void Allocator::Free(PendingWork* transaction, size_t index) {
AutoLock lock(&lock_);
#ifdef __Fuchsia__
ZX_DEBUG_ASSERT(!swap_out_.GetOne(index));
#endif
ZX_DEBUG_ASSERT(map_.GetOne(index));
map_.ClearOne(index);
storage_->PersistRange(transaction, GetMapDataLocked(), index, 1);
storage_->PersistRelease(transaction, 1);
if (index < first_free_) {
first_free_ = index;
}
}
zx_status_t Allocator::GrowMapLocked(size_t new_size, size_t* old_size) {
ZX_DEBUG_ASSERT(new_size >= map_.size());
*old_size = map_.size();
// Grow before shrinking to ensure the underlying storage is a multiple
// of kMinfsBlockSize.
zx_status_t status;
if ((status = map_.Grow(fbl::round_up(new_size, kMinfsBlockBits))) != ZX_OK) {
fprintf(stderr, "minfs::Allocator failed to Grow (in memory): %d\n", status);
return ZX_ERR_NO_SPACE;
}
map_.Shrink(new_size);
return ZX_OK;
}
zx_status_t Allocator::Reserve(AllocatorReservationKey, PendingWork* transaction, size_t count,
AllocatorReservation* reservation) {
AutoLock lock(&lock_);
if (GetAvailableLocked() < count) {
// If we do not have enough free elements, attempt to extend the partition.
auto grow_map =
([this](size_t pool_size, size_t* old_pool_size) FS_TA_NO_THREAD_SAFETY_ANALYSIS {
return this->GrowMapLocked(pool_size, old_pool_size);
});
zx_status_t status;
// TODO(planders): Allow Extend to take in count.
if ((status = storage_->Extend(transaction, GetMapDataLocked(), grow_map)) != ZX_OK) {
return status;
}
ZX_DEBUG_ASSERT(GetAvailableLocked() >= count);
}
reserved_ += count;
return ZX_OK;
}
bool Allocator::CheckAllocated(size_t index) const {
AutoLock lock(&lock_);
return map_.Get(index, index + 1);
}
size_t Allocator::Allocate(AllocatorReservationKey, PendingWork* transaction) {
AutoLock lock(&lock_);
ZX_DEBUG_ASSERT(reserved_ > 0);
size_t bitoff_start = FindLocked();
ZX_ASSERT(map_.SetOne(bitoff_start) == ZX_OK);
storage_->PersistRange(transaction, GetMapDataLocked(), bitoff_start, 1);
reserved_ -= 1;
storage_->PersistAllocate(transaction, 1);
first_free_ = bitoff_start + 1;
return bitoff_start;
}
void Allocator::Unreserve(AllocatorReservationKey, size_t count) {
AutoLock lock(&lock_);
#ifdef __Fuchsia__
ZX_DEBUG_ASSERT(swap_in_.num_bits() == 0);
ZX_DEBUG_ASSERT(swap_out_.num_bits() == 0);
#endif
ZX_DEBUG_ASSERT(reserved_ >= count);
reserved_ -= count;
}
} // namespace minfs