2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-04 11:24:04 +08:00

f2fs updates for v3.10

This patch-set includes the following major enhancement patches.
 o introduce a new gloabl lock scheme
 o add tracepoints on several major functions
 o fix the overall cleaning process focused on victim selection
 o apply the block plugging to merge IOs as much as possible
 o enhance management of free nids and its list
 o enhance the readahead mode for node pages
 o address several cretical deadlock conditions
 o reduce lock_page calls
 
 The other minor bug fixes and enhancements are as follows.
 o calculation mistakes: overflow
 o bio types: READ, READA, and READ_SYNC
 o fix the recovery flow, data races, and null pointer errors
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJRijCLAAoJEEAUqH6CSFDSg9kQAIqxmQzCUvCN3HcyVe8bGhKz
 8xhKrAY6ySRCKMuBbFRQsNrXUhckE3A44DgzYm5/gQikr/c8zhbqPVrtZ968eCKb
 wm3J+Re/uwZr5eOXlJEaHIiSkMDtERN7Cu2oYJWZi2B9wCSZcgvoWQ3c3LUVk6yF
 GFdi1Y00ll5tFKbEGbXSsfdul9P8jp0MmuMnWBBQZF3TrjETXMdThA5FXN0yTf9s
 XkcGE9vTCCPk8p7P3YmGGw6CwlaL8oallm0//iL4nMNpJzveq2C09IlY2BNrxU3L
 iTNXeIBdbhwXpnh2zq26Cy+cIEDIp0oXYui5BYdr/LWyWU3T/INa+hjUUszsESxF
 51LIUA1rA9nX/BSmj2QomswZ3lt4u5jl6rSBFKv3NG1KsFrAdb8S4tHukRSTSxAJ
 gzpY6kLT1+bgciA16F5W4yhzMYPN5hPa8s6hx4LHlpoqQICQsurjtS9KW7vncLFt
 ttmCMn8ehHcTzKRNNqYaBerCtSB3Z3G/uAy1y+DB7Zx2h2mqhCBXRalyRvs7RKvK
 d5OyYCpHntxuzDwVuivnr9Ddp30LUP1WqexxK+ykn99Ji3leMmffHP8Oari8w96b
 RxSbjoo8hOgoS5xZ4v3AaqtLDlBpxC6oWJzDaq/fJeKxOx22Z5BDFUM9mBGxrouJ
 AATl8b+cW/aTZ4l7WOPU
 =Hqii
 -----END PGP SIGNATURE-----

Merge tag 'f2fs-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs updates from Jaegeuk Kim:
 "This patch-set includes the following major enhancement patches.
   - introduce a new gloabl lock scheme
   - add tracepoints on several major functions
   - fix the overall cleaning process focused on victim selection
   - apply the block plugging to merge IOs as much as possible
   - enhance management of free nids and its list
   - enhance the readahead mode for node pages
   - address several cretical deadlock conditions
   - reduce lock_page calls

  The other minor bug fixes and enhancements are as follows.
   - calculation mistakes: overflow
   - bio types: READ, READA, and READ_SYNC
   - fix the recovery flow, data races, and null pointer errors"

* tag 'f2fs-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (68 commits)
  f2fs: cover free_nid management with spin_lock
  f2fs: optimize scan_nat_page()
  f2fs: code cleanup for scan_nat_page() and build_free_nids()
  f2fs: bugfix for alloc_nid_failed()
  f2fs: recover when journal contains deleted files
  f2fs: continue to mount after failing recovery
  f2fs: avoid deadlock during evict after f2fs_gc
  f2fs: modify the number of issued pages to merge IOs
  f2fs: remove useless #include <linux/proc_fs.h> as we're now using sysfs as debug entry.
  f2fs: fix inconsistent using of NM_WOUT_THRESHOLD
  f2fs: check truncation of mapping after lock_page
  f2fs: enhance alloc_nid and build_free_nids flows
  f2fs: add a tracepoint on f2fs_new_inode
  f2fs: check nid == 0 in add_free_nid
  f2fs: add REQ_META about metadata requests for submit
  f2fs: give a chance to merge IOs by IO scheduler
  f2fs: avoid frequent background GC
  f2fs: add tracepoints to debug checkpoint request
  f2fs: add tracepoints for write page operations
  f2fs: add tracepoints to debug the block allocation
  ...
This commit is contained in:
Linus Torvalds 2013-05-08 15:11:48 -07:00
commit 942d33da99
20 changed files with 1680 additions and 695 deletions

View File

@ -146,7 +146,7 @@ USAGE
Format options Format options
-------------- --------------
-l [label] : Give a volume label, up to 256 unicode name. -l [label] : Give a volume label, up to 512 unicode name.
-a [0 or 1] : Split start location of each area for heap-based allocation. -a [0 or 1] : Split start location of each area for heap-based allocation.
1 is set by default, which performs this. 1 is set by default, which performs this.
-o [int] : Set overprovision ratio in percent over volume size. -o [int] : Set overprovision ratio in percent over volume size.
@ -156,6 +156,8 @@ Format options
-z [int] : Set the number of sections per zone. -z [int] : Set the number of sections per zone.
1 is set by default. 1 is set by default.
-e [str] : Set basic extension list. e.g. "mp3,gif,mov" -e [str] : Set basic extension list. e.g. "mp3,gif,mov"
-t [0 or 1] : Disable discard command or not.
1 is set by default, which conducts discard.
================================================================================ ================================================================================
DESIGN DESIGN

View File

@ -20,6 +20,7 @@
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "segment.h" #include "segment.h"
#include <trace/events/f2fs.h>
static struct kmem_cache *orphan_entry_slab; static struct kmem_cache *orphan_entry_slab;
static struct kmem_cache *inode_entry_slab; static struct kmem_cache *inode_entry_slab;
@ -57,13 +58,19 @@ repeat:
cond_resched(); cond_resched();
goto repeat; goto repeat;
} }
if (f2fs_readpage(sbi, page, index, READ_SYNC)) { if (PageUptodate(page))
goto out;
if (f2fs_readpage(sbi, page, index, READ_SYNC))
goto repeat;
lock_page(page);
if (page->mapping != mapping) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
goto repeat; goto repeat;
} }
out:
mark_page_accessed(page); mark_page_accessed(page);
/* We do not allow returning an errorneous page */
return page; return page;
} }
@ -541,54 +548,44 @@ retry:
*/ */
static void block_operations(struct f2fs_sb_info *sbi) static void block_operations(struct f2fs_sb_info *sbi)
{ {
int t;
struct writeback_control wbc = { struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL, .sync_mode = WB_SYNC_ALL,
.nr_to_write = LONG_MAX, .nr_to_write = LONG_MAX,
.for_reclaim = 0, .for_reclaim = 0,
}; };
struct blk_plug plug;
/* Stop renaming operation */ blk_start_plug(&plug);
mutex_lock_op(sbi, RENAME);
mutex_lock_op(sbi, DENTRY_OPS); retry_flush_dents:
mutex_lock_all(sbi);
retry_dents:
/* write all the dirty dentry pages */ /* write all the dirty dentry pages */
sync_dirty_dir_inodes(sbi);
mutex_lock_op(sbi, DATA_WRITE);
if (get_pages(sbi, F2FS_DIRTY_DENTS)) { if (get_pages(sbi, F2FS_DIRTY_DENTS)) {
mutex_unlock_op(sbi, DATA_WRITE); mutex_unlock_all(sbi);
goto retry_dents; sync_dirty_dir_inodes(sbi);
goto retry_flush_dents;
} }
/* block all the operations */
for (t = DATA_NEW; t <= NODE_TRUNC; t++)
mutex_lock_op(sbi, t);
mutex_lock(&sbi->write_inode);
/* /*
* POR: we should ensure that there is no dirty node pages * POR: we should ensure that there is no dirty node pages
* until finishing nat/sit flush. * until finishing nat/sit flush.
*/ */
retry: retry_flush_nodes:
sync_node_pages(sbi, 0, &wbc); mutex_lock(&sbi->node_write);
mutex_lock_op(sbi, NODE_WRITE);
if (get_pages(sbi, F2FS_DIRTY_NODES)) { if (get_pages(sbi, F2FS_DIRTY_NODES)) {
mutex_unlock_op(sbi, NODE_WRITE); mutex_unlock(&sbi->node_write);
goto retry; sync_node_pages(sbi, 0, &wbc);
goto retry_flush_nodes;
} }
mutex_unlock(&sbi->write_inode); blk_finish_plug(&plug);
} }
static void unblock_operations(struct f2fs_sb_info *sbi) static void unblock_operations(struct f2fs_sb_info *sbi)
{ {
int t; mutex_unlock(&sbi->node_write);
for (t = NODE_WRITE; t >= RENAME; t--) mutex_unlock_all(sbi);
mutex_unlock_op(sbi, t);
} }
static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
@ -727,9 +724,13 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
unsigned long long ckpt_ver; unsigned long long ckpt_ver;
trace_f2fs_write_checkpoint(sbi->sb, is_umount, "start block_ops");
mutex_lock(&sbi->cp_mutex); mutex_lock(&sbi->cp_mutex);
block_operations(sbi); block_operations(sbi);
trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish block_ops");
f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, DATA, true);
f2fs_submit_bio(sbi, NODE, true); f2fs_submit_bio(sbi, NODE, true);
f2fs_submit_bio(sbi, META, true); f2fs_submit_bio(sbi, META, true);
@ -746,13 +747,13 @@ void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
flush_nat_entries(sbi); flush_nat_entries(sbi);
flush_sit_entries(sbi); flush_sit_entries(sbi);
reset_victim_segmap(sbi);
/* unlock all the fs_lock[] in do_checkpoint() */ /* unlock all the fs_lock[] in do_checkpoint() */
do_checkpoint(sbi, is_umount); do_checkpoint(sbi, is_umount);
unblock_operations(sbi); unblock_operations(sbi);
mutex_unlock(&sbi->cp_mutex); mutex_unlock(&sbi->cp_mutex);
trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish checkpoint");
} }
void init_orphan_info(struct f2fs_sb_info *sbi) void init_orphan_info(struct f2fs_sb_info *sbi)

View File

@ -22,6 +22,7 @@
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "segment.h" #include "segment.h"
#include <trace/events/f2fs.h>
/* /*
* Lock ordering for the change of data block address: * Lock ordering for the change of data block address:
@ -55,6 +56,8 @@ int reserve_new_block(struct dnode_of_data *dn)
if (!inc_valid_block_count(sbi, dn->inode, 1)) if (!inc_valid_block_count(sbi, dn->inode, 1))
return -ENOSPC; return -ENOSPC;
trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node);
__set_data_blkaddr(dn, NEW_ADDR); __set_data_blkaddr(dn, NEW_ADDR);
dn->data_blkaddr = NEW_ADDR; dn->data_blkaddr = NEW_ADDR;
sync_inode_page(dn); sync_inode_page(dn);
@ -134,7 +137,7 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn)
goto end_update; goto end_update;
} }
/* Frone merge */ /* Front merge */
if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) { if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) {
fi->ext.fofs--; fi->ext.fofs--;
fi->ext.blk_addr--; fi->ext.blk_addr--;
@ -170,7 +173,7 @@ end_update:
return; return;
} }
struct page *find_data_page(struct inode *inode, pgoff_t index) struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
@ -184,7 +187,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, RDONLY_NODE); err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
@ -200,12 +203,20 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); if (PageUptodate(page)) {
if (err) { unlock_page(page);
f2fs_put_page(page, 1); return page;
return ERR_PTR(err); }
err = f2fs_readpage(sbi, page, dn.data_blkaddr,
sync ? READ_SYNC : READA);
if (sync) {
wait_on_page_locked(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 0);
return ERR_PTR(-EIO);
}
} }
unlock_page(page);
return page; return page;
} }
@ -223,14 +234,14 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
int err; int err;
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, RDONLY_NODE); err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
if (dn.data_blkaddr == NULL_ADDR) if (dn.data_blkaddr == NULL_ADDR)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
repeat:
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -242,9 +253,17 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
BUG_ON(dn.data_blkaddr == NULL_ADDR); BUG_ON(dn.data_blkaddr == NULL_ADDR);
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
} }
return page; return page;
} }
@ -252,6 +271,9 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
/* /*
* Caller ensures that this data page is never allocated. * Caller ensures that this data page is never allocated.
* A new zero-filled data page is allocated in the page cache. * A new zero-filled data page is allocated in the page cache.
*
* Also, caller should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op().
*/ */
struct page *get_new_data_page(struct inode *inode, pgoff_t index, struct page *get_new_data_page(struct inode *inode, pgoff_t index,
bool new_i_size) bool new_i_size)
@ -263,7 +285,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
int err; int err;
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, 0); err = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
@ -274,7 +296,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
} }
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
repeat:
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -284,14 +306,21 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
SetPageUptodate(page);
} else { } else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
} }
} }
SetPageUptodate(page);
if (new_i_size && if (new_i_size &&
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
@ -326,21 +355,15 @@ static void read_end_io(struct bio *bio, int err)
/* /*
* Fill the locked page with data located in the block address. * Fill the locked page with data located in the block address.
* Read operation is synchronous, and caller must unlock the page. * Return unlocked page.
*/ */
int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
block_t blk_addr, int type) block_t blk_addr, int type)
{ {
struct block_device *bdev = sbi->sb->s_bdev; struct block_device *bdev = sbi->sb->s_bdev;
bool sync = (type == READ_SYNC);
struct bio *bio; struct bio *bio;
/* This page can be already read by other threads */ trace_f2fs_readpage(page, blk_addr, type);
if (PageUptodate(page)) {
if (!sync)
unlock_page(page);
return 0;
}
down_read(&sbi->bio_sem); down_read(&sbi->bio_sem);
@ -355,18 +378,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
kfree(bio->bi_private); kfree(bio->bi_private);
bio_put(bio); bio_put(bio);
up_read(&sbi->bio_sem); up_read(&sbi->bio_sem);
f2fs_put_page(page, 1);
return -EFAULT; return -EFAULT;
} }
submit_bio(type, bio); submit_bio(type, bio);
up_read(&sbi->bio_sem); up_read(&sbi->bio_sem);
/* wait for read completion if sync */
if (sync) {
lock_page(page);
if (PageError(page))
return -EIO;
}
return 0; return 0;
} }
@ -388,14 +405,18 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock,
/* Get the page offset from the block offset(iblock) */ /* Get the page offset from the block offset(iblock) */
pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits)); pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits));
if (check_extent_cache(inode, pgofs, bh_result)) if (check_extent_cache(inode, pgofs, bh_result)) {
trace_f2fs_get_data_block(inode, iblock, bh_result, 0);
return 0; return 0;
}
/* When reading holes, we need its node page */ /* When reading holes, we need its node page */
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, pgofs, RDONLY_NODE); err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA);
if (err) if (err) {
trace_f2fs_get_data_block(inode, iblock, bh_result, err);
return (err == -ENOENT) ? 0 : err; return (err == -ENOENT) ? 0 : err;
}
/* It does not support data allocation */ /* It does not support data allocation */
BUG_ON(create); BUG_ON(create);
@ -420,6 +441,7 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock,
bh_result->b_size = (i << blkbits); bh_result->b_size = (i << blkbits);
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
trace_f2fs_get_data_block(inode, iblock, bh_result, 0);
return 0; return 0;
} }
@ -438,13 +460,12 @@ static int f2fs_read_data_pages(struct file *file,
int do_write_data_page(struct page *page) int do_write_data_page(struct page *page)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
block_t old_blk_addr, new_blk_addr; block_t old_blk_addr, new_blk_addr;
struct dnode_of_data dn; struct dnode_of_data dn;
int err = 0; int err = 0;
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, page->index, RDONLY_NODE); err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
if (err) if (err)
return err; return err;
@ -468,8 +489,6 @@ int do_write_data_page(struct page *page)
write_data_page(inode, page, &dn, write_data_page(inode, page, &dn,
old_blk_addr, &new_blk_addr); old_blk_addr, &new_blk_addr);
update_extent_cache(new_blk_addr, &dn); update_extent_cache(new_blk_addr, &dn);
F2FS_I(inode)->data_version =
le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
} }
out_writepage: out_writepage:
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
@ -485,10 +504,11 @@ static int f2fs_write_data_page(struct page *page,
const pgoff_t end_index = ((unsigned long long) i_size) const pgoff_t end_index = ((unsigned long long) i_size)
>> PAGE_CACHE_SHIFT; >> PAGE_CACHE_SHIFT;
unsigned offset; unsigned offset;
bool need_balance_fs = false;
int err = 0; int err = 0;
if (page->index < end_index) if (page->index < end_index)
goto out; goto write;
/* /*
* If the offset is out-of-range of file size, * If the offset is out-of-range of file size,
@ -500,50 +520,46 @@ static int f2fs_write_data_page(struct page *page,
dec_page_count(sbi, F2FS_DIRTY_DENTS); dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(inode); inode_dec_dirty_dents(inode);
} }
goto unlock_out; goto out;
} }
zero_user_segment(page, offset, PAGE_CACHE_SIZE); zero_user_segment(page, offset, PAGE_CACHE_SIZE);
out: write:
if (sbi->por_doing) if (sbi->por_doing) {
err = AOP_WRITEPAGE_ACTIVATE;
goto redirty_out; goto redirty_out;
}
if (wbc->for_reclaim && !S_ISDIR(inode->i_mode) && !is_cold_data(page)) /* Dentry blocks are controlled by checkpoint */
goto redirty_out;
mutex_lock_op(sbi, DATA_WRITE);
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
dec_page_count(sbi, F2FS_DIRTY_DENTS); dec_page_count(sbi, F2FS_DIRTY_DENTS);
inode_dec_dirty_dents(inode); inode_dec_dirty_dents(inode);
err = do_write_data_page(page);
} else {
int ilock = mutex_lock_op(sbi);
err = do_write_data_page(page);
mutex_unlock_op(sbi, ilock);
need_balance_fs = true;
} }
err = do_write_data_page(page); if (err == -ENOENT)
if (err && err != -ENOENT) { goto out;
wbc->pages_skipped++; else if (err)
set_page_dirty(page); goto redirty_out;
}
mutex_unlock_op(sbi, DATA_WRITE);
if (wbc->for_reclaim) if (wbc->for_reclaim)
f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, DATA, true);
if (err == -ENOENT)
goto unlock_out;
clear_cold_data(page); clear_cold_data(page);
out:
unlock_page(page); unlock_page(page);
if (need_balance_fs)
if (!wbc->for_reclaim && !S_ISDIR(inode->i_mode))
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
return 0; return 0;
unlock_out:
unlock_page(page);
return (err == -ENOENT) ? 0 : err;
redirty_out: redirty_out:
wbc->pages_skipped++; wbc->pages_skipped++;
set_page_dirty(page); set_page_dirty(page);
return AOP_WRITEPAGE_ACTIVATE; return err;
} }
#define MAX_DESIRED_PAGES_WP 4096 #define MAX_DESIRED_PAGES_WP 4096
@ -562,19 +578,26 @@ static int f2fs_write_data_pages(struct address_space *mapping,
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
bool locked = false;
int ret; int ret;
long excess_nrtw = 0, desired_nrtw; long excess_nrtw = 0, desired_nrtw;
/* deal with chardevs and other special file */
if (!mapping->a_ops->writepage)
return 0;
if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) { if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) {
desired_nrtw = MAX_DESIRED_PAGES_WP; desired_nrtw = MAX_DESIRED_PAGES_WP;
excess_nrtw = desired_nrtw - wbc->nr_to_write; excess_nrtw = desired_nrtw - wbc->nr_to_write;
wbc->nr_to_write = desired_nrtw; wbc->nr_to_write = desired_nrtw;
} }
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode)) {
mutex_lock(&sbi->writepages); mutex_lock(&sbi->writepages);
locked = true;
}
ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
if (!S_ISDIR(inode->i_mode)) if (locked)
mutex_unlock(&sbi->writepages); mutex_unlock(&sbi->writepages);
f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL));
@ -594,39 +617,33 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT;
struct dnode_of_data dn; struct dnode_of_data dn;
int err = 0; int err = 0;
int ilock;
/* for nobh_write_end */ /* for nobh_write_end */
*fsdata = NULL; *fsdata = NULL;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
repeat:
page = grab_cache_page_write_begin(mapping, index, flags); page = grab_cache_page_write_begin(mapping, index, flags);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
*pagep = page; *pagep = page;
mutex_lock_op(sbi, DATA_NEW); ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, 0); err = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (err) { if (err)
mutex_unlock_op(sbi, DATA_NEW); goto err;
f2fs_put_page(page, 1);
return err;
}
if (dn.data_blkaddr == NULL_ADDR) { if (dn.data_blkaddr == NULL_ADDR)
err = reserve_new_block(&dn); err = reserve_new_block(&dn);
if (err) {
f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, DATA_NEW);
f2fs_put_page(page, 1);
return err;
}
}
f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, DATA_NEW); f2fs_put_dnode(&dn);
if (err)
goto err;
mutex_unlock_op(sbi, ilock);
if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
return 0; return 0;
@ -637,21 +654,34 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
/* Reading beyond i_size is simple: memset to zero */ /* Reading beyond i_size is simple: memset to zero */
zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
return 0; goto out;
} }
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else { } else {
err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
if (err) { if (err)
f2fs_put_page(page, 1);
return err; return err;
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return -EIO;
}
if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
} }
} }
out:
SetPageUptodate(page); SetPageUptodate(page);
clear_cold_data(page); clear_cold_data(page);
return 0; return 0;
err:
mutex_unlock_op(sbi, ilock);
f2fs_put_page(page, 1);
return err;
} }
static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
@ -682,7 +712,7 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned long offset)
static int f2fs_release_data_page(struct page *page, gfp_t wait) static int f2fs_release_data_page(struct page *page, gfp_t wait)
{ {
ClearPagePrivate(page); ClearPagePrivate(page);
return 0; return 1;
} }
static int f2fs_set_data_page_dirty(struct page *page) static int f2fs_set_data_page_dirty(struct page *page)

