f2fs: limit # of inmemory pages
authorJaegeuk Kim <jaegeuk@kernel.org>
Thu, 19 Oct 2017 02:05:57 +0000 (19:05 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 26 Oct 2017 08:44:21 +0000 (10:44 +0200)
If some abnormal users try lots of atomic write operations, f2fs is able to
produce pinned pages in the main memory which affects system performance.
This patch limits that as 20% over total memory size, and if f2fs reaches
to the limit, it will drop all the inmemory pages.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/node.c
fs/f2fs/node.h
fs/f2fs/segment.c
fs/f2fs/super.c

index 95fdbe3..7fd0983 100644 (file)
@@ -1944,6 +1944,12 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
 
        trace_f2fs_write_begin(inode, pos, len, flags);
 
+       if (f2fs_is_atomic_file(inode) &&
+                       !available_free_memory(sbi, INMEM_PAGES)) {
+               err = -ENOMEM;
+               goto fail;
+       }
+
        /*
         * We should check this at this moment to avoid deadlock on inode page
         * and #0 page. The locking rule for inline_data conversion should be:
@@ -2021,6 +2027,8 @@ repeat:
 fail:
        f2fs_put_page(page, 1);
        f2fs_write_failed(mapping, pos + len);
+       if (f2fs_is_atomic_file(inode))
+               drop_inmem_pages_all(sbi);
        return err;
 }
 
index 3cf7494..0b0e4fb 100644 (file)
@@ -601,6 +601,7 @@ struct f2fs_inode_info {
 #endif
        struct list_head dirty_list;    /* dirty list for dirs and files */
        struct list_head gdirty_list;   /* linked in global dirty list */
+       struct list_head inmem_ilist;   /* list for inmem inodes */
        struct list_head inmem_pages;   /* inmemory pages managed by f2fs */
        struct task_struct *inmem_task; /* store inmemory task */
        struct mutex inmem_lock;        /* lock for inmemory pages */
@@ -965,6 +966,7 @@ enum inode_type {
        DIR_INODE,                      /* for dirty dir inode */
        FILE_INODE,                     /* for dirty regular/symlink inode */
        DIRTY_META,                     /* for all dirtied inode metadata */
+       ATOMIC_FILE,                    /* for all atomic files */
        NR_INODE_TYPE,
 };
 
@@ -2544,6 +2546,7 @@ void destroy_node_manager_caches(void);
  */
 bool need_SSR(struct f2fs_sb_info *sbi);
 void register_inmem_page(struct inode *inode, struct page *page);
+void drop_inmem_pages_all(struct f2fs_sb_info *sbi);
 void drop_inmem_pages(struct inode *inode);
 void drop_inmem_page(struct inode *inode, struct page *page);
 int commit_inmem_pages(struct inode *inode);
index 20f60f9..d571961 100644 (file)
@@ -74,6 +74,10 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
                                atomic_read(&sbi->total_ext_node) *
                                sizeof(struct extent_node)) >> PAGE_SHIFT;
                res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
+       } else if (type == INMEM_PAGES) {
+               /* it allows 20% / total_ram for inmemory pages */
+               mem_size = get_pages(sbi, F2FS_INMEM_PAGES);
+               res = mem_size < (val.totalram / 5);
        } else {
                if (!sbi->sb->s_bdi->wb.dirty_exceeded)
                        return true;
index e91b08b..0ee3e5f 100644 (file)
@@ -140,6 +140,7 @@ enum mem_type {
        DIRTY_DENTS,    /* indicates dirty dentry pages */
        INO_ENTRIES,    /* indicates inode entries */
        EXTENT_CACHE,   /* indicates extent cache */
+       INMEM_PAGES,    /* indicates inmemory pages */
        BASE_CHECK,     /* check kernel status */
 };
 
index bfbcff8..049bbeb 100644 (file)
@@ -186,6 +186,7 @@ bool need_SSR(struct f2fs_sb_info *sbi)
 
 void register_inmem_page(struct inode *inode, struct page *page)
 {
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
        struct inmem_pages *new;
 
@@ -204,6 +205,10 @@ void register_inmem_page(struct inode *inode, struct page *page)
        mutex_lock(&fi->inmem_lock);
        get_page(page);
        list_add_tail(&new->list, &fi->inmem_pages);
+       spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+       if (list_empty(&fi->inmem_ilist))
+               list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
+       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
        inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
        mutex_unlock(&fi->inmem_lock);
 
@@ -262,12 +267,41 @@ next:
        return err;
 }
 
+void drop_inmem_pages_all(struct f2fs_sb_info *sbi)
+{
+       struct list_head *head = &sbi->inode_list[ATOMIC_FILE];
+       struct inode *inode;
+       struct f2fs_inode_info *fi;
+next:
+       spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+       if (list_empty(head)) {
+               spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+               return;
+       }
+       fi = list_first_entry(head, struct f2fs_inode_info, inmem_ilist);
+       inode = igrab(&fi->vfs_inode);
+       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+
+       if (inode) {
+               drop_inmem_pages(inode);
+               iput(inode);
+       }
+       congestion_wait(BLK_RW_ASYNC, HZ/50);
+       cond_resched();
+       goto next;
+}
+
 void drop_inmem_pages(struct inode *inode)
 {
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
 
        mutex_lock(&fi->inmem_lock);
        __revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
+       spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+       if (!list_empty(&fi->inmem_ilist))
+               list_del_init(&fi->inmem_ilist);
+       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
        mutex_unlock(&fi->inmem_lock);
 
        clear_inode_flag(inode, FI_ATOMIC_FILE);
@@ -399,6 +433,10 @@ int commit_inmem_pages(struct inode *inode)
                /* drop all uncommitted pages */
                __revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
        }
+       spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+       if (!list_empty(&fi->inmem_ilist))
+               list_del_init(&fi->inmem_ilist);
+       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
        mutex_unlock(&fi->inmem_lock);
 
        clear_inode_flag(inode, FI_ATOMIC_COMMIT);
index dce5c4e..21e524e 100644 (file)
@@ -625,6 +625,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
        init_rwsem(&fi->i_sem);
        INIT_LIST_HEAD(&fi->dirty_list);
        INIT_LIST_HEAD(&fi->gdirty_list);
+       INIT_LIST_HEAD(&fi->inmem_ilist);
        INIT_LIST_HEAD(&fi->inmem_pages);
        mutex_init(&fi->inmem_lock);
        init_rwsem(&fi->dio_rwsem[READ]);