Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 942d33da authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
  ...
parents 246e6a0d 59bbd474
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -146,7 +146,7 @@ USAGE

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.
               1 is set by default, which performs this.
-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.
               1 is set by default.
-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
+32 −31
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include <trace/events/f2fs.h>

static struct kmem_cache *orphan_entry_slab;
static struct kmem_cache *inode_entry_slab;
@@ -57,13 +58,19 @@ repeat:
		cond_resched();
		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);
		goto repeat;
	}
out:
	mark_page_accessed(page);

	/* We do not allow returning an errorneous page */
	return page;
}

@@ -541,54 +548,44 @@ retry:
 */
static void block_operations(struct f2fs_sb_info *sbi)
{
	int t;
	struct writeback_control wbc = {
		.sync_mode = WB_SYNC_ALL,
		.nr_to_write = LONG_MAX,
		.for_reclaim = 0,
	};
	struct blk_plug plug;

	/* Stop renaming operation */
	mutex_lock_op(sbi, RENAME);
	mutex_lock_op(sbi, DENTRY_OPS);
	blk_start_plug(&plug);

retry_dents:
	/* write all the dirty dentry pages */
	sync_dirty_dir_inodes(sbi);
retry_flush_dents:
	mutex_lock_all(sbi);

	mutex_lock_op(sbi, DATA_WRITE);
	/* write all the dirty dentry pages */
	if (get_pages(sbi, F2FS_DIRTY_DENTS)) {
		mutex_unlock_op(sbi, DATA_WRITE);
		goto retry_dents;
		mutex_unlock_all(sbi);
		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
	 * until finishing nat/sit flush.
	 */
retry:
	sync_node_pages(sbi, 0, &wbc);

	mutex_lock_op(sbi, NODE_WRITE);
retry_flush_nodes:
	mutex_lock(&sbi->node_write);

	if (get_pages(sbi, F2FS_DIRTY_NODES)) {
		mutex_unlock_op(sbi, NODE_WRITE);
		goto retry;
		mutex_unlock(&sbi->node_write);
		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)
{
	int t;
	for (t = NODE_WRITE; t >= RENAME; t--)
		mutex_unlock_op(sbi, t);
	mutex_unlock(&sbi->node_write);
	mutex_unlock_all(sbi);
}

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);
	unsigned long long ckpt_ver;

	trace_f2fs_write_checkpoint(sbi->sb, is_umount, "start block_ops");

	mutex_lock(&sbi->cp_mutex);
	block_operations(sbi);

	trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish block_ops");

	f2fs_submit_bio(sbi, DATA, true);
	f2fs_submit_bio(sbi, NODE, 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_sit_entries(sbi);

	reset_victim_segmap(sbi);

	/* unlock all the fs_lock[] in do_checkpoint() */
	do_checkpoint(sbi, is_umount);

	unblock_operations(sbi);
	mutex_unlock(&sbi->cp_mutex);

	trace_f2fs_write_checkpoint(sbi->sb, is_umount, "finish checkpoint");
}

void init_orphan_info(struct f2fs_sb_info *sbi)
+116 −86
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include <trace/events/f2fs.h>

/*
 * 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))
		return -ENOSPC;

	trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node);

	__set_data_blkaddr(dn, NEW_ADDR);
	dn->data_blkaddr = NEW_ADDR;
	sync_inode_page(dn);
@@ -134,7 +137,7 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn)
		goto end_update;
	}

	/* Frone merge */
	/* Front merge */
	if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) {
		fi->ext.fofs--;
		fi->ext.blk_addr--;
@@ -170,7 +173,7 @@ end_update:
	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 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);

	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)
		return ERR_PTR(err);
	f2fs_put_dnode(&dn);
@@ -200,15 +203,23 @@ struct page *find_data_page(struct inode *inode, pgoff_t index)
	if (!page)
		return ERR_PTR(-ENOMEM);

	err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
	if (err) {
		f2fs_put_page(page, 1);
		return ERR_PTR(err);
	}
	if (PageUptodate(page)) {
		unlock_page(page);
		return page;
	}

	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);
		}
	}
	return page;
}