View File

@ -13,7 +13,6 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/proc_fs.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
@ -106,7 +105,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
} }
} }
mutex_unlock(&sit_i->sentry_lock); mutex_unlock(&sit_i->sentry_lock);
dist = sbi->total_sections * hblks_per_sec * hblks_per_sec / 100; dist = TOTAL_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
si->bimodal = bimodal / dist; si->bimodal = bimodal / dist;
if (si->dirty_count) if (si->dirty_count)
si->avg_vblocks = total_vblocks / ndirty; si->avg_vblocks = total_vblocks / ndirty;
@ -138,14 +137,13 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi)); si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi));
si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * TOTAL_SEGS(sbi); si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * TOTAL_SEGS(sbi);
if (sbi->segs_per_sec > 1) if (sbi->segs_per_sec > 1)
si->base_mem += sbi->total_sections * si->base_mem += TOTAL_SECS(sbi) * sizeof(struct sec_entry);
sizeof(struct sec_entry);
si->base_mem += __bitmap_size(sbi, SIT_BITMAP); si->base_mem += __bitmap_size(sbi, SIT_BITMAP);
/* build free segmap */ /* build free segmap */
si->base_mem += sizeof(struct free_segmap_info); si->base_mem += sizeof(struct free_segmap_info);
si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi)); si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi));
si->base_mem += f2fs_bitmap_size(sbi->total_sections); si->base_mem += f2fs_bitmap_size(TOTAL_SECS(sbi));
/* build curseg */ /* build curseg */
si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE; si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE;
@ -154,7 +152,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
/* build dirty segmap */ /* build dirty segmap */
si->base_mem += sizeof(struct dirty_seglist_info); si->base_mem += sizeof(struct dirty_seglist_info);
si->base_mem += NR_DIRTY_TYPE * f2fs_bitmap_size(TOTAL_SEGS(sbi)); si->base_mem += NR_DIRTY_TYPE * f2fs_bitmap_size(TOTAL_SEGS(sbi));
si->base_mem += 2 * f2fs_bitmap_size(TOTAL_SEGS(sbi)); si->base_mem += f2fs_bitmap_size(TOTAL_SECS(sbi));
/* buld nm */ /* buld nm */
si->base_mem += sizeof(struct f2fs_nm_info); si->base_mem += sizeof(struct f2fs_nm_info);

View File

@ -148,7 +148,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
for (; bidx < end_block; bidx++) { for (; bidx < end_block; bidx++) {
/* no need to allocate new dentry pages to all the indices */ /* no need to allocate new dentry pages to all the indices */
dentry_page = find_data_page(dir, bidx); dentry_page = find_data_page(dir, bidx, true);
if (IS_ERR(dentry_page)) { if (IS_ERR(dentry_page)) {
room = true; room = true;
continue; continue;
@ -189,6 +189,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
unsigned int max_depth; unsigned int max_depth;
unsigned int level; unsigned int level;
if (namelen > F2FS_NAME_LEN)
return NULL;
if (npages == 0) if (npages == 0)
return NULL; return NULL;
@ -246,9 +249,6 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
struct page *page, struct inode *inode) struct page *page, struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
mutex_lock_op(sbi, DENTRY_OPS);
lock_page(page); lock_page(page);
wait_on_page_writeback(page); wait_on_page_writeback(page);
de->ino = cpu_to_le32(inode->i_ino); de->ino = cpu_to_le32(inode->i_ino);
@ -262,7 +262,6 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
F2FS_I(inode)->i_pino = dir->i_ino; F2FS_I(inode)->i_pino = dir->i_ino;
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
mutex_unlock_op(sbi, DENTRY_OPS);
} }
void init_dent_inode(const struct qstr *name, struct page *ipage) void init_dent_inode(const struct qstr *name, struct page *ipage)
@ -281,6 +280,43 @@ void init_dent_inode(const struct qstr *name, struct page *ipage)
set_page_dirty(ipage); set_page_dirty(ipage);
} }
static int make_empty_dir(struct inode *inode, struct inode *parent)
{
struct page *dentry_page;
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de;
void *kaddr;
dentry_page = get_new_data_page(inode, 0, true);
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
kaddr = kmap_atomic(dentry_page);
dentry_blk = (struct f2fs_dentry_block *)kaddr;
de = &dentry_blk->dentry[0];
de->name_len = cpu_to_le16(1);
de->hash_code = 0;
de->ino = cpu_to_le32(inode->i_ino);
memcpy(dentry_blk->filename[0], ".", 1);
set_de_type(de, inode);
de = &dentry_blk->dentry[1];
de->hash_code = 0;
de->name_len = cpu_to_le16(2);
de->ino = cpu_to_le32(parent->i_ino);
memcpy(dentry_blk->filename[1], "..", 2);
set_de_type(de, inode);
test_and_set_bit_le(0, &dentry_blk->dentry_bitmap);
test_and_set_bit_le(1, &dentry_blk->dentry_bitmap);
kunmap_atomic(kaddr);
set_page_dirty(dentry_page);
f2fs_put_page(dentry_page, 1);
return 0;
}
static int init_inode_metadata(struct inode *inode, static int init_inode_metadata(struct inode *inode,
struct inode *dir, const struct qstr *name) struct inode *dir, const struct qstr *name)
{ {
@ -291,7 +327,7 @@ static int init_inode_metadata(struct inode *inode,
return err; return err;
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
err = f2fs_make_empty(inode, dir); err = make_empty_dir(inode, dir);
if (err) { if (err) {
remove_inode_page(inode); remove_inode_page(inode);
return err; return err;
@ -314,7 +350,7 @@ static int init_inode_metadata(struct inode *inode,
} }
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) {
inc_nlink(inode); inc_nlink(inode);
f2fs_write_inode(inode, NULL); update_inode_page(inode);
} }
return 0; return 0;
} }
@ -338,7 +374,7 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
} }
if (need_dir_update) if (need_dir_update)
f2fs_write_inode(dir, NULL); update_inode_page(dir);
else else
mark_inode_dirty(dir); mark_inode_dirty(dir);
@ -370,6 +406,10 @@ next:
goto next; goto next;
} }
/*
* Caller should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op().
*/
int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode) int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode)
{ {
unsigned int bit_pos; unsigned int bit_pos;
@ -379,7 +419,6 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
f2fs_hash_t dentry_hash; f2fs_hash_t dentry_hash;
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
unsigned int nbucket, nblock; unsigned int nbucket, nblock;
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
size_t namelen = name->len; size_t namelen = name->len;
struct page *dentry_page = NULL; struct page *dentry_page = NULL;
struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_block *dentry_blk = NULL;
@ -409,12 +448,9 @@ start:
bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket)); bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket));
for (block = bidx; block <= (bidx + nblock - 1); block++) { for (block = bidx; block <= (bidx + nblock - 1); block++) {
mutex_lock_op(sbi, DENTRY_OPS);
dentry_page = get_new_data_page(dir, block, true); dentry_page = get_new_data_page(dir, block, true);
if (IS_ERR(dentry_page)) { if (IS_ERR(dentry_page))
mutex_unlock_op(sbi, DENTRY_OPS);
return PTR_ERR(dentry_page); return PTR_ERR(dentry_page);
}
dentry_blk = kmap(dentry_page); dentry_blk = kmap(dentry_page);
bit_pos = room_for_filename(dentry_blk, slots); bit_pos = room_for_filename(dentry_blk, slots);
@ -423,7 +459,6 @@ start:
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
mutex_unlock_op(sbi, DENTRY_OPS);
} }
/* Move to next level to find the empty slot for new dentry */ /* Move to next level to find the empty slot for new dentry */
@ -453,7 +488,6 @@ add_dentry:
fail: fail:
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
mutex_unlock_op(sbi, DENTRY_OPS);
return err; return err;
} }
@ -473,8 +507,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
void *kaddr = page_address(page); void *kaddr = page_address(page);
int i; int i;
mutex_lock_op(sbi, DENTRY_OPS);
lock_page(page); lock_page(page);
wait_on_page_writeback(page); wait_on_page_writeback(page);
@ -494,7 +526,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
if (inode && S_ISDIR(inode->i_mode)) { if (inode && S_ISDIR(inode->i_mode)) {
drop_nlink(dir); drop_nlink(dir);
f2fs_write_inode(dir, NULL); update_inode_page(dir);
} else { } else {
mark_inode_dirty(dir); mark_inode_dirty(dir);
} }
@ -506,7 +538,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
drop_nlink(inode); drop_nlink(inode);
i_size_write(inode, 0); i_size_write(inode, 0);
} }
f2fs_write_inode(inode, NULL); update_inode_page(inode);
if (inode->i_nlink == 0) if (inode->i_nlink == 0)
add_orphan_inode(sbi, inode->i_ino); add_orphan_inode(sbi, inode->i_ino);
} }
@ -519,45 +552,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
inode_dec_dirty_dents(dir); inode_dec_dirty_dents(dir);
} }
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
mutex_unlock_op(sbi, DENTRY_OPS);
}
int f2fs_make_empty(struct inode *inode, struct inode *parent)
{
struct page *dentry_page;
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de;
void *kaddr;
dentry_page = get_new_data_page(inode, 0, true);
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
kaddr = kmap_atomic(dentry_page);
dentry_blk = (struct f2fs_dentry_block *)kaddr;
de = &dentry_blk->dentry[0];
de->name_len = cpu_to_le16(1);
de->hash_code = f2fs_dentry_hash(".", 1);
de->ino = cpu_to_le32(inode->i_ino);
memcpy(dentry_blk->filename[0], ".", 1);
set_de_type(de, inode);
de = &dentry_blk->dentry[1];
de->hash_code = f2fs_dentry_hash("..", 2);
de->name_len = cpu_to_le16(2);
de->ino = cpu_to_le32(parent->i_ino);
memcpy(dentry_blk->filename[1], "..", 2);
set_de_type(de, inode);
test_and_set_bit_le(0, &dentry_blk->dentry_bitmap);
test_and_set_bit_le(1, &dentry_blk->dentry_bitmap);
kunmap_atomic(kaddr);
set_page_dirty(dentry_page);
f2fs_put_page(dentry_page, 1);
return 0;
} }
bool f2fs_empty_dir(struct inode *dir) bool f2fs_empty_dir(struct inode *dir)

View File

