[f2fs] Enable mount options and internal features
The configurable options are as below.
- discard
- # of active logs
- cold data separation based on file extension
TODO: noheap can be turned off after gc impl.
Test: fx test f2fs-unittest fs-tests large-fs-tests
Change-Id: I01ad30709c1fd3da24113f7630f5d1fa41a65ca9
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/543303
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 3a58c58..f2d0cf4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -29,6 +29,7 @@
"file.cc",
"gc.cc",
"mkfs.cc",
+ "mount.cc",
"namei.cc",
"node.cc",
"recovery.cc",
@@ -49,6 +50,7 @@
"f2fs_types.h",
"file.h",
"mkfs.h",
+ "mount.h",
"node.h",
"node.h",
"segment.h",
diff --git a/bcache.cc b/bcache.cc
index 853a5b0..348d9d8 100644
--- a/bcache.cc
+++ b/bcache.cc
@@ -26,6 +26,32 @@
namespace f2fs {
+zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool* out_readonly,
+ std::unique_ptr<f2fs::Bcache>* out) {
+ fuchsia_hardware_block_BlockInfo info;
+ zx_status_t status = device->BlockGetInfo(&info);
+ if (status != ZX_OK) {
+ FX_LOGS(ERROR) << "Coult not access device info: " << status;
+ return status;
+ }
+
+ uint64_t device_size = info.block_size * info.block_count;
+
+ if (device_size == 0) {
+ FX_LOGS(ERROR) << "Invalid device size";
+ return status;
+ }
+ uint64_t block_count = device_size / kBlockSize;
+
+ // The maximum volume size of f2fs is 16TB
+ if (block_count >= std::numeric_limits<uint32_t>::max()) {
+ FX_LOGS(ERROR) << "Block count overflow";
+ return ZX_ERR_OUT_OF_RANGE;
+ }
+
+ return f2fs::Bcache::Create(std::move(device), block_count, kBlockSize, out);
+}
+
std::unique_ptr<block_client::BlockDevice> Bcache::Destroy(std::unique_ptr<Bcache> bcache) {
{
// Destroy the VmoBuffer before extracting the underlying device, as it needs
@@ -61,6 +87,22 @@
return RunOperation(operation, &buffer_);
}
+zx_status_t Bcache::Trim(block_t start, block_t num) {
+ if (!(info_.flags & fuchsia_hardware_block_FLAG_TRIM_SUPPORT)) {
+ return ZX_ERR_NOT_SUPPORTED;
+ }
+
+ block_fifo_request_t request = {
+ .opcode = BLOCKIO_TRIM,
+ .vmoid = BLOCK_VMOID_INVALID,
+ .length = static_cast<uint32_t>(BlockNumberToDevice(num)),
+ .vmo_offset = 0,
+ .dev_offset = BlockNumberToDevice(start),
+ };
+
+ return device()->FifoTransaction(&request, 1);
+}
+
zx_status_t Bcache::BlockAttachVmo(const zx::vmo& vmo, storage::Vmoid* out) {
return device()->BlockAttachVmo(vmo, out);
}
@@ -69,12 +111,6 @@
return device()->BlockDetachVmo(std::move(vmoid));
}
-zx_status_t Bcache::Sync() {
- block_fifo_request_t request = {};
- request.opcode = BLOCKIO_FLUSH;
- return device_->FifoTransaction(&request, 1);
-}
-
zx_status_t FdToBlockDevice(fbl::unique_fd& fd, std::unique_ptr<block_client::BlockDevice>* out) {
zx::channel channel, server;
zx_status_t status = zx::channel::create(0, &channel, &server);
@@ -99,44 +135,17 @@
return ZX_OK;
}
-zx_status_t Bcache::Create(std::unique_ptr<block_client::BlockDevice> device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out) {
- zx_status_t status = Create(device.get(), max_blocks, out);
+zx_status_t Bcache::Create(std::unique_ptr<block_client::BlockDevice> device, uint64_t max_blocks,
+ uint64_t block_size, std::unique_ptr<Bcache>* out) {
+ zx_status_t status = Create(device.get(), max_blocks, block_size, out);
if (status == ZX_OK) {
(*out)->owned_device_ = std::move(device);
}
return status;
}
-zx_status_t Bcache::Create(block_client::BlockDevice* device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out) {
- std::unique_ptr<Bcache> bcache(new Bcache(device, max_blocks, kBlockSize));
-
- zx_status_t status = bcache->buffer_.Initialize(bcache.get(), 1, kBlockSize, "scratch-block");
- if (status != ZX_OK) {
- return status;
- }
-
- status = bcache->VerifyDeviceInfo();
- if (status != ZX_OK) {
- return status;
- }
-
- *out = std::move(bcache);
- return ZX_OK;
-}
-
-zx_status_t Bcache::Create(std::unique_ptr<block_client::BlockDevice> device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out, uint32_t block_size) {
- zx_status_t status = Create(device.get(), max_blocks, out, block_size);
- if (status == ZX_OK) {
- (*out)->owned_device_ = std::move(device);
- }
- return status;
-}
-
-zx_status_t Bcache::Create(block_client::BlockDevice* device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out, uint32_t block_size) {
+zx_status_t Bcache::Create(block_client::BlockDevice* device, uint64_t max_blocks,
+ uint64_t block_size, std::unique_ptr<Bcache>* out) {
std::unique_ptr<Bcache> bcache(new Bcache(device, max_blocks, block_size));
zx_status_t status = bcache->buffer_.Initialize(bcache.get(), 1, block_size, "scratch-block");
@@ -153,9 +162,9 @@
return ZX_OK;
}
-uint32_t Bcache::DeviceBlockSize() const { return info_.block_size; }
+uint64_t Bcache::DeviceBlockSize() const { return info_.block_size; }
-Bcache::Bcache(block_client::BlockDevice* device, uint32_t max_blocks, uint32_t block_size)
+Bcache::Bcache(block_client::BlockDevice* device, uint64_t max_blocks, uint64_t block_size)
: max_blocks_(max_blocks), block_size_(block_size), device_(device) {}
zx_status_t Bcache::VerifyDeviceInfo() {
diff --git a/bcache.h b/bcache.h
index 2fd3193..b5d3b7f 100644
--- a/bcache.h
+++ b/bcache.h
@@ -29,11 +29,6 @@
namespace f2fs {
-#ifdef __Fuchsia__
-
-// A helper function for converting "fd" to "BlockDevice".
-zx_status_t FdToBlockDevice(fbl::unique_fd& fd, std::unique_ptr<block_client::BlockDevice>* out);
-
class Bcache : public fs::DeviceTransactionHandler, public storage::VmoidRegistry {
public:
// Not copyable or movable
@@ -61,14 +56,18 @@
block_client::BlockDevice* GetDevice() final { return device_; }
- uint32_t DeviceBlockSize() const;
+ uint64_t DeviceBlockSize() const;
- // Raw block read functions.
+ // Raw block read/write/trim functions
+ // |bno| is a target LBA in a 4KB block size
+ // TODO: create io functions with vmo and request for removing memory copy
// These do not track blocks (or attempt to access the block cache)
// NOTE: Not marked as final, since these are overridden methods on host,
// but not on __Fuchsia__.
zx_status_t Readblk(block_t bno, void* data);
zx_status_t Writeblk(block_t bno, const void* data);
+ zx_status_t Trim(block_t start, block_t num);
+ zx_status_t Flush() override { return DeviceTransactionHandler::Flush(); }
// TODO(rvargas): Move this to BlockDevice.
// VmoidRegistry interface:
@@ -80,28 +79,18 @@
// This factory allows building this object from a BlockDevice. Bcache can take ownership of the
// device (the first Create method), or not (the second Create method).
- static zx_status_t Create(std::unique_ptr<block_client::BlockDevice> device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out);
+ static zx_status_t Create(std::unique_ptr<block_client::BlockDevice> device, uint64_t max_blocks,
+ uint64_t block_size, std::unique_ptr<Bcache>* out);
- static zx_status_t Create(block_client::BlockDevice* device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out);
+ static zx_status_t Create(block_client::BlockDevice* device, uint64_t max_blocks,
+ uint64_t block_size, std::unique_ptr<Bcache>* out);
- static zx_status_t Create(std::unique_ptr<block_client::BlockDevice> device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out, uint32_t block_size);
-
- static zx_status_t Create(block_client::BlockDevice* device, uint32_t max_blocks,
- std::unique_ptr<Bcache>* out, uint32_t block_size);
-
- // Returns the maximum number of available blocks,
- // assuming the filesystem is non-resizable.
- uint32_t Maxblk() const { return max_blocks_; }
- uint32_t BlockSize() const { return block_size_; }
+ uint64_t Maxblk() const { return max_blocks_; }
+ uint64_t BlockSize() const { return block_size_; }
block_client::BlockDevice* device() { return device_; }
const block_client::BlockDevice* device() const { return device_; }
- zx_status_t Sync();
-
// Blocks all I/O operations to the underlying device (that go via the RunRequests method). This
// does *not* block operations that go directly to the device.
void Pause();
@@ -112,13 +101,13 @@
private:
friend class BlockNode;
- Bcache(block_client::BlockDevice* device, uint32_t max_blocks, uint32_t block_size);
+ Bcache(block_client::BlockDevice* device, uint64_t max_blocks, uint64_t block_size);
// Used during initialization of this object.
zx_status_t VerifyDeviceInfo();
- uint32_t max_blocks_;
- const uint32_t block_size_;
+ const uint64_t max_blocks_;
+ const uint64_t block_size_;
fuchsia_hardware_block_BlockInfo info_ = {};
std::unique_ptr<block_client::BlockDevice> owned_device_; // The device, if owned.
block_client::BlockDevice* device_; // Pointer to the device, irrespective of ownership.
@@ -127,63 +116,10 @@
std::shared_mutex mutex_;
};
-#else // __Fuchsia__
-
-class Bcache : public fs::TransactionHandler {
- public:
- // Not copyable or movable
- Bcache(const Bcache&) = delete;
- Bcache& operator=(const Bcache&) = delete;
- Bcache(Bcache&&) = delete;
- Bcache& operator=(Bcache&&) = delete;
-
- ~Bcache() {}
-
- ////////////////
- // fs::TransactionHandler interface.
-
- uint64_t BlockNumberToDevice(uint64_t block_num) const final { return block_num; }
-
- zx_status_t RunRequests(const std::vector<storage::BufferedOperation>& operations) final;
-
- // Raw block read functions.
- // These do not track blocks (or attempt to access the block cache)
- // NOTE: Not marked as final, since these are overridden methods on host,
- // but not on __Fuchsia__.
- zx_status_t Readblk(block_t bno, void* data);
- zx_status_t Writeblk(block_t bno, const void* data);
-
- ////////////////
- // Other methods.
-
- static zx_status_t Create(fbl::unique_fd fd, uint32_t max_blocks, std::unique_ptr<Bcache>* out);
-
- // Returns the maximum number of available blocks,
- // assuming the filesystem is non-resizable.
- uint32_t Maxblk() const { return max_blocks_; }
-
- // Lengths of each extent (in bytes)
- fbl::Array<size_t> extent_lengths_;
- // Tell Bcache to look for Minfs partition starting at |offset| bytes
- zx_status_t SetOffset(off_t offset);
- // Tell the Bcache it is pointing at a sparse file
- // |offset| indicates where the minfs partition begins within the file
- // |extent_lengths| contains the length of each extent (in bytes)
- zx_status_t SetSparse(off_t offset, const fbl::Vector<size_t>& extent_lengths);
-
- int Sync();
-
- private:
- friend class BlockNode;
-
- Bcache(fbl::unique_fd fd, uint32_t max_blocks);
-
- const fbl::unique_fd fd_;
- uint32_t max_blocks_;
- off_t offset_ = 0;
-};
-
-#endif
+// A helper function for converting "fd" to "BlockDevice".
+zx_status_t FdToBlockDevice(fbl::unique_fd& fd, std::unique_ptr<block_client::BlockDevice>* out);
+zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool* out_readonly,
+ std::unique_ptr<Bcache>* out);
} // namespace f2fs
diff --git a/dir.h b/dir.h
index c9db068..7c69ae2 100644
--- a/dir.h
+++ b/dir.h
@@ -76,7 +76,7 @@
zx_status_t NewInode(uint32_t mode, fbl::RefPtr<VnodeF2fs> *out);
int IsMultimediaFile(const char *s, const char *sub);
- void SetColdFile(const char *name);
+ void SetColdFile(const char *name, VnodeF2fs *vnode);
zx_status_t DoCreate(std::string_view name, uint32_t mode, fbl::RefPtr<fs::Vnode> *out);
#if 0 // porting needed
// int F2fsLink(dentry *old_dentry, dentry *dentry);
diff --git a/f2fs.cc b/f2fs.cc
index 40f31df..a8cf2f8 100644
--- a/f2fs.cc
+++ b/f2fs.cc
@@ -29,33 +29,6 @@
namespace f2fs {
-// This is the component's main class. It holds all of the component's state.
-zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool* out_readonly,
- std::unique_ptr<f2fs::Bcache>* out) {
- fuchsia_hardware_block_BlockInfo info;
- zx_status_t status = device->BlockGetInfo(&info);
- if (status != ZX_OK) {
- FX_LOGS(ERROR) << "Coult not access device info: " << status;
- return status;
- }
-
- uint64_t device_size = info.block_size * info.block_count;
-
- if (device_size == 0) {
- FX_LOGS(ERROR) << "Invalid device size";
- return status;
- }
- uint64_t block_count = device_size / kBlockSize;
-
- if (block_count >= std::numeric_limits<uint32_t>::max()) {
- FX_LOGS(ERROR) << "Block count overflow";
- return ZX_ERR_OUT_OF_RANGE;
- }
-
- return f2fs::Bcache::Create(std::move(device), static_cast<uint32_t>(block_count), out,
- kBlockSize);
-}
-
zx_status_t Fsck(const MountOptions& options, std::unique_ptr<f2fs::Bcache> bc) { return ZX_OK; }
F2fs::F2fs(std::unique_ptr<f2fs::Bcache> bc, SuperBlock* sb, const MountOptions& mount_options)
@@ -74,19 +47,6 @@
F2fs::~F2fs() { vnode_table_.clear(); }
-zx_status_t LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info) {
- char buf[8192];
-
- if (zx_status_t status = bc->Readblk(kSuperblockStart, buf); status != ZX_OK) {
- FX_LOGS(ERROR) << "could not read info block.";
- return status;
- }
-
- memcpy(out_info, buf + 1024, sizeof(SuperBlock));
-
- return ZX_OK;
-}
-
zx_status_t F2fs::Create(std::unique_ptr<f2fs::Bcache> bc, const MountOptions& options,
std::unique_ptr<F2fs>* out) {
SuperBlock* info;
@@ -98,6 +58,11 @@
*out = std::unique_ptr<F2fs>(new F2fs(std::move(bc), info, options));
+ if (zx_status_t status = (*out)->FillSuper(); status != ZX_OK) {
+ FX_LOGS(ERROR) << "failed to initialize fs." << status;
+ return status;
+ }
+
return ZX_OK;
}
@@ -132,36 +97,44 @@
vnode_table_.erase(*vn);
}
-zx_status_t CreateFsAndRoot(const MountOptions& mount_options, async_dispatcher_t* dispatcher,
- std::unique_ptr<f2fs::Bcache> bcache, zx::channel mount_channel,
- fbl::Closure on_unmount, ServeLayout serve_layout) {
+zx_status_t LoadSuperblock(f2fs::Bcache* bc, SuperBlock* out_info) {
+ // TODO: define ino for superblock after cache impl.
+ Page* page = GrabCachePage(nullptr, 0, 0);
+ if (zx_status_t status = bc->Readblk(kSuperblockStart, PageAddress(page)); status != ZX_OK) {
+ if (status = bc->Readblk(kSuperblockStart + 1, PageAddress(page)); status != ZX_OK) {
+ FX_LOGS(ERROR) << "failed to read superblock." << status;
+ F2fsPutPage(page, 1);
+ return status;
+ }
+ }
+ memcpy(out_info, static_cast<uint8_t*>(PageAddress(page)) + kSuperOffset, sizeof(SuperBlock));
+ F2fsPutPage(page, 1);
+ return ZX_OK;
+}
+
+zx::status<std::unique_ptr<F2fs>> CreateFsAndRoot(const MountOptions& mount_options,
+ async_dispatcher_t* dispatcher,
+ std::unique_ptr<f2fs::Bcache> bcache,
+ zx::channel mount_channel,
+ fbl::Closure on_unmount,
+ ServeLayout serve_layout) {
TRACE_DURATION("f2fs", "CreateFsAndRoot");
- f2fs::MountOptions options = mount_options;
std::unique_ptr<F2fs> fs;
- if (zx_status_t status = F2fs::Create(std::move(bcache), options, &fs); status != ZX_OK) {
+ if (zx_status_t status = F2fs::Create(std::move(bcache), mount_options, &fs); status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create filesystem object " << status;
- return status;
- }
-
- if (zx_status_t ret = fs->FillSuper(); ret != ZX_OK) {
- std::cout << "FillSuper error " << ret << std::endl;
- return ret;
+ return zx::error(status);
}
fbl::RefPtr<VnodeF2fs> data_root;
if (zx_status_t status = VnodeF2fs::Vget(fs.get(), fs->RawSb().root_ino, &data_root);
status != ZX_OK) {
FX_LOGS(ERROR) << "cannot find root inode: " << status;
- return status;
+ return zx::error(status);
}
- __UNUSED auto r = fs.release();
-
- F2fs* vfs = data_root->Vfs();
-
- vfs->SetUnmountCallback(std::move(on_unmount));
- vfs->SetDispatcher(dispatcher);
+ fs->SetUnmountCallback(std::move(on_unmount));
+ fs->SetDispatcher(dispatcher);
fbl::RefPtr<fs::Vnode> export_root;
switch (serve_layout) {
@@ -175,49 +148,13 @@
break;
}
- return vfs->ServeDirectory(std::move(export_root), std::move(mount_channel));
-}
-
-zx_status_t Mount(const MountOptions& options, std::unique_ptr<f2fs::Bcache> bc) {
- zx::channel outgoing_server = zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST));
- zx::channel root_server = zx::channel(zx_take_startup_handle(FS_HANDLE_ROOT_ID));
-
- if (outgoing_server.is_valid() && root_server.is_valid()) {
- FX_LOGS(ERROR) << "both PA_DIRECTORY_REQUEST and FS_HANDLE_ROOT_ID provided - need one or the "
- "other.";
- return ZX_ERR_BAD_STATE;
+ if (zx_status_t status = fs->ServeDirectory(std::move(export_root), std::move(mount_channel));
+ status != ZX_OK) {
+ FX_LOGS(ERROR) << "failed to establish mount_channel" << status;
+ return zx::error(status);
}
- zx::channel export_root;
- f2fs::ServeLayout serve_layout;
- if (outgoing_server.is_valid()) {
- export_root = std::move(outgoing_server);
- serve_layout = f2fs::ServeLayout::kExportDirectory;
- } else if (root_server.is_valid()) {
- export_root = std::move(root_server);
- serve_layout = f2fs::ServeLayout::kDataRootOnly;
- } else {
- FX_LOGS(ERROR) << "could not get startup handle to serve on";
- return ZX_ERR_BAD_STATE;
- }
-
- async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
- trace::TraceProviderWithFdio trace_provider(loop.dispatcher());
-
- auto on_unmount = [&loop]() {
- loop.Quit();
- FX_LOGS(WARNING) << "Unmounted";
- };
-
- zx_status_t status = CreateFsAndRoot(options, loop.dispatcher(), std::move(bc),
- std::move(export_root), std::move(on_unmount), serve_layout);
- if (status != ZX_OK) {
- return -1;
- }
-
- ZX_ASSERT(loop.Run() == ZX_ERR_CANCELED);
-
- return ZX_OK;
+ return zx::ok(std::move(fs));
}
void Sync(SyncCallback closure) {
diff --git a/f2fs.h b/f2fs.h
index fcdf6d2..4bc72e5 100644
--- a/f2fs.h
+++ b/f2fs.h
@@ -41,21 +41,11 @@
#include "node.h"
#include "segment.h"
#include "mkfs.h"
+#include "mount.h"
// clang-format on
namespace f2fs {
-struct MountOptions {
- bool background_gc = false;
- bool disable_roll_forward = true;
- bool discard = false;
- bool noheap = true;
- bool xattr_user = false;
- bool posix_acl = false;
- bool disable_ext_identify = true;
- uint32_t active_logs = 6;
-};
-
enum class ServeLayout {
// The root of the filesystem is exposed directly.
kDataRootOnly,
@@ -66,12 +56,15 @@
};
zx_status_t Fsck(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc);
-zx_status_t Mount(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc);
-
-zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool *out_readonly,
- std::unique_ptr<f2fs::Bcache> *out);
zx_status_t LoadSuperblock(f2fs::Bcache *bc, SuperBlock *out_info);
+zx::status<std::unique_ptr<F2fs>> CreateFsAndRoot(const MountOptions &mount_options,
+ async_dispatcher_t *dispatcher,
+ std::unique_ptr<f2fs::Bcache> bcache,
+ zx::channel mount_channel,
+ fbl::Closure on_unmount,
+ ServeLayout serve_layout);
+
using SyncCallback = fs::Vnode::SyncCallback;
class F2fs : public fs::ManagedVfs {
@@ -125,13 +118,13 @@
// VnodeF2fs *F2fsNfsGetInode(uint64_t ino, uint32_t generation);
// dentry *F2fsFhToDentry(fid *fid, int fh_len, int fh_type);
// dentry *F2fsFhToParent(fid *fid, int fh_len, int fh_type);
- // int ParseOptions(char *options);
#endif
loff_t MaxFileSize(unsigned bits);
int SanityCheckRawSuper();
int SanityCheckCkpt();
void InitSbInfo();
zx_status_t FillSuper();
+ void ParseOptions();
#if 0 // porting needed
// dentry *F2fsMount(file_system_type *fs_type, int flags,
// const char *dev_name, void *data);
diff --git a/f2fs_internal.h b/f2fs_internal.h
index 468c856..4c9bc58 100644
--- a/f2fs_internal.h
+++ b/f2fs_internal.h
@@ -10,21 +10,6 @@
class VnodeF2fs;
/*
- * For mount options
- */
-constexpr uint64_t kMountBgGC = 0x00000001;
-constexpr uint64_t kMountDisableRollForward = 0x00000002;
-constexpr uint64_t kMountDiscard = 0x00000004;
-constexpr uint64_t kMountNoheap = 0x00000008;
-constexpr uint64_t kMountXattrUser = 0x00000010;
-constexpr uint64_t kMountPosixAcl = 0x00000020;
-constexpr uint64_t kMountDisableExtIdentify = 0x00000040;
-
-struct MountInfo {
- uint64_t opt;
-};
-
-/*
* For checkpoint manager
*/
enum class MetaBitmap { kNatBitmap, kSitBitmap };
@@ -362,7 +347,7 @@
uint32_t s_next_generation = 0; /* for NFS support */
atomic_t nr_pages[static_cast<int>(CountType::kNrCountType)]; /* # of pages, see count_type */
- MountInfo mount_opt; /* mount options */
+ uint64_t mount_opt = 0; // set with kMountOptxxxx bits according to F2fs::mount_options_
/* for cleaning operations */
mtx_t gc_mutex; /* mutex for GC */
@@ -396,10 +381,6 @@
// return (SbInfo *)sb->s_fs_info;
//}
-inline void ClearOpt(SbInfo *sbi, uint64_t option) { sbi->mount_opt.opt &= ~option; }
-inline void SetOpt(SbInfo *sbi, uint64_t option) { sbi->mount_opt.opt |= option; }
-inline uint64_t TestOpt(SbInfo *sbi, uint64_t option) { return sbi->mount_opt.opt & option; }
-
static inline const SuperBlock *RawSuper(SbInfo *sbi) {
return static_cast<const SuperBlock *>(sbi->raw_super);
}
diff --git a/f2fs_layout.h b/f2fs_layout.h
index 356436b..4a79139 100644
--- a/f2fs_layout.h
+++ b/f2fs_layout.h
@@ -9,17 +9,15 @@
namespace f2fs {
-constexpr uint64_t kSuperOffset = 1024; /* byte-size offset */
-constexpr uint32_t kLogSectorSize = 9; /* 9 bits for 512 byte */
-constexpr uint32_t kLogSectorsPerBlock = 3; /* 4KB: kBlkSize */
-constexpr size_t kBlkSize = 4096; /* support only 4KB block */
-constexpr int kMaxExtension = 64; /* # of extension entries */
+constexpr uint64_t kSuperOffset = 1024; // byte-size offset
+constexpr uint32_t kLogSectorSize = 9; // 9 bits for 512 byte
+constexpr uint32_t kLogSectorsPerBlock = 3; // log2 # of sectors per block
+constexpr uint32_t kBlockSize = 4096; // F2fs block size in byte
+constexpr int kMaxExtension = 64; // # of extension entries
constexpr block_t kNullAddr = 0x0U;
constexpr block_t kNewAddr = -1U;
-constexpr uint32_t kBlockSize = 4096;
-
// Superblock location.
constexpr size_t kSuperblockStart = 0;
diff --git a/mkfs.cc b/mkfs.cc
index 8c99f7f..67c66a4 100644
--- a/mkfs.cc
+++ b/mkfs.cc
@@ -3,13 +3,13 @@
// found in the LICENSE file.
#include <getopt.h>
+#include <lib/syslog/cpp/macros.h>
#include <cmath>
#include <codecvt>
#include "f2fs.h"
#include "src/lib/uuid/uuid.h"
-#include <lib/syslog/cpp/macros.h>
namespace f2fs {
@@ -111,9 +111,9 @@
if (zx_status_t ret = FormatDevice(); ret != ZX_OK)
return ret;
-
- fprintf(stderr, "Info: format successful\n");
-
+#ifdef F2FS_BU_DEBUG
+ FX_LOGS(INFO) << "Formated successfully";
+#endif
return ZX_OK;
}
@@ -284,7 +284,7 @@
super_block_.block_count = CpuToLe((params_.total_sectors * kDefaultSectorSize) / blk_size_bytes);
uint64_t zone_align_start_offset =
- (params_.start_sector * kDefaultSectorSize + 2 * kBlkSize + zone_size_bytes - 1) /
+ (params_.start_sector * kDefaultSectorSize + 2 * kBlockSize + zone_size_bytes - 1) /
zone_size_bytes * zone_size_bytes -
params_.start_sector * kDefaultSectorSize;
@@ -499,13 +499,13 @@
}
zx_status_t MkfsWorker::WriteCheckPointPack() {
- Checkpoint *ckp = static_cast<Checkpoint *>(calloc(kBlkSize, 1));
+ Checkpoint *ckp = static_cast<Checkpoint *>(calloc(kBlockSize, 1));
if (ckp == nullptr) {
printf("\n\tError: Calloc Failed for Checkpoint!!!\n");
return ZX_ERR_NO_MEMORY;
}
- SummaryBlock *sum = static_cast<SummaryBlock *>(calloc(kBlkSize, 1));
+ SummaryBlock *sum = static_cast<SummaryBlock *>(calloc(kBlockSize, 1));
if (sum == nullptr) {
printf("\n\tError: Calloc Failed for summay_node!!!\n");
return ZX_ERR_NO_MEMORY;
@@ -565,7 +565,7 @@
uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
uint64_t cp_seg_blk_offset = LeToCpu(super_block_.segment0_blkaddr) * blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the ckp to disk!!!\n");
return ret;
}
@@ -578,7 +578,7 @@
sum->entries[0].ofs_in_node = 0;
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
@@ -588,7 +588,7 @@
SetSumType((&sum->footer), kSumTypeData);
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
@@ -623,7 +623,7 @@
CpuToLe(uint16_t{(static_cast<int>(CursegType::kCursegColdData) << 10)});
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
@@ -636,7 +636,7 @@
sum->entries[0].ofs_in_node = 0;
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
@@ -646,7 +646,7 @@
SetSumType((&sum->footer), kSumTypeNode);
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
@@ -655,14 +655,14 @@
memset(sum, 0, sizeof(SummaryBlock));
SetSumType((&sum->footer), kSumTypeNode);
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the sum_blk to disk!!!\n");
return ret;
}
/* 8. cp page2 */
cp_seg_blk_offset += blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the ckp to disk!!!\n");
return ret;
}
@@ -678,7 +678,7 @@
cp_seg_blk_offset =
(LeToCpu(super_block_.segment0_blkaddr) + params_.blks_per_seg) * blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the ckp to disk!!!\n");
return ret;
}
@@ -689,12 +689,12 @@
}
zx_status_t MkfsWorker::WriteSuperBlock() {
- uint8_t *zero_buff = static_cast<uint8_t *>(calloc(kBlkSize, 1));
+ uint8_t *zero_buff = static_cast<uint8_t *>(calloc(kBlockSize, 1));
memcpy(zero_buff + kSuperOffset, &super_block_, sizeof(super_block_));
for (uint32_t index = 0; index < 2; index++) {
- if (zx_status_t ret = WriteToDisk(zero_buff, index * kBlkSize, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(zero_buff, index * kBlockSize, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While while writing supe_blk on disk!!! index : %d\n", index);
return ret;
}
@@ -705,7 +705,7 @@
}
zx_status_t MkfsWorker::WriteRootInode() {
- Node *raw_node = static_cast<Node *>(calloc(kBlkSize, 1));
+ Node *raw_node = static_cast<Node *>(calloc(kBlockSize, 1));
if (raw_node == nullptr) {
printf("\n\tError: Calloc Failed for raw_node!!!\n");
return ZX_ERR_NO_MEMORY;
@@ -754,7 +754,7 @@
params_.cur_seg[static_cast<int>(CursegType::kCursegHotNode)] * params_.blks_per_seg;
main_area_node_seg_blk_offset *= blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset, kBlkSize);
+ if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset, kBlockSize);
ret != ZX_OK) {
printf("\n\tError: While writing the raw_node to disk!!!, size = %lu\n", sizeof(Node));
return ret;
@@ -762,7 +762,7 @@
memset(raw_node, 0xff, sizeof(Node));
- if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset + 4096, kBlkSize);
+ if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset + 4096, kBlockSize);
ret != ZX_OK) {
printf("\n\tError: While writing the raw_node to disk!!!\n");
return ret;
@@ -772,7 +772,7 @@
}
zx_status_t MkfsWorker::UpdateNatRoot() {
- NatBlock *nat_blk = static_cast<NatBlock *>(calloc(kBlkSize, 1));
+ NatBlock *nat_blk = static_cast<NatBlock *>(calloc(kBlockSize, 1));
if (nat_blk == nullptr) {
printf("\n\tError: Calloc Failed for nat_blk!!!\n");
return ZX_ERR_NO_MEMORY;
@@ -796,7 +796,7 @@
uint64_t nat_seg_blk_offset = LeToCpu(super_block_.nat_blkaddr) * blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(nat_blk, nat_seg_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(nat_blk, nat_seg_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the nat_blk set0 to disk!!!\n");
return ret;
}
@@ -806,7 +806,7 @@
}
zx_status_t MkfsWorker::AddDefaultDentryRoot() {
- DentryBlock *dent_blk = static_cast<DentryBlock *>(calloc(kBlkSize, 1));
+ DentryBlock *dent_blk = static_cast<DentryBlock *>(calloc(kBlockSize, 1));
if (dent_blk == nullptr) {
printf("\n\tError: Calloc Failed for dent_blk!!!\n");
return ZX_ERR_NO_MEMORY;
@@ -832,7 +832,7 @@
params_.cur_seg[static_cast<int>(CursegType::kCursegHotData)] * params_.blks_per_seg) *
blk_size_bytes;
- if (zx_status_t ret = WriteToDisk(dent_blk, data_blk_offset, kBlkSize); ret != ZX_OK) {
+ if (zx_status_t ret = WriteToDisk(dent_blk, data_blk_offset, kBlockSize); ret != ZX_OK) {
printf("\n\tError: While writing the dentry_blk to disk!!!\n");
return ret;
}
@@ -859,30 +859,7 @@
return err;
}
-#if 0 // porting needed
-// int MkfsWorker::F2fsTrimDevice()
-// {
-// uint64_t range[2];
-// stat stat_buf;
-
-// range[0] = 0;
-// range[1] = params_.total_sectors * kDefaultSectorSizes;
-
-// if (fstat(params_.fd, &stat_buf) < 0 ) {
-// printf("\n\tError: Failed to get the device stat!!!\n");
-// return -1;
-// }
-
-// if (S_ISREG(stat_buf.st_mode))
-// return 0;
-// else if (S_ISBLK(stat_buf.st_mode)) {
-// // if (ioctl(params_.fd, BLKDISCARD, &range) < 0)
-// printf("Info: This device doesn't support TRIM\n");
-// } else
-// return -1;
-// return 0;
-// }
-#endif
+zx_status_t MkfsWorker::TrimDevice() { return bc_->Trim(0, params_.total_sectors); }
zx_status_t MkfsWorker::FormatDevice() {
zx_status_t err = ZX_OK;
@@ -891,14 +868,17 @@
FX_LOGS(ERROR) << err_msg << "Failed to prepare superblock information";
return err;
}
-#if 0 // porting needed
- // TRIM is not supported
- // err = f2fs_trim_device();
- // if (err < 0) {
- // printf("\n\tError: Failed to trim whole device!!!\n");
- // break;
- // }
-#endif
+
+ if (err = TrimDevice(); err != ZX_OK) {
+ if (err == ZX_ERR_NOT_SUPPORTED) {
+ FX_LOGS(INFO) << "This device doesn't support TRIM";
+ } else {
+ FX_LOGS(ERROR) << err_msg << "Failed to trim whole device" << err;
+ return err;
+ ;
+ }
+ }
+
if (err = InitSitArea(); err != ZX_OK) {
FX_LOGS(ERROR) << err_msg << "Failed to Initialise the SIT AREA" << err;
return err;
@@ -924,12 +904,8 @@
return err;
}
- /*
- * We should call fsync() to flush out all the dirty pages
- * in the block device page cache.
- */
- bc_->Sync();
-
+ // Ensure that all cached data is flushed in the underlying block device
+ bc_->Flush();
return err;
}
diff --git a/mkfs.h b/mkfs.h
index f5bc25c..0198a6d 100644
--- a/mkfs.h
+++ b/mkfs.h
@@ -61,10 +61,7 @@
void PrintUsage();
void PrintCurrentOption();
-#if 0 // porting needed
- // TODO: Trim support
- // int f2fs_trim_device()
-#endif
+ zx_status_t TrimDevice();
};
zx_status_t Mkfs(Bcache* bc, int argc, char** argv);
diff --git a/mount.cc b/mount.cc
new file mode 100644
index 0000000..f4c9866
--- /dev/null
+++ b/mount.cc
@@ -0,0 +1,134 @@
+// Copyright 2021 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/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
+#include <lib/async/dispatcher.h>
+#include <lib/syslog/cpp/macros.h>
+#include <lib/trace-provider/provider.h>
+
+#include "f2fs.h"
+
+namespace f2fs {
+
+// TODO: change .configurable to true once the feature is supported
+const MountOpt default_option[] = {
+ {"background_gc_off", 1, false},
+#ifdef F2FS_ROLL_FORWARD
+ {"disable_roll_forward", 0, false},
+#else
+ {"disable_roll_forward", 1, false},
+#endif
+ {"discard", 1, true},
+ {"no_heap", 1, false},
+ {"nouser_xattr", 1, false},
+ {"noacl", 1, false},
+ {"disable_ext_identify", 0, true},
+ {"active_logs", 6, true},
+};
+
+zx_status_t Mount(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc) {
+ zx::channel outgoing_server = zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST));
+ zx::channel root_server = zx::channel(zx_take_startup_handle(FS_HANDLE_ROOT_ID));
+
+ if (outgoing_server.is_valid() && root_server.is_valid()) {
+ FX_LOGS(ERROR) << "both PA_DIRECTORY_REQUEST and FS_HANDLE_ROOT_ID provided - need one or the "
+ "other.";
+ return ZX_ERR_BAD_STATE;
+ }
+
+ zx::channel export_root;
+ f2fs::ServeLayout serve_layout;
+ if (outgoing_server.is_valid()) {
+ export_root = std::move(outgoing_server);
+ serve_layout = f2fs::ServeLayout::kExportDirectory;
+ } else if (root_server.is_valid()) {
+ export_root = std::move(root_server);
+ serve_layout = f2fs::ServeLayout::kDataRootOnly;
+ } else {
+ FX_LOGS(ERROR) << "could not get startup handle to serve on";
+ return ZX_ERR_BAD_STATE;
+ }
+
+ async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
+ trace::TraceProviderWithFdio trace_provider(loop.dispatcher());
+
+ auto on_unmount = [&loop]() {
+ loop.Quit();
+ FX_LOGS(WARNING) << "Unmounted";
+ };
+
+ auto fs_or = CreateFsAndRoot(options, loop.dispatcher(), std::move(bc), std::move(export_root),
+ std::move(on_unmount), serve_layout);
+ if (fs_or.is_error()) {
+ FX_LOGS(ERROR) << "failed to create filesystem object " << fs_or.status_string();
+ return fs_or.error_value();
+ }
+
+ // Relinquish ownership of F2fs object
+ std::unique_ptr<f2fs::F2fs> fs = std::move(fs_or).value();
+ __UNUSED auto relinquish = fs.release();
+
+ ZX_ASSERT(loop.Run() == ZX_ERR_CANCELED);
+
+ return ZX_OK;
+}
+
+MountOptions::MountOptions() {
+ for (uint32_t i = 0; i < kOptMaxNum; i++) {
+ opt_[i] = default_option[i];
+ }
+}
+
+zx_status_t MountOptions::GetValue(const uint32_t opt_id, uint32_t *out) {
+ if (opt_id >= kOptMaxNum)
+ return ZX_ERR_INVALID_ARGS;
+ *out = opt_[opt_id].value;
+ return ZX_OK;
+}
+
+uint32_t MountOptions::GetOptionID(const std::string_view &opt) {
+ uint32_t i;
+ for (i = 0; i < kOptMaxNum; i++) {
+ if (opt_[i].name.compare(opt) == 0) {
+ break;
+ }
+ }
+ return i;
+}
+
+zx_status_t MountOptions::SetValue(const std::string_view &opt, const uint32_t value) {
+ zx_status_t ret = ZX_ERR_INVALID_ARGS;
+ uint32_t id = GetOptionID(opt);
+ if (id < kOptMaxNum && !opt_[id].configurable) {
+ std::cout << opt << " is not configurable." << std::endl;
+ } else {
+ switch (id) {
+ case kOptActiveLogs:
+ if (value != 2 && value != 4 && value != 6) {
+ std::cout << opt << " can be set only to 2, 4, or 6." << std::endl;
+ } else {
+ opt_[id].value = value;
+ ret = ZX_OK;
+ }
+ break;
+ case kOptDiscard:
+ case kOptBgGcOff:
+ case kOptNoHeap:
+ case kOptDisableExtIdentify:
+ case kOptNoUserXAttr:
+ case kOptNoAcl:
+ case kOptDisableRollForward:
+ opt_[id].value = value;
+ ret = ZX_OK;
+ break;
+ default:
+ std::cout << opt << " is not supported." << std::endl;
+ break;
+ };
+ }
+ return ret;
+}
+
+} // namespace f2fs
diff --git a/mount.h b/mount.h
new file mode 100644
index 0000000..73392b4
--- /dev/null
+++ b/mount.h
@@ -0,0 +1,58 @@
+// Copyright 2021 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.
+
+#ifndef THIRD_PARTY_F2FS_MOUNT_H_
+#define THIRD_PARTY_F2FS_MOUNT_H_
+
+namespace f2fs {
+
+constexpr uint32_t kOptMaxNum = 8;
+constexpr uint32_t kOptBgGcOff = 0;
+constexpr uint32_t kOptDisableRollForward = 1;
+constexpr uint32_t kOptDiscard = 2;
+constexpr uint32_t kOptNoHeap = 3;
+constexpr uint32_t kOptNoUserXAttr = 4;
+constexpr uint32_t kOptNoAcl = 5;
+constexpr uint32_t kOptDisableExtIdentify = 6;
+constexpr uint32_t kOptActiveLogs = (kOptMaxNum - 1);
+
+constexpr uint64_t kMountBgGcOff = (1 << kOptBgGcOff);
+constexpr uint64_t kMountDisableRollForward = (1 << kOptDisableRollForward);
+constexpr uint64_t kMountDiscard = (1 << kOptDiscard);
+constexpr uint64_t kMountNoheap = (1 << kOptNoHeap);
+constexpr uint64_t kMountNoXAttr = (1 << kOptNoUserXAttr);
+constexpr uint64_t kMountNoAcl = (1 << kOptNoAcl);
+constexpr uint64_t kMountDisableExtIdentify = (1 << kOptDisableExtIdentify);
+
+struct MountOpt {
+ std::string name;
+ uint32_t value;
+ bool configurable;
+};
+
+class MountOptions {
+ public:
+ MountOptions();
+ MountOptions(const MountOptions &) = default;
+
+ zx_status_t GetValue(const uint32_t opt_id, uint32_t *out);
+ uint32_t GetOptionID(const std::string_view &opt);
+ zx_status_t SetValue(const std::string_view &opt, const uint32_t value);
+ const std::string_view GetNameView(const uint32_t opt_id) {
+ ZX_ASSERT(opt_id < kOptMaxNum);
+ return opt_[opt_id].name;
+ }
+
+ private:
+ MountOpt opt_[kOptMaxNum];
+};
+
+inline void ClearOpt(SbInfo *sbi, uint64_t option) { sbi->mount_opt &= ~option; }
+inline void SetOpt(SbInfo *sbi, uint64_t option) { sbi->mount_opt |= option; }
+inline bool TestOpt(SbInfo *sbi, uint64_t option) { return ((sbi->mount_opt & option) != 0); }
+zx_status_t Mount(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc);
+
+} // namespace f2fs
+
+#endif // THIRD_PARTY_F2FS_MOUNT_H_
diff --git a/namei.cc b/namei.cc
index 5128ae5..bf17419 100644
--- a/namei.cc
+++ b/namei.cc
@@ -75,14 +75,14 @@
/**
* Set multimedia files as cold files for hot/cold data separation
*/
-inline void Dir::SetColdFile(const char *name) {
+inline void Dir::SetColdFile(const char *name, VnodeF2fs *vnode) {
int i;
uint8_t(*extlist)[8] = Vfs()->RawSb().extension_list;
int count = LeToCpu(Vfs()->RawSb().extension_count);
for (i = 0; i < count; i++) {
if (!IsMultimediaFile(name, reinterpret_cast<const char *>(extlist[i]))) {
- fi_.i_advise |= kFAdviseColdBit;
+ vnode->fi_.i_advise |= kFAdviseColdBit;
break;
}
}
@@ -100,7 +100,7 @@
vnode->i_name_sp_ = name;
if (!TestOpt(&sbi, kMountDisableExtIdentify))
- SetColdFile(name.data());
+ SetColdFile(name.data(), vnode);
SetInodeFlag(&vnode->fi_, InodeInfoFlag::kIncLink);
if (zx_status_t err = AddLink(name, vnode); err != ZX_OK) {
diff --git a/node.cc b/node.cc
index 1d416d3..aed5abd 100644
--- a/node.cc
+++ b/node.cc
@@ -242,7 +242,9 @@
* - Mark cold node blocks in their node footer
* - Mark cold data pages in page cache
*/
-int NodeMgr::IsColdFile(VnodeF2fs *vnode) { return vnode->fi_.i_advise & kFAdviseColdBit; }
+bool NodeMgr::IsColdFile(VnodeF2fs *vnode) {
+ return ((vnode->fi_.i_advise & kFAdviseColdBit) != 0);
+}
int NodeMgr::IsColdData(Page *page) {
#if 0 // porting needed
diff --git a/node.h b/node.h
index bb842f4..90a8958 100644
--- a/node.h
+++ b/node.h
@@ -103,7 +103,7 @@
uint32_t OfsOfNode(Page *node_page);
static int IsColdNode(Page *page);
- static int IsColdFile(VnodeF2fs *vnode);
+ static bool IsColdFile(VnodeF2fs *vnode);
static int IsColdData(Page *page);
uint8_t IsDentDnode(Page *page);
@@ -143,6 +143,8 @@
zx_status_t RecoverInodePage(Page *page);
void RecoverNodePage(Page *page, Summary *sum, NodeInfo *ni, block_t new_blkaddr);
+ void SetColdNode(VnodeF2fs *vnode, Page *page);
+
private:
F2fs *fs_;
@@ -161,8 +163,6 @@
void SetColdData(Page *page);
#endif
- void SetColdNode(VnodeF2fs *vnode, Page *page);
-
// Functions
void ClearNodePageDirty(Page *page);
Page *GetCurrentNatPage(nid_t nid);
diff --git a/segment.cc b/segment.cc
index aab275b..c2fd2b0 100644
--- a/segment.cc
+++ b/segment.cc
@@ -532,16 +532,8 @@
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[static_cast<int>(DirtyType::kPre)]))
dirty_i->nr_dirty[static_cast<int>(DirtyType::kPre)]--;
-#if 0 // porting needed (Trim)
- /* Let's use trim */
- // if (TestOpt(sbi, kMountDiscard))
- // blkdev_issue_discard(sbi->sb->s_bdev,
- // StartBlock(sbi, segno) <<
- // sbi->log_sectors_per_block,
- // 1 << (sbi->log_sectors_per_block +
- // sbi->log_blocks_per_seg),
- // GFP_NOFS, 0);
-#endif
+ if (TestOpt(&sbi, kMountDiscard))
+ fs_->bc_->Trim(StartBlock(&sbi, segno), (1 << sbi.log_blocks_per_seg));
}
mtx_unlock(&dirty_i->seglist_lock);
}
@@ -1085,7 +1077,7 @@
std::cout << "SubmitWritePage error " << ret << std::endl;
}
#if 0 // porting needed (bio)
- // fs_->bc_->Sync();
+ // fs_->bc_->Flush();
// block_device *bdev = sbi->sb->s_bdev;
diff --git a/super.cc b/super.cc
index cf7ec0d..e028594 100644
--- a/super.cc
+++ b/super.cc
@@ -12,30 +12,6 @@
#if 0 // porting needed
// static kmem_cache *Inode_cachep;
-// enum {
-// Opt_gc_background_off,
-// Opt_disable_roll_forward,
-// Opt_discard,
-// Opt_noheap,
-// Opt_nouser_xattr,
-// Opt_noacl,
-// Opt_active_logs,
-// Opt_disable_ext_identify,
-// Opt_err,
-// };
-
-// static match_table_t f2fs_tokens = {
-// {Opt_gc_background_off, "background_gc_off"},
-// {Opt_disable_roll_forward, "disable_roll_forward"},
-// {Opt_discard, "discard"},
-// {Opt_noheap, "no_heap"},
-// {Opt_nouser_xattr, "nouser_xattr"},
-// {Opt_noacl, "noacl"},
-// {Opt_active_logs, "active_logs=%u"},
-// {Opt_disable_ext_identify, "disable_ext_identify"},
-// {Opt_err, NULL},
-// };
-
// void F2fs::InitOnce(void *foo)
// {
// InodeInfo *fi = (InodeInfo *) foo;
@@ -208,57 +184,52 @@
// return generic_fh_to_parent(sb, fid, fh_len, fh_type,
// f2fs_nfs_get_inode);
// }
-
-// int F2fs::ParseOptions(char *options) {
- // substring_t args[MAX_OPT_ARGS];
- // char *p;
- // int arg = 0;
-
- // if (!options)
- // return 0;
-
- // while ((p = strsep(&options, ",")) != NULL) {
- // int token;
- // if (!*p)
- // continue;
- // /*
- // * Initialize args struct so we know whether arg was
- // * found; some options take optional arguments.
- // */
- // args[0].to = args[0].from = NULL;
- // token = match_token(p, f2fs_tokens, args);
-
- // switch (token) {
- // case Opt_gc_background_off:
- // ClearOpt(&sbi_.get(), kMountBgGC);
- // break;
- // case Opt_disable_roll_forward:
- // SetOpt(sbi_.get(), kMountDisableRollForward);
- // break;
- // case Opt_discard:
- // SetOpt(sbi_.get(), kMountDiscard);
- // break;
- // case Opt_noheap:
- // SetOpt(sbi_.get(), kMountNoheap);
- // break;
- // case Opt_active_logs:
- // if (args->from && match_int(args, &arg))
- // return -EINVAL;
- // if (arg != 2 && arg != 4 && arg != 6)
- // return -EINVAL;
- // sbi_.get()->active_logs = arg;
- // break;
- // case Opt_disable_ext_identify:
- // SetOpt(sbi_.get(), kMountDisableExtIdentify);
- // break;
- // default:
- // return -EINVAL;
- // }
- // }
- // return 0;
-// }
#endif
+void F2fs::ParseOptions() {
+ for (uint32_t i = 0; i < kOptMaxNum; i++) {
+ uint32_t value;
+ if (mount_options_.GetValue(i, &value) == ZX_OK) {
+ switch (i) {
+ case kOptActiveLogs:
+ sbi_->active_logs = value;
+ break;
+ case kOptDiscard:
+ if (value)
+ SetOpt(sbi_.get(), kMountDiscard);
+ break;
+ case kOptBgGcOff:
+ if (value)
+ SetOpt(sbi_.get(), kMountBgGcOff);
+ break;
+ case kOptNoHeap:
+ if (value)
+ SetOpt(sbi_.get(), kMountNoheap);
+ break;
+ case kOptDisableExtIdentify:
+ if (value)
+ SetOpt(sbi_.get(), kMountDisableExtIdentify);
+ break;
+ case kOptNoUserXAttr:
+ if (value)
+ SetOpt(sbi_.get(), kMountNoXAttr);
+ break;
+ case kOptNoAcl:
+ if (value)
+ SetOpt(sbi_.get(), kMountNoAcl);
+ break;
+ case kOptDisableRollForward:
+ if (value)
+ SetOpt(sbi_.get(), kMountDisableRollForward);
+ break;
+ default:
+ std::cout << mount_options_.GetNameView(i) << " is not supported." << std::endl;
+ break;
+ };
+ }
+ }
+}
+
loff_t F2fs::MaxFileSize(unsigned bits) {
loff_t result = kAddrsPerInode;
loff_t leaf_count = kAddrsPerBlock;
@@ -349,29 +320,11 @@
// super_block *sb = sbi_->sb;
/* set a temporary block size */
- // if (!SbSetBlocksize(sb, kBlkSize))
+ // if (!SbSetBlocksize(sb, kBlockSize))
// goto free_sbi;
#endif
- // TODO: remove them after supporting mount options
- ClearOpt(sbi_.get(), kMountBgGC);
- ClearOpt(sbi_.get(), kMountDiscard);
- SetOpt(sbi_.get(), kMountNoheap);
- ClearOpt(sbi_.get(), kMountXattrUser);
- ClearOpt(sbi_.get(), kMountPosixAcl);
- sbi_->active_logs = kNrCursegType;
- SetOpt(sbi_.get(), kMountDisableExtIdentify);
-#ifdef F2FS_ROLL_FORWARD
- ClearOpt(sbi_.get(), kMountDisableRollForward);
-#else
- SetOpt(sbi_.get(), kMountDisableRollForward);
-#endif
-
-#if 0 // porting needed
- /* parse mount options */
- // if (ParseOptions((char *)data))
- // goto free_sb_buf;
-#endif
+ ParseOptions();
/* sanity checking of raw super */
if (SanityCheckRawSuper())
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 4c41c24..ae54da2 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -13,6 +13,7 @@
test("f2fs_unittest") {
sources = [
+ "unit/bcache.cc",
"unit/mkfs.cc",
"unit/mount.cc",
]
diff --git a/test/unit/bcache.cc b/test/unit/bcache.cc
new file mode 100644
index 0000000..d586a33
--- /dev/null
+++ b/test/unit/bcache.cc
@@ -0,0 +1,38 @@
+// Copyright 2021 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 <block-client/cpp/fake-device.h>
+#include <gtest/gtest.h>
+
+#include "third_party/f2fs/f2fs.h"
+
+namespace f2fs {
+namespace {
+
+using block_client::FakeBlockDevice;
+constexpr uint32_t kNumBlocks = kMinVolumeSize / kBlockSize;
+
+TEST(BCacheTest, TrimTest) {
+ {
+ std::unique_ptr<f2fs::Bcache> bc;
+ bool readonly_device = false;
+ auto device = std::make_unique<FakeBlockDevice>(FakeBlockDevice::Config{
+ .block_count = kNumBlocks, .block_size = kDefaultSectorSize, .supports_trim = false});
+ ASSERT_TRUE(device);
+ ASSERT_EQ(f2fs::CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+ ASSERT_EQ(bc->Trim(0, bc->Maxblk()), ZX_ERR_NOT_SUPPORTED);
+ }
+ {
+ std::unique_ptr<f2fs::Bcache> bc;
+ bool readonly_device = false;
+ auto device = std::make_unique<FakeBlockDevice>(FakeBlockDevice::Config{
+ .block_count = kNumBlocks, .block_size = kDefaultSectorSize, .supports_trim = true});
+ ASSERT_TRUE(device);
+ ASSERT_EQ(f2fs::CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+ ASSERT_EQ(bc->Trim(0, bc->Maxblk()), ZX_OK);
+ }
+}
+
+} // namespace
+} // namespace f2fs
diff --git a/test/unit/mount.cc b/test/unit/mount.cc
index 08669ce..f53f167 100644
--- a/test/unit/mount.cc
+++ b/test/unit/mount.cc
@@ -1,3 +1,233 @@
// Copyright 2021 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/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
+
+#include <block-client/cpp/fake-device.h>
+#include <gtest/gtest.h>
+
+#include "third_party/f2fs/f2fs.h"
+
+namespace f2fs {
+namespace {
+
+using block_client::FakeBlockDevice;
+constexpr uint32_t kMountVerifyTest = 0;
+constexpr uint32_t kMountDisableExtTest = 1;
+constexpr uint32_t kMountActiveLogsTest = 2;
+
+void MountTestVerifyOptions(F2fs *fs, MountOptions &options) {
+ uint32_t value;
+ SbInfo &sbi = fs->GetSbInfo();
+ for (uint32_t i = 0; i < kOptMaxNum; i++) {
+ ASSERT_EQ(options.GetValue(i, &value), ZX_OK);
+ switch (i) {
+ case kOptActiveLogs:
+ ASSERT_EQ(static_cast<uint32_t>(sbi.active_logs), value);
+ break;
+ case kOptDiscard:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDiscard) == 0));
+ break;
+ case kOptBgGcOff:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountBgGcOff) == 0));
+ break;
+ case kOptNoHeap:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoheap) == 0));
+ break;
+ case kOptDisableExtIdentify:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDisableExtIdentify) == 0));
+ break;
+ case kOptNoUserXAttr:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoXAttr) == 0));
+ break;
+ case kOptNoAcl:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoAcl) == 0));
+ break;
+ case kOptDisableRollForward:
+ ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDisableRollForward) == 0));
+ break;
+ };
+ }
+ ASSERT_EQ(options.GetValue(kOptMaxNum, &value), ZX_ERR_INVALID_ARGS);
+}
+
+void MountTestDisableExt(F2fs *fs, uint32_t expectation) {
+ fbl::RefPtr<VnodeF2fs> data_root;
+ bool result = (expectation > 0 ? true : false);
+ ASSERT_EQ(VnodeF2fs::Vget(fs, fs->RawSb().root_ino, &data_root), ZX_OK);
+ Dir *root_dir = static_cast<Dir *>(data_root.get());
+
+ for (const char *ext_item : kMediaExtList) {
+ std::string name = "test.";
+ name += ext_item;
+ fbl::RefPtr<fs::Vnode> vnode;
+ // Use S_IFDIR to create regular file
+ ASSERT_EQ(root_dir->Create(name, S_IFREG, &vnode), ZX_OK);
+ File *file = static_cast<File *>(vnode.get());
+ ASSERT_EQ(NodeMgr::IsColdFile(file), result);
+ vnode->Close();
+ }
+}
+
+void TestSegmentType(F2fs *fs, Dir *root_dir, const std::string_view name, bool is_dir,
+ std::vector<CursegType> &out) {
+ fbl::RefPtr<fs::Vnode> vnode;
+ uint64_t flag = (is_dir ? S_IFDIR : S_IFREG);
+ nid_t nid = 100;
+ uint32_t inode_ofs = 0;
+ uint32_t indirect_node_ofs = 3;
+ ASSERT_EQ(root_dir->Create(name, flag, &vnode), ZX_OK);
+ VnodeF2fs *vn = static_cast<VnodeF2fs *>(vnode.get());
+
+ // data block test
+ Page *page = GrabCachePage(vn, vn->Ino(), 0);
+ CursegType type = fs->Segmgr().GetSegmentType(page, PageType::kData);
+ out.push_back(type);
+ F2fsPutPage(page, 1);
+
+ // Dnode block test
+ page = GrabCachePage(nullptr, NodeIno(&fs->GetSbInfo()), vn->Ino());
+ fs->Nodemgr().FillNodeFooter(page, page->index, vn->Ino(), inode_ofs, true);
+ fs->Nodemgr().SetColdNode(vn, page);
+ type = fs->Segmgr().GetSegmentType(page, PageType::kNode);
+ out.push_back(type);
+ F2fsPutPage(page, 1);
+
+ // indirect node block test
+ page = GrabCachePage(nullptr, NodeIno(&fs->GetSbInfo()), nid);
+ fs->Nodemgr().FillNodeFooter(page, page->index, vn->Ino(), indirect_node_ofs, true);
+ fs->Nodemgr().SetColdNode(vn, page);
+ type = fs->Segmgr().GetSegmentType(page, PageType::kNode);
+ out.push_back(type);
+ F2fsPutPage(page, 1);
+ vnode->Close();
+}
+
+void MountTestActiveLogs(F2fs *fs, MountOptions options) {
+ fbl::RefPtr<VnodeF2fs> data_root;
+ ASSERT_EQ(VnodeF2fs::Vget(fs, fs->RawSb().root_ino, &data_root), ZX_OK);
+ Dir *root_dir = static_cast<Dir *>(data_root.get());
+ const char *filenames[] = {"dir", "warm.exe", "cold.mp4"};
+ std::vector<CursegType> results(3, CursegType::kNoCheckType);
+ uint32_t num_logs = 0;
+ ASSERT_EQ(options.GetValue(kOptActiveLogs, &num_logs), ZX_OK);
+
+ constexpr int dir_file = 0;
+ constexpr int warm_file = 1;
+ __UNUSED constexpr int cold_file = 2;
+
+ __UNUSED constexpr int data_block = 0;
+ constexpr int dnode_block = 1;
+ constexpr int indirect_node_block = 2;
+
+ for (int i = 0; i < 3; i++) {
+ results.clear();
+ TestSegmentType(fs, root_dir, filenames[i], (i == dir_file), results);
+ for (int j = 0; j < 3; j++) {
+ CursegType type = results[j];
+ if (j == indirect_node_block) {
+ if (num_logs > 2)
+ ASSERT_EQ(type, CursegType::kCursegColdNode);
+ else
+ ASSERT_EQ(type, CursegType::kCursegHotNode);
+ } else if (j == dnode_block) {
+ if (i == dir_file || num_logs == 2) {
+ ASSERT_EQ(type, CursegType::kCursegHotNode);
+ } else if (num_logs == 6) {
+ ASSERT_EQ(type, CursegType::kCursegWarmNode);
+ } else
+ ASSERT_EQ(type, CursegType::kCursegColdNode);
+
+ } else { // data block case
+ if (i == dir_file || num_logs == 2) {
+ ASSERT_EQ(type, CursegType::kCursegHotData);
+ } else {
+ if (i == warm_file && num_logs == 6)
+ ASSERT_EQ(type, CursegType::kCursegWarmData);
+ else
+ ASSERT_EQ(type, CursegType::kCursegColdData);
+ }
+ }
+ }
+ }
+}
+
+void MountTestMain(MountOptions &options, uint32_t test, uint32_t priv) {
+ std::unique_ptr<f2fs::Bcache> bc;
+ bool readonly_device = false;
+ auto device = std::make_unique<FakeBlockDevice>(
+ FakeBlockDevice::Config{.block_count = kMinVolumeSize / kDefaultSectorSize,
+ .block_size = kDefaultSectorSize,
+ .supports_trim = false});
+
+ ASSERT_TRUE(device);
+ ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+ MkfsWorker mkfs(bc.get());
+ ASSERT_EQ(mkfs.DoMkfs(), ZX_OK);
+
+ std::unique_ptr<F2fs> fs;
+ async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
+
+ // Enclose the channels in a scope to ensure they are closed before we shut down.
+ {
+ zx::channel mount_channel, remote_mount_channel;
+ ASSERT_EQ(zx::channel::create(0, &mount_channel, &remote_mount_channel), ZX_OK);
+ auto fs_or = f2fs::CreateFsAndRoot(
+ options, loop.dispatcher(), std::move(bc), std::move(mount_channel), [] {},
+ ServeLayout::kExportDirectory);
+
+ ASSERT_EQ(fs_or.is_ok(), true);
+ fs = std::move(fs_or).value();
+ switch (test) {
+ case kMountVerifyTest:
+ MountTestVerifyOptions(fs.get(), options);
+ break;
+ case kMountDisableExtTest:
+ MountTestDisableExt(fs.get(), priv);
+ break;
+ case kMountActiveLogsTest:
+ MountTestActiveLogs(fs.get(), options);
+ break;
+ default:
+ ASSERT_EQ(0, 1);
+ break;
+ };
+ }
+ fs->Shutdown([&loop](zx_status_t) { loop.Quit(); });
+ __UNUSED F2fs *relinquish = fs.release();
+
+ ASSERT_EQ(loop.Run(), ZX_ERR_CANCELED);
+}
+
+TEST(MountTest, Verify) {
+ MountOptions options;
+ MountTestMain(options, kMountVerifyTest, 0);
+}
+
+TEST(MountTest, DisableExtOptions) {
+ constexpr uint32_t ShouldNotBeCold = 0;
+ MountOptions options{};
+ ASSERT_EQ(options.SetValue(options.GetNameView(kOptDisableExtIdentify), 1), ZX_OK);
+ MountTestMain(options, kMountDisableExtTest, ShouldNotBeCold);
+}
+
+TEST(MountTest, EnableExtOptions) {
+ constexpr uint32_t ShouldBeCold = 1;
+ MountOptions options{};
+ ASSERT_EQ(options.SetValue(options.GetNameView(kOptDisableExtIdentify), 0), ZX_OK);
+ MountTestMain(options, kMountDisableExtTest, ShouldBeCold);
+}
+
+TEST(MountTest, ActiveLogsOptions) {
+ for (uint32_t i = 2; i <= 6; i += 2) {
+ MountOptions options{};
+ ASSERT_EQ(options.SetValue(options.GetNameView(kOptActiveLogs), i), ZX_OK);
+ MountTestMain(options, kMountActiveLogsTest, 0);
+ }
+}
+
+} // namespace
+} // namespace f2fs
diff --git a/tools/main.cc b/tools/main.cc
index b5f22f3..cdcf160 100644
--- a/tools/main.cc
+++ b/tools/main.cc
@@ -28,6 +28,7 @@
int main(int argc, char** argv) {
f2fs::MountOptions options;
+#ifdef F2FS_BU_DEBUG
std::cout << "f2fs arg= ";
for (int i = 0; i < argc; i++) {
@@ -35,13 +36,13 @@
}
std::cout << std::endl;
+#endif
if (argc <= 1) {
- std::cout << "Hello F2FS!!!" << std::endl;
- fprintf(stderr, "usage: mkfs [ <options>*] devicepath f2fs\n");
- fprintf(stderr, "usage: fsck [ <options>*] devicepath f2fs\n");
- fprintf(stderr, "usage: mount [ <options>*] devicepath f2fs\n");
- fprintf(stderr, "usage: fsync filename\n");
+ fprintf(stderr, "usage: f2fs mkfs [ <options>*] devicepath f2fs\n");
+ fprintf(stderr, "usage: f2fs fsck [ <options>*] devicepath f2fs\n");
+ fprintf(stderr, "usage: f2fs mount [ <options>*] devicepath f2fs\n");
+ fprintf(stderr, "usage: f2fs fsync filename\n");
return 0;
}
@@ -75,7 +76,7 @@
bool readonly_device = false;
if (f2fs::CreateBcache(std::move(device), &readonly_device, &bc) != ZX_OK) {
- fprintf(stderr, "f2fs: error: cannot create block cache\n");
+ FX_LOGS(ERROR) << "f2fs: error: cannot create block cache";
return -1;
}
@@ -87,7 +88,7 @@
} else if (!strcmp(argv[1], "mount")) {
f2fs::Mount(options, std::move(bc));
} else {
- std::cout << "f2fs: unknown operation:" << argv[1] << std::endl;
+ FX_LOGS(ERROR) << "unknown operation:" << argv[1];
}
return 0;
diff --git a/vnode.cc b/vnode.cc
index 1af9a3a..6436765 100644
--- a/vnode.cc
+++ b/vnode.cc
@@ -63,8 +63,6 @@
SetInodeFlag(&vnode->fi_, InodeInfoFlag::kNewInode);
}
-// TODO(sukka): fill vfs->members in addtion to size
-// TODO(sukka): if dir/file vnode are defined as different class, check if the ino is for dir/file
void VnodeF2fs::Create(F2fs *fs, ino_t ino, fbl::RefPtr<VnodeF2fs> *out) {
Page *node_page = nullptr;
Inode *ri;
@@ -85,7 +83,6 @@
rn = static_cast<Node *>(PageAddress(node_page));
ri = &(rn->i);
- // [sukka] need to check result?
if (S_ISDIR(ri->i_mode)) {
*out = fbl::MakeRefCounted<Dir>(fs, ino);
} else {