blob: 0ea8fb2b2d2dce058ffa88db973e166c63cdde67 [file] [log] [blame]
// 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 <sys/stat.h>
#include "f2fs.h"
namespace f2fs {
// Orinially in f2fs_internal.h
static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, VnodeF2fs *vnode,
blkcnt_t count) {
block_t valid_block_count;
spin_lock(&sbi->stat_lock);
valid_block_count = sbi->total_valid_block_count + (block_t)count;
if (valid_block_count > sbi->user_block_count) {
spin_unlock(&sbi->stat_lock);
return false;
}
vnode->i_blocks += count;
sbi->total_valid_block_count = valid_block_count;
sbi->alloc_valid_block_count += (block_t)count;
spin_unlock(&sbi->stat_lock);
return true;
}
// gc
block_t start_bidx_of_node(unsigned int node_ofs) {
block_t start_bidx;
unsigned int bidx, indirect_blks;
int dec;
indirect_blks = 2 * NIDS_PER_BLOCK + 4;
start_bidx = 1;
if (node_ofs == 0) {
start_bidx = 0;
} else if (node_ofs <= 2) {
bidx = node_ofs - 1;
} else if (node_ofs <= indirect_blks) {
dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
bidx = node_ofs - 2 - dec;
} else {
dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
bidx = node_ofs - 5 - dec;
}
if (start_bidx)
start_bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE;
return start_bidx;
}
/**
* Lock ordering for the change of data block address:
* ->data_page
* ->node_page
* update block addresses in the node page
*/
void VnodeF2fs::__SetDataBlkaddr(struct dnode_of_data *dn, block_t new_addr) {
struct f2fs_node *rn;
__le32 *addr_array;
Page *node_page = dn->node_page;
unsigned int ofs_in_node = dn->ofs_in_node;
wait_on_page_writeback(node_page);
rn = (struct f2fs_node *)page_address(node_page);
/* Get physical address of data block */
addr_array = blkaddr_in_node(rn);
addr_array[ofs_in_node] = cpu_to_le32(new_addr);
#if 0 // porting needed
// set_page_dirty(node_page);
#else
FlushDirtyNodePage(Vfs(), node_page);
#endif
}
int VnodeF2fs::ReserveNewBlock(struct dnode_of_data *dn) {
struct f2fs_sb_info &sbi = fs_->SbInfo();
if (is_inode_flag_set(&dn->vnode->fi, FI_NO_ALLOC))
return -EPERM;
if (!inc_valid_block_count(&sbi, dn->vnode, 1))
return -ENOSPC;
__SetDataBlkaddr(dn, NEW_ADDR);
dn->data_blkaddr = NEW_ADDR;
fs_->Nodemgr().SyncInodePage(dn);
return 0;
}
#if 0 // porting needed
// int VnodeF2fs::CheckExtentCache(struct inode *inode, pgoff_t pgofs,
// struct buffer_head *bh_result)
// {
// struct f2fs_inode_info *fi = F2FS_I(inode);
// //struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
// struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
// pgoff_t start_fofs, end_fofs;
// block_t start_blkaddr;
// read_lock(&fi->ext.ext_lock);
// if (fi->ext.len == 0) {
// read_unlock(&fi->ext.ext_lock);
// return 0;
// }
// sbi->total_hit_ext++;
// start_fofs = fi->ext.fofs;
// end_fofs = fi->ext.fofs + fi->ext.len - 1;
// start_blkaddr = fi->ext.blk_addr;
// if (pgofs >= start_fofs && pgofs <= end_fofs) {
// unsigned int blkbits = inode->i_sb->s_blocksize_bits;
// size_t count;
// clear_buffer_new(bh_result);
// map_bh(bh_result, inode->i_sb,
// start_blkaddr + pgofs - start_fofs);
// count = end_fofs - pgofs + 1;
// if (count < (UINT_MAX >> blkbits))
// bh_result->b_size = (count << blkbits);
// else
// bh_result->b_size = UINT_MAX;
// sbi->read_hit_ext++;
// read_unlock(&fi->ext.ext_lock);
// return 1;
// }
// read_unlock(&fi->ext.ext_lock);
// return 0;
// }
#endif
void VnodeF2fs::UpdateExtentCache(block_t blk_addr, struct dnode_of_data *dn) {
struct f2fs_inode_info *fi = &dn->vnode->fi;
pgoff_t fofs, start_fofs, end_fofs;
block_t start_blkaddr, end_blkaddr;
ZX_ASSERT(blk_addr != NEW_ADDR);
fofs = start_bidx_of_node(Vfs()->Nodemgr().OfsOfNode(dn->node_page)) + dn->ofs_in_node;
/* Update the page address in the parent node */
__SetDataBlkaddr(dn, blk_addr);
write_lock(&fi->ext.ext_lock);
start_fofs = fi->ext.fofs;
end_fofs = fi->ext.fofs + fi->ext.len - 1;
start_blkaddr = fi->ext.blk_addr;
end_blkaddr = fi->ext.blk_addr + fi->ext.len - 1;
/* Drop and initialize the matched extent */
if (fi->ext.len == 1 && fofs == start_fofs)
fi->ext.len = 0;
/* Initial extent */
if (fi->ext.len == 0) {
if (blk_addr != NULL_ADDR) {
fi->ext.fofs = fofs;
fi->ext.blk_addr = blk_addr;
fi->ext.len = 1;
}
goto end_update;
}
/* Frone merge */
if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) {
fi->ext.fofs--;
fi->ext.blk_addr--;
fi->ext.len++;
goto end_update;
}
/* Back merge */
if (fofs == end_fofs + 1 && blk_addr == end_blkaddr + 1) {
fi->ext.len++;
goto end_update;
}
/* Split the existing extent */
if (fi->ext.len > 1 && fofs >= start_fofs && fofs <= end_fofs) {
if ((end_fofs - fofs) < (fi->ext.len >> 1)) {
fi->ext.len = fofs - start_fofs;
} else {
fi->ext.fofs = fofs + 1;
fi->ext.blk_addr = start_blkaddr + fofs - start_fofs + 1;
fi->ext.len -= fofs - start_fofs + 1;
}
goto end_update;
}
write_unlock(&fi->ext.ext_lock);
return;
end_update:
write_unlock(&fi->ext.ext_lock);
Vfs()->Nodemgr().SyncInodePage(dn);
}
zx_status_t VnodeF2fs::FindDataPage(pgoff_t index, struct Page **out) {
#if 0 // porting needed
// struct address_space *mapping = inode->i_mapping;
#endif
struct dnode_of_data dn;
Page *page = nullptr;
zx_status_t err;
#if 0 // porting needed
// page = find_get_page(mapping, index);
// if (page && PageUptodate(page))
// return page;
// F2fsPutPage(page, 0);
#endif
SetNewDnode(&dn, this, NULL, NULL, 0);
err = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, RDONLY_NODE);
if (err)
return err;
F2fsPutDnode(&dn);
if (dn.data_blkaddr == NULL_ADDR)
return ZX_ERR_NOT_FOUND;
/* By fallocate(), there is no cached page, but with NEW_ADDR */
if (dn.data_blkaddr == NEW_ADDR)
return ZX_ERR_INVALID_ARGS;
page = grab_cache_page(this, ino_, index);
if (!page)
return ZX_ERR_NO_MEMORY;
err = F2fsReadpage(Vfs(), page, dn.data_blkaddr, READ_SYNC);
if (err) {
F2fsPutPage(page, 1);
return err;
}
#if 0 // porting needed
// unlock_page(page);
#endif
*out = page;
return ZX_OK;
}
/**
* If it tries to access a hole, return an error.
* Because, the callers, functions in dir.c and GC, should be able to know
* whether this page exists or not.
*/
zx_status_t VnodeF2fs::GetLockDataPage(pgoff_t index, Page **out) {
struct dnode_of_data dn;
Page *page;
zx_status_t err;
page = nullptr;
SetNewDnode(&dn, this, NULL, NULL, 0);
err = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, RDONLY_NODE);
if (err)
return err;
F2fsPutDnode(&dn);
if (dn.data_blkaddr == NULL_ADDR) {
return ZX_ERR_NOT_FOUND;
}
page = grab_cache_page(this, ino_, index);
if (!page)
return ZX_ERR_NO_MEMORY;
if (PageUptodate(page)) {
*out = page;
return ZX_OK;
}
ZX_ASSERT(dn.data_blkaddr != NEW_ADDR);
ZX_ASSERT(dn.data_blkaddr != NULL_ADDR);
err = VnodeF2fs::F2fsReadpage(fs_, page, dn.data_blkaddr, READ_SYNC);
if (err) {
F2fsPutPage(page, 1);
return err;
}
*out = page;
return ZX_OK;
}
/**
* Caller ensures that this data page is never allocated.
* A new zero-filled data page is allocated in the page cache.
*/
zx_status_t VnodeF2fs::GetNewDataPage(pgoff_t index, bool new_i_size, Page **out) {
#if 0 // porting needed
// struct address_space *mapping = inode->i_mapping;
#endif
Page *page;
struct dnode_of_data dn;
zx_status_t err;
SetNewDnode(&dn, this, NULL, NULL, 0);
err = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, 0);
if (err)
return err;
if (dn.data_blkaddr == NULL_ADDR) {
if (ReserveNewBlock(&dn)) {
F2fsPutDnode(&dn);
return ZX_ERR_NO_SPACE;
}
}
F2fsPutDnode(&dn);
page = grab_cache_page(this, ino_, index);
if (!page)
return ZX_ERR_NO_MEMORY;
if (PageUptodate(page)) {
*out = page;
return ZX_OK;
}
if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else {
err = F2fsReadpage(fs_, page, dn.data_blkaddr, READ_SYNC);
if (err) {
F2fsPutPage(page, 1);
return err;
}
}
SetPageUptodate(page);
// if (new_i_size &&
// i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
// i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
// mark_inode_dirty_sync(inode);
// }
if (new_i_size && i_size < (off_t)((index + 1) << PAGE_CACHE_SHIFT)) {
i_size = (index + 1) << PAGE_CACHE_SHIFT;
#if 0 // porting needed
// mark_inode_dirty_sync(inode);
#endif
}
*out = page;
return ZX_OK;
}
#if 0 // porting needed
// static void read_end_io(struct bio *bio, int err)
// {
// const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
// struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
// do {
// struct page *page = bvec->bv_page;
// if (--bvec >= bio->bi_io_vec)
// prefetchw(&bvec->bv_page->flags);
// if (uptodate) {
// SetPageUptodate(page);
// } else {
// ClearPageUptodate(page);
// SetPageError(page);
// }
// unlock_page(page);
// } while (bvec >= bio->bi_io_vec);
// kfree(bio->bi_private);
// bio_put(bio);
// }
#endif
/**
* Fill the locked page with data located in the block address.
* Read operation is synchronous, and caller must unlock the page.
*/
zx_status_t VnodeF2fs::F2fsReadpage(F2fs *fs, Page *page, block_t blk_addr, int type) {
#if 0 // porting needed
// struct block_device *bdev = sbi->sb->s_bdev;
// bool sync = (type == READ_SYNC);
// struct bio *bio;
// /* This page can be already read by other threads */
// if (PageUptodate(page)) {
// if (!sync)
// unlock_page(page);
// return 0;
// }
// down_read(&sbi->bio_sem);
// /* Allocate a new bio */
// bio = f2fs_bio_alloc(bdev, blk_addr << (sbi->log_blocksize - 9),
// 1, GFP_NOFS | __GFP_HIGH);
// /* Initialize the bio */
// bio->bi_end_io = read_end_io;
// if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) {
// kfree(bio->bi_private);
// bio_put(bio);
// up_read(&sbi->bio_sem);
// return -EFAULT;
// }
// submit_bio(type, bio);
// up_read(&sbi->bio_sem);
// /* wait for read completion if sync */
// if (sync) {
// lock_page(page);
// if (PageError(page))
// return -EIO;
// }
// return 0;
#else
return fs->bc_->Readblk(blk_addr, page->data);
#endif
}
/**
* This function should be used by the data read flow only where it
* does not check the "create" flag that indicates block allocation.
* The reason for this special functionality is to exploit VFS readahead
* mechanism.
*/
#if 0 // porting needed
// int VnodeF2fs::GetDataBlockRo(struct inode *inode, sector_t iblock,
// struct buffer_head *bh_result, int create)
// {
// unsigned int blkbits = inode->i_sb->s_blocksize_bits;
// unsigned maxblocks = bh_result->b_size >> blkbits;
// struct dnode_of_data dn;
// pgoff_t pgofs;
// //int err = 0;
// /* Get the page offset from the block offset(iblock) */
// pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits));
// if (VnodeF2fs::CheckExtentCache(inode, pgofs, bh_result))
// return 0;
// /* When reading holes, we need its node page */
// //TODO(unknown): inode should be replaced with vnodef2fs
// //SetNewDnode(&dn, inode, NULL, NULL, 0);
// // TODO(unknown): shoud be replaced with NodeMgr->GetDnodeOfData
// /*err = get_dnode_of_data(&dn, pgofs, RDONLY_NODE);
// if (err)
// return (err == ZX_ERR_NOT_FOUND) ? 0 : err; */
// /* It does not support data allocation */
// ZX_ASSERT(!create);
// if (dn.data_blkaddr != NEW_ADDR && dn.data_blkaddr != NULL_ADDR) {
// unsigned int i;
// unsigned int end_offset;
// end_offset = IS_INODE(dn.node_page) ?
// ADDRS_PER_INODE :
// ADDRS_PER_BLOCK;
// clear_buffer_new(bh_result);
// /* Give more consecutive addresses for the read ahead */
// for (i = 0; i < end_offset - dn.ofs_in_node; i++)
// if (((datablock_addr(dn.node_page,
// dn.ofs_in_node + i))
// != (dn.data_blkaddr + i)) || maxblocks == i)
// break;
// //map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
// bh_result->b_size = (i << blkbits);
// }
// F2fsPutDnode(&dn);
// return 0;
// }
// int VnodeF2fs::F2fsReadDataPage(struct file *file, struct page *page)
// {
// return mpage_readpage(page, VnodeF2fs::GetDataBlockRo);
// }
// int VnodeF2fs::F2fsReadDataPages(struct file *file,
// struct address_space *mapping,
// list_node_t *pages, unsigned nr_pages)
// {
// return mpage_readpages(mapping, pages, nr_pages, VnodeF2fs::GetDataBlockRo);
// }
#endif
zx_status_t VnodeF2fs::DoWriteDataPage(Page *page) {
#if 0 // porting needed
// struct inode *inode = page->mapping->host;
#endif
struct f2fs_sb_info &sbi = Vfs()->SbInfo();
block_t old_blk_addr, new_blk_addr;
struct dnode_of_data dn;
zx_status_t err = ZX_OK;
SetNewDnode(&dn, this, NULL, NULL, 0);
err = Vfs()->Nodemgr().GetDnodeOfData(&dn, page->index, RDONLY_NODE);
if (err)
return err;
old_blk_addr = dn.data_blkaddr;
/* This page is already truncated */
if (old_blk_addr == NULL_ADDR)
goto out_writepage;
set_page_writeback(page);
/*
* If current allocation needs SSR,
* it had better in-place writes for updated data.
*/
if (old_blk_addr != NEW_ADDR && !Vfs()->Nodemgr().IsColdData(page) &&
Vfs()->Segmgr().NeedInplaceUpdate(this)) {
Vfs()->Segmgr().RewriteDataPage(page, old_blk_addr);
} else {
Vfs()->Segmgr().WriteDataPage(this, page, &dn, old_blk_addr, &new_blk_addr);
UpdateExtentCache(new_blk_addr, &dn);
fi.data_version = le64_to_cpu(F2FS_CKPT(&sbi)->checkpoint_ver);
}
out_writepage:
F2fsPutDnode(&dn);
return err;
}
zx_status_t VnodeF2fs::F2fsWriteDataPage(Page *page, struct writeback_control *wbc) {
struct f2fs_sb_info &sbi = Vfs()->SbInfo();
const pgoff_t end_index = ((unsigned long long)i_size) >> PAGE_CACHE_SHIFT;
unsigned offset;
zx_status_t err = 0;
if (page->index < end_index)
goto out;
/*
* If the offset is out-of-range of file size,
* this page does not have to be written to disk.
*/
offset = i_size & (PAGE_CACHE_SIZE - 1);
if ((page->index >= end_index + 1) || !offset) {
if (S_ISDIR(i_mode)) {
DecPageCount(&sbi, F2FS_DIRTY_DENTS);
#if 0 // porting needed
// inode_dec_dirty_dents(inode);
#endif
}
goto unlock_out;
}
zero_user_segment(page, offset, PAGE_CACHE_SIZE);
out:
if (sbi.por_doing)
goto redirty_out;
#if 0 // porting needed
// if (wbc->for_reclaim && !S_ISDIR(i_mode) && !Vfs()->Nodemgr().IsColdData(page))
// goto redirty_out;
#endif
mutex_lock_op(&sbi, DATA_WRITE);
if (S_ISDIR(i_mode)) {
DecPageCount(&sbi, F2FS_DIRTY_DENTS);
#if 0 // porting needed
// inode_dec_dirty_dents(inode);
#endif
}
err = DoWriteDataPage(page);
if (err && err != ZX_ERR_NOT_FOUND) {
#if 0 // porting needed
// wbc->pages_skipped++;
// set_page_dirty(page);
#endif
FlushDirtyDataPage(Vfs(), page);
}
mutex_unlock_op(&sbi, DATA_WRITE);
#if 0 // porting needed
// if (wbc->for_reclaim)
// f2fs_submit_bio(sbi, DATA, true);
#endif
if (err == ZX_ERR_NOT_FOUND)
goto unlock_out;
Vfs()->Nodemgr().ClearColdData(page);
#if 0 // porting needed
// unlock_page(page);
// if (!wbc->for_reclaim && !S_ISDIR(i_mode))
// fs->Segmgr().F2fsBalanceFs();
#endif
return ZX_OK;
unlock_out:
#if 0 // porting needed
// unlock_page(page);
#endif
return (err == ZX_ERR_NOT_FOUND) ? 0 : err;
redirty_out:
#if 0 // porting needed
// wbc->pages_skipped++;
// set_page_dirty(page);
#else
FlushDirtyDataPage(Vfs(), page);
#endif
return AOP_WRITEPAGE_ACTIVATE;
}
#if 0 // porting needed
// #define MAX_DESIRED_PAGES_WP 4096
// int VnodeF2fs::F2fsWriteDataPages(/*struct address_space *mapping,*/
// struct writeback_control *wbc) {
// // struct inode *inode = mapping->host;
// // struct f2fs_sb_info &sbi = Vfs()->SbInfo();
// int ret;
// // long excess_nrtw = 0, desired_nrtw;
// // if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) {
// // desired_nrtw = MAX_DESIRED_PAGES_WP;
// // excess_nrtw = desired_nrtw - wbc->nr_to_write;
// // wbc->nr_to_write = desired_nrtw;
// // }
// // if (!S_ISDIR(i_mode))
// // mutex_lock(&sbi->writepages);
// // ret = generic_writepages(mapping, wbc);
// ret = 0;
// // if (!S_ISDIR(i_mode))
// // mutex_unlock(&sbi->writepages);
// // Vfs()->Segmgr().F2fsSubmitBio(DATA, (wbc->sync_mode == WB_SYNC_ALL));
// Vfs()->RemoveDirtyDirInode(this);
// // wbc->nr_to_write -= excess_nrtw;
// return ret;
// }
#endif
zx_status_t VnodeF2fs::F2fsWriteBegin(size_t pos, size_t len, Page **pagep) {
struct f2fs_sb_info &sbi = Vfs()->SbInfo();
pgoff_t index = ((unsigned long long)pos) >> PAGE_CACHE_SHIFT;
struct dnode_of_data dn;
zx_status_t err = 0;
Vfs()->Segmgr().F2fsBalanceFs();
#if 0 // porting needed
// page = grab_cache_page_write_begin(/*mapping,*/ index, flags);
#else
*pagep = grab_cache_page(this, ino_, index);
if (!*pagep)
return ZX_ERR_NO_MEMORY;
#endif
mutex_lock_op(&sbi, DATA_NEW);
SetNewDnode(&dn, this, NULL, NULL, 0);
err = Vfs()->Nodemgr().GetDnodeOfData(&dn, index, 0);
if (err) {
mutex_unlock_op(&sbi, DATA_NEW);
F2fsPutPage(*pagep, 1);
return err;
}
if (dn.data_blkaddr == NULL_ADDR) {
err = ReserveNewBlock(&dn);
if (err) {
F2fsPutDnode(&dn);
mutex_unlock_op(&sbi, DATA_NEW);
F2fsPutPage(*pagep, 1);
return err;
}
}
F2fsPutDnode(&dn);
mutex_unlock_op(&sbi, DATA_NEW);
if ((len == PAGE_CACHE_SIZE) || PageUptodate(*pagep))
return ZX_OK;
if (((loff_t)pos & PAGE_CACHE_MASK) >= i_size) {
unsigned start = pos & (PAGE_CACHE_SIZE - 1);
unsigned end = start + len;
/* Reading beyond i_size is simple: memset to zero */
zero_user_segments(*pagep, 0, start, end, PAGE_CACHE_SIZE);
return ZX_OK;
}
if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(*pagep, 0, PAGE_CACHE_SIZE);
} else {
err = F2fsReadpage(fs_, *pagep, dn.data_blkaddr, READ_SYNC);
if (err) {
F2fsPutPage(*pagep, 1);
return err;
}
}
SetPageUptodate(*pagep);
Vfs()->Nodemgr().ClearColdData(*pagep);
return ZX_OK;
}
#if 0 // porting needed
// ssize_t VnodeF2fs::F2fsDirectIO(/*int rw, struct kiocb *iocb,
// const struct iovec *iov, */ loff_t offset, unsigned long nr_segs) {
// // struct file *file = iocb->ki_filp;
// // struct inode *inode = file->f_mapping->host;
// // if (rw == WRITE)
// // return 0;
// // /* Needs synchronization with the cleaner */
// // return blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
// // get_data_block_ro);
// return 0;
// }
// void VnodeF2fs::F2fsInvalidateDataPage(struct Page *page, unsigned long offset)
// {
// // struct inode *inode = page->mapping->host;
// struct inode *inode = new struct inode();
// struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
// if (S_ISDIR(inode->i_mode) && PageDirty(page)) {
// DecPageCount(sbi, F2FS_DIRTY_DENTS);
// InodeDecDirtyDents(inode);
// }
// ClearPagePrivate(page);
// }
// int VnodeF2fs::F2fsReleaseDataPage(struct Page *page, gfp_t wait)
// {
// ClearPagePrivate(page);
// return 0;
// }
// int VnodeF2fs::F2fsSetDataPageDirty(Page *page) {
// SetPageUptodate(page);
// if (!PageDirty(page)) {
// // __set_page_dirty_nobuffers(page);
// FlushDirtyDataPage(Vfs(), page);
// Vfs()->SetDirtyDirPage(this, page);
// return 1;
// }
// return 0;
// }
#endif
} // namespace f2fs