@ -125,11 +125,15 @@ static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i)
* file keeping -1 as its node offset to * file keeping -1 as its node offset to
* distinguish from index node blocks. * distinguish from index node blocks.
*/ */
#define RDONLY_NODE 1 /* enum {
* specify a read-only mode when getting ALLOC_NODE, /* allocate a new node page if needed */
* a node block. 0 is read-write mode. LOOKUP_NODE, /* look up a node without readahead */
* used by get_dnode_of_data(). LOOKUP_NODE_RA, /*
* look up a node with readahead called
* by get_datablock_ro.
*/ */
};
#define F2FS_LINK_MAX 32000 /* maximum link count per file */ #define F2FS_LINK_MAX 32000 /* maximum link count per file */
/* for in-memory extent cache entry */ /* for in-memory extent cache entry */
@ -144,6 +148,7 @@ struct extent_info {
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later. * i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
*/ */
#define FADVISE_COLD_BIT 0x01 #define FADVISE_COLD_BIT 0x01
#define FADVISE_CP_BIT 0x02
struct f2fs_inode_info { struct f2fs_inode_info {
struct inode vfs_inode; /* serve a vfs inode */ struct inode vfs_inode; /* serve a vfs inode */
@ -155,7 +160,6 @@ struct f2fs_inode_info {
/* Use below internally in f2fs*/ /* Use below internally in f2fs*/
unsigned long flags; /* use to pass per-file flags */ unsigned long flags; /* use to pass per-file flags */
unsigned long long data_version;/* latest version of data for fsync */
atomic_t dirty_dents; /* # of dirty dentry pages */ atomic_t dirty_dents; /* # of dirty dentry pages */
f2fs_hash_t chash; /* hash value of given file name */ f2fs_hash_t chash; /* hash value of given file name */
unsigned int clevel; /* maximum level of given file name */ unsigned int clevel; /* maximum level of given file name */
@ -186,7 +190,6 @@ static inline void set_raw_extent(struct extent_info *ext,
struct f2fs_nm_info { struct f2fs_nm_info {
block_t nat_blkaddr; /* base disk address of NAT */ block_t nat_blkaddr; /* base disk address of NAT */
nid_t max_nid; /* maximum possible node ids */ nid_t max_nid; /* maximum possible node ids */
nid_t init_scan_nid; /* the first nid to be scanned */
nid_t next_scan_nid; /* the next nid to be scanned */ nid_t next_scan_nid; /* the next nid to be scanned */
/* NAT cache management */ /* NAT cache management */
@ -305,23 +308,12 @@ enum count_type {
}; };
/* /*
* FS_LOCK nesting subclasses for the lock validator: * Uses as sbi->fs_lock[NR_GLOBAL_LOCKS].
* * The checkpoint procedure blocks all the locks in this fs_lock array.
* The locking order between these classes is * Some FS operations grab free locks, and if there is no free lock,
* RENAME -> DENTRY_OPS -> DATA_WRITE -> DATA_NEW * then wait to grab a lock in a round-robin manner.
* -> DATA_TRUNC -> NODE_WRITE -> NODE_NEW -> NODE_TRUNC
*/ */
enum lock_type { #define NR_GLOBAL_LOCKS 8
RENAME, /* for renaming operations */
DENTRY_OPS, /* for directory operations */
DATA_WRITE, /* for data write */
DATA_NEW, /* for data allocation */
DATA_TRUNC, /* for data truncate */
NODE_NEW, /* for node allocation */
NODE_TRUNC, /* for node truncate */
NODE_WRITE, /* for node write */
NR_LOCK_TYPE,
};
/* /*
* The below are the page types of bios used in submti_bio(). * The below are the page types of bios used in submti_bio().
@ -361,11 +353,13 @@ struct f2fs_sb_info {
/* for checkpoint */ /* for checkpoint */
struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */
struct inode *meta_inode; /* cache meta blocks */ struct inode *meta_inode; /* cache meta blocks */
struct mutex cp_mutex; /* for checkpoint procedure */ struct mutex cp_mutex; /* checkpoint procedure lock */
struct mutex fs_lock[NR_LOCK_TYPE]; /* for blocking FS operations */ struct mutex fs_lock[NR_GLOBAL_LOCKS]; /* blocking FS operations */
struct mutex write_inode; /* mutex for write inode */ struct mutex node_write; /* locking node writes */
struct mutex writepages; /* mutex for writepages() */ struct mutex writepages; /* mutex for writepages() */
unsigned char next_lock_num; /* round-robin global locks */
int por_doing; /* recovery is doing or not */ int por_doing; /* recovery is doing or not */
int on_build_free_nids; /* build_free_nids is doing */
/* for orphan inode management */ /* for orphan inode management */
struct list_head orphan_inode_list; /* orphan inode list */ struct list_head orphan_inode_list; /* orphan inode list */
@ -406,6 +400,7 @@ struct f2fs_sb_info {
/* for cleaning operations */ /* for cleaning operations */
struct mutex gc_mutex; /* mutex for GC */ struct mutex gc_mutex; /* mutex for GC */
struct f2fs_gc_kthread *gc_thread; /* GC thread */ struct f2fs_gc_kthread *gc_thread; /* GC thread */
unsigned int cur_victim_sec; /* current victim section num */
/* /*
* for stat information. * for stat information.
@ -498,22 +493,51 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
cp->ckpt_flags = cpu_to_le32(ckpt_flags); cp->ckpt_flags = cpu_to_le32(ckpt_flags);
} }
static inline void mutex_lock_op(struct f2fs_sb_info *sbi, enum lock_type t) static inline void mutex_lock_all(struct f2fs_sb_info *sbi)
{ {
mutex_lock_nested(&sbi->fs_lock[t], t); int i = 0;
for (; i < NR_GLOBAL_LOCKS; i++)
mutex_lock(&sbi->fs_lock[i]);
} }
static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, enum lock_type t) static inline void mutex_unlock_all(struct f2fs_sb_info *sbi)
{ {
mutex_unlock(&sbi->fs_lock[t]); int i = 0;
for (; i < NR_GLOBAL_LOCKS; i++)
mutex_unlock(&sbi->fs_lock[i]);
}
static inline int mutex_lock_op(struct f2fs_sb_info *sbi)
{
unsigned char next_lock = sbi->next_lock_num % NR_GLOBAL_LOCKS;
int i = 0;
for (; i < NR_GLOBAL_LOCKS; i++)
if (mutex_trylock(&sbi->fs_lock[i]))
return i;
mutex_lock(&sbi->fs_lock[next_lock]);
sbi->next_lock_num++;
return next_lock;
}
static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, int ilock)
{
if (ilock < 0)
return;
BUG_ON(ilock >= NR_GLOBAL_LOCKS);
mutex_unlock(&sbi->fs_lock[ilock]);
} }
/* /*
* Check whether the given nid is within node id range. * Check whether the given nid is within node id range.
*/ */
static inline void check_nid_range(struct f2fs_sb_info *sbi, nid_t nid) static inline int check_nid_range(struct f2fs_sb_info *sbi, nid_t nid)
{ {
BUG_ON((nid >= NM_I(sbi)->max_nid)); WARN_ON((nid >= NM_I(sbi)->max_nid));
if (nid >= NM_I(sbi)->max_nid)
return -EINVAL;
return 0;
} }
#define F2FS_DEFAULT_ALLOCATED_BLOCKS 1 #define F2FS_DEFAULT_ALLOCATED_BLOCKS 1
@ -819,7 +843,6 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr)
/* used for f2fs_inode_info->flags */ /* used for f2fs_inode_info->flags */
enum { enum {
FI_NEW_INODE, /* indicate newly allocated inode */ FI_NEW_INODE, /* indicate newly allocated inode */
FI_NEED_CP, /* need to do checkpoint during fsync */
FI_INC_LINK, /* need to increment i_nlink */ FI_INC_LINK, /* need to increment i_nlink */
FI_ACL_MODE, /* indicate acl mode */ FI_ACL_MODE, /* indicate acl mode */
FI_NO_ALLOC, /* should not allocate any blocks */ FI_NO_ALLOC, /* should not allocate any blocks */
@ -872,6 +895,7 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long);
void f2fs_set_inode_flags(struct inode *); void f2fs_set_inode_flags(struct inode *);
struct inode *f2fs_iget(struct super_block *, unsigned long); struct inode *f2fs_iget(struct super_block *, unsigned long);
void update_inode(struct inode *, struct page *); void update_inode(struct inode *, struct page *);
int update_inode_page(struct inode *);
int f2fs_write_inode(struct inode *, struct writeback_control *); int f2fs_write_inode(struct inode *, struct writeback_control *);
void f2fs_evict_inode(struct inode *); void f2fs_evict_inode(struct inode *);
@ -973,7 +997,6 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *,
int, unsigned int, int); int, unsigned int, int);
void flush_sit_entries(struct f2fs_sb_info *); void flush_sit_entries(struct f2fs_sb_info *);
int build_segment_manager(struct f2fs_sb_info *); int build_segment_manager(struct f2fs_sb_info *);
void reset_victim_segmap(struct f2fs_sb_info *);
void destroy_segment_manager(struct f2fs_sb_info *); void destroy_segment_manager(struct f2fs_sb_info *);
/* /*
@ -1000,7 +1023,7 @@ void destroy_checkpoint_caches(void);
*/ */
int reserve_new_block(struct dnode_of_data *); int reserve_new_block(struct dnode_of_data *);
void update_extent_cache(block_t, struct dnode_of_data *); void update_extent_cache(block_t, struct dnode_of_data *);
struct page *find_data_page(struct inode *, pgoff_t); struct page *find_data_page(struct inode *, pgoff_t, bool);
struct page *get_lock_data_page(struct inode *, pgoff_t); struct page *get_lock_data_page(struct inode *, pgoff_t);
struct page *get_new_data_page(struct inode *, pgoff_t, bool); struct page *get_new_data_page(struct inode *, pgoff_t, bool);
int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int); int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int);
@ -1020,7 +1043,7 @@ void destroy_gc_caches(void);
/* /*
* recovery.c * recovery.c
*/ */
void recover_fsync_data(struct f2fs_sb_info *); int recover_fsync_data(struct f2fs_sb_info *);
bool space_for_roll_forward(struct f2fs_sb_info *); bool space_for_roll_forward(struct f2fs_sb_info *);
/* /*

View File

@ -13,6 +13,7 @@
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/blkdev.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/compat.h> #include <linux/compat.h>
@ -24,6 +25,7 @@
#include "segment.h" #include "segment.h"
#include "xattr.h" #include "xattr.h"
#include "acl.h" #include "acl.h"
#include <trace/events/f2fs.h>
static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
struct vm_fault *vmf) struct vm_fault *vmf)
@ -33,19 +35,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
block_t old_blk_addr; block_t old_blk_addr;
struct dnode_of_data dn; struct dnode_of_data dn;
int err; int err, ilock;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
sb_start_pagefault(inode->i_sb); sb_start_pagefault(inode->i_sb);
mutex_lock_op(sbi, DATA_NEW);
/* block allocation */ /* block allocation */
ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, page->index, 0); err = get_dnode_of_data(&dn, page->index, ALLOC_NODE);
if (err) { if (err) {
mutex_unlock_op(sbi, DATA_NEW); mutex_unlock_op(sbi, ilock);
goto out; goto out;
} }
@ -55,13 +56,12 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
err = reserve_new_block(&dn); err = reserve_new_block(&dn);
if (err) { if (err) {
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, DATA_NEW); mutex_unlock_op(sbi, ilock);
goto out; goto out;
} }
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, ilock);
mutex_unlock_op(sbi, DATA_NEW);
lock_page(page); lock_page(page);
if (page->mapping != inode->i_mapping || if (page->mapping != inode->i_mapping ||
@ -102,28 +102,10 @@ static const struct vm_operations_struct f2fs_file_vm_ops = {
.remap_pages = generic_file_remap_pages, .remap_pages = generic_file_remap_pages,
}; };
static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode)
{
struct dentry *dentry;
nid_t pino;
inode = igrab(inode);
dentry = d_find_any_alias(inode);
if (!dentry) {
iput(inode);
return 0;
}
pino = dentry->d_parent->d_inode->i_ino;
dput(dentry);
iput(inode);
return !is_checkpointed_node(sbi, pino);
}
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
unsigned long long cur_version;
int ret = 0; int ret = 0;
bool need_cp = false; bool need_cp = false;
struct writeback_control wbc = { struct writeback_control wbc = {
@ -135,9 +117,12 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (inode->i_sb->s_flags & MS_RDONLY) if (inode->i_sb->s_flags & MS_RDONLY)
return 0; return 0;
trace_f2fs_sync_file_enter(inode);
ret = filemap_write_and_wait_range(inode->i_mapping, start, end); ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret) if (ret) {
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret; return ret;
}
/* guarantee free sections for fsync */ /* guarantee free sections for fsync */
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -147,28 +132,18 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
goto out; goto out;
mutex_lock(&sbi->cp_mutex);
cur_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
mutex_unlock(&sbi->cp_mutex);
if (F2FS_I(inode)->data_version != cur_version &&
!(inode->i_state & I_DIRTY))
goto out;
F2FS_I(inode)->data_version--;
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
need_cp = true; need_cp = true;
else if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) else if (is_cp_file(inode))
need_cp = true; need_cp = true;
else if (!space_for_roll_forward(sbi)) else if (!space_for_roll_forward(sbi))
need_cp = true; need_cp = true;
else if (need_to_sync_dir(sbi, inode)) else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
need_cp = true; need_cp = true;
if (need_cp) { if (need_cp) {
/* all the dirty node pages should be flushed for POR */ /* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1); ret = f2fs_sync_fs(inode->i_sb, 1);
clear_inode_flag(F2FS_I(inode), FI_NEED_CP);
} else { } else {
/* if there is no written node page, write its inode page */ /* if there is no written node page, write its inode page */
while (!sync_node_pages(sbi, inode->i_ino, &wbc)) { while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
@ -178,9 +153,11 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
} }
filemap_fdatawait_range(sbi->node_inode->i_mapping, filemap_fdatawait_range(sbi->node_inode->i_mapping,
0, LONG_MAX); 0, LONG_MAX);
ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
} }
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret; return ret;
} }
@ -216,6 +193,9 @@ static int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
sync_inode_page(dn); sync_inode_page(dn);
} }
dn->ofs_in_node = ofs; dn->ofs_in_node = ofs;
trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
dn->ofs_in_node, nr_free);
return nr_free; return nr_free;
} }
@ -232,11 +212,15 @@ static void truncate_partial_data_page(struct inode *inode, u64 from)
if (!offset) if (!offset)
return; return;
page = find_data_page(inode, from >> PAGE_CACHE_SHIFT); page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
if (IS_ERR(page)) if (IS_ERR(page))
return; return;
lock_page(page); lock_page(page);
if (page->mapping != inode->i_mapping) {
f2fs_put_page(page, 1);
return;
}
wait_on_page_writeback(page); wait_on_page_writeback(page);
zero_user(page, offset, PAGE_CACHE_SIZE - offset); zero_user(page, offset, PAGE_CACHE_SIZE - offset);
set_page_dirty(page); set_page_dirty(page);
@ -249,20 +233,22 @@ static int truncate_blocks(struct inode *inode, u64 from)
unsigned int blocksize = inode->i_sb->s_blocksize; unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn; struct dnode_of_data dn;
pgoff_t free_from; pgoff_t free_from;
int count = 0; int count = 0, ilock = -1;
int err; int err;
trace_f2fs_truncate_blocks_enter(inode, from);
free_from = (pgoff_t) free_from = (pgoff_t)
((from + blocksize - 1) >> (sbi->log_blocksize)); ((from + blocksize - 1) >> (sbi->log_blocksize));
mutex_lock_op(sbi, DATA_TRUNC); ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, free_from, RDONLY_NODE); err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
if (err) { if (err) {
if (err == -ENOENT) if (err == -ENOENT)
goto free_next; goto free_next;
mutex_unlock_op(sbi, DATA_TRUNC); mutex_unlock_op(sbi, ilock);
trace_f2fs_truncate_blocks_exit(inode, err);
return err; return err;
} }
@ -273,6 +259,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
count -= dn.ofs_in_node; count -= dn.ofs_in_node;
BUG_ON(count < 0); BUG_ON(count < 0);
if (dn.ofs_in_node || IS_INODE(dn.node_page)) { if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
truncate_data_blocks_range(&dn, count); truncate_data_blocks_range(&dn, count);
free_from += count; free_from += count;
@ -281,11 +268,12 @@ static int truncate_blocks(struct inode *inode, u64 from)
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
free_next: free_next:
err = truncate_inode_blocks(inode, free_from); err = truncate_inode_blocks(inode, free_from);
mutex_unlock_op(sbi, DATA_TRUNC); mutex_unlock_op(sbi, ilock);
/* lastly zero out the first data page */ /* lastly zero out the first data page */
truncate_partial_data_page(inode, from); truncate_partial_data_page(inode, from);
trace_f2fs_truncate_blocks_exit(inode, err);
return err; return err;
} }
@ -295,6 +283,8 @@ void f2fs_truncate(struct inode *inode)
S_ISLNK(inode->i_mode))) S_ISLNK(inode->i_mode)))
return; return;
trace_f2fs_truncate(inode);
if (!truncate_blocks(inode, i_size_read(inode))) { if (!truncate_blocks(inode, i_size_read(inode))) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
@ -389,15 +379,16 @@ static void fill_zero(struct inode *inode, pgoff_t index,
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *page; struct page *page;
int ilock;
if (!len) if (!len)
return; return;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
mutex_lock_op(sbi, DATA_NEW); ilock = mutex_lock_op(sbi);
page = get_new_data_page(inode, index, false); page = get_new_data_page(inode, index, false);
mutex_unlock_op(sbi, DATA_NEW); mutex_unlock_op(sbi, ilock);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
wait_on_page_writeback(page); wait_on_page_writeback(page);
@ -414,15 +405,10 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
for (index = pg_start; index < pg_end; index++) { for (index = pg_start; index < pg_end; index++) {
struct dnode_of_data dn; struct dnode_of_data dn;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
f2fs_balance_fs(sbi);
mutex_lock_op(sbi, DATA_TRUNC);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, RDONLY_NODE); err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) { if (err) {
mutex_unlock_op(sbi, DATA_TRUNC);
if (err == -ENOENT) if (err == -ENOENT)
continue; continue;
return err; return err;
@ -431,7 +417,6 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
if (dn.data_blkaddr != NULL_ADDR) if (dn.data_blkaddr != NULL_ADDR)
truncate_data_blocks_range(&dn, 1); truncate_data_blocks_range(&dn, 1);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, DATA_TRUNC);
} }
return 0; return 0;
} }
@ -461,12 +446,19 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode)
if (pg_start < pg_end) { if (pg_start < pg_end) {
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
loff_t blk_start, blk_end; loff_t blk_start, blk_end;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
int ilock;
f2fs_balance_fs(sbi);
blk_start = pg_start << PAGE_CACHE_SHIFT; blk_start = pg_start << PAGE_CACHE_SHIFT;
blk_end = pg_end << PAGE_CACHE_SHIFT; blk_end = pg_end << PAGE_CACHE_SHIFT;
truncate_inode_pages_range(mapping, blk_start, truncate_inode_pages_range(mapping, blk_start,
blk_end - 1); blk_end - 1);
ilock = mutex_lock_op(sbi);
ret = truncate_hole(inode, pg_start, pg_end); ret = truncate_hole(inode, pg_start, pg_end);
mutex_unlock_op(sbi, ilock);
} }
} }
@ -500,13 +492,13 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
for (index = pg_start; index <= pg_end; index++) { for (index = pg_start; index <= pg_end; index++) {
struct dnode_of_data dn; struct dnode_of_data dn;
int ilock;
mutex_lock_op(sbi, DATA_NEW); ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
ret = get_dnode_of_data(&dn, index, 0); ret = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (ret) { if (ret) {
mutex_unlock_op(sbi, DATA_NEW); mutex_unlock_op(sbi, ilock);
break; break;
} }
@ -514,13 +506,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
ret = reserve_new_block(&dn); ret = reserve_new_block(&dn);
if (ret) { if (ret) {
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, DATA_NEW); mutex_unlock_op(sbi, ilock);
break; break;
} }
} }
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, ilock);
mutex_unlock_op(sbi, DATA_NEW);
if (pg_start == pg_end) if (pg_start == pg_end)
new_size = offset + len; new_size = offset + len;
@ -559,6 +550,7 @@ static long f2fs_fallocate(struct file *file, int mode,
inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
trace_f2fs_fallocate(inode, mode, offset, len, ret);
return ret; return ret;
} }

View File

@ -11,7 +11,6 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/proc_fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include <linux/kthread.h> #include <linux/kthread.h>
@ -23,6 +22,7 @@
#include "node.h" #include "node.h"
#include "segment.h" #include "segment.h"
#include "gc.h" #include "gc.h"
#include <trace/events/f2fs.h>
static struct kmem_cache *winode_slab; static struct kmem_cache *winode_slab;
@ -81,9 +81,6 @@ static int gc_thread_func(void *data)
/* if return value is not zero, no victim was selected */ /* if return value is not zero, no victim was selected */
if (f2fs_gc(sbi)) if (f2fs_gc(sbi))
wait_ms = GC_THREAD_NOGC_SLEEP_TIME; wait_ms = GC_THREAD_NOGC_SLEEP_TIME;
else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME)
wait_ms = GC_THREAD_MAX_SLEEP_TIME;
} while (!kthread_should_stop()); } while (!kthread_should_stop());
return 0; return 0;
} }
@ -131,7 +128,7 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
if (p->alloc_mode) { if (p->alloc_mode == SSR) {
p->gc_mode = GC_GREEDY; p->gc_mode = GC_GREEDY;
p->dirty_segmap = dirty_i->dirty_segmap[type]; p->dirty_segmap = dirty_i->dirty_segmap[type];
p->ofs_unit = 1; p->ofs_unit = 1;
@ -160,18 +157,21 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) static unsigned int check_bg_victims(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned int segno; unsigned int hint = 0;
unsigned int secno;
/* /*
* If the gc_type is FG_GC, we can select victim segments * If the gc_type is FG_GC, we can select victim segments
* selected by background GC before. * selected by background GC before.
* Those segments guarantee they have small valid blocks. * Those segments guarantee they have small valid blocks.
*/ */
segno = find_next_bit(dirty_i->victim_segmap[BG_GC], next:
TOTAL_SEGS(sbi), 0); secno = find_next_bit(dirty_i->victim_secmap, TOTAL_SECS(sbi), hint++);
if (segno < TOTAL_SEGS(sbi)) { if (secno < TOTAL_SECS(sbi)) {
clear_bit(segno, dirty_i->victim_segmap[BG_GC]); if (sec_usage_check(sbi, secno))
return segno; goto next;
clear_bit(secno, dirty_i->victim_secmap);
return secno * sbi->segs_per_sec;
} }
return NULL_SEGNO; return NULL_SEGNO;
} }
@ -234,7 +234,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct victim_sel_policy p; struct victim_sel_policy p;
unsigned int segno; unsigned int secno;
int nsearched = 0; int nsearched = 0;
p.alloc_mode = alloc_mode; p.alloc_mode = alloc_mode;
@ -253,6 +253,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
while (1) { while (1) {
unsigned long cost; unsigned long cost;
unsigned int segno;
segno = find_next_bit(p.dirty_segmap, segno = find_next_bit(p.dirty_segmap,
TOTAL_SEGS(sbi), p.offset); TOTAL_SEGS(sbi), p.offset);
@ -265,13 +266,11 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
break; break;
} }
p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit; p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit;
secno = GET_SECNO(sbi, segno);
if (test_bit(segno, dirty_i->victim_segmap[FG_GC])) if (sec_usage_check(sbi, secno))
continue; continue;
if (gc_type == BG_GC && if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
test_bit(segno, dirty_i->victim_segmap[BG_GC]))
continue;
if (IS_CURSEC(sbi, GET_SECNO(sbi, segno)))
continue; continue;
cost = get_gc_cost(sbi, segno, &p); cost = get_gc_cost(sbi, segno, &p);
@ -291,13 +290,18 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
} }
got_it: got_it:
if (p.min_segno != NULL_SEGNO) { if (p.min_segno != NULL_SEGNO) {
*result = (p.min_segno / p.ofs_unit) * p.ofs_unit;
if (p.alloc_mode == LFS) { if (p.alloc_mode == LFS) {
int i; secno = GET_SECNO(sbi, p.min_segno);
for (i = 0; i < p.ofs_unit; i++) if (gc_type == FG_GC)
set_bit(*result + i, sbi->cur_victim_sec = secno;
dirty_i->victim_segmap[gc_type]); else
set_bit(secno, dirty_i->victim_secmap);
} }
*result = (p.min_segno / p.ofs_unit) * p.ofs_unit;
trace_f2fs_get_victim(sbi->sb, type, gc_type, &p,
sbi->cur_victim_sec,
prefree_segments(sbi), free_segments(sbi));
} }
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
@ -381,6 +385,7 @@ static void gc_node_segment(struct f2fs_sb_info *sbi,
next_step: next_step:
entry = sum; entry = sum;
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
nid_t nid = le32_to_cpu(entry->nid); nid_t nid = le32_to_cpu(entry->nid);
struct page *node_page; struct page *node_page;
@ -401,11 +406,18 @@ next_step:
continue; continue;
/* set page dirty and write it */ /* set page dirty and write it */
if (!PageWriteback(node_page)) if (gc_type == FG_GC) {
f2fs_submit_bio(sbi, NODE, true);
wait_on_page_writeback(node_page);
set_page_dirty(node_page); set_page_dirty(node_page);
} else {
if (!PageWriteback(node_page))
set_page_dirty(node_page);
}
f2fs_put_page(node_page, 1); f2fs_put_page(node_page, 1);
stat_inc_node_blk_count(sbi, 1); stat_inc_node_blk_count(sbi, 1);
} }
if (initial) { if (initial) {
initial = false; initial = false;
goto next_step; goto next_step;
@ -418,6 +430,13 @@ next_step:
.for_reclaim = 0, .for_reclaim = 0,
}; };
sync_node_pages(sbi, 0, &wbc); sync_node_pages(sbi, 0, &wbc);
/*
* In the case of FG_GC, it'd be better to reclaim this victim
* completely.
*/
if (get_valid_blocks(sbi, segno, 1) != 0)
goto next_step;
} }
} }
@ -481,21 +500,19 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static void move_data_page(struct inode *inode, struct page *page, int gc_type) static void move_data_page(struct inode *inode, struct page *page, int gc_type)
{ {
if (page->mapping != inode->i_mapping)
goto out;
if (inode != page->mapping->host)
goto out;
if (PageWriteback(page))
goto out;
if (gc_type == BG_GC) { if (gc_type == BG_GC) {
if (PageWriteback(page))
goto out;
set_page_dirty(page); set_page_dirty(page);
set_cold_data(page); set_cold_data(page);
} else { } else {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
mutex_lock_op(sbi, DATA_WRITE);
if (PageWriteback(page)) {
f2fs_submit_bio(sbi, DATA, true);
wait_on_page_writeback(page);
}
if (clear_page_dirty_for_io(page) && if (clear_page_dirty_for_io(page) &&
S_ISDIR(inode->i_mode)) { S_ISDIR(inode->i_mode)) {
dec_page_count(sbi, F2FS_DIRTY_DENTS); dec_page_count(sbi, F2FS_DIRTY_DENTS);
@ -503,7 +520,6 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
} }
set_cold_data(page); set_cold_data(page);
do_write_data_page(page); do_write_data_page(page);
mutex_unlock_op(sbi, DATA_WRITE);
clear_cold_data(page); clear_cold_data(page);
} }
out: out:
@ -530,6 +546,7 @@ static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
next_step: next_step:
entry = sum; entry = sum;
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
struct page *data_page; struct page *data_page;
struct inode *inode; struct inode *inode;
@ -567,7 +584,7 @@ next_step:
continue; continue;
data_page = find_data_page(inode, data_page = find_data_page(inode,
start_bidx + ofs_in_node); start_bidx + ofs_in_node, false);
if (IS_ERR(data_page)) if (IS_ERR(data_page))
goto next_iput; goto next_iput;
@ -588,11 +605,22 @@ next_step:
next_iput: next_iput:
iput(inode); iput(inode);
} }
if (++phase < 4) if (++phase < 4)
goto next_step; goto next_step;
if (gc_type == FG_GC) if (gc_type == FG_GC) {
f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, DATA, true);
/*
* In the case of FG_GC, it'd be better to reclaim this victim
* completely.
*/
if (get_valid_blocks(sbi, segno, 1) != 0) {
phase = 2;
goto next_step;
}
}
} }
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
@ -611,18 +639,15 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
{ {
struct page *sum_page; struct page *sum_page;
struct f2fs_summary_block *sum; struct f2fs_summary_block *sum;
struct blk_plug plug;
/* read segment summary of victim */ /* read segment summary of victim */
sum_page = get_sum_page(sbi, segno); sum_page = get_sum_page(sbi, segno);
if (IS_ERR(sum_page)) if (IS_ERR(sum_page))
return; return;
/* blk_start_plug(&plug);
* CP needs to lock sum_page. In this time, we don't need
* to lock this page, because this summary page is not gone anywhere.
* Also, this page is not gonna be updated before GC is done.
*/
unlock_page(sum_page);
sum = page_address(sum_page); sum = page_address(sum_page);
switch (GET_SUM_TYPE((&sum->footer))) { switch (GET_SUM_TYPE((&sum->footer))) {
@ -633,10 +658,12 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
break; break;
} }
blk_finish_plug(&plug);
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
stat_inc_call_count(sbi->stat_info); stat_inc_call_count(sbi->stat_info);
f2fs_put_page(sum_page, 0); f2fs_put_page(sum_page, 1);
} }
int f2fs_gc(struct f2fs_sb_info *sbi) int f2fs_gc(struct f2fs_sb_info *sbi)
@ -652,8 +679,10 @@ gc_more:
if (!(sbi->sb->s_flags & MS_ACTIVE)) if (!(sbi->sb->s_flags & MS_ACTIVE))
goto stop; goto stop;
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) {
gc_type = FG_GC; gc_type = FG_GC;
write_checkpoint(sbi, false);
}
if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
goto stop; goto stop;
@ -662,9 +691,11 @@ gc_more:
for (i = 0; i < sbi->segs_per_sec; i++) for (i = 0; i < sbi->segs_per_sec; i++)
do_garbage_collect(sbi, segno + i, &ilist, gc_type); do_garbage_collect(sbi, segno + i, &ilist, gc_type);
if (gc_type == FG_GC && if (gc_type == FG_GC) {
get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) sbi->cur_victim_sec = NULL_SEGNO;
nfree++; nfree++;
WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec));
}
if (has_not_enough_free_secs(sbi, nfree)) if (has_not_enough_free_secs(sbi, nfree))
goto gc_more; goto gc_more;