/*
 * If it tries to access a hole, return an error.
 * Because, the callers, functions in dir.c and GC, should be able to know
@@ -223,14 +234,14 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
	int err;

	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)
		return ERR_PTR(err);
	f2fs_put_dnode(&dn);

	if (dn.data_blkaddr == NULL_ADDR)
		return ERR_PTR(-ENOENT);

repeat:
	page = grab_cache_page(mapping, index);
	if (!page)
		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);

	err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
	if (err) {
		f2fs_put_page(page, 1);
	if (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;
}
@@ -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.
 * 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,
						bool new_i_size)
@@ -263,7 +285,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
	int err;

	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)
		return ERR_PTR(err);

@@ -274,7 +296,7 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
		}
	}
	f2fs_put_dnode(&dn);

repeat:
	page = grab_cache_page(mapping, index);
	if (!page)
		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) {
		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
		SetPageUptodate(page);
	} else {
		err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
		if (err) {
			f2fs_put_page(page, 1);
		if (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 &&
		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.
 * Read operation is synchronous, and caller must unlock the page.
 * Return unlocked page.
 */
int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
					block_t blk_addr, int type)
{
	struct block_device *bdev = sbi->sb->s_bdev;
	bool sync = (type == READ_SYNC);
	struct bio *bio;

	/* This page can be already read by other threads */
	if (PageUptodate(page)) {
		if (!sync)
			unlock_page(page);
		return 0;
	}
	trace_f2fs_readpage(page, blk_addr, type);

	down_read(&sbi->bio_sem);

@@ -355,18 +378,12 @@ int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page,
		kfree(bio->bi_private);
		bio_put(bio);
		up_read(&sbi->bio_sem);
		f2fs_put_page(page, 1);
		return -EFAULT;
	}

	submit_bio(type, bio);
	up_read(&sbi->bio_sem);

	/* wait for read completion if sync */
	if (sync) {
		lock_page(page);
		if (PageError(page))
			return -EIO;
	}
	return 0;
}

@@ -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) */
	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;
	}

	/* When reading holes, we need its node page */
	set_new_dnode(&dn, inode, NULL, NULL, 0);
	err = get_dnode_of_data(&dn, pgofs, RDONLY_NODE);
	if (err)
	err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA);
	if (err) {
		trace_f2fs_get_data_block(inode, iblock, bh_result, err);
		return (err == -ENOENT) ? 0 : err;
	}

	/* It does not support data allocation */
	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);
	}
	f2fs_put_dnode(&dn);
	trace_f2fs_get_data_block(inode, iblock, bh_result, 0);
	return 0;
}

@@ -438,13 +460,12 @@ static int f2fs_read_data_pages(struct file *file,
int do_write_data_page(struct page *page)
{
	struct inode *inode = page->mapping->host;
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	block_t old_blk_addr, new_blk_addr;
	struct dnode_of_data dn;
	int err = 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)
		return err;

@@ -468,8 +489,6 @@ int do_write_data_page(struct page *page)
		write_data_page(inode, page, &dn,
				old_blk_addr, &new_blk_addr);
		update_extent_cache(new_blk_addr, &dn);
		F2FS_I(inode)->data_version =
			le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
	}
