| // 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 <lib/ftl/volume.h> |
| #include <zircon/assert.h> |
| |
| #include "ftl.h" |
| #include "ftl_private.h" |
| |
| namespace ftl { |
| |
| const char* VolumeImpl::Init(std::unique_ptr<NdmDriver> driver) { |
| ZX_DEBUG_ASSERT(!driver_); |
| driver_ = std::move(driver); |
| |
| if (!InitModules()) { |
| return "Module initialization failed"; |
| } |
| |
| return Attach(); |
| } |
| |
| const char* VolumeImpl::ReAttach() { |
| if (!driver_->Detach()) { |
| return "Failed to remove volume"; |
| } |
| name_ = nullptr; |
| |
| return Attach(); |
| } |
| |
| zx_status_t VolumeImpl::Read(uint32_t first_page, int num_pages, void* buffer) { |
| if (read_pages_(buffer, first_page, num_pages, vol_) != 0) { |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Write(uint32_t first_page, int num_pages, const void* buffer) { |
| if (write_pages_(const_cast<void*>(buffer), first_page, num_pages, vol_) != 0) { |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Format() { |
| if (report_(vol_, FS_FORMAT) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::FormatAndLevel() { |
| if (report_(vol_, FS_FORMAT_RESET_WC) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Mount() { |
| if (report_(vol_, FS_MOUNT) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Unmount() { |
| if (report_(vol_, FS_UNMOUNT) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Flush() { |
| if (report_(vol_, FS_SYNC) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::Trim(uint32_t first_page, uint32_t num_pages) { |
| if (report_(vol_, FS_MARK_UNUSED, first_page, num_pages) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::GarbageCollect() { |
| int result = report_(vol_, FS_VCLEAN); |
| if (result < 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| if (result == 0) { |
| return ZX_ERR_STOP; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::GetStats(Stats* stats) { |
| vstat buffer; |
| if (report_(vol_, FS_VSTAT, &buffer) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| stats->ram_used = buffer.ndm.ram_used; |
| stats->wear_count = buffer.ndm.wear_count; |
| stats->garbage_level = buffer.garbage_level; |
| memcpy(stats->wear_histogram, buffer.wear_histogram, sizeof(stats->wear_histogram)); |
| stats->num_blocks = buffer.num_blocks; |
| return ZX_OK; |
| } |
| |
| zx_status_t VolumeImpl::GetCounters(Counters* counters) { |
| FtlCounters ftl_counters = {}; |
| if (report_(vol_, FS_COUNTERS, &ftl_counters) != 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| counters->wear_count = ftl_counters.wear_count; |
| return ZX_OK; |
| } |
| |
| bool VolumeImpl::OnVolumeAdded(const XfsVol* ftl) { |
| ZX_DEBUG_ASSERT(!Created()); |
| vol_ = ftl->vol; |
| name_ = ftl->name; |
| report_ = ftl->report; |
| write_pages_ = ftl->write_pages; |
| read_pages_ = ftl->read_pages; |
| |
| return owner_->OnVolumeAdded(ftl->page_size, ftl->num_pages); |
| } |
| |
| bool VolumeImpl::Created() const { return name_; } |
| |
| const char* VolumeImpl::Attach() { |
| const char* error = driver_->Attach(this); |
| if (error) { |
| return error; |
| } |
| |
| if (!Created()) { |
| return "No volume added"; |
| } |
| |
| if (Mount() != ZX_OK) { |
| return "Mount failed"; |
| } |
| return nullptr; |
| } |
| |
| } // namespace ftl |
| |
| // Callback from the FTL. |
| int XfsAddVol(XfsVol* ftl) { |
| ftl::VolumeImpl* volume = reinterpret_cast<ftl::VolumeImpl*>(ftl->ftl_volume); |
| if (volume && !volume->OnVolumeAdded(ftl)) { |
| return -1; |
| } |
| return 0; |
| } |