View File

@ -13,9 +13,9 @@
* whether IO subsystem is idle * whether IO subsystem is idle
* or not * or not
*/ */
#define GC_THREAD_MIN_SLEEP_TIME 10000 /* milliseconds */ #define GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */
#define GC_THREAD_MAX_SLEEP_TIME 30000 #define GC_THREAD_MAX_SLEEP_TIME 60000
#define GC_THREAD_NOGC_SLEEP_TIME 10000 #define GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ #define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ #define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
@ -58,6 +58,9 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
static inline long increase_sleep_time(long wait) static inline long increase_sleep_time(long wait)
{ {
if (wait == GC_THREAD_NOGC_SLEEP_TIME)
return wait;
wait += GC_THREAD_MIN_SLEEP_TIME; wait += GC_THREAD_MIN_SLEEP_TIME;
if (wait > GC_THREAD_MAX_SLEEP_TIME) if (wait > GC_THREAD_MAX_SLEEP_TIME)
wait = GC_THREAD_MAX_SLEEP_TIME; wait = GC_THREAD_MAX_SLEEP_TIME;
@ -66,6 +69,9 @@ static inline long increase_sleep_time(long wait)
static inline long decrease_sleep_time(long wait) static inline long decrease_sleep_time(long wait)
{ {
if (wait == GC_THREAD_NOGC_SLEEP_TIME)
wait = GC_THREAD_MAX_SLEEP_TIME;
wait -= GC_THREAD_MIN_SLEEP_TIME; wait -= GC_THREAD_MIN_SLEEP_TIME;
if (wait <= GC_THREAD_MIN_SLEEP_TIME) if (wait <= GC_THREAD_MIN_SLEEP_TIME)
wait = GC_THREAD_MIN_SLEEP_TIME; wait = GC_THREAD_MIN_SLEEP_TIME;

View File

@ -16,6 +16,8 @@
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include <trace/events/f2fs.h>
void f2fs_set_inode_flags(struct inode *inode) void f2fs_set_inode_flags(struct inode *inode)
{ {
unsigned int flags = F2FS_I(inode)->i_flags; unsigned int flags = F2FS_I(inode)->i_flags;
@ -44,7 +46,11 @@ static int do_read_inode(struct inode *inode)
struct f2fs_inode *ri; struct f2fs_inode *ri;
/* Check if ino is within scope */ /* Check if ino is within scope */
check_nid_range(sbi, inode->i_ino); if (check_nid_range(sbi, inode->i_ino)) {
f2fs_msg(inode->i_sb, KERN_ERR, "bad inode number: %lu",
(unsigned long) inode->i_ino);
return -EINVAL;
}
node_page = get_node_page(sbi, inode->i_ino); node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page)) if (IS_ERR(node_page))
@ -76,7 +82,6 @@ static int do_read_inode(struct inode *inode)
fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid); fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid);
fi->i_flags = le32_to_cpu(ri->i_flags); fi->i_flags = le32_to_cpu(ri->i_flags);
fi->flags = 0; fi->flags = 0;
fi->data_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver) - 1;
fi->i_advise = ri->i_advise; fi->i_advise = ri->i_advise;
fi->i_pino = le32_to_cpu(ri->i_pino); fi->i_pino = le32_to_cpu(ri->i_pino);
get_extent_info(&fi->ext, ri->i_ext); get_extent_info(&fi->ext, ri->i_ext);
@ -88,13 +93,16 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode; struct inode *inode;
int ret; int ret = 0;
inode = iget_locked(sb, ino); inode = iget_locked(sb, ino);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
if (!(inode->i_state & I_NEW)) {
trace_f2fs_iget(inode);
return inode; return inode;
}
if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
goto make_now; goto make_now;
@ -136,11 +144,12 @@ make_now:
goto bad_inode; goto bad_inode;
} }
unlock_new_inode(inode); unlock_new_inode(inode);
trace_f2fs_iget(inode);
return inode; return inode;
bad_inode: bad_inode:
iget_failed(inode); iget_failed(inode);
trace_f2fs_iget_exit(inode, ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -192,11 +201,24 @@ void update_inode(struct inode *inode, struct page *node_page)
set_page_dirty(node_page); set_page_dirty(node_page);
} }
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) int update_inode_page(struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *node_page; struct page *node_page;
bool need_lock = false;
node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page))
return PTR_ERR(node_page);
update_inode(inode, node_page);
f2fs_put_page(node_page, 1);
return 0;
}
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
int ret, ilock;
if (inode->i_ino == F2FS_NODE_INO(sbi) || if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi)) inode->i_ino == F2FS_META_INO(sbi))
@ -205,25 +227,14 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
if (wbc) if (wbc)
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
node_page = get_node_page(sbi, inode->i_ino); /*
if (IS_ERR(node_page)) * We need to lock here to prevent from producing dirty node pages
return PTR_ERR(node_page); * during the urgent cleaning time when runing out of free sections.
*/
if (!PageDirty(node_page)) { ilock = mutex_lock_op(sbi);
need_lock = true; ret = update_inode_page(inode);
f2fs_put_page(node_page, 1); mutex_unlock_op(sbi, ilock);
mutex_lock(&sbi->write_inode); return ret;
node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page)) {
mutex_unlock(&sbi->write_inode);
return PTR_ERR(node_page);
}
}
update_inode(inode, node_page);
f2fs_put_page(node_page, 1);
if (need_lock)
mutex_unlock(&sbi->write_inode);
return 0;
} }
/* /*
@ -232,7 +243,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
void f2fs_evict_inode(struct inode *inode) void f2fs_evict_inode(struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
int ilock;
trace_f2fs_evict_inode(inode);
truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(&inode->i_data, 0);
if (inode->i_ino == F2FS_NODE_INO(sbi) || if (inode->i_ino == F2FS_NODE_INO(sbi) ||
@ -252,7 +265,10 @@ void f2fs_evict_inode(struct inode *inode)
if (F2FS_HAS_BLOCKS(inode)) if (F2FS_HAS_BLOCKS(inode))
f2fs_truncate(inode); f2fs_truncate(inode);
ilock = mutex_lock_op(sbi);
remove_inode_page(inode); remove_inode_page(inode);
mutex_unlock_op(sbi, ilock);
sb_end_intwrite(inode->i_sb); sb_end_intwrite(inode->i_sb);
no_delete: no_delete:
clear_inode(inode); clear_inode(inode);

View File

@ -15,8 +15,10 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h"
#include "xattr.h" #include "xattr.h"
#include "acl.h" #include "acl.h"
#include <trace/events/f2fs.h>
static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
{ {
@ -25,19 +27,19 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
nid_t ino; nid_t ino;
struct inode *inode; struct inode *inode;
bool nid_free = false; bool nid_free = false;
int err; int err, ilock;
inode = new_inode(sb); inode = new_inode(sb);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
mutex_lock_op(sbi, NODE_NEW); ilock = mutex_lock_op(sbi);
if (!alloc_nid(sbi, &ino)) { if (!alloc_nid(sbi, &ino)) {
mutex_unlock_op(sbi, NODE_NEW); mutex_unlock_op(sbi, ilock);
err = -ENOSPC; err = -ENOSPC;
goto fail; goto fail;
} }
mutex_unlock_op(sbi, NODE_NEW); mutex_unlock_op(sbi, ilock);
inode->i_uid = current_fsuid(); inode->i_uid = current_fsuid();
@ -61,7 +63,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
nid_free = true; nid_free = true;
goto out; goto out;
} }
trace_f2fs_new_inode(inode, 0);
mark_inode_dirty(inode); mark_inode_dirty(inode);
return inode; return inode;
@ -69,6 +71,8 @@ out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
fail: fail:
trace_f2fs_new_inode(inode, err);
make_bad_inode(inode);
iput(inode); iput(inode);
if (nid_free) if (nid_free)
alloc_nid_failed(sbi, ino); alloc_nid_failed(sbi, ino);
@ -82,7 +86,7 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
int ret; int ret;
if (sublen > slen) if (sublen > slen)
return 1; return 0;
ret = memcmp(s + slen - sublen, sub, sublen); ret = memcmp(s + slen - sublen, sub, sublen);
if (ret) { /* compare upper case */ if (ret) { /* compare upper case */
@ -90,16 +94,16 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
char upper_sub[8]; char upper_sub[8];
for (i = 0; i < sublen && i < sizeof(upper_sub); i++) for (i = 0; i < sublen && i < sizeof(upper_sub); i++)
upper_sub[i] = toupper(sub[i]); upper_sub[i] = toupper(sub[i]);
return memcmp(s + slen - sublen, upper_sub, sublen); return !memcmp(s + slen - sublen, upper_sub, sublen);
} }
return ret; return !ret;
} }
/* /*
* Set multimedia files as cold files for hot/cold data separation * Set multimedia files as cold files for hot/cold data separation
*/ */
static inline void set_cold_file(struct f2fs_sb_info *sbi, struct inode *inode, static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode,
const unsigned char *name) const unsigned char *name)
{ {
int i; int i;
@ -107,8 +111,8 @@ static inline void set_cold_file(struct f2fs_sb_info *sbi, struct inode *inode,
int count = le32_to_cpu(sbi->raw_super->extension_count); int count = le32_to_cpu(sbi->raw_super->extension_count);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (!is_multimedia_file(name, extlist[i])) { if (is_multimedia_file(name, extlist[i])) {
F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT; set_cold_file(inode);
break; break;
} }
} }
@ -121,7 +125,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode; struct inode *inode;
nid_t ino = 0; nid_t ino = 0;
int err; int err, ilock;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -130,14 +134,16 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
return PTR_ERR(inode); return PTR_ERR(inode);
if (!test_opt(sbi, DISABLE_EXT_IDENTIFY)) if (!test_opt(sbi, DISABLE_EXT_IDENTIFY))
set_cold_file(sbi, inode, dentry->d_name.name); set_cold_files(sbi, inode, dentry->d_name.name);
inode->i_op = &f2fs_file_inode_operations; inode->i_op = &f2fs_file_inode_operations;
inode->i_fop = &f2fs_file_operations; inode->i_fop = &f2fs_file_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
ino = inode->i_ino; ino = inode->i_ino;
ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode); err = f2fs_add_link(dentry, inode);
mutex_unlock_op(sbi, ilock);
if (err) if (err)
goto out; goto out;
@ -150,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, ino); alloc_nid_failed(sbi, ino);
return err; return err;
@ -161,7 +168,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
int err; int err, ilock;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -169,14 +176,23 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
set_inode_flag(F2FS_I(inode), FI_INC_LINK); set_inode_flag(F2FS_I(inode), FI_INC_LINK);
ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode); err = f2fs_add_link(dentry, inode);
mutex_unlock_op(sbi, ilock);
if (err) if (err)
goto out; goto out;
/*
* This file should be checkpointed during fsync.
* We lost i_pino from now on.
*/
set_cp_file(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
return 0; return 0;
out: out:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
make_bad_inode(inode);
iput(inode); iput(inode);
return err; return err;
} }
@ -197,7 +213,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
struct page *page; struct page *page;
if (dentry->d_name.len > F2FS_MAX_NAME_LEN) if (dentry->d_name.len > F2FS_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
de = f2fs_find_entry(dir, &dentry->d_name, &page); de = f2fs_find_entry(dir, &dentry->d_name, &page);
@ -222,7 +238,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
struct page *page; struct page *page;
int err = -ENOENT; int err = -ENOENT;
int ilock;
trace_f2fs_unlink_enter(dir, dentry);
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
de = f2fs_find_entry(dir, &dentry->d_name, &page); de = f2fs_find_entry(dir, &dentry->d_name, &page);
@ -236,11 +254,14 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
goto fail; goto fail;
} }
ilock = mutex_lock_op(sbi);
f2fs_delete_entry(de, page, inode); f2fs_delete_entry(de, page, inode);
mutex_unlock_op(sbi, ilock);
/* In order to evict this inode, we set it dirty */ /* In order to evict this inode, we set it dirty */
mark_inode_dirty(inode); mark_inode_dirty(inode);
fail: fail:
trace_f2fs_unlink_exit(inode, err);
return err; return err;
} }
@ -251,7 +272,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode; struct inode *inode;
size_t symlen = strlen(symname) + 1; size_t symlen = strlen(symname) + 1;
int err; int err, ilock;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -262,7 +283,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
inode->i_op = &f2fs_symlink_inode_operations; inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode); err = f2fs_add_link(dentry, inode);
mutex_unlock_op(sbi, ilock);
if (err) if (err)
goto out; goto out;
@ -275,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
@ -284,7 +308,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
struct inode *inode; struct inode *inode;
int err; int err, ilock;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -298,7 +322,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
set_inode_flag(F2FS_I(inode), FI_INC_LINK); set_inode_flag(F2FS_I(inode), FI_INC_LINK);
ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode); err = f2fs_add_link(dentry, inode);
mutex_unlock_op(sbi, ilock);
if (err) if (err)
goto out_fail; goto out_fail;
@ -313,6 +339,7 @@ out_fail:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
@ -333,6 +360,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct inode *inode; struct inode *inode;
int err = 0; int err = 0;
int ilock;
if (!new_valid_dev(rdev)) if (!new_valid_dev(rdev))
return -EINVAL; return -EINVAL;
@ -346,7 +374,9 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
init_special_inode(inode, inode->i_mode, rdev); init_special_inode(inode, inode->i_mode, rdev);
inode->i_op = &f2fs_special_inode_operations; inode->i_op = &f2fs_special_inode_operations;
ilock = mutex_lock_op(sbi);
err = f2fs_add_link(dentry, inode); err = f2fs_add_link(dentry, inode);
mutex_unlock_op(sbi, ilock);
if (err) if (err)
goto out; goto out;
@ -357,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
@ -374,7 +405,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_dir_entry = NULL;
struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *old_entry;
struct f2fs_dir_entry *new_entry; struct f2fs_dir_entry *new_entry;
int err = -ENOENT; int err = -ENOENT, ilock = -1;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
@ -389,7 +420,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out_old; goto out_old;
} }
mutex_lock_op(sbi, RENAME); ilock = mutex_lock_op(sbi);
if (new_inode) { if (new_inode) {
struct page *new_page; struct page *new_page;
@ -412,7 +443,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
drop_nlink(new_inode); drop_nlink(new_inode);
if (!new_inode->i_nlink) if (!new_inode->i_nlink)
add_orphan_inode(sbi, new_inode->i_ino); add_orphan_inode(sbi, new_inode->i_ino);
f2fs_write_inode(new_inode, NULL); update_inode_page(new_inode);
} else { } else {
err = f2fs_add_link(new_dentry, old_inode); err = f2fs_add_link(new_dentry, old_inode);
if (err) if (err)
@ -420,12 +451,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dir_entry) { if (old_dir_entry) {
inc_nlink(new_dir); inc_nlink(new_dir);
f2fs_write_inode(new_dir, NULL); update_inode_page(new_dir);
} }
} }
old_inode->i_ctime = CURRENT_TIME; old_inode->i_ctime = CURRENT_TIME;
set_inode_flag(F2FS_I(old_inode), FI_NEED_CP);
mark_inode_dirty(old_inode); mark_inode_dirty(old_inode);
f2fs_delete_entry(old_entry, old_page, NULL); f2fs_delete_entry(old_entry, old_page, NULL);
@ -439,10 +469,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_put_page(old_dir_page, 0); f2fs_put_page(old_dir_page, 0);
} }
drop_nlink(old_dir); drop_nlink(old_dir);
f2fs_write_inode(old_dir, NULL); update_inode_page(old_dir);
} }
mutex_unlock_op(sbi, RENAME); mutex_unlock_op(sbi, ilock);
return 0; return 0;
out_dir: out_dir:
@ -450,7 +480,7 @@ out_dir:
kunmap(old_dir_page); kunmap(old_dir_page);
f2fs_put_page(old_dir_page, 0); f2fs_put_page(old_dir_page, 0);
} }
mutex_unlock_op(sbi, RENAME); mutex_unlock_op(sbi, ilock);
out_old: out_old:
kunmap(old_page); kunmap(old_page);
f2fs_put_page(old_page, 0); f2fs_put_page(old_page, 0);

View File