out_writepage:
	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)
							>> PAGE_CACHE_SHIFT;
	unsigned offset;
	bool need_balance_fs = false;
	int err = 0;

	if (page->index < end_index)
		goto out;
		goto write;

	/*
	 * 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);
			inode_dec_dirty_dents(inode);
		}
		goto unlock_out;
		goto out;
	}

	zero_user_segment(page, offset, PAGE_CACHE_SIZE);
out:
	if (sbi->por_doing)
		goto redirty_out;

	if (wbc->for_reclaim && !S_ISDIR(inode->i_mode) && !is_cold_data(page))
write:
	if (sbi->por_doing) {
		err = AOP_WRITEPAGE_ACTIVATE;
		goto redirty_out;
	}

	mutex_lock_op(sbi, DATA_WRITE);
	/* Dentry blocks are controlled by checkpoint */
	if (S_ISDIR(inode->i_mode)) {
		dec_page_count(sbi, F2FS_DIRTY_DENTS);
		inode_dec_dirty_dents(inode);
	}
		err = do_write_data_page(page);
	if (err && err != -ENOENT) {
		wbc->pages_skipped++;
		set_page_dirty(page);
	} else {
		int ilock = mutex_lock_op(sbi);
		err = do_write_data_page(page);
		mutex_unlock_op(sbi, ilock);
		need_balance_fs = true;
	}
	mutex_unlock_op(sbi, DATA_WRITE);
	if (err == -ENOENT)
		goto out;
	else if (err)
		goto redirty_out;

	if (wbc->for_reclaim)
		f2fs_submit_bio(sbi, DATA, true);

	if (err == -ENOENT)
		goto unlock_out;

	clear_cold_data(page);
out:
	unlock_page(page);

	if (!wbc->for_reclaim && !S_ISDIR(inode->i_mode))
	if (need_balance_fs)
		f2fs_balance_fs(sbi);
	return 0;

unlock_out:
	unlock_page(page);
	return (err == -ENOENT) ? 0 : err;

redirty_out:
	wbc->pages_skipped++;
	set_page_dirty(page);
	return AOP_WRITEPAGE_ACTIVATE;
	return err;
}

#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 f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	bool locked = false;
	int ret;
	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) {
		desired_nrtw = MAX_DESIRED_PAGES_WP;
		excess_nrtw = desired_nrtw - wbc->nr_to_write;
		wbc->nr_to_write = desired_nrtw;
	}

	if (!S_ISDIR(inode->i_mode))
	if (!S_ISDIR(inode->i_mode)) {
		mutex_lock(&sbi->writepages);
		locked = true;
	}
	ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
	if (!S_ISDIR(inode->i_mode))
	if (locked)
		mutex_unlock(&sbi->writepages);
	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;
	struct dnode_of_data dn;
	int err = 0;
	int ilock;

	/* for nobh_write_end */
	*fsdata = NULL;

	f2fs_balance_fs(sbi);

repeat:
	page = grab_cache_page_write_begin(mapping, index, flags);
	if (!page)
		return -ENOMEM;
	*pagep = page;

	mutex_lock_op(sbi, DATA_NEW);
	ilock = mutex_lock_op(sbi);

	set_new_dnode(&dn, inode, NULL, NULL, 0);
	err = get_dnode_of_data(&dn, index, 0);
	if (err) {
		mutex_unlock_op(sbi, DATA_NEW);
		f2fs_put_page(page, 1);
		return err;
	}
	err = get_dnode_of_data(&dn, index, ALLOC_NODE);
	if (err)
		goto err;

	if (dn.data_blkaddr == NULL_ADDR) {
	if (dn.data_blkaddr == NULL_ADDR)
		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);
	if (err)
		goto err;

	mutex_unlock_op(sbi, DATA_NEW);
	mutex_unlock_op(sbi, ilock);

	if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
		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 */
		zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
		return 0;
		goto out;
	}

	if (dn.data_blkaddr == NEW_ADDR) {
		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
	} else {
		err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC);
		if (err) {
			f2fs_put_page(page, 1);
		if (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);
	clear_cold_data(page);
	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,
@@ -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)
{
	ClearPagePrivate(page);
	return 0;
	return 1;
}

static int f2fs_set_data_page_dirty(struct page *page)
+4 −6
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@