@ -19,6 +19,7 @@
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "segment.h" #include "segment.h"
#include <trace/events/f2fs.h>
static struct kmem_cache *nat_entry_slab; static struct kmem_cache *nat_entry_slab;
static struct kmem_cache *free_nid_slab; static struct kmem_cache *free_nid_slab;
@ -88,10 +89,13 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
{ {
struct address_space *mapping = sbi->meta_inode->i_mapping; struct address_space *mapping = sbi->meta_inode->i_mapping;
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct blk_plug plug;
struct page *page; struct page *page;
pgoff_t index; pgoff_t index;
int i; int i;
blk_start_plug(&plug);
for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) { for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) {
if (nid >= nm_i->max_nid) if (nid >= nm_i->max_nid)
nid = 0; nid = 0;
@ -100,12 +104,16 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid)
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
continue; continue;
if (f2fs_readpage(sbi, page, index, READ)) { if (PageUptodate(page)) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
continue; continue;
} }
if (f2fs_readpage(sbi, page, index, READ))
continue;
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
} }
blk_finish_plug(&plug);
} }
static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n) static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n)
@ -236,7 +244,7 @@ static int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
if (nm_i->nat_cnt < 2 * NM_WOUT_THRESHOLD) if (nm_i->nat_cnt <= NM_WOUT_THRESHOLD)
return 0; return 0;
write_lock(&nm_i->nat_tree_lock); write_lock(&nm_i->nat_tree_lock);
@ -320,15 +328,14 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[0] = 0; noffset[0] = 0;
if (block < direct_index) { if (block < direct_index) {
offset[n++] = block; offset[n] = block;
level = 0;
goto got; goto got;
} }
block -= direct_index; block -= direct_index;
if (block < direct_blks) { if (block < direct_blks) {
offset[n++] = NODE_DIR1_BLOCK; offset[n++] = NODE_DIR1_BLOCK;
noffset[n] = 1; noffset[n] = 1;
offset[n++] = block; offset[n] = block;
level = 1; level = 1;
goto got; goto got;
} }
@ -336,7 +343,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
if (block < direct_blks) { if (block < direct_blks) {
offset[n++] = NODE_DIR2_BLOCK; offset[n++] = NODE_DIR2_BLOCK;
noffset[n] = 2; noffset[n] = 2;
offset[n++] = block; offset[n] = block;
level = 1; level = 1;
goto got; goto got;
} }
@ -346,7 +353,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 3; noffset[n] = 3;
offset[n++] = block / direct_blks; offset[n++] = block / direct_blks;
noffset[n] = 4 + offset[n - 1]; noffset[n] = 4 + offset[n - 1];
offset[n++] = block % direct_blks; offset[n] = block % direct_blks;
level = 2; level = 2;
goto got; goto got;
} }
@ -356,7 +363,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 4 + dptrs_per_blk; noffset[n] = 4 + dptrs_per_blk;
offset[n++] = block / direct_blks; offset[n++] = block / direct_blks;
noffset[n] = 5 + dptrs_per_blk + offset[n - 1]; noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
offset[n++] = block % direct_blks; offset[n] = block % direct_blks;
level = 2; level = 2;
goto got; goto got;
} }
@ -371,7 +378,7 @@ static int get_node_path(long block, int offset[4], unsigned int noffset[4])
noffset[n] = 7 + (dptrs_per_blk * 2) + noffset[n] = 7 + (dptrs_per_blk * 2) +
offset[n - 2] * (dptrs_per_blk + 1) + offset[n - 2] * (dptrs_per_blk + 1) +
offset[n - 1]; offset[n - 1];
offset[n++] = block % direct_blks; offset[n] = block % direct_blks;
level = 3; level = 3;
goto got; goto got;
} else { } else {
@ -383,8 +390,11 @@ got:
/* /*
* Caller should call f2fs_put_dnode(dn). * Caller should call f2fs_put_dnode(dn).
* Also, it should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op() only if ro is not set RDONLY_NODE.
* In the case of RDONLY_NODE, we don't need to care about mutex.
*/ */
int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro) int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
struct page *npage[4]; struct page *npage[4];
@ -403,7 +413,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
return PTR_ERR(npage[0]); return PTR_ERR(npage[0]);
parent = npage[0]; parent = npage[0];
nids[1] = get_nid(parent, offset[0], true); if (level != 0)
nids[1] = get_nid(parent, offset[0], true);
dn->inode_page = npage[0]; dn->inode_page = npage[0];
dn->inode_page_locked = true; dn->inode_page_locked = true;
@ -411,12 +422,9 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
for (i = 1; i <= level; i++) { for (i = 1; i <= level; i++) {
bool done = false; bool done = false;
if (!nids[i] && !ro) { if (!nids[i] && mode == ALLOC_NODE) {
mutex_lock_op(sbi, NODE_NEW);
/* alloc new node */ /* alloc new node */
if (!alloc_nid(sbi, &(nids[i]))) { if (!alloc_nid(sbi, &(nids[i]))) {
mutex_unlock_op(sbi, NODE_NEW);
err = -ENOSPC; err = -ENOSPC;
goto release_pages; goto release_pages;
} }
@ -425,16 +433,14 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro)
npage[i] = new_node_page(dn, noffset[i]); npage[i] = new_node_page(dn, noffset[i]);
if (IS_ERR(npage[i])) { if (IS_ERR(npage[i])) {
alloc_nid_failed(sbi, nids[i]); alloc_nid_failed(sbi, nids[i]);
mutex_unlock_op(sbi, NODE_NEW);
err = PTR_ERR(npage[i]); err = PTR_ERR(npage[i]);
goto release_pages; goto release_pages;
} }
set_nid(parent, offset[i - 1], nids[i], i == 1); set_nid(parent, offset[i - 1], nids[i], i == 1);
alloc_nid_done(sbi, nids[i]); alloc_nid_done(sbi, nids[i]);
mutex_unlock_op(sbi, NODE_NEW);
done = true; done = true;
} else if (ro && i == level && level > 1) { } else if (mode == LOOKUP_NODE_RA && i == level && level > 1) {
npage[i] = get_node_page_ra(parent, offset[i - 1]); npage[i] = get_node_page_ra(parent, offset[i - 1]);
if (IS_ERR(npage[i])) { if (IS_ERR(npage[i])) {
err = PTR_ERR(npage[i]); err = PTR_ERR(npage[i]);
@ -507,6 +513,7 @@ invalidate:
f2fs_put_page(dn->node_page, 1); f2fs_put_page(dn->node_page, 1);
dn->node_page = NULL; dn->node_page = NULL;
trace_f2fs_truncate_node(dn->inode, dn->nid, ni.blk_addr);
} }
static int truncate_dnode(struct dnode_of_data *dn) static int truncate_dnode(struct dnode_of_data *dn)
@ -547,9 +554,13 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
if (dn->nid == 0) if (dn->nid == 0)
return NIDS_PER_BLOCK + 1; return NIDS_PER_BLOCK + 1;
trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr);
page = get_node_page(sbi, dn->nid); page = get_node_page(sbi, dn->nid);
if (IS_ERR(page)) if (IS_ERR(page)) {
trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(page));
return PTR_ERR(page); return PTR_ERR(page);
}
rn = (struct f2fs_node *)page_address(page); rn = (struct f2fs_node *)page_address(page);
if (depth < 3) { if (depth < 3) {
@ -591,10 +602,12 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
} else { } else {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
trace_f2fs_truncate_nodes_exit(dn->inode, freed);
return freed; return freed;
out_err: out_err:
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
trace_f2fs_truncate_nodes_exit(dn->inode, ret);
return ret; return ret;
} }
@ -649,6 +662,9 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
fail: fail:
for (i = depth - 3; i >= 0; i--) for (i = depth - 3; i >= 0; i--)
f2fs_put_page(pages[i], 1); f2fs_put_page(pages[i], 1);
trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err);
return err; return err;
} }
@ -658,6 +674,7 @@ fail:
int truncate_inode_blocks(struct inode *inode, pgoff_t from) int truncate_inode_blocks(struct inode *inode, pgoff_t from)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct address_space *node_mapping = sbi->node_inode->i_mapping;
int err = 0, cont = 1; int err = 0, cont = 1;
int level, offset[4], noffset[4]; int level, offset[4], noffset[4];
unsigned int nofs = 0; unsigned int nofs = 0;
@ -665,11 +682,15 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from)
struct dnode_of_data dn; struct dnode_of_data dn;
struct page *page; struct page *page;
level = get_node_path(from, offset, noffset); trace_f2fs_truncate_inode_blocks_enter(inode, from);
level = get_node_path(from, offset, noffset);
restart:
page = get_node_page(sbi, inode->i_ino); page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(page)) if (IS_ERR(page)) {
trace_f2fs_truncate_inode_blocks_exit(inode, PTR_ERR(page));
return PTR_ERR(page); return PTR_ERR(page);
}
set_new_dnode(&dn, inode, page, NULL, 0); set_new_dnode(&dn, inode, page, NULL, 0);
unlock_page(page); unlock_page(page);
@ -728,6 +749,10 @@ skip_partial:
if (offset[1] == 0 && if (offset[1] == 0 &&
rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK]) { rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK]) {
lock_page(page); lock_page(page);
if (page->mapping != node_mapping) {
f2fs_put_page(page, 1);
goto restart;
}
wait_on_page_writeback(page); wait_on_page_writeback(page);
rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK] = 0;
set_page_dirty(page); set_page_dirty(page);
@ -739,9 +764,14 @@ skip_partial:
} }
fail: fail:
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
trace_f2fs_truncate_inode_blocks_exit(inode, err);
return err > 0 ? 0 : err; return err > 0 ? 0 : err;
} }
/*
* Caller should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op().
*/
int remove_inode_page(struct inode *inode) int remove_inode_page(struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
@ -749,21 +779,16 @@ int remove_inode_page(struct inode *inode)
nid_t ino = inode->i_ino; nid_t ino = inode->i_ino;
struct dnode_of_data dn; struct dnode_of_data dn;
mutex_lock_op(sbi, NODE_TRUNC);
page = get_node_page(sbi, ino); page = get_node_page(sbi, ino);
if (IS_ERR(page)) { if (IS_ERR(page))
mutex_unlock_op(sbi, NODE_TRUNC);
return PTR_ERR(page); return PTR_ERR(page);
}
if (F2FS_I(inode)->i_xattr_nid) { if (F2FS_I(inode)->i_xattr_nid) {
nid_t nid = F2FS_I(inode)->i_xattr_nid; nid_t nid = F2FS_I(inode)->i_xattr_nid;
struct page *npage = get_node_page(sbi, nid); struct page *npage = get_node_page(sbi, nid);
if (IS_ERR(npage)) { if (IS_ERR(npage))
mutex_unlock_op(sbi, NODE_TRUNC);
return PTR_ERR(npage); return PTR_ERR(npage);
}
F2FS_I(inode)->i_xattr_nid = 0; F2FS_I(inode)->i_xattr_nid = 0;
set_new_dnode(&dn, inode, page, npage, nid); set_new_dnode(&dn, inode, page, npage, nid);
@ -775,23 +800,18 @@ int remove_inode_page(struct inode *inode)
BUG_ON(inode->i_blocks != 0 && inode->i_blocks != 1); BUG_ON(inode->i_blocks != 0 && inode->i_blocks != 1);
set_new_dnode(&dn, inode, page, page, ino); set_new_dnode(&dn, inode, page, page, ino);
truncate_node(&dn); truncate_node(&dn);
mutex_unlock_op(sbi, NODE_TRUNC);
return 0; return 0;
} }
int new_inode_page(struct inode *inode, const struct qstr *name) int new_inode_page(struct inode *inode, const struct qstr *name)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct page *page; struct page *page;
struct dnode_of_data dn; struct dnode_of_data dn;
/* allocate inode page for new inode */ /* allocate inode page for new inode */
set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino);
mutex_lock_op(sbi, NODE_NEW);
page = new_node_page(&dn, 0); page = new_node_page(&dn, 0);
init_dent_inode(name, page); init_dent_inode(name, page);
mutex_unlock_op(sbi, NODE_NEW);
if (IS_ERR(page)) if (IS_ERR(page))
return PTR_ERR(page); return PTR_ERR(page);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
@ -844,6 +864,12 @@ fail:
return ERR_PTR(err); return ERR_PTR(err);
} }
/*
* Caller should do after getting the following values.
* 0: f2fs_put_page(page, 0)
* LOCKED_PAGE: f2fs_put_page(page, 1)
* error: nothing
*/
static int read_node_page(struct page *page, int type) static int read_node_page(struct page *page, int type)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb);
@ -851,8 +877,14 @@ static int read_node_page(struct page *page, int type)
get_node_info(sbi, page->index, &ni); get_node_info(sbi, page->index, &ni);
if (ni.blk_addr == NULL_ADDR) if (ni.blk_addr == NULL_ADDR) {
f2fs_put_page(page, 1);
return -ENOENT; return -ENOENT;
}
if (PageUptodate(page))
return LOCKED_PAGE;
return f2fs_readpage(sbi, page, ni.blk_addr, type); return f2fs_readpage(sbi, page, ni.blk_addr, type);
} }
@ -863,40 +895,53 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
{ {
struct address_space *mapping = sbi->node_inode->i_mapping; struct address_space *mapping = sbi->node_inode->i_mapping;
struct page *apage; struct page *apage;
int err;
apage = find_get_page(mapping, nid); apage = find_get_page(mapping, nid);
if (apage && PageUptodate(apage)) if (apage && PageUptodate(apage)) {
goto release_out; f2fs_put_page(apage, 0);
return;
}
f2fs_put_page(apage, 0); f2fs_put_page(apage, 0);
apage = grab_cache_page(mapping, nid); apage = grab_cache_page(mapping, nid);
if (!apage) if (!apage)
return; return;
if (read_node_page(apage, READA)) err = read_node_page(apage, READA);
unlock_page(apage); if (err == 0)
f2fs_put_page(apage, 0);
release_out: else if (err == LOCKED_PAGE)
f2fs_put_page(apage, 0); f2fs_put_page(apage, 1);
return; return;
} }
struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
{ {
int err;
struct page *page;
struct address_space *mapping = sbi->node_inode->i_mapping; struct address_space *mapping = sbi->node_inode->i_mapping;
struct page *page;
int err;
repeat:
page = grab_cache_page(mapping, nid); page = grab_cache_page(mapping, nid);
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = read_node_page(page, READ_SYNC); err = read_node_page(page, READ_SYNC);
if (err) { if (err < 0)
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
} else if (err == LOCKED_PAGE)
goto got_it;
lock_page(page);
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
if (page->mapping != mapping) {
f2fs_put_page(page, 1);
goto repeat;
}
got_it:
BUG_ON(nid != nid_of_node(page)); BUG_ON(nid != nid_of_node(page));
mark_page_accessed(page); mark_page_accessed(page);
return page; return page;
@ -910,31 +955,27 @@ struct page *get_node_page_ra(struct page *parent, int start)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(parent->mapping->host->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(parent->mapping->host->i_sb);
struct address_space *mapping = sbi->node_inode->i_mapping; struct address_space *mapping = sbi->node_inode->i_mapping;
int i, end; struct blk_plug plug;
int err = 0;
nid_t nid;
struct page *page; struct page *page;
int err, i, end;
nid_t nid;
/* First, try getting the desired direct node. */ /* First, try getting the desired direct node. */
nid = get_nid(parent, start, false); nid = get_nid(parent, start, false);
if (!nid) if (!nid)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
page = find_get_page(mapping, nid);
if (page && PageUptodate(page))
goto page_hit;
f2fs_put_page(page, 0);
repeat: repeat:
page = grab_cache_page(mapping, nid); page = grab_cache_page(mapping, nid);
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
err = read_node_page(page, READA); err = read_node_page(page, READ_SYNC);
if (err) { if (err < 0)
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
} else if (err == LOCKED_PAGE)
goto page_hit;
blk_start_plug(&plug);
/* Then, try readahead for siblings of the desired node */ /* Then, try readahead for siblings of the desired node */
end = start + MAX_RA_NODE; end = start + MAX_RA_NODE;
@ -946,18 +987,19 @@ repeat:
ra_node_page(sbi, nid); ra_node_page(sbi, nid);
} }
page_hit: blk_finish_plug(&plug);
lock_page(page);
if (PageError(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
/* Has the page been truncated? */ lock_page(page);
if (page->mapping != mapping) { if (page->mapping != mapping) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
goto repeat; goto repeat;
} }
page_hit:
if (!PageUptodate(page)) {
f2fs_put_page(page, 1);
return ERR_PTR(-EIO);
}
mark_page_accessed(page);
return page; return page;
} }
@ -972,7 +1014,7 @@ void sync_inode_page(struct dnode_of_data *dn)
if (!dn->inode_page_locked) if (!dn->inode_page_locked)
unlock_page(dn->inode_page); unlock_page(dn->inode_page);
} else { } else {
f2fs_write_inode(dn->inode, NULL); update_inode_page(dn->inode);
} }
} }
@ -1087,17 +1129,8 @@ static int f2fs_write_node_page(struct page *page,
block_t new_addr; block_t new_addr;
struct node_info ni; struct node_info ni;
if (wbc->for_reclaim) {
dec_page_count(sbi, F2FS_DIRTY_NODES);
wbc->pages_skipped++;
set_page_dirty(page);
return AOP_WRITEPAGE_ACTIVATE;
}
wait_on_page_writeback(page); wait_on_page_writeback(page);
mutex_lock_op(sbi, NODE_WRITE);
/* get old block addr of this node page */ /* get old block addr of this node page */
nid = nid_of_node(page); nid = nid_of_node(page);
BUG_ON(page->index != nid); BUG_ON(page->index != nid);
@ -1105,17 +1138,25 @@ static int f2fs_write_node_page(struct page *page,
get_node_info(sbi, nid, &ni); get_node_info(sbi, nid, &ni);
/* This page is already truncated */ /* This page is already truncated */
if (ni.blk_addr == NULL_ADDR) if (ni.blk_addr == NULL_ADDR) {
dec_page_count(sbi, F2FS_DIRTY_NODES);
unlock_page(page);
return 0; return 0;
}
if (wbc->for_reclaim) {
dec_page_count(sbi, F2FS_DIRTY_NODES);
wbc->pages_skipped++;
set_page_dirty(page);
return AOP_WRITEPAGE_ACTIVATE;
}
mutex_lock(&sbi->node_write);
set_page_writeback(page); set_page_writeback(page);
/* insert node offset */
write_node_page(sbi, page, nid, ni.blk_addr, &new_addr); write_node_page(sbi, page, nid, ni.blk_addr, &new_addr);
set_node_addr(sbi, &ni, new_addr); set_node_addr(sbi, &ni, new_addr);
dec_page_count(sbi, F2FS_DIRTY_NODES); dec_page_count(sbi, F2FS_DIRTY_NODES);
mutex_unlock(&sbi->node_write);
mutex_unlock_op(sbi, NODE_WRITE);
unlock_page(page); unlock_page(page);
return 0; return 0;
} }
@ -1130,12 +1171,11 @@ static int f2fs_write_node_pages(struct address_space *mapping,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb);
struct block_device *bdev = sbi->sb->s_bdev;
long nr_to_write = wbc->nr_to_write; long nr_to_write = wbc->nr_to_write;
/* First check balancing cached NAT entries */ /* First check balancing cached NAT entries */
if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) {
write_checkpoint(sbi, false); f2fs_sync_fs(sbi->sb, true);
return 0; return 0;
} }
@ -1144,10 +1184,9 @@ static int f2fs_write_node_pages(struct address_space *mapping,
return 0; return 0;
/* if mounting is failed, skip writing node pages */ /* if mounting is failed, skip writing node pages */
wbc->nr_to_write = bio_get_nr_vecs(bdev); wbc->nr_to_write = max_hw_blocks(sbi);
sync_node_pages(sbi, 0, wbc); sync_node_pages(sbi, 0, wbc);
wbc->nr_to_write = nr_to_write - wbc->nr_to_write = nr_to_write - (max_hw_blocks(sbi) - wbc->nr_to_write);
(bio_get_nr_vecs(bdev) - wbc->nr_to_write);
return 0; return 0;
} }
@ -1178,7 +1217,7 @@ static void f2fs_invalidate_node_page(struct page *page, unsigned long offset)
static int f2fs_release_node_page(struct page *page, gfp_t wait) static int f2fs_release_node_page(struct page *page, gfp_t wait)
{ {
ClearPagePrivate(page); ClearPagePrivate(page);
return 0; return 1;
} }
/* /*
@ -1195,14 +1234,13 @@ const struct address_space_operations f2fs_node_aops = {
static struct free_nid *__lookup_free_nid_list(nid_t n, struct list_head *head) static struct free_nid *__lookup_free_nid_list(nid_t n, struct list_head *head)
{ {
struct list_head *this; struct list_head *this;
struct free_nid *i = NULL; struct free_nid *i;
list_for_each(this, head) { list_for_each(this, head) {
i = list_entry(this, struct free_nid, list); i = list_entry(this, struct free_nid, list);
if (i->nid == n) if (i->nid == n)
break; return i;
i = NULL;
} }
return i; return NULL;
} }
static void __del_from_free_nid_list(struct free_nid *i) static void __del_from_free_nid_list(struct free_nid *i)
@ -1211,11 +1249,29 @@ static void __del_from_free_nid_list(struct free_nid *i)
kmem_cache_free(free_nid_slab, i); kmem_cache_free(free_nid_slab, i);
} }
static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid) static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
{ {
struct free_nid *i; struct free_nid *i;
struct nat_entry *ne;
bool allocated = false;
if (nm_i->fcnt > 2 * MAX_FREE_NIDS) if (nm_i->fcnt > 2 * MAX_FREE_NIDS)
return -1;
/* 0 nid should not be used */
if (nid == 0)
return 0;
if (!build)
goto retry;
/* do not add allocated nids */
read_lock(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
if (ne && nat_get_blkaddr(ne) != NULL_ADDR)
allocated = true;
read_unlock(&nm_i->nat_tree_lock);
if (allocated)
return 0; return 0;
retry: retry:
i = kmem_cache_alloc(free_nid_slab, GFP_NOFS); i = kmem_cache_alloc(free_nid_slab, GFP_NOFS);
@ -1250,63 +1306,59 @@ static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid)
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
} }
static int scan_nat_page(struct f2fs_nm_info *nm_i, static void scan_nat_page(struct f2fs_nm_info *nm_i,
struct page *nat_page, nid_t start_nid) struct page *nat_page, nid_t start_nid)
{ {
struct f2fs_nat_block *nat_blk = page_address(nat_page); struct f2fs_nat_block *nat_blk = page_address(nat_page);
block_t blk_addr; block_t blk_addr;
int fcnt = 0;
int i; int i;
/* 0 nid should not be used */
if (start_nid == 0)
++start_nid;
i = start_nid % NAT_ENTRY_PER_BLOCK; i = start_nid % NAT_ENTRY_PER_BLOCK;
for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) {
blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
if (start_nid >= nm_i->max_nid)
break;
blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr);
BUG_ON(blk_addr == NEW_ADDR); BUG_ON(blk_addr == NEW_ADDR);
if (blk_addr == NULL_ADDR) if (blk_addr == NULL_ADDR) {
fcnt += add_free_nid(nm_i, start_nid); if (add_free_nid(nm_i, start_nid, true) < 0)
break;
}
} }
return fcnt;
} }
static void build_free_nids(struct f2fs_sb_info *sbi) static void build_free_nids(struct f2fs_sb_info *sbi)
{ {
struct free_nid *fnid, *next_fnid;
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
struct f2fs_summary_block *sum = curseg->sum_blk; struct f2fs_summary_block *sum = curseg->sum_blk;
nid_t nid = 0; int i = 0;
bool is_cycled = false; nid_t nid = nm_i->next_scan_nid;
int fcnt = 0;
int i;
nid = nm_i->next_scan_nid; /* Enough entries */
nm_i->init_scan_nid = nid; if (nm_i->fcnt > NAT_ENTRY_PER_BLOCK)
return;
/* readahead nat pages to be scanned */
ra_nat_pages(sbi, nid); ra_nat_pages(sbi, nid);
while (1) { while (1) {
struct page *page = get_current_nat_page(sbi, nid); struct page *page = get_current_nat_page(sbi, nid);
fcnt += scan_nat_page(nm_i, page, nid); scan_nat_page(nm_i, page, nid);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK)); nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK));
if (nid >= nm_i->max_nid)
if (nid >= nm_i->max_nid) {
nid = 0; nid = 0;
is_cycled = true;
} if (i++ == FREE_NID_PAGES)
if (fcnt > MAX_FREE_NIDS)
break;
if (is_cycled && nm_i->init_scan_nid <= nid)
break; break;
} }
/* go to the next free nat pages to find free nids abundantly */
nm_i->next_scan_nid = nid; nm_i->next_scan_nid = nid;
/* find free nids from current sum_pages */ /* find free nids from current sum_pages */
@ -1315,22 +1367,11 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr); block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr);
nid = le32_to_cpu(nid_in_journal(sum, i)); nid = le32_to_cpu(nid_in_journal(sum, i));
if (addr == NULL_ADDR) if (addr == NULL_ADDR)
add_free_nid(nm_i, nid); add_free_nid(nm_i, nid, true);
else else
remove_free_nid(nm_i, nid); remove_free_nid(nm_i, nid);
} }
mutex_unlock(&curseg->curseg_mutex); mutex_unlock(&curseg->curseg_mutex);
/* remove the free nids from current allocated nids */
list_for_each_entry_safe(fnid, next_fnid, &nm_i->free_nid_list, list) {
struct nat_entry *ne;
read_lock(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, fnid->nid);
if (ne && nat_get_blkaddr(ne) != NULL_ADDR)
remove_free_nid(nm_i, fnid->nid);
read_unlock(&nm_i->nat_tree_lock);
}
} }
/* /*
@ -1344,41 +1385,36 @@ bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
struct free_nid *i = NULL; struct free_nid *i = NULL;
struct list_head *this; struct list_head *this;
retry: retry:
mutex_lock(&nm_i->build_lock); if (sbi->total_valid_node_count + 1 >= nm_i->max_nid)
if (!nm_i->fcnt) { return false;
/* scan NAT in order to build free nid list */
build_free_nids(sbi);
if (!nm_i->fcnt) {
mutex_unlock(&nm_i->build_lock);
return false;
}
}
mutex_unlock(&nm_i->build_lock);
/*
* We check fcnt again since previous check is racy as
* we didn't hold free_nid_list_lock. So other thread
* could consume all of free nids.
*/
spin_lock(&nm_i->free_nid_list_lock); spin_lock(&nm_i->free_nid_list_lock);
if (!nm_i->fcnt) {
/* We should not use stale free nids created by build_free_nids */
if (nm_i->fcnt && !sbi->on_build_free_nids) {
BUG_ON(list_empty(&nm_i->free_nid_list));
list_for_each(this, &nm_i->free_nid_list) {
i = list_entry(this, struct free_nid, list);
if (i->state == NID_NEW)
break;
}
BUG_ON(i->state != NID_NEW);
*nid = i->nid;
i->state = NID_ALLOC;
nm_i->fcnt--;
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
goto retry; return true;
} }
BUG_ON(list_empty(&nm_i->free_nid_list));
list_for_each(this, &nm_i->free_nid_list) {
i = list_entry(this, struct free_nid, list);
if (i->state == NID_NEW)
break;
}
BUG_ON(i->state != NID_NEW);
*nid = i->nid;
i->state = NID_ALLOC;
nm_i->fcnt--;
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
return true;
/* Let's scan nat pages and its caches to get free nids */
mutex_lock(&nm_i->build_lock);
sbi->on_build_free_nids = 1;
build_free_nids(sbi);
sbi->on_build_free_nids = 0;
mutex_unlock(&nm_i->build_lock);
goto retry;
} }
/* /*
@ -1391,10 +1427,8 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid)
spin_lock(&nm_i->free_nid_list_lock); spin_lock(&nm_i->free_nid_list_lock);
i = __lookup_free_nid_list(nid, &nm_i->free_nid_list); i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
if (i) { BUG_ON(!i || i->state != NID_ALLOC);
BUG_ON(i->state != NID_ALLOC); __del_from_free_nid_list(i);
__del_from_free_nid_list(i);
}
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
} }
@ -1403,8 +1437,19 @@ void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid)
*/ */
void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
{ {
alloc_nid_done(sbi, nid); struct f2fs_nm_info *nm_i = NM_I(sbi);
add_free_nid(NM_I(sbi), nid); struct free_nid *i;
spin_lock(&nm_i->free_nid_list_lock);
i = __lookup_free_nid_list(nid, &nm_i->free_nid_list);
BUG_ON(!i || i->state != NID_ALLOC);
if (nm_i->fcnt > 2 * MAX_FREE_NIDS) {
__del_from_free_nid_list(i);
} else {
i->state = NID_NEW;
nm_i->fcnt++;
}
spin_unlock(&nm_i->free_nid_list_lock);
} }
void recover_node_page(struct f2fs_sb_info *sbi, struct page *page, void recover_node_page(struct f2fs_sb_info *sbi, struct page *page,
@ -1475,23 +1520,24 @@ int restore_node_summary(struct f2fs_sb_info *sbi,
sum_entry = &sum->entries[0]; sum_entry = &sum->entries[0];
for (i = 0; i < last_offset; i++, sum_entry++) { for (i = 0; i < last_offset; i++, sum_entry++) {
if (f2fs_readpage(sbi, page, addr, READ_SYNC))
goto out;
rn = (struct f2fs_node *)page_address(page);
sum_entry->nid = rn->footer.nid;
sum_entry->version = 0;
sum_entry->ofs_in_node = 0;
addr++;
/* /*
* In order to read next node page, * In order to read next node page,
* we must clear PageUptodate flag. * we must clear PageUptodate flag.
*/ */
ClearPageUptodate(page); ClearPageUptodate(page);
if (f2fs_readpage(sbi, page, addr, READ_SYNC))
goto out;
lock_page(page);
rn = (struct f2fs_node *)page_address(page);
sum_entry->nid = rn->footer.nid;
sum_entry->version = 0;
sum_entry->ofs_in_node = 0;
addr++;
} }
out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
return 0; return 0;
} }
@ -1614,13 +1660,11 @@ flush_now:
nid_in_journal(sum, offset) = cpu_to_le32(nid); nid_in_journal(sum, offset) = cpu_to_le32(nid);
} }
if (nat_get_blkaddr(ne) == NULL_ADDR) { if (nat_get_blkaddr(ne) == NULL_ADDR &&
add_free_nid(NM_I(sbi), nid, false) <= 0) {
write_lock(&nm_i->nat_tree_lock); write_lock(&nm_i->nat_tree_lock);
__del_from_nat_cache(nm_i, ne); __del_from_nat_cache(nm_i, ne);
write_unlock(&nm_i->nat_tree_lock); write_unlock(&nm_i->nat_tree_lock);
/* We can reuse this freed nid at this point */
add_free_nid(NM_I(sbi), nid);
} else { } else {
write_lock(&nm_i->nat_tree_lock); write_lock(&nm_i->nat_tree_lock);
__clear_nat_cache_dirty(nm_i, ne); __clear_nat_cache_dirty(nm_i, ne);
@ -1661,19 +1705,16 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
spin_lock_init(&nm_i->free_nid_list_lock); spin_lock_init(&nm_i->free_nid_list_lock);
rwlock_init(&nm_i->nat_tree_lock); rwlock_init(&nm_i->nat_tree_lock);
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
nm_i->init_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
nm_i->nat_bitmap = kzalloc(nm_i->bitmap_size, GFP_KERNEL);
if (!nm_i->nat_bitmap)
return -ENOMEM;
version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP); version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP);
if (!version_bitmap) if (!version_bitmap)
return -EFAULT; return -EFAULT;
/* copy version bitmap */ nm_i->nat_bitmap = kmemdup(version_bitmap, nm_i->bitmap_size,
memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size); GFP_KERNEL);
if (!nm_i->nat_bitmap)
return -ENOMEM;
return 0; return 0;
} }

View File

@ -29,6 +29,9 @@
/* vector size for gang look-up from nat cache that consists of radix tree */ /* vector size for gang look-up from nat cache that consists of radix tree */
#define NATVEC_SIZE 64 #define NATVEC_SIZE 64
/* return value for read_node_page */
#define LOCKED_PAGE 1
/* /*
* For node information * For node information
*/ */
@ -239,7 +242,7 @@ static inline bool IS_DNODE(struct page *node_page)
return false; return false;
if (ofs >= 6 + 2 * NIDS_PER_BLOCK) { if (ofs >= 6 + 2 * NIDS_PER_BLOCK) {
ofs -= 6 + 2 * NIDS_PER_BLOCK; ofs -= 6 + 2 * NIDS_PER_BLOCK;
if ((long int)ofs % (NIDS_PER_BLOCK + 1)) if (!((long int)ofs % (NIDS_PER_BLOCK + 1)))
return false; return false;
} }
return true; return true;
@ -277,6 +280,21 @@ static inline int is_cold_file(struct inode *inode)
return F2FS_I(inode)->i_advise & FADVISE_COLD_BIT; return F2FS_I(inode)->i_advise & FADVISE_COLD_BIT;
} }
static inline void set_cold_file(struct inode *inode)
{
F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT;
}
static inline int is_cp_file(struct inode *inode)
{
return F2FS_I(inode)->i_advise & FADVISE_CP_BIT;
}
static inline void set_cp_file(struct inode *inode)
{
F2FS_I(inode)->i_advise |= FADVISE_CP_BIT;
}
static inline int is_cold_data(struct page *page) static inline int is_cold_data(struct page *page)
{ {
return PageChecked(page); return PageChecked(page);

View File

@ -53,7 +53,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
dir = f2fs_iget(inode->i_sb, le32_to_cpu(raw_inode->i_pino)); dir = f2fs_iget(inode->i_sb, le32_to_cpu(raw_inode->i_pino));
if (IS_ERR(dir)) { if (IS_ERR(dir)) {
err = -EINVAL; err = PTR_ERR(dir);
goto out; goto out;
} }
@ -112,11 +112,14 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
while (1) { while (1) {
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC);
if (err)
goto out; goto out;
lock_page(page);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
goto out; goto unlock_out;
if (!is_fsync_dnode(page)) if (!is_fsync_dnode(page))
goto next; goto next;
@ -129,24 +132,23 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
FI_INC_LINK); FI_INC_LINK);
} else { } else {
if (IS_INODE(page) && is_dent_dnode(page)) { if (IS_INODE(page) && is_dent_dnode(page)) {
if (recover_inode_page(sbi, page)) { err = recover_inode_page(sbi, page);
err = -ENOMEM; if (err)
goto out; goto unlock_out;
}
} }
/* add this fsync inode to the list */ /* add this fsync inode to the list */
entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS);
if (!entry) { if (!entry) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto unlock_out;
} }
entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
if (IS_ERR(entry->inode)) { if (IS_ERR(entry->inode)) {
err = PTR_ERR(entry->inode); err = PTR_ERR(entry->inode);
kmem_cache_free(fsync_entry_slab, entry); kmem_cache_free(fsync_entry_slab, entry);
goto out; goto unlock_out;
} }
list_add_tail(&entry->list, head); list_add_tail(&entry->list, head);
@ -154,16 +156,20 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
} }
if (IS_INODE(page)) { if (IS_INODE(page)) {
err = recover_inode(entry->inode, page); err = recover_inode(entry->inode, page);
if (err) if (err == -ENOENT) {
goto out; goto next;
} else if (err) {
err = -EINVAL;
goto unlock_out;
}
} }
next: next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
ClearPageUptodate(page);
} }
out: unlock_out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
return err; return err;
} }
@ -232,13 +238,15 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
iput(inode); iput(inode);
} }
static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
struct page *page, block_t blkaddr) struct page *page, block_t blkaddr)
{ {
unsigned int start, end; unsigned int start, end;
struct dnode_of_data dn; struct dnode_of_data dn;
struct f2fs_summary sum; struct f2fs_summary sum;
struct node_info ni; struct node_info ni;
int err = 0;
int ilock;
start = start_bidx_of_node(ofs_of_node(page)); start = start_bidx_of_node(ofs_of_node(page));
if (IS_INODE(page)) if (IS_INODE(page))
@ -246,9 +254,14 @@ static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
else else
end = start + ADDRS_PER_BLOCK; end = start + ADDRS_PER_BLOCK;
ilock = mutex_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
if (get_dnode_of_data(&dn, start, 0))
return; err = get_dnode_of_data(&dn, start, ALLOC_NODE);
if (err) {
mutex_unlock_op(sbi, ilock);
return err;
}
wait_on_page_writeback(dn.node_page); wait_on_page_writeback(dn.node_page);
@ -293,14 +306,17 @@ static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr); recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, ilock);
return 0;
} }
static void recover_data(struct f2fs_sb_info *sbi, static int recover_data(struct f2fs_sb_info *sbi,
struct list_head *head, int type) struct list_head *head, int type)
{ {
unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver); unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver);
struct curseg_info *curseg; struct curseg_info *curseg;
struct page *page; struct page *page;
int err = 0;
block_t blkaddr; block_t blkaddr;
/* get node pages in the current segment */ /* get node pages in the current segment */
@ -310,23 +326,29 @@ static void recover_data(struct f2fs_sb_info *sbi,
/* read node page */ /* read node page */
page = alloc_page(GFP_NOFS | __GFP_ZERO); page = alloc_page(GFP_NOFS | __GFP_ZERO);
if (IS_ERR(page)) if (IS_ERR(page))
return; return -ENOMEM;
lock_page(page); lock_page(page);
while (1) { while (1) {
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) err = f2fs_readpage(sbi, page, blkaddr, READ_SYNC);
if (err)
goto out; goto out;
lock_page(page);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
goto out; goto unlock_out;
entry = get_fsync_inode(head, ino_of_node(page)); entry = get_fsync_inode(head, ino_of_node(page));
if (!entry) if (!entry)
goto next; goto next;
do_recover_data(sbi, entry->inode, page, blkaddr); err = do_recover_data(sbi, entry->inode, page, blkaddr);
if (err)
goto out;
if (entry->blkaddr == blkaddr) { if (entry->blkaddr == blkaddr) {
iput(entry->inode); iput(entry->inode);
@ -336,28 +358,32 @@ static void recover_data(struct f2fs_sb_info *sbi,
next: next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
ClearPageUptodate(page);
} }
out: unlock_out:
unlock_page(page); unlock_page(page);
out:
__free_pages(page, 0); __free_pages(page, 0);
allocate_new_segments(sbi); if (!err)
allocate_new_segments(sbi);
return err;
} }
void recover_fsync_data(struct f2fs_sb_info *sbi) int recover_fsync_data(struct f2fs_sb_info *sbi)
{ {
struct list_head inode_list; struct list_head inode_list;
int err;
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
sizeof(struct fsync_inode_entry), NULL); sizeof(struct fsync_inode_entry), NULL);
if (unlikely(!fsync_entry_slab)) if (unlikely(!fsync_entry_slab))
return; return -ENOMEM;
INIT_LIST_HEAD(&inode_list); INIT_LIST_HEAD(&inode_list);
/* step #1: find fsynced inode numbers */ /* step #1: find fsynced inode numbers */
if (find_fsync_dnodes(sbi, &inode_list)) err = find_fsync_dnodes(sbi, &inode_list);
if (err)
goto out; goto out;
if (list_empty(&inode_list)) if (list_empty(&inode_list))
@ -365,11 +391,12 @@ void recover_fsync_data(struct f2fs_sb_info *sbi)
/* step #2: recover data */ /* step #2: recover data */
sbi->por_doing = 1; sbi->por_doing = 1;
recover_data(sbi, &inode_list, CURSEG_WARM_NODE); err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
sbi->por_doing = 0; sbi->por_doing = 0;
BUG_ON(!list_empty(&inode_list)); BUG_ON(!list_empty(&inode_list));
out: out:
destroy_fsync_dnodes(sbi, &inode_list); destroy_fsync_dnodes(sbi, &inode_list);
kmem_cache_destroy(fsync_entry_slab); kmem_cache_destroy(fsync_entry_slab);
write_checkpoint(sbi, false); write_checkpoint(sbi, false);
return err;
} }

View File