#include <linux/fs.h>
#include <linux/backing-dev.h>
#include <linux/proc_fs.h>
#include <linux/f2fs_fs.h>
#include <linux/blkdev.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);
	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;
	if (si->dirty_count)
		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 += 2 * SIT_VBLOCK_MAP_SIZE * TOTAL_SEGS(sbi);
	if (sbi->segs_per_sec > 1)
		si->base_mem += sbi->total_sections *
			sizeof(struct sec_entry);
		si->base_mem += TOTAL_SECS(sbi) * sizeof(struct sec_entry);
	si->base_mem += __bitmap_size(sbi, SIT_BITMAP);

	/* build free segmap */
	si->base_mem += sizeof(struct free_segmap_info);
	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 */
	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 */
	si->base_mem += sizeof(struct dirty_seglist_info);
	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 */
	si->base_mem += sizeof(struct f2fs_nm_info);
+52 −58
Original line number Diff line number Diff line
@@ -148,7 +148,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,

	for (; bidx < end_block; bidx++) {
		/* 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)) {
			room = true;
			continue;
@@ -189,6 +189,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
	unsigned int max_depth;
	unsigned int level;

	if (namelen > F2FS_NAME_LEN)
		return NULL;

	if (npages == 0)
		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,
		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);
	wait_on_page_writeback(page);
	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_put_page(page, 1);
	mutex_unlock_op(sbi, DENTRY_OPS);
}

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);
}

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,
		struct inode *dir, const struct qstr *name)
{
@@ -291,7 +327,7 @@ static int init_inode_metadata(struct inode *inode,
			return err;

		if (S_ISDIR(inode->i_mode)) {
			err = f2fs_make_empty(inode, dir);
			err = make_empty_dir(inode, dir);
			if (err) {
				remove_inode_page(inode);
				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)) {
		inc_nlink(inode);
		f2fs_write_inode(inode, NULL);
		update_inode_page(inode);
	}
	return 0;
}
@@ -338,7 +374,7 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
	}

	if (need_dir_update)
		f2fs_write_inode(dir, NULL);
		update_inode_page(dir);
	else
		mark_inode_dirty(dir);

@@ -370,6 +406,10 @@ 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)
{
	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;
	struct f2fs_dir_entry *de;
	unsigned int nbucket, nblock;
	struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
	size_t namelen = name->len;
	struct page *dentry_page = NULL;
	struct f2fs_dentry_block *dentry_blk = NULL;
@@ -409,12 +448,9 @@ start:
	bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket));

	for (block = bidx; block <= (bidx + nblock - 1); block++) {
		mutex_lock_op(sbi, DENTRY_OPS);
		dentry_page = get_new_data_page(dir, block, true);
		if (IS_ERR(dentry_page)) {
			mutex_unlock_op(sbi, DENTRY_OPS);
		if (IS_ERR(dentry_page))
			return PTR_ERR(dentry_page);
		}

		dentry_blk = kmap(dentry_page);
		bit_pos = room_for_filename(dentry_blk, slots);
@@ -423,7 +459,6 @@ start:

		kunmap(dentry_page);
		f2fs_put_page(dentry_page, 1);
		mutex_unlock_op(sbi, DENTRY_OPS);
	}

	/* Move to next level to find the empty slot for new dentry */
@@ -453,7 +488,6 @@ add_dentry:
fail:
	kunmap(dentry_page);
	f2fs_put_page(dentry_page, 1);
	mutex_unlock_op(sbi, DENTRY_OPS);
	return err;
}

@@ -473,8 +507,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
	void *kaddr = page_address(page);
	int i;

	mutex_lock_op(sbi, DENTRY_OPS);

	lock_page(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)) {
		drop_nlink(dir);
		f2fs_write_inode(dir, NULL);
		update_inode_page(dir);
	} else {
		mark_inode_dirty(dir);
	}
@@ -506,7 +538,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
			drop_nlink(inode);
			i_size_write(inode, 0);
		}
		f2fs_write_inode(inode, NULL);
		update_inode_page(inode);

		if (inode->i_nlink == 0)
			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);
	}
	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)
Loading