@ -18,6 +18,7 @@
#include "f2fs.h" #include "f2fs.h"
#include "segment.h" #include "segment.h"
#include "node.h" #include "node.h"
#include <trace/events/f2fs.h>
/* /*
* This function balances dirty node and dentry pages. * This function balances dirty node and dentry pages.
@ -49,9 +50,20 @@ static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
if (dirty_type == DIRTY) { if (dirty_type == DIRTY) {
struct seg_entry *sentry = get_seg_entry(sbi, segno); struct seg_entry *sentry = get_seg_entry(sbi, segno);
enum dirty_type t = DIRTY_HOT_DATA;
dirty_type = sentry->type; dirty_type = sentry->type;
if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type]))
dirty_i->nr_dirty[dirty_type]++; dirty_i->nr_dirty[dirty_type]++;
/* Only one bitmap should be set */
for (; t <= DIRTY_COLD_NODE; t++) {
if (t == dirty_type)
continue;
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
dirty_i->nr_dirty[t]--;
}
} }
} }
@ -64,13 +76,16 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
dirty_i->nr_dirty[dirty_type]--; dirty_i->nr_dirty[dirty_type]--;
if (dirty_type == DIRTY) { if (dirty_type == DIRTY) {
struct seg_entry *sentry = get_seg_entry(sbi, segno); enum dirty_type t = DIRTY_HOT_DATA;
dirty_type = sentry->type;
if (test_and_clear_bit(segno, /* clear all the bitmaps */
dirty_i->dirty_segmap[dirty_type])) for (; t <= DIRTY_COLD_NODE; t++)
dirty_i->nr_dirty[dirty_type]--; if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
clear_bit(segno, dirty_i->victim_segmap[FG_GC]); dirty_i->nr_dirty[t]--;
clear_bit(segno, dirty_i->victim_segmap[BG_GC]);
if (get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0)
clear_bit(GET_SECNO(sbi, segno),
dirty_i->victim_secmap);
} }
} }
@ -296,13 +311,12 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, int type)
int ofs_unit, int type)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE]; unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE];
unsigned int segno, next_segno, i; unsigned int segno;
int ofs = 0; unsigned int ofs = 0;
/* /*
* If there is not enough reserved sections, * If there is not enough reserved sections,
@ -318,28 +332,46 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi,
if (IS_NODESEG(type)) if (IS_NODESEG(type))
return NULL_SEGNO; return NULL_SEGNO;
next: next:
segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs++); segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs);
ofs = ((segno / ofs_unit) * ofs_unit) + ofs_unit; ofs += sbi->segs_per_sec;
if (segno < TOTAL_SEGS(sbi)) { if (segno < TOTAL_SEGS(sbi)) {
int i;
/* skip intermediate segments in a section */ /* skip intermediate segments in a section */
if (segno % ofs_unit) if (segno % sbi->segs_per_sec)
goto next;
/* skip if the section is currently used */
if (sec_usage_check(sbi, GET_SECNO(sbi, segno)))
goto next; goto next;
/* skip if whole section is not prefree */ /* skip if whole section is not prefree */
next_segno = find_next_zero_bit(prefree_segmap, for (i = 1; i < sbi->segs_per_sec; i++)
TOTAL_SEGS(sbi), segno + 1); if (!test_bit(segno + i, prefree_segmap))
if (next_segno - segno < ofs_unit) goto next;
goto next;
/* skip if whole section was not free at the last checkpoint */ /* skip if whole section was not free at the last checkpoint */
for (i = 0; i < ofs_unit; i++) for (i = 0; i < sbi->segs_per_sec; i++)
if (get_seg_entry(sbi, segno)->ckpt_valid_blocks) if (get_seg_entry(sbi, segno + i)->ckpt_valid_blocks)
goto next; goto next;
return segno; return segno;
} }
return NULL_SEGNO; return NULL_SEGNO;
} }
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
{
struct curseg_info *curseg = CURSEG_I(sbi, type);
unsigned int segno = curseg->segno;
struct free_segmap_info *free_i = FREE_I(sbi);
if (segno + 1 < TOTAL_SEGS(sbi) && (segno + 1) % sbi->segs_per_sec)
return !test_bit(segno + 1, free_i->free_segmap);
return 0;
}
/* /*
* Find a new segment from the free segments bitmap to right order * Find a new segment from the free segments bitmap to right order
* This function should be returned with success, otherwise BUG * This function should be returned with success, otherwise BUG
@ -348,9 +380,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
unsigned int *newseg, bool new_sec, int dir) unsigned int *newseg, bool new_sec, int dir)
{ {
struct free_segmap_info *free_i = FREE_I(sbi); struct free_segmap_info *free_i = FREE_I(sbi);
unsigned int total_secs = sbi->total_sections;
unsigned int segno, secno, zoneno; unsigned int segno, secno, zoneno;
unsigned int total_zones = sbi->total_sections / sbi->secs_per_zone; unsigned int total_zones = TOTAL_SECS(sbi) / sbi->secs_per_zone;
unsigned int hint = *newseg / sbi->segs_per_sec; unsigned int hint = *newseg / sbi->segs_per_sec;
unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg); unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg);
unsigned int left_start = hint; unsigned int left_start = hint;
@ -363,16 +394,17 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
segno = find_next_zero_bit(free_i->free_segmap, segno = find_next_zero_bit(free_i->free_segmap,
TOTAL_SEGS(sbi), *newseg + 1); TOTAL_SEGS(sbi), *newseg + 1);
if (segno < TOTAL_SEGS(sbi)) if (segno - *newseg < sbi->segs_per_sec -
(*newseg % sbi->segs_per_sec))
goto got_it; goto got_it;
} }
find_other_zone: find_other_zone:
secno = find_next_zero_bit(free_i->free_secmap, total_secs, hint); secno = find_next_zero_bit(free_i->free_secmap, TOTAL_SECS(sbi), hint);
if (secno >= total_secs) { if (secno >= TOTAL_SECS(sbi)) {
if (dir == ALLOC_RIGHT) { if (dir == ALLOC_RIGHT) {
secno = find_next_zero_bit(free_i->free_secmap, secno = find_next_zero_bit(free_i->free_secmap,
total_secs, 0); TOTAL_SECS(sbi), 0);
BUG_ON(secno >= total_secs); BUG_ON(secno >= TOTAL_SECS(sbi));
} else { } else {
go_left = 1; go_left = 1;
left_start = hint - 1; left_start = hint - 1;
@ -387,8 +419,8 @@ find_other_zone:
continue; continue;
} }
left_start = find_next_zero_bit(free_i->free_secmap, left_start = find_next_zero_bit(free_i->free_secmap,
total_secs, 0); TOTAL_SECS(sbi), 0);
BUG_ON(left_start >= total_secs); BUG_ON(left_start >= TOTAL_SECS(sbi));
break; break;
} }
secno = left_start; secno = left_start;
@ -561,20 +593,20 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
int type, bool force) int type, bool force)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, type); struct curseg_info *curseg = CURSEG_I(sbi, type);
unsigned int ofs_unit;
if (force) { if (force) {
new_curseg(sbi, type, true); new_curseg(sbi, type, true);
goto out; goto out;
} }
ofs_unit = need_SSR(sbi) ? 1 : sbi->segs_per_sec; curseg->next_segno = check_prefree_segments(sbi, type);
curseg->next_segno = check_prefree_segments(sbi, ofs_unit, type);
if (curseg->next_segno != NULL_SEGNO) if (curseg->next_segno != NULL_SEGNO)
change_curseg(sbi, type, false); change_curseg(sbi, type, false);
else if (type == CURSEG_WARM_NODE) else if (type == CURSEG_WARM_NODE)
new_curseg(sbi, type, false); new_curseg(sbi, type, false);
else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
new_curseg(sbi, type, false);
else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) else if (need_SSR(sbi) && get_ssr_segment(sbi, type))
change_curseg(sbi, type, true); change_curseg(sbi, type, true);
else else
@ -656,10 +688,16 @@ static void do_submit_bio(struct f2fs_sb_info *sbi,
if (type >= META_FLUSH) if (type >= META_FLUSH)
rw = WRITE_FLUSH_FUA; rw = WRITE_FLUSH_FUA;
if (btype == META)
rw |= REQ_META;
if (sbi->bio[btype]) { if (sbi->bio[btype]) {
struct bio_private *p = sbi->bio[btype]->bi_private; struct bio_private *p = sbi->bio[btype]->bi_private;
p->sbi = sbi; p->sbi = sbi;
sbi->bio[btype]->bi_end_io = f2fs_end_io_write; sbi->bio[btype]->bi_end_io = f2fs_end_io_write;
trace_f2fs_do_submit_bio(sbi->sb, btype, sync, sbi->bio[btype]);
if (type == META_FLUSH) { if (type == META_FLUSH) {
DECLARE_COMPLETION_ONSTACK(wait); DECLARE_COMPLETION_ONSTACK(wait);
p->is_sync = true; p->is_sync = true;
@ -696,7 +734,7 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page,
do_submit_bio(sbi, type, false); do_submit_bio(sbi, type, false);
alloc_new: alloc_new:
if (sbi->bio[type] == NULL) { if (sbi->bio[type] == NULL) {
sbi->bio[type] = f2fs_bio_alloc(bdev, bio_get_nr_vecs(bdev)); sbi->bio[type] = f2fs_bio_alloc(bdev, max_hw_blocks(sbi));
sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
/* /*
* The end_io will be assigned at the sumbission phase. * The end_io will be assigned at the sumbission phase.
@ -714,6 +752,7 @@ alloc_new:
sbi->last_block_in_bio[type] = blk_addr; sbi->last_block_in_bio[type] = blk_addr;
up_write(&sbi->bio_sem); up_write(&sbi->bio_sem);
trace_f2fs_submit_write_page(page, blk_addr, type);
} }
static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
@ -1390,7 +1429,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
} }
if (sbi->segs_per_sec > 1) { if (sbi->segs_per_sec > 1) {
sit_i->sec_entries = vzalloc(sbi->total_sections * sit_i->sec_entries = vzalloc(TOTAL_SECS(sbi) *
sizeof(struct sec_entry)); sizeof(struct sec_entry));
if (!sit_i->sec_entries) if (!sit_i->sec_entries)
return -ENOMEM; return -ENOMEM;
@ -1403,10 +1442,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
bitmap_size = __bitmap_size(sbi, SIT_BITMAP); bitmap_size = __bitmap_size(sbi, SIT_BITMAP);
src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP);
dst_bitmap = kzalloc(bitmap_size, GFP_KERNEL); dst_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL);
if (!dst_bitmap) if (!dst_bitmap)
return -ENOMEM; return -ENOMEM;
memcpy(dst_bitmap, src_bitmap, bitmap_size);
/* init SIT information */ /* init SIT information */
sit_i->s_ops = &default_salloc_ops; sit_i->s_ops = &default_salloc_ops;
@ -1442,7 +1480,7 @@ static int build_free_segmap(struct f2fs_sb_info *sbi)
if (!free_i->free_segmap) if (!free_i->free_segmap)
return -ENOMEM; return -ENOMEM;
sec_bitmap_size = f2fs_bitmap_size(sbi->total_sections); sec_bitmap_size = f2fs_bitmap_size(TOTAL_SECS(sbi));
free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL); free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL);
if (!free_i->free_secmap) if (!free_i->free_secmap)
return -ENOMEM; return -ENOMEM;
@ -1559,14 +1597,13 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
} }
} }
static int init_victim_segmap(struct f2fs_sb_info *sbi) static int init_victim_secmap(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SECS(sbi));
dirty_i->victim_segmap[FG_GC] = kzalloc(bitmap_size, GFP_KERNEL); dirty_i->victim_secmap = kzalloc(bitmap_size, GFP_KERNEL);
dirty_i->victim_segmap[BG_GC] = kzalloc(bitmap_size, GFP_KERNEL); if (!dirty_i->victim_secmap)
if (!dirty_i->victim_segmap[FG_GC] || !dirty_i->victim_segmap[BG_GC])
return -ENOMEM; return -ENOMEM;
return 0; return 0;
} }
@ -1593,7 +1630,7 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
} }
init_dirty_segmap(sbi); init_dirty_segmap(sbi);
return init_victim_segmap(sbi); return init_victim_secmap(sbi);
} }
/* /*
@ -1680,18 +1717,10 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
} }
void reset_victim_segmap(struct f2fs_sb_info *sbi) static void destroy_victim_secmap(struct f2fs_sb_info *sbi)
{
unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi));
memset(DIRTY_I(sbi)->victim_segmap[FG_GC], 0, bitmap_size);
}
static void destroy_victim_segmap(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
kfree(dirty_i->victim_secmap);
kfree(dirty_i->victim_segmap[FG_GC]);
kfree(dirty_i->victim_segmap[BG_GC]);
} }
static void destroy_dirty_segmap(struct f2fs_sb_info *sbi) static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
@ -1706,7 +1735,7 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
for (i = 0; i < NR_DIRTY_TYPE; i++) for (i = 0; i < NR_DIRTY_TYPE; i++)
discard_dirty_segmap(sbi, i); discard_dirty_segmap(sbi, i);
destroy_victim_segmap(sbi); destroy_victim_secmap(sbi);
SM_I(sbi)->dirty_info = NULL; SM_I(sbi)->dirty_info = NULL;
kfree(dirty_i); kfree(dirty_i);
} }

View File

@ -8,10 +8,13 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/blkdev.h>
/* constant macro */ /* constant macro */
#define NULL_SEGNO ((unsigned int)(~0)) #define NULL_SEGNO ((unsigned int)(~0))
#define NULL_SECNO ((unsigned int)(~0))
/* V: Logical segment # in volume, R: Relative segment # in main area */ /* L: Logical segment # in volume, R: Relative segment # in main area */
#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) #define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno)
#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno) #define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno)
@ -23,13 +26,13 @@
((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \ ((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \
(t == CURSEG_WARM_NODE)) (t == CURSEG_WARM_NODE))
#define IS_CURSEG(sbi, segno) \ #define IS_CURSEG(sbi, seg) \
((segno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ ((seg == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
(segno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ (seg == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
(segno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \ (seg == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
(segno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \ (seg == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
(segno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \ (seg == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
(segno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno)) (seg == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
#define IS_CURSEC(sbi, secno) \ #define IS_CURSEC(sbi, secno) \
((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \ ((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \
@ -81,9 +84,12 @@
#define f2fs_bitmap_size(nr) \ #define f2fs_bitmap_size(nr) \
(BITS_TO_LONGS(nr) * sizeof(unsigned long)) (BITS_TO_LONGS(nr) * sizeof(unsigned long))
#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments) #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
#define TOTAL_SECS(sbi) (sbi->total_sections)
#define SECTOR_FROM_BLOCK(sbi, blk_addr) \ #define SECTOR_FROM_BLOCK(sbi, blk_addr) \
(blk_addr << ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE)) (blk_addr << ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE))
#define SECTOR_TO_BLOCK(sbi, sectors) \
(sectors >> ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE))
/* during checkpoint, bio_private is used to synchronize the last bio */ /* during checkpoint, bio_private is used to synchronize the last bio */
struct bio_private { struct bio_private {
@ -213,7 +219,7 @@ struct dirty_seglist_info {
unsigned long *dirty_segmap[NR_DIRTY_TYPE]; unsigned long *dirty_segmap[NR_DIRTY_TYPE];
struct mutex seglist_lock; /* lock for segment bitmaps */ struct mutex seglist_lock; /* lock for segment bitmaps */
int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */ int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */
unsigned long *victim_segmap[2]; /* BG_GC, FG_GC */ unsigned long *victim_secmap; /* background GC victims */
}; };
/* victim selection function for cleaning and SSR */ /* victim selection function for cleaning and SSR */
@ -464,8 +470,7 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
static inline int utilization(struct f2fs_sb_info *sbi) static inline int utilization(struct f2fs_sb_info *sbi)
{ {
return (long int)valid_user_blocks(sbi) * 100 / return div_u64(valid_user_blocks(sbi) * 100, sbi->user_block_count);
(long int)sbi->user_block_count;
} }
/* /*
@ -616,3 +621,17 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count) le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count)
- (base + 1) + type; - (base + 1) + type;
} }
static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno)
{
if (IS_CURSEC(sbi, secno) || (sbi->cur_victim_sec == secno))
return true;
return false;
}
static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
{
struct block_device *bdev = sbi->sb->s_bdev;
struct request_queue *q = bdev_get_queue(bdev);
return SECTOR_TO_BLOCK(sbi, queue_max_sectors(q));
}

View File

@ -12,7 +12,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/proc_fs.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/kthread.h> #include <linux/kthread.h>
@ -21,12 +20,17 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/blkdev.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "segment.h"
#include "xattr.h" #include "xattr.h"
#define CREATE_TRACE_POINTS
#include <trace/events/f2fs.h>
static struct kmem_cache *f2fs_inode_cachep; static struct kmem_cache *f2fs_inode_cachep;
enum { enum {
@ -94,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
return &fi->vfs_inode; return &fi->vfs_inode;
} }
static int f2fs_drop_inode(struct inode *inode)
{
/*
* This is to avoid a deadlock condition like below.
* writeback_single_inode(inode)
* - f2fs_write_data_page
* - f2fs_gc -> iput -> evict
* - inode_wait_for_writeback(inode)
*/
if (!inode_unhashed(inode) && inode->i_state & I_SYNC)
return 0;
return generic_drop_inode(inode);
}
static void f2fs_i_callback(struct rcu_head *head) static void f2fs_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); struct inode *inode = container_of(head, struct inode, i_rcu);
@ -132,13 +150,18 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
trace_f2fs_sync_fs(sb, sync);
if (!sbi->s_dirty && !get_pages(sbi, F2FS_DIRTY_NODES)) if (!sbi->s_dirty && !get_pages(sbi, F2FS_DIRTY_NODES))
return 0; return 0;
if (sync) if (sync) {
mutex_lock(&sbi->gc_mutex);
write_checkpoint(sbi, false); write_checkpoint(sbi, false);
else mutex_unlock(&sbi->gc_mutex);
} else {
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
}
return 0; return 0;
} }
@ -180,7 +203,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_files = sbi->total_node_count; buf->f_files = sbi->total_node_count;
buf->f_ffree = sbi->total_node_count - valid_inode_count(sbi); buf->f_ffree = sbi->total_node_count - valid_inode_count(sbi);
buf->f_namelen = F2FS_MAX_NAME_LEN; buf->f_namelen = F2FS_NAME_LEN;
buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[0] = (u32)id;
buf->f_fsid.val[1] = (u32)(id >> 32); buf->f_fsid.val[1] = (u32)(id >> 32);
@ -223,6 +246,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
static struct super_operations f2fs_sops = { static struct super_operations f2fs_sops = {
.alloc_inode = f2fs_alloc_inode, .alloc_inode = f2fs_alloc_inode,
.drop_inode = f2fs_drop_inode,
.destroy_inode = f2fs_destroy_inode, .destroy_inode = f2fs_destroy_inode,
.write_inode = f2fs_write_inode, .write_inode = f2fs_write_inode,
.show_options = f2fs_show_options, .show_options = f2fs_show_options,
@ -457,6 +481,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
sbi->root_ino_num = le32_to_cpu(raw_super->root_ino); sbi->root_ino_num = le32_to_cpu(raw_super->root_ino);
sbi->node_ino_num = le32_to_cpu(raw_super->node_ino); sbi->node_ino_num = le32_to_cpu(raw_super->node_ino);
sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino); sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino);
sbi->cur_victim_sec = NULL_SECNO;
for (i = 0; i < NR_COUNT_TYPE; i++) for (i = 0; i < NR_COUNT_TYPE; i++)
atomic_set(&sbi->nr_pages[i], 0); atomic_set(&sbi->nr_pages[i], 0);
@ -473,7 +498,7 @@ static int validate_superblock(struct super_block *sb,
if (!*raw_super_buf) { if (!*raw_super_buf) {
f2fs_msg(sb, KERN_ERR, "unable to read %s superblock", f2fs_msg(sb, KERN_ERR, "unable to read %s superblock",
super); super);
return 1; return -EIO;
} }
*raw_super = (struct f2fs_super_block *) *raw_super = (struct f2fs_super_block *)
@ -485,7 +510,7 @@ static int validate_superblock(struct super_block *sb,
f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem " f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem "
"in %s superblock", super); "in %s superblock", super);
return 1; return -EINVAL;
} }
static int f2fs_fill_super(struct super_block *sb, void *data, int silent) static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
@ -508,9 +533,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_sbi; goto free_sbi;
} }
if (validate_superblock(sb, &raw_super, &raw_super_buf, 0)) { err = validate_superblock(sb, &raw_super, &raw_super_buf, 0);
if (err) {
brelse(raw_super_buf); brelse(raw_super_buf);
if (validate_superblock(sb, &raw_super, &raw_super_buf, 1)) /* check secondary superblock when primary failed */
err = validate_superblock(sb, &raw_super, &raw_super_buf, 1);
if (err)
goto free_sb_buf; goto free_sb_buf;
} }
/* init some FS parameters */ /* init some FS parameters */
@ -525,7 +553,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sbi, POSIX_ACL); set_opt(sbi, POSIX_ACL);
#endif #endif
/* parse mount options */ /* parse mount options */
if (parse_options(sb, sbi, (char *)data)) err = parse_options(sb, sbi, (char *)data);
if (err)
goto free_sb_buf; goto free_sb_buf;
sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize));
@ -547,11 +576,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sbi->raw_super = raw_super; sbi->raw_super = raw_super;
sbi->raw_super_buf = raw_super_buf; sbi->raw_super_buf = raw_super_buf;
mutex_init(&sbi->gc_mutex); mutex_init(&sbi->gc_mutex);
mutex_init(&sbi->write_inode);
mutex_init(&sbi->writepages); mutex_init(&sbi->writepages);
mutex_init(&sbi->cp_mutex); mutex_init(&sbi->cp_mutex);
for (i = 0; i < NR_LOCK_TYPE; i++) for (i = 0; i < NR_GLOBAL_LOCKS; i++)
mutex_init(&sbi->fs_lock[i]); mutex_init(&sbi->fs_lock[i]);
mutex_init(&sbi->node_write);
sbi->por_doing = 0; sbi->por_doing = 0;
spin_lock_init(&sbi->stat_lock); spin_lock_init(&sbi->stat_lock);
init_rwsem(&sbi->bio_sem); init_rwsem(&sbi->bio_sem);
@ -638,8 +667,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
} }
/* recover fsynced data */ /* recover fsynced data */
if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
recover_fsync_data(sbi); err = recover_fsync_data(sbi);
if (err)
f2fs_msg(sb, KERN_ERR,
"Cannot recover all fsync data errno=%ld", err);
}
/* After POR, we can run background GC thread */ /* After POR, we can run background GC thread */
err = start_gc_thread(sbi); err = start_gc_thread(sbi);
@ -650,6 +683,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err) if (err)
goto fail; goto fail;
if (test_opt(sbi, DISCARD)) {
struct request_queue *q = bdev_get_queue(sb->s_bdev);
if (!blk_queue_discard(q))
f2fs_msg(sb, KERN_WARNING,
"mounting with \"discard\" option, but "
"the device does not support discard");
}
return 0; return 0;
fail: fail:
stop_gc_thread(sbi); stop_gc_thread(sbi);

View File

@ -307,27 +307,30 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
int error, found, free, newsize; int error, found, free, newsize;
size_t name_len; size_t name_len;
char *pval; char *pval;
int ilock;
if (name == NULL) if (name == NULL)
return -EINVAL; return -EINVAL;
name_len = strlen(name);
if (value == NULL) if (value == NULL)
value_len = 0; value_len = 0;
if (name_len > 255 || value_len > MAX_VALUE_LEN) name_len = strlen(name);
if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN)
return -ERANGE; return -ERANGE;
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
mutex_lock_op(sbi, NODE_NEW); ilock = mutex_lock_op(sbi);
if (!fi->i_xattr_nid) { if (!fi->i_xattr_nid) {
/* Allocate new attribute block */ /* Allocate new attribute block */
struct dnode_of_data dn; struct dnode_of_data dn;
if (!alloc_nid(sbi, &fi->i_xattr_nid)) { if (!alloc_nid(sbi, &fi->i_xattr_nid)) {
mutex_unlock_op(sbi, NODE_NEW); error = -ENOSPC;
return -ENOSPC; goto exit;
} }
set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid); set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid);
mark_inode_dirty(inode); mark_inode_dirty(inode);
@ -336,8 +339,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
if (IS_ERR(page)) { if (IS_ERR(page)) {
alloc_nid_failed(sbi, fi->i_xattr_nid); alloc_nid_failed(sbi, fi->i_xattr_nid);
fi->i_xattr_nid = 0; fi->i_xattr_nid = 0;
mutex_unlock_op(sbi, NODE_NEW); error = PTR_ERR(page);
return PTR_ERR(page); goto exit;
} }
alloc_nid_done(sbi, fi->i_xattr_nid); alloc_nid_done(sbi, fi->i_xattr_nid);
@ -349,8 +352,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
/* The inode already has an extended attribute block. */ /* The inode already has an extended attribute block. */
page = get_node_page(sbi, fi->i_xattr_nid); page = get_node_page(sbi, fi->i_xattr_nid);
if (IS_ERR(page)) { if (IS_ERR(page)) {
mutex_unlock_op(sbi, NODE_NEW); error = PTR_ERR(page);
return PTR_ERR(page); goto exit;
} }
base_addr = page_address(page); base_addr = page_address(page);
@ -432,12 +435,13 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
clear_inode_flag(fi, FI_ACL_MODE); clear_inode_flag(fi, FI_ACL_MODE);
} }
f2fs_write_inode(inode, NULL); update_inode_page(inode);
mutex_unlock_op(sbi, NODE_NEW); mutex_unlock_op(sbi, ilock);
return 0; return 0;
cleanup: cleanup:
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
mutex_unlock_op(sbi, NODE_NEW); exit:
mutex_unlock_op(sbi, ilock);
return error; return error;
} }

View File

@ -139,7 +139,7 @@ struct f2fs_extent {
__le32 len; /* lengh of the extent */ __le32 len; /* lengh of the extent */
} __packed; } __packed;
#define F2FS_MAX_NAME_LEN 256 #define F2FS_NAME_LEN 255
#define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
@ -165,7 +165,8 @@ struct f2fs_inode {
__le32 i_flags; /* file attributes */ __le32 i_flags; /* file attributes */
__le32 i_pino; /* parent inode number */ __le32 i_pino; /* parent inode number */
__le32 i_namelen; /* file name length */ __le32 i_namelen; /* file name length */
__u8 i_name[F2FS_MAX_NAME_LEN]; /* file name for SPOR */ __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */
__u8 i_reserved2; /* for backward compatibility */
struct f2fs_extent i_ext; /* caching a largest extent */ struct f2fs_extent i_ext; /* caching a largest extent */
@ -362,10 +363,10 @@ struct f2fs_summary_block {
typedef __le32 f2fs_hash_t; typedef __le32 f2fs_hash_t;
/* One directory entry slot covers 8bytes-long file name */ /* One directory entry slot covers 8bytes-long file name */
#define F2FS_NAME_LEN 8 #define F2FS_SLOT_LEN 8
#define F2FS_NAME_LEN_BITS 3 #define F2FS_SLOT_LEN_BITS 3
#define GET_DENTRY_SLOTS(x) ((x + F2FS_NAME_LEN - 1) >> F2FS_NAME_LEN_BITS) #define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
/* the number of dentry in a block */ /* the number of dentry in a block */
#define NR_DENTRY_IN_BLOCK 214 #define NR_DENTRY_IN_BLOCK 214
@ -377,10 +378,10 @@ typedef __le32 f2fs_hash_t;
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ #define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
BITS_PER_BYTE) BITS_PER_BYTE)
#define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \ #define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \
F2FS_NAME_LEN) * \ F2FS_SLOT_LEN) * \
NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP))
/* One directory entry slot representing F2FS_NAME_LEN-sized file name */ /* One directory entry slot representing F2FS_SLOT_LEN-sized file name */
struct f2fs_dir_entry { struct f2fs_dir_entry {
__le32 hash_code; /* hash code of file name */ __le32 hash_code; /* hash code of file name */
__le32 ino; /* inode number */ __le32 ino; /* inode number */
@ -394,7 +395,7 @@ struct f2fs_dentry_block {
__u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP];
__u8 reserved[SIZE_OF_RESERVED]; __u8 reserved[SIZE_OF_RESERVED];
struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK];
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_NAME_LEN]; __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
} __packed; } __packed;
/* file types used in inode_info->flags */ /* file types used in inode_info->flags */

682
include/trace/events/f2fs.h Normal file
View File

@ -0,0 +1,682 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM f2fs
#if !defined(_TRACE_F2FS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_F2FS_H
#include <linux/tracepoint.h>
#define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev)
#define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino
#define show_block_type(type) \
__print_symbolic(type, \
{ NODE, "NODE" }, \
{ DATA, "DATA" }, \
{ META, "META" }, \
{ META_FLUSH, "META_FLUSH" })
#define show_bio_type(type) \
__print_symbolic(type, \
{ READ, "READ" }, \
{ READA, "READAHEAD" }, \
{ READ_SYNC, "READ_SYNC" }, \
{ WRITE, "WRITE" }, \
{ WRITE_SYNC, "WRITE_SYNC" }, \
{ WRITE_FLUSH, "WRITE_FLUSH" }, \
{ WRITE_FUA, "WRITE_FUA" })
#define show_data_type(type) \
__print_symbolic(type, \
{ CURSEG_HOT_DATA, "Hot DATA" }, \
{ CURSEG_WARM_DATA, "Warm DATA" }, \
{ CURSEG_COLD_DATA, "Cold DATA" }, \
{ CURSEG_HOT_NODE, "Hot NODE" }, \
{ CURSEG_WARM_NODE, "Warm NODE" }, \
{ CURSEG_COLD_NODE, "Cold NODE" }, \
{ NO_CHECK_TYPE, "No TYPE" })
#define show_gc_type(type) \
__print_symbolic(type, \
{ FG_GC, "Foreground GC" }, \
{ BG_GC, "Background GC" })
#define show_alloc_mode(type) \
__print_symbolic(type, \
{ LFS, "LFS-mode" }, \
{ SSR, "SSR-mode" })
#define show_victim_policy(type) \
__print_symbolic(type, \
{ GC_GREEDY, "Greedy" }, \
{ GC_CB, "Cost-Benefit" })
struct victim_sel_policy;
DECLARE_EVENT_CLASS(f2fs__inode,
TP_PROTO(struct inode *inode),
TP_ARGS(inode),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(ino_t, pino)
__field(umode_t, mode)
__field(loff_t, size)
__field(unsigned int, nlink)
__field(blkcnt_t, blocks)
__field(__u8, advise)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->pino = F2FS_I(inode)->i_pino;
__entry->mode = inode->i_mode;
__entry->nlink = inode->i_nlink;
__entry->size = inode->i_size;
__entry->blocks = inode->i_blocks;
__entry->advise = F2FS_I(inode)->i_advise;
),
TP_printk("dev = (%d,%d), ino = %lu, pino = %lu, i_mode = 0x%hx, "
"i_size = %lld, i_nlink = %u, i_blocks = %llu, i_advise = 0x%x",
show_dev_ino(__entry),
(unsigned long)__entry->pino,
__entry->mode,
__entry->size,
(unsigned int)__entry->nlink,
(unsigned long long)__entry->blocks,
(unsigned char)__entry->advise)
);
DECLARE_EVENT_CLASS(f2fs__inode_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(int, ret)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->ret = ret;
),
TP_printk("dev = (%d,%d), ino = %lu, ret = %d",
show_dev_ino(__entry),
__entry->ret)
);
DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter,
TP_PROTO(struct inode *inode),
TP_ARGS(inode)
);
TRACE_EVENT(f2fs_sync_file_exit,
TP_PROTO(struct inode *inode, bool need_cp, int datasync, int ret),
TP_ARGS(inode, need_cp, datasync, ret),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(bool, need_cp)
__field(int, datasync)
__field(int, ret)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->need_cp = need_cp;
__entry->datasync = datasync;
__entry->ret = ret;
),
TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, "
"datasync = %d, ret = %d",
show_dev_ino(__entry),
__entry->need_cp ? "needed" : "not needed",
__entry->datasync,
__entry->ret)
);
TRACE_EVENT(f2fs_sync_fs,
TP_PROTO(struct super_block *sb, int wait),
TP_ARGS(sb, wait),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(int, dirty)
__field(int, wait)
),
TP_fast_assign(
__entry->dev = sb->s_dev;
__entry->dirty = F2FS_SB(sb)->s_dirty;
__entry->wait = wait;
),
TP_printk("dev = (%d,%d), superblock is %s, wait = %d",
show_dev(__entry),
__entry->dirty ? "dirty" : "not dirty",
__entry->wait)
);
DEFINE_EVENT(f2fs__inode, f2fs_iget,
TP_PROTO(struct inode *inode),
TP_ARGS(inode)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_iget_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
DEFINE_EVENT(f2fs__inode, f2fs_evict_inode,
TP_PROTO(struct inode *inode),
TP_ARGS(inode)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_new_inode,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
TRACE_EVENT(f2fs_unlink_enter,
TP_PROTO(struct inode *dir, struct dentry *dentry),
TP_ARGS(dir, dentry),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(loff_t, size)
__field(blkcnt_t, blocks)
__field(const char *, name)
),
TP_fast_assign(
__entry->dev = dir->i_sb->s_dev;
__entry->ino = dir->i_ino;
__entry->size = dir->i_size;
__entry->blocks = dir->i_blocks;
__entry->name = dentry->d_name.name;
),
TP_printk("dev = (%d,%d), dir ino = %lu, i_size = %lld, "
"i_blocks = %llu, name = %s",
show_dev_ino(__entry),
__entry->size,
(unsigned long long)__entry->blocks,
__entry->name)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_unlink_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
DEFINE_EVENT(f2fs__inode, f2fs_truncate,
TP_PROTO(struct inode *inode),
TP_ARGS(inode)
);
TRACE_EVENT(f2fs_truncate_data_blocks_range,
TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs, int free),
TP_ARGS(inode, nid, ofs, free),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(nid_t, nid)
__field(unsigned int, ofs)
__field(int, free)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->nid = nid;
__entry->ofs = ofs;
__entry->free = free;
),
TP_printk("dev = (%d,%d), ino = %lu, nid = %u, offset = %u, freed = %d",
show_dev_ino(__entry),
(unsigned int)__entry->nid,
__entry->ofs,
__entry->free)
);
DECLARE_EVENT_CLASS(f2fs__truncate_op,
TP_PROTO(struct inode *inode, u64 from),
TP_ARGS(inode, from),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(loff_t, size)
__field(blkcnt_t, blocks)
__field(u64, from)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->size = inode->i_size;
__entry->blocks = inode->i_blocks;
__entry->from = from;
),
TP_printk("dev = (%d,%d), ino = %lu, i_size = %lld, i_blocks = %llu, "
"start file offset = %llu",
show_dev_ino(__entry),
__entry->size,
(unsigned long long)__entry->blocks,
(unsigned long long)__entry->from)
);
DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_blocks_enter,
TP_PROTO(struct inode *inode, u64 from),
TP_ARGS(inode, from)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_blocks_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_inode_blocks_enter,
TP_PROTO(struct inode *inode, u64 from),
TP_ARGS(inode, from)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_inode_blocks_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
DECLARE_EVENT_CLASS(f2fs__truncate_node,
TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
TP_ARGS(inode, nid, blk_addr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(nid_t, nid)
__field(block_t, blk_addr)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->nid = nid;
__entry->blk_addr = blk_addr;
),
TP_printk("dev = (%d,%d), ino = %lu, nid = %u, block_address = 0x%llx",
show_dev_ino(__entry),
(unsigned int)__entry->nid,
(unsigned long long)__entry->blk_addr)
);
DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_nodes_enter,
TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
TP_ARGS(inode, nid, blk_addr)
);
DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_nodes_exit,
TP_PROTO(struct inode *inode, int ret),
TP_ARGS(inode, ret)
);
DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_node,
TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr),
TP_ARGS(inode, nid, blk_addr)
);
TRACE_EVENT(f2fs_truncate_partial_nodes,
TP_PROTO(struct inode *inode, nid_t nid[], int depth, int err),
TP_ARGS(inode, nid, depth, err),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(nid_t, nid[3])
__field(int, depth)
__field(int, err)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->nid[0] = nid[0];
__entry->nid[1] = nid[1];
__entry->nid[2] = nid[2];
__entry->depth = depth;
__entry->err = err;
),
TP_printk("dev = (%d,%d), ino = %lu, "
"nid[0] = %u, nid[1] = %u, nid[2] = %u, depth = %d, err = %d",
show_dev_ino(__entry),
(unsigned int)__entry->nid[0],
(unsigned int)__entry->nid[1],
(unsigned int)__entry->nid[2],
__entry->depth,
__entry->err)
);
TRACE_EVENT_CONDITION(f2fs_readpage,
TP_PROTO(struct page *page, sector_t blkaddr, int type),
TP_ARGS(page, blkaddr, type),
TP_CONDITION(page->mapping),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(pgoff_t, index)
__field(sector_t, blkaddr)
__field(int, type)
),
TP_fast_assign(
__entry->dev = page->mapping->host->i_sb->s_dev;
__entry->ino = page->mapping->host->i_ino;
__entry->index = page->index;
__entry->blkaddr = blkaddr;
__entry->type = type;
),
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
"blkaddr = 0x%llx, bio_type = %s",
show_dev_ino(__entry),
(unsigned long)__entry->index,
(unsigned long long)__entry->blkaddr,
show_bio_type(__entry->type))
);
TRACE_EVENT(f2fs_get_data_block,
TP_PROTO(struct inode *inode, sector_t iblock,
struct buffer_head *bh, int ret),
TP_ARGS(inode, iblock, bh, ret),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(sector_t, iblock)
__field(sector_t, bh_start)
__field(size_t, bh_size)
__field(int, ret)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->iblock = iblock;
__entry->bh_start = bh->b_blocknr;
__entry->bh_size = bh->b_size;
__entry->ret = ret;
),
TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, "
"start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d",
show_dev_ino(__entry),
(unsigned long long)__entry->iblock,
(unsigned long long)__entry->bh_start,
(unsigned long long)__entry->bh_size,
__entry->ret)
);
TRACE_EVENT(f2fs_get_victim,
TP_PROTO(struct super_block *sb, int type, int gc_type,
struct victim_sel_policy *p, unsigned int pre_victim,
unsigned int prefree, unsigned int free),
TP_ARGS(sb, type, gc_type, p, pre_victim, prefree, free),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(int, type)
__field(int, gc_type)
__field(int, alloc_mode)
__field(int, gc_mode)
__field(unsigned int, victim)
__field(unsigned int, ofs_unit)
__field(unsigned int, pre_victim)
__field(unsigned int, prefree)
__field(unsigned int, free)
),
TP_fast_assign(
__entry->dev = sb->s_dev;
__entry->type = type;
__entry->gc_type = gc_type;
__entry->alloc_mode = p->alloc_mode;
__entry->gc_mode = p->gc_mode;
__entry->victim = p->min_segno;
__entry->ofs_unit = p->ofs_unit;
__entry->pre_victim = pre_victim;
__entry->prefree = prefree;
__entry->free = free;
),
TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), victim = %u "
"ofs_unit = %u, pre_victim_secno = %d, prefree = %u, free = %u",
show_dev(__entry),
show_data_type(__entry->type),
show_gc_type(__entry->gc_type),
show_alloc_mode(__entry->alloc_mode),
show_victim_policy(__entry->gc_mode),
__entry->victim,
__entry->ofs_unit,
(int)__entry->pre_victim,
__entry->prefree,
__entry->free)
);
TRACE_EVENT(f2fs_fallocate,
TP_PROTO(struct inode *inode, int mode,
loff_t offset, loff_t len, int ret),
TP_ARGS(inode, mode, offset, len, ret),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(int, mode)
__field(loff_t, offset)
__field(loff_t, len)
__field(loff_t, size)
__field(blkcnt_t, blocks)
__field(int, ret)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->mode = mode;
__entry->offset = offset;
__entry->len = len;
__entry->size = inode->i_size;
__entry->blocks = inode->i_blocks;
__entry->ret = ret;
),
TP_printk("dev = (%d,%d), ino = %lu, mode = %x, offset = %lld, "
"len = %lld, i_size = %lld, i_blocks = %llu, ret = %d",
show_dev_ino(__entry),
__entry->mode,
(unsigned long long)__entry->offset,
(unsigned long long)__entry->len,
(unsigned long long)__entry->size,
(unsigned long long)__entry->blocks,
__entry->ret)
);
TRACE_EVENT(f2fs_reserve_new_block,
TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node),
TP_ARGS(inode, nid, ofs_in_node),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(nid_t, nid)
__field(unsigned int, ofs_in_node)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->nid = nid;
__entry->ofs_in_node = ofs_in_node;
),
TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u",
show_dev(__entry),
(unsigned int)__entry->nid,
__entry->ofs_in_node)
);
TRACE_EVENT(f2fs_do_submit_bio,
TP_PROTO(struct super_block *sb, int btype, bool sync, struct bio *bio),
TP_ARGS(sb, btype, sync, bio),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(int, btype)
__field(bool, sync)
__field(sector_t, sector)
__field(unsigned int, size)
),
TP_fast_assign(
__entry->dev = sb->s_dev;
__entry->btype = btype;
__entry->sync = sync;
__entry->sector = bio->bi_sector;
__entry->size = bio->bi_size;
),
TP_printk("dev = (%d,%d), type = %s, io = %s, sector = %lld, size = %u",
show_dev(__entry),
show_block_type(__entry->btype),
__entry->sync ? "sync" : "no sync",
(unsigned long long)__entry->sector,
__entry->size)
);
TRACE_EVENT(f2fs_submit_write_page,
TP_PROTO(struct page *page, block_t blk_addr, int type),
TP_ARGS(page, blk_addr, type),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(int, type)
__field(pgoff_t, index)
__field(block_t, block)
),
TP_fast_assign(
__entry->dev = page->mapping->host->i_sb->s_dev;
__entry->ino = page->mapping->host->i_ino;
__entry->type = type;
__entry->index = page->index;
__entry->block = blk_addr;
),
TP_printk("dev = (%d,%d), ino = %lu, %s, index = %lu, blkaddr = 0x%llx",
show_dev_ino(__entry),
show_block_type(__entry->type),
(unsigned long)__entry->index,
(unsigned long long)__entry->block)
);
TRACE_EVENT(f2fs_write_checkpoint,
TP_PROTO(struct super_block *sb, bool is_umount, char *msg),
TP_ARGS(sb, is_umount, msg),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(bool, is_umount)
__field(char *, msg)
),
TP_fast_assign(
__entry->dev = sb->s_dev;
__entry->is_umount = is_umount;
__entry->msg = msg;
),
TP_printk("dev = (%d,%d), checkpoint for %s, state = %s",
show_dev(__entry),
__entry->is_umount ? "clean umount" : "consistency",
__entry->msg)
);
#endif /* _TRACE_F2FS_H */
/* This part must be outside protection */
#include <trace/define_trace.h>