f2fs: introduce flash-friendly file system
Subsystems (3)
PATCH v2 13/16 f2fs: add xattr and acl functionalities
In reply to Jaegeuk KimThis implements xattr and acl functionalities.
- F2FS uses a node page to contain use extended attributes.
--- fs/f2fs/acl.c | 465 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/acl.h | 57 +++++++ fs/f2fs/xattr.c | 389 ++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/xattr.h | 145 +++++++++++++++++ 4 files changed, 1056 insertions(+) create mode 100644 fs/f2fs/acl.c create mode 100644 fs/f2fs/acl.h create mode 100644 fs/f2fs/xattr.c create mode 100644 fs/f2fs/xattr.h
fs/f2fs/acl.c new file +465 -0
@@ -0,0 +1,465 @@ +/** +* fs/f2fs/acl.c +* +* Copyright (c) 2012 Samsung Electronics Co., Ltd. +* http://www.samsung.com/ +* +* Portions of this code from linux/fs/ext2/acl.c +* +* Copyright (C) 2001-2003 Andreas Gruenbacher, <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ +#include <linux/f2fs_fs.h> +#include "f2fs.h" +#include "xattr.h" +#include "acl.h" + +#define get_inode_mode (i) ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ + (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) + +static inline size_t f2fs_acl_size (int count) +{ +if (count <=4 ) { +return sizeof (struct f2fs_acl_header ) + + count *sizeof (struct f2fs_acl_entry_short ); + }else { +return sizeof (struct f2fs_acl_header ) + +4 *sizeof (struct f2fs_acl_entry_short ) + + (count -4 ) *sizeof (struct f2fs_acl_entry ); + } +} + +static inline int f2fs_acl_count (size_t size) +{ +ssize_t s; + size -=sizeof (struct f2fs_acl_header ); + s = size -4 *sizeof (struct f2fs_acl_entry_short ); +if (s <0 ) { +if (size %sizeof (struct f2fs_acl_entry_short )) +return -1 ; +return size /sizeof (struct f2fs_acl_entry_short ); + }else { +if (s %sizeof (struct f2fs_acl_entry )) +return -1 ; +return s /sizeof (struct f2fs_acl_entry ) +4 ; + } +} + +static struct posix_acl *f2fs_acl_from_disk (const char *value,size_t size) +{ +int i, count; +struct posix_acl *acl; +struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; +struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr +1 ); +const char *end = value + size; + +if (hdr->a_version !=cpu_to_le32 (F2FS_ACL_VERSION )) +return ERR_PTR (-EINVAL ); + + count =f2fs_acl_count (size); +if (count <0 ) +return ERR_PTR (-EINVAL ); +if (count ==0 ) +return NULL ; + + acl =posix_acl_alloc (count,GFP_KERNEL ); +if (!acl) +return ERR_PTR (-ENOMEM ); + +for (i =0 ; i < count; i++) { + +if ((char *)entry > end) + goto fail; + + acl->a_entries[i].e_tag =le16_to_cpu (entry->e_tag); + acl->a_entries[i].e_perm =le16_to_cpu (entry->e_perm); + +switch (acl->a_entries[i].e_tag) { +case ACL_USER_OBJ : +case ACL_GROUP_OBJ : +case ACL_MASK : +case ACL_OTHER : + acl->a_entries[i].e_id =ACL_UNDEFINED_ID ; + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry_short )); +break ; + +case ACL_USER : + acl->a_entries[i].e_uid = +make_kuid (&init_user_ns, +le32_to_cpu (entry->e_id)); + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry )); +break ; +case ACL_GROUP : + acl->a_entries[i].e_gid = +make_kgid (&init_user_ns, +le32_to_cpu (entry->e_id)); + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry )); +break ; +default : + goto fail; + } + } +if ((char *)entry != end) + goto fail; +return acl; +fail: +posix_acl_release (acl); +return ERR_PTR (-EINVAL ); +} + +static void *f2fs_acl_to_disk (const struct posix_acl *acl,size_t *size) +{ +struct f2fs_acl_header *f2fs_acl; +struct f2fs_acl_entry *entry; +int i; + + f2fs_acl =kmalloc (sizeof (struct f2fs_acl_header ) + acl->a_count * +sizeof (struct f2fs_acl_entry ),GFP_KERNEL ); +if (!f2fs_acl) +return ERR_PTR (-ENOMEM ); + + f2fs_acl->a_version =cpu_to_le32 (F2FS_ACL_VERSION ); + entry = (struct f2fs_acl_entry *)(f2fs_acl +1 ); + +for (i =0 ; i < acl->a_count; i++) { + + entry->e_tag =cpu_to_le16 (acl->a_entries[i].e_tag); + entry->e_perm =cpu_to_le16 (acl->a_entries[i].e_perm); + +switch (acl->a_entries[i].e_tag) { +case ACL_USER : + entry->e_id =cpu_to_le32 ( +from_kuid (&init_user_ns, + acl->a_entries[i].e_uid)); + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry )); +break ; +case ACL_GROUP : + entry->e_id =cpu_to_le32 ( +from_kgid (&init_user_ns, + acl->a_entries[i].e_gid)); + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry )); +break ; +case ACL_USER_OBJ : +case ACL_GROUP_OBJ : +case ACL_MASK : +case ACL_OTHER : + entry = (struct f2fs_acl_entry *)((char *)entry + +sizeof (struct f2fs_acl_entry_short )); +break ; +default : + goto fail; + } + } + *size =f2fs_acl_size (acl->a_count); +return (void *)f2fs_acl; + +fail: +kfree (f2fs_acl); +return ERR_PTR (-EINVAL ); +} + +struct posix_acl *f2fs_get_acl (struct inode *inode,int type) +{ +struct f2fs_sb_info *sbi =F2FS_SB (inode->i_sb); +int name_index =F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT ; +void *value =NULL ; +struct posix_acl *acl; +int retval; + +if (!test_opt (sbi,POSIX_ACL )) +return NULL ; + + acl =get_cached_acl (inode, type); +if (acl !=ACL_NOT_CACHED ) +return acl; + +if (type ==ACL_TYPE_ACCESS ) + name_index =F2FS_XATTR_INDEX_POSIX_ACL_ACCESS ; + + retval =f2fs_getxattr (inode, name_index,"" ,NULL ,0 ); +if (retval >0 ) { + value =kmalloc (retval,GFP_KERNEL ); +if (!value) +return ERR_PTR (-ENOMEM ); + retval =f2fs_getxattr (inode, name_index,"" , value, retval); + } + +if (retval <0 ) { +if (retval == -ENODATA ) + acl =NULL ; +else + acl =ERR_PTR (retval); + }else { + acl =f2fs_acl_from_disk (value, retval); + } +kfree (value); +if (!IS_ERR (acl)) +set_cached_acl (inode, type, acl); + +return acl; +} + +static int f2fs_set_acl (struct inode *inode,int type,struct posix_acl *acl) +{ +struct f2fs_sb_info *sbi =F2FS_SB (inode->i_sb); +struct f2fs_inode_info *fi =F2FS_I (inode); +int name_index; +void *value =NULL ; +size_t size =0 ; +int error; + +if (!test_opt (sbi,POSIX_ACL )) +return 0 ; +if (S_ISLNK (inode->i_mode)) +return -EOPNOTSUPP ; + +switch (type) { +case ACL_TYPE_ACCESS : + name_index =F2FS_XATTR_INDEX_POSIX_ACL_ACCESS ; +if (acl) { + error =posix_acl_equiv_mode (acl, &inode->i_mode); +if (error <0 ) +return error; +set_acl_inode (fi, inode->i_mode); +if (error ==0 ) + acl =NULL ; + } +break ; + +case ACL_TYPE_DEFAULT : + name_index =F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT ; +if (!S_ISDIR (inode->i_mode)) +return acl ? -EACCES :0 ; +break ; + +default : +return -EINVAL ; + } + +if (acl) { + value =f2fs_acl_to_disk (acl, &size); +if (IS_ERR (value)) { +cond_clear_inode_flag (fi,FI_ACL_MODE ); +return (int )PTR_ERR (value); + } + } + + error =f2fs_setxattr (inode, name_index,"" , value, size); + +kfree (value); +if (!error) +set_cached_acl (inode, type, acl); + +cond_clear_inode_flag (fi,FI_ACL_MODE ); +return error; +} + +int f2fs_init_acl (struct inode *inode,struct inode *dir) +{ +struct posix_acl *acl =NULL ; +struct f2fs_sb_info *sbi =F2FS_SB (dir->i_sb); +int error =0 ; + +if (!S_ISLNK (inode->i_mode)) { +if (test_opt (sbi,POSIX_ACL )) { + acl =f2fs_get_acl (dir,ACL_TYPE_DEFAULT ); +if (IS_ERR (acl)) +return PTR_ERR (acl); + } +if (!acl) + inode->i_mode &= ~current_umask (); + } + +if (test_opt (sbi,POSIX_ACL ) && acl) { + +if (S_ISDIR (inode->i_mode)) { + error =f2fs_set_acl (inode,ACL_TYPE_DEFAULT , acl); +if (error) + goto cleanup; + } + error =posix_acl_create (&acl,GFP_KERNEL , &inode->i_mode); +if (error <0 ) +return error; +if (error >0 ) + error =f2fs_set_acl (inode,ACL_TYPE_ACCESS , acl); + } +cleanup: +posix_acl_release (acl); +return error; +} + +int f2fs_acl_chmod (struct inode *inode) +{ +struct f2fs_sb_info *sbi =F2FS_SB (inode->i_sb); +struct posix_acl *acl; +int error; +mode_t mode =get_inode_mode (inode); + +if (!test_opt (sbi,POSIX_ACL )) +return 0 ; +if (S_ISLNK (mode)) +return -EOPNOTSUPP ; + + acl =f2fs_get_acl (inode,ACL_TYPE_ACCESS ); +if (IS_ERR (acl) || !acl) +return PTR_ERR (acl); + + error =posix_acl_chmod (&acl,GFP_KERNEL , mode); +if (error) +return error; + error =f2fs_set_acl (inode,ACL_TYPE_ACCESS , acl); +posix_acl_release (acl); +return error; +} + +static size_t f2fs_xattr_list_acl (struct dentry *dentry,char *list, +size_t list_size,const char *name,size_t name_len,int type) +{ +struct f2fs_sb_info *sbi =F2FS_SB (dentry->d_sb); +const char *xname =POSIX_ACL_XATTR_DEFAULT ; +size_t size; + +if (!test_opt (sbi,POSIX_ACL )) +return 0 ; + +if (type ==ACL_TYPE_ACCESS ) + xname =POSIX_ACL_XATTR_ACCESS ; + + size =strlen (xname) +1 ; +if (list && size <= list_size) +memcpy (list, xname, size); +return size; +} + +static int f2fs_xattr_get_acl (struct dentry *dentry,const char *name, +void *buffer,size_t size,int type) +{ +struct f2fs_sb_info *sbi =F2FS_SB (dentry->d_sb); +struct posix_acl *acl; +int error; + +if (strcmp (name,"" ) !=0 ) +return -EINVAL ; +if (!test_opt (sbi,POSIX_ACL )) +return -EOPNOTSUPP ; + + acl =f2fs_get_acl (dentry->d_inode, type); +if (IS_ERR (acl)) +return PTR_ERR (acl); +if (!acl) +return -ENODATA ; + error =posix_acl_to_xattr (&init_user_ns, acl, buffer, size); +posix_acl_release (acl); + +return error; +} + +static int f2fs_xattr_set_acl (struct dentry *dentry,const char *name, +const void *value,size_t size,int flags,int type) +{ +struct f2fs_sb_info *sbi =F2FS_SB (dentry->d_sb); +struct inode *inode = dentry->d_inode; +struct posix_acl *acl =NULL ; +int error; + +if (strcmp (name,"" ) !=0 ) +return -EINVAL ; +if (!test_opt (sbi,POSIX_ACL )) +return -EOPNOTSUPP ; +if (!inode_owner_or_capable (inode)) +return -EPERM ; + +if (value) { + acl =posix_acl_from_xattr (&init_user_ns, value, size); +if (IS_ERR (acl)) +return PTR_ERR (acl); +if (acl) { + error =posix_acl_valid (acl); +if (error) + goto release_and_out; + } + }else { + acl =NULL ; + } + + error =f2fs_set_acl (inode, type, acl); + +release_and_out: +posix_acl_release (acl); +return error; +} + +const struct xattr_handler f2fs_xattr_acl_default_handler = { + .prefix =POSIX_ACL_XATTR_DEFAULT , + .flags =ACL_TYPE_DEFAULT , + .list = f2fs_xattr_list_acl, + .get = f2fs_xattr_get_acl, + .set = f2fs_xattr_set_acl, +}; + +const struct xattr_handler f2fs_xattr_acl_access_handler = { + .prefix =POSIX_ACL_XATTR_ACCESS , + .flags =ACL_TYPE_ACCESS , + .list = f2fs_xattr_list_acl, + .get = f2fs_xattr_get_acl, + .set = f2fs_xattr_set_acl, +}; + +static size_t f2fs_xattr_advise_list (struct dentry *dentry,char *list, +size_t list_size,const char *name,size_t name_len,int type) +{ +const char *xname =F2FS_SYSTEM_ADVISE_PREFIX ; +size_t size; + +if (type !=F2FS_XATTR_INDEX_ADVISE ) +return 0 ; + + size =strlen (xname) +1 ; +if (list && size <= list_size) +memcpy (list, xname, size); +return size; +} + +static int f2fs_xattr_advise_get (struct dentry *dentry,const char *name, +void *buffer,size_t size,int type) +{ +struct inode *inode = dentry->d_inode; + +if (strcmp (name,"" ) !=0 ) +return -EINVAL ; + + *((char *)buffer) =F2FS_I (inode)->i_advise; +return sizeof (char ); +} + +static int f2fs_xattr_advise_set (struct dentry *dentry,const char *name, +const void *value,size_t size,int flags,int type) +{ +struct inode *inode = dentry->d_inode; + +if (strcmp (name,"" ) !=0 ) +return -EINVAL ; +if (!inode_owner_or_capable (inode)) +return -EPERM ; +if (value ==NULL ) +return -EINVAL ; + +F2FS_I (inode)->i_advise |= *(char *)value; +return 0 ; +} + +const struct xattr_handler f2fs_xattr_advise_handler = { + .prefix =F2FS_SYSTEM_ADVISE_PREFIX , + .flags =F2FS_XATTR_INDEX_ADVISE , + .list = f2fs_xattr_advise_list, + .get = f2fs_xattr_advise_get, + .set = f2fs_xattr_advise_set, +};
fs/f2fs/acl.h new file +57 -0
57 lines not on this page · raw message
fs/f2fs/xattr.c new file +389 -0
389 lines not on this page · raw message
fs/f2fs/xattr.h new file +145 -0
145 lines not on this page · raw message
Signature · 8 lines
1.7.9.5 --- Jaegeuk Kim Samsung
PATCH v2 14/16 f2fs: add garbage collection functions
In reply to Jaegeuk KimThis adds on-demand and background cleaning functions.
- The basic background cleaning policy is trying to do cleaning jobs as much as possible whenever the system is idle. Once the background cleaning is done, the cleaner sleeps an amount of time not to interfere with VFS calls. The time is dynamically adjusted according to the status of whole segments, which is decreased when the following conditions are satisfied.
. GC is not conducted currently, and
. IO subsystem is idle by checking the number of requets in bdev's request
list, and
. There are enough dirty segments.
Otherwise, the time is increased incrementally until to the maximum time. Note that, min and max times are 10 secs and 30 secs by default.
- F2FS adopts a default victim selection policy where background cleaning uses a cost-benefit algorithm, while on-demand cleaning uses a greedy algorithm.
- The method of moving data during the cleaning is slightly different between background and on-demand cleaning schemes. In the case of background cleaning, F2FS loads the data, and marks them as dirty. Then, F2FS expects that the data will be moved by flusher or VM. In the case of on-demand cleaning, F2FS should move the data right away.
- In order to identify valid blocks in a victim segment, F2FS scans the bitmap of the segment managed as an SIT entry.
--- fs/f2fs/gc.c | 1139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/gc.h | 203 +++++++++++ 2 files changed, 1342 insertions(+) create mode 100644 fs/f2fs/gc.c create mode 100644 fs/f2fs/gc.h
fs/f2fs/gc.c new file +1139 -0
@@ -0,0 +1,1139 @@ +/** +* fs/f2fs/gc.c +* +* Copyright (c) 2012 Samsung Electronics Co., Ltd. +* http://www.samsung.com/ +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/backing-dev.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/f2fs_fs.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/blkdev.h> + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "gc.h" + +static LIST_HEAD (f2fs_stat_list); +static struct kmem_cache *winode_slab; + +static int gc_thread_func (void *data) +{ +struct f2fs_sb_info *sbi = data; +wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; +long wait_ms; + + wait_ms =GC_THREAD_MIN_SLEEP_TIME ; + +do { +if (try_to_freeze ()) +continue ; +else +wait_event_interruptible_timeout (*wq, +kthread_should_stop (), +msecs_to_jiffies (wait_ms)); +if (kthread_should_stop ()) +break ; + +f2fs_balance_fs (sbi); + +if (!test_opt (sbi,BG_GC )) +continue ; + +/* +* [GC triggering condition] +* 0. GC is not conducted currently. +* 1. There are enough dirty segments. +* 2. IO subsystem is idle by checking the # of writeback pages. +* 3. IO subsystem is idle by checking the # of requests in +* bdev's request list. +* +* Note) We have to avoid triggering GCs too much frequently. +* Because it is possible that some segments can be +* invalidated soon after by user update or deletion. +* So, I'd like to wait some time to collect dirty segments. +*/ +if (!mutex_trylock (&sbi->gc_mutex)) +continue ; + +if (!is_idle (sbi)) { + wait_ms =increase_sleep_time (wait_ms); +mutex_unlock (&sbi->gc_mutex); +continue ; + } + +if (has_enough_invalid_blocks (sbi)) + wait_ms =decrease_sleep_time (wait_ms); +else + wait_ms =increase_sleep_time (wait_ms); + + sbi->bg_gc++; + +if (f2fs_gc (sbi,1 ) ==GC_NONE ) + 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 ()); +return 0 ; +} + +int start_gc_thread (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_kthread *gc_th =NULL ; + + gc_th =kmalloc (sizeof (struct f2fs_gc_kthread ),GFP_KERNEL ); +if (!gc_th) +return -ENOMEM ; + + sbi->gc_thread = gc_th; +init_waitqueue_head (&sbi->gc_thread->gc_wait_queue_head); + sbi->gc_thread->f2fs_gc_task =kthread_run (gc_thread_func, sbi, +GC_THREAD_NAME ); +if (IS_ERR (gc_th->f2fs_gc_task)) { +kfree (gc_th); +return -ENOMEM ; + } +return 0 ; +} + +void stop_gc_thread (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_kthread *gc_th = sbi->gc_thread; +if (!gc_th) +return ; +kthread_stop (gc_th->f2fs_gc_task); +kfree (gc_th); + sbi->gc_thread =NULL ; +} + +static int select_gc_type (int gc_type) +{ +return (gc_type ==BG_GC ) ?GC_CB :GC_GREEDY ; +} + +static void select_policy (struct f2fs_sb_info *sbi,int gc_type, +int type,struct victim_sel_policy *p) +{ +struct dirty_seglist_info *dirty_i =DIRTY_I (sbi); + +if (IS_SSR_TYPE (type)) { + p->alloc_mode =SSR ; + p->gc_mode =GC_GREEDY ; + p->type =GET_SSR_TYPE (type); + p->dirty_segmap = dirty_i->dirty_segmap[p->type]; + p->ofs_unit =1 ; + }else { + p->alloc_mode =LFS ; + p->gc_mode =select_gc_type (gc_type); + p->type =0 ; + p->dirty_segmap = dirty_i->dirty_segmap[DIRTY ]; + p->ofs_unit = sbi->segs_per_sec; + } + p->offset = sbi->last_victim[p->gc_mode]; +} + +static unsigned int get_max_cost (struct f2fs_sb_info *sbi, +struct victim_sel_policy *p) +{ +if (p->gc_mode ==GC_GREEDY ) +return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; +else if (p->gc_mode ==GC_CB ) +return UINT_MAX ; +else /* No other gc_mode */ +return 0 ; +} + +static unsigned int check_bg_victims (struct f2fs_sb_info *sbi) +{ +struct dirty_seglist_info *dirty_i =DIRTY_I (sbi); +unsigned int segno; + +/* +* If the gc_type is FG_GC, we can select victim segments +* selected by background GC before. +* Those segments guarantee they have small valid blocks. +*/ + segno =find_next_bit (dirty_i->victim_segmap[BG_GC ], +TOTAL_SEGS (sbi),0 ); +if (segno <TOTAL_SEGS (sbi)) { +clear_bit (segno, dirty_i->victim_segmap[BG_GC ]); +return segno; + } +return NULL_SEGNO ; +} + +static unsigned int get_cb_cost (struct f2fs_sb_info *sbi,unsigned int segno) +{ +struct sit_info *sit_i =SIT_I (sbi); +unsigned int secno =GET_SECNO (sbi, segno); +unsigned int start = secno * sbi->segs_per_sec; +unsigned long long mtime =0 ; +unsigned int vblocks; +unsigned char age =0 ; +unsigned char u; +unsigned int i; + +for (i =0 ; i < sbi->segs_per_sec; i++) + mtime +=get_seg_entry (sbi, start + i)->mtime; + vblocks =get_valid_blocks (sbi, segno, sbi->segs_per_sec); + + mtime =div_u64 (mtime, sbi->segs_per_sec); + vblocks =div_u64 (vblocks, sbi->segs_per_sec); + + u = (vblocks *100 ) >> sbi->log_blocks_per_seg; + +/* Handle if the system time is changed by user */ +if (mtime < sit_i->min_mtime) + sit_i->min_mtime = mtime; +if (mtime > sit_i->max_mtime) + sit_i->max_mtime = mtime; +if (sit_i->max_mtime != sit_i->min_mtime) + age =100 -div64_u64 (100 * (mtime - sit_i->min_mtime), + sit_i->max_mtime - sit_i->min_mtime); + +return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); +} + +static unsigned int get_gc_cost (struct f2fs_sb_info *sbi,unsigned int segno, +struct victim_sel_policy *p) +{ +if (p->alloc_mode ==SSR ) +return get_seg_entry (sbi, segno)->ckpt_valid_blocks; + +/* alloc_mode == LFS */ +if (p->gc_mode ==GC_GREEDY ) +return get_valid_blocks (sbi, segno, sbi->segs_per_sec); +else +return get_cb_cost (sbi, segno); +} + +/** +* This function is called from two pathes. +* One is garbage collection and the other is SSR segment selection. +* When it is called during GC, it just gets a victim segment +* and it does not remove it from dirty seglist. +* When it is called from SSR segment selection, it finds a segment +* which has minimum valid blocks and removes it from dirty seglist. +*/ +static int get_victim_by_default (struct f2fs_sb_info *sbi, +unsigned int *result,int gc_type,int type) +{ +struct dirty_seglist_info *dirty_i =DIRTY_I (sbi); +struct victim_sel_policy p; +unsigned int segno; +int nsearched =0 ; + +select_policy (sbi, gc_type, type, &p); + + p.min_segno =NULL_SEGNO ; + p.min_cost =get_max_cost (sbi, &p); + +mutex_lock (&dirty_i->seglist_lock); + +if (p.alloc_mode ==LFS && gc_type ==FG_GC ) { + p.min_segno =check_bg_victims (sbi); +if (p.min_segno !=NULL_SEGNO ) + goto got_it; + } + +while (1 ) { +unsigned long cost; + + segno =find_next_bit (p.dirty_segmap, +TOTAL_SEGS (sbi), p.offset); +if (segno >=TOTAL_SEGS (sbi)) { +if (sbi->last_victim[p.gc_mode]) { + sbi->last_victim[p.gc_mode] =0 ; + p.offset =0 ; +continue ; + } +break ; + } + p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit; + +if (test_bit (segno, dirty_i->victim_segmap[FG_GC ])) +continue ; +if (gc_type ==BG_GC && +test_bit (segno, dirty_i->victim_segmap[BG_GC ])) +continue ; +if (IS_CURSEC (sbi,GET_SECNO (sbi, segno))) +continue ; + + cost =get_gc_cost (sbi, segno, &p); + +if (p.min_cost > cost) { + p.min_segno = segno; + p.min_cost = cost; + } + +if (cost ==get_max_cost (sbi, &p)) +continue ; + +if (nsearched++ >=MAX_VICTIM_SEARCH ) { + sbi->last_victim[p.gc_mode] = segno; +break ; + } + } +got_it: +if (p.min_segno !=NULL_SEGNO ) { + *result = (p.min_segno / p.ofs_unit) * p.ofs_unit; +if (p.alloc_mode ==LFS ) { +int i; +for (i =0 ; i < p.ofs_unit; i++) +set_bit (*result + i, + dirty_i->victim_segmap[gc_type]); + } + } +mutex_unlock (&dirty_i->seglist_lock); + +return (p.min_segno ==NULL_SEGNO ) ?0 :1 ; +} + +static const struct victim_selection default_v_ops = { + .get_victim = get_victim_by_default, +}; + +static struct inode *find_gc_inode (nid_t ino,struct list_head *ilist) +{ +struct list_head *this; +struct inode_entry *ie; + +list_for_each (this, ilist) { + ie = list_entry(this, struct inode_entry, list); +if (ie->inode->i_ino == ino) +return ie->inode; + } +return NULL ; +} + +static void add_gc_inode (struct inode *inode,struct list_head *ilist) +{ +struct list_head *this; +struct inode_entry *new_ie, *ie; + +list_for_each (this, ilist) { + ie = list_entry(this, struct inode_entry, list); +if (ie->inode == inode) { +iput (inode); +return ; + } + } +repeat: + new_ie =kmem_cache_alloc (winode_slab,GFP_NOFS ); +if (!new_ie) { +cond_resched (); + goto repeat; + } + new_ie->inode = inode; +list_add_tail (&new_ie->list, ilist); +} + +static void put_gc_inode (struct list_head *ilist) +{ +struct inode_entry *ie, *next_ie; +list_for_each_entry_safe (ie, next_ie, ilist, list) { +iput (ie->inode); +list_del (&ie->list); +kmem_cache_free (winode_slab, ie); + } +} + +static int check_valid_map (struct f2fs_sb_info *sbi, +unsigned int segno,int offset) +{ +struct sit_info *sit_i =SIT_I (sbi); +struct seg_entry *sentry; +int ret; + +mutex_lock (&sit_i->sentry_lock); + sentry =get_seg_entry (sbi, segno); + ret =f2fs_test_bit (offset, sentry->cur_valid_map); +mutex_unlock (&sit_i->sentry_lock); +return ret ?GC_OK :GC_NEXT ; +} + +/** +* This function compares node address got in summary with that in NAT. +* On validity, copy that node with cold status, otherwise (invalid node) +* ignore that. +*/ +static int gc_node_segment (struct f2fs_sb_info *sbi, +struct f2fs_summary *sum,unsigned int segno,int gc_type) +{ +bool initial = true; +struct f2fs_summary *entry; +int off; + +next_step: + entry = sum; +for (off =0 ; off < sbi->blocks_per_seg; off++, entry++) { +nid_t nid =le32_to_cpu (entry->nid); +struct page *node_page; +int err; + +/* +* It makes sure that free segments are able to write +* all the dirty node pages before CP after this CP. +* So let's check the space of dirty node pages. +*/ +if (should_do_checkpoint (sbi)) { +mutex_lock (&sbi->cp_mutex); +block_operations (sbi); +return GC_BLOCKED ; + } + + err =check_valid_map (sbi, segno, off); +if (err ==GC_ERROR ) +return err; +else if (err ==GC_NEXT ) +continue ; + +if (initial) { +ra_node_page (sbi, nid); +continue ; + } + node_page =get_node_page (sbi, nid); +if (IS_ERR (node_page)) +continue ; + +/* set page dirty and write it */ +if (!PageWriteback (node_page)) +set_page_dirty (node_page); +f2fs_put_page (node_page,1 ); +gc_stat_inc_node_blk_count (sbi,1 ); + } +if (initial) { + initial = false; + goto next_step; + } + +if (gc_type ==FG_GC ) { +struct writeback_control wbc = { + .sync_mode =WB_SYNC_ALL , + .nr_to_write =LONG_MAX , + .for_reclaim =0 , + }; +sync_node_pages (sbi,0 , &wbc); + } +return GC_DONE ; +} + +/** +* Calculate start block index that this node page contains +*/ +block_t start_bidx_of_node (unsigned int node_ofs) +{ +block_t start_bidx; +unsigned int bidx, indirect_blks; +int dec; + + indirect_blks =2 *NIDS_PER_BLOCK +4 ; + + start_bidx =1 ; +if (node_ofs ==0 ) { + start_bidx =0 ; + }else if (node_ofs <=2 ) { + bidx = node_ofs -1 ; + }else if (node_ofs <= indirect_blks) { + dec = (node_ofs -4 ) / (NIDS_PER_BLOCK +1 ); + bidx = node_ofs -2 - dec; + }else { + dec = (node_ofs - indirect_blks -3 ) / (NIDS_PER_BLOCK +1 ); + bidx = node_ofs -5 - dec; + } + +if (start_bidx) + start_bidx = bidx *ADDRS_PER_BLOCK +ADDRS_PER_INODE ; +return start_bidx; +} + +static int check_dnode (struct f2fs_sb_info *sbi,struct f2fs_summary *sum, +struct node_info *dni,block_t blkaddr,unsigned int *nofs) +{ +struct page *node_page; +nid_t nid; +unsigned int ofs_in_node; +block_t source_blkaddr; + + nid =le32_to_cpu (sum->nid); + ofs_in_node =le16_to_cpu (sum->ofs_in_node); + + node_page =get_node_page (sbi, nid); +if (IS_ERR (node_page)) +return GC_NEXT ; + +get_node_info (sbi, nid, dni); + +if (sum->version != dni->version) { +f2fs_put_page (node_page,1 ); +return GC_NEXT ; + } + + *nofs =ofs_of_node (node_page); + source_blkaddr =datablock_addr (node_page, ofs_in_node); +f2fs_put_page (node_page,1 ); + +if (source_blkaddr != blkaddr) +return GC_NEXT ; +return GC_OK ; +} + +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 ) { +set_page_dirty (page); +set_cold_data (page); + }else { +struct f2fs_sb_info *sbi =F2FS_SB (inode->i_sb); +mutex_lock_op (sbi,DATA_WRITE ); +if (clear_page_dirty_for_io (page) && +S_ISDIR (inode->i_mode)) { +dec_page_count (sbi,F2FS_DIRTY_DENTS ); +inode_dec_dirty_dents (inode); + } +set_cold_data (page); +do_write_data_page (page); +mutex_unlock_op (sbi,DATA_WRITE ); +clear_cold_data (page); + } +out: +f2fs_put_page (page,1 ); +} + +/** +* This function tries to get parent node of victim data block, and identifies +* data block validity. If the block is valid, copy that with cold status and +* modify parent node. +* If the parent node is not valid or the data block address is different, +* the victim data block is ignored. +*/ +static int gc_data_segment (struct f2fs_sb_info *sbi,struct f2fs_summary *sum, +struct list_head *ilist,unsigned int segno,int gc_type) +{ +struct super_block *sb = sbi->sb; +struct f2fs_summary *entry; +block_t start_addr; +int err, off; +int phase =0 ; + + start_addr =START_BLOCK (sbi, segno); + +next_step: + entry = sum; +for (off =0 ; off < sbi->blocks_per_seg; off++, entry++) { +struct page *data_page; +struct inode *inode; +struct node_info dni;/* dnode info for the data */ +unsigned int ofs_in_node, nofs; +block_t start_bidx; + +/* +* It makes sure that free segments are able to write +* all the dirty node pages before CP after this CP. +* So let's check the space of dirty node pages. +*/ +if (should_do_checkpoint (sbi)) { +mutex_lock (&sbi->cp_mutex); +block_operations (sbi); + err =GC_BLOCKED ; + goto stop; + } + + err =check_valid_map (sbi, segno, off); +if (err ==GC_ERROR ) + goto stop; +else if (err ==GC_NEXT ) +continue ; + +if (phase ==0 ) { +ra_node_page (sbi,le32_to_cpu (entry->nid)); +continue ; + } + +/* Get an inode by ino with checking validity */ + err =check_dnode (sbi, entry, &dni, start_addr + off, &nofs); +if (err ==GC_ERROR ) + goto stop; +else if (err ==GC_NEXT ) +continue ; + +if (phase ==1 ) { +ra_node_page (sbi, dni.ino); +continue ; + } + + start_bidx =start_bidx_of_node (nofs); + ofs_in_node =le16_to_cpu (entry->ofs_in_node); + +if (phase ==2 ) { + inode =f2fs_iget_nowait (sb, dni.ino); +if (IS_ERR (inode)) +continue ; + + data_page =find_data_page (inode, + start_bidx + ofs_in_node); +if (IS_ERR (data_page)) + goto next_iput; + +f2fs_put_page (data_page,0 ); +add_gc_inode (inode, ilist); + }else { + inode =find_gc_inode (dni.ino, ilist); +if (inode) { + data_page =get_lock_data_page (inode, + start_bidx + ofs_in_node); +if (IS_ERR (data_page)) +continue ; +move_data_page (inode, data_page, gc_type); +gc_stat_inc_data_blk_count (sbi,1 ); + } + } +continue ; +next_iput: +iput (inode); + } +if (++phase <4 ) + goto next_step; + err =GC_DONE ; +stop: +if (gc_type ==FG_GC ) +f2fs_submit_bio (sbi,DATA , true); +return err; +} + +static int __get_victim (struct f2fs_sb_info *sbi,unsigned int *result, +int gc_type,int type) +{ +struct sit_info *sit_i =SIT_I (sbi); +int ret; +mutex_lock (&sit_i->sentry_lock); + ret =DIRTY_I (sbi)->v_ops->get_victim (sbi, result, gc_type, type); +mutex_unlock (&sit_i->sentry_lock); +return ret; +} + +static int do_garbage_collect (struct f2fs_sb_info *sbi,unsigned int segno, +struct list_head *ilist,int gc_type) +{ +struct page *sum_page; +struct f2fs_summary_block *sum; +int ret =GC_DONE ; + +/* read segment summary of victim */ + sum_page =get_sum_page (sbi, segno); +if (IS_ERR (sum_page)) +return GC_ERROR ; + +/* +* 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); + +switch (GET_SUM_TYPE ((&sum->footer))) { +case SUM_TYPE_NODE : + ret =gc_node_segment (sbi, sum->entries, segno, gc_type); +break ; +case SUM_TYPE_DATA : + ret =gc_data_segment (sbi, sum->entries, ilist, segno, gc_type); +break ; + } +gc_stat_inc_seg_count (sbi,GET_SUM_TYPE ((&sum->footer))); +gc_stat_inc_call_count (sbi->gc_info); + +f2fs_put_page (sum_page,0 ); +return ret; +} + +int f2fs_gc (struct f2fs_sb_info *sbi,int nGC) +{ +unsigned int segno; +int old_free_secs, cur_free_secs; +int gc_status, nfree; +struct list_head ilist; +int gc_type =BG_GC ; + +INIT_LIST_HEAD (&ilist); +gc_more: + nfree =0 ; + gc_status =GC_NONE ; + +if (has_not_enough_free_secs (sbi)) + old_free_secs =reserved_sections (sbi); +else + old_free_secs =free_sections (sbi); + +while (sbi->sb->s_flags &MS_ACTIVE ) { +int i; +if (has_not_enough_free_secs (sbi)) + gc_type =FG_GC ; + + cur_free_secs =free_sections (sbi) + nfree; + +/* We got free space successfully. */ +if (nGC < cur_free_secs - old_free_secs) +break ; + +if (!__get_victim (sbi, &segno, gc_type,NO_CHECK_TYPE )) +break ; + +for (i =0 ; i < sbi->segs_per_sec; i++) { +/* +* do_garbage_collect will give us three gc_status: +* GC_ERROR, GC_DONE, and GC_BLOCKED. +* If GC is finished uncleanly, we have to return +* the victim to dirty segment list. +*/ + gc_status =do_garbage_collect (sbi, segno + i, + &ilist, gc_type); +if (gc_status !=GC_DONE ) + goto stop; + nfree++; + } + } +stop: +if (has_not_enough_free_secs (sbi) || gc_status ==GC_BLOCKED ) { +write_checkpoint (sbi, (gc_status ==GC_BLOCKED ), false); +if (nfree) + goto gc_more; + } + sbi->last_gc_status = gc_status; +mutex_unlock (&sbi->gc_mutex); + +put_gc_inode (&ilist); +BUG_ON (!list_empty (&ilist)); +return gc_status; +} + +#ifdef CONFIG_F2FS_STAT_FS +void f2fs_update_stat (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_info *gc_i = sbi->gc_info; +struct f2fs_stat_info *si = gc_i->stat_info; +int i; + +/* valid check of the segment numbers */ + si->hit_ext = sbi->read_hit_ext; + si->total_ext = sbi->total_hit_ext; + si->ndirty_node =get_pages (sbi,F2FS_DIRTY_NODES ); + si->ndirty_dent =get_pages (sbi,F2FS_DIRTY_DENTS ); + si->ndirty_dirs = sbi->n_dirty_dirs; + si->ndirty_meta =get_pages (sbi,F2FS_DIRTY_META ); + si->total_count = (int )sbi->user_block_count / sbi->blocks_per_seg; + si->rsvd_segs =reserved_segments (sbi); + si->overp_segs =overprovision_segments (sbi); + si->valid_count =valid_user_blocks (sbi); + si->valid_node_count =valid_node_count (sbi); + si->valid_inode_count =valid_inode_count (sbi); + si->utilization =utilization (sbi); + + si->free_segs =free_segments (sbi); + si->free_secs =free_sections (sbi); + si->prefree_count =prefree_segments (sbi); + si->dirty_count =dirty_segments (sbi); + si->node_pages = sbi->node_inode->i_mapping->nrpages; + si->meta_pages = sbi->meta_inode->i_mapping->nrpages; + si->nats =NM_I (sbi)->nat_cnt; + si->sits =SIT_I (sbi)->dirty_sentries; + si->fnids =NM_I (sbi)->fcnt; + si->bg_gc = sbi->bg_gc; + si->util_free = (int )(free_user_blocks (sbi) >> sbi->log_blocks_per_seg) + *100 / (int )(sbi->user_block_count >> sbi->log_blocks_per_seg) + /2 ; + si->util_valid = (int )(written_block_count (sbi) >> + sbi->log_blocks_per_seg) + *100 / (int )(sbi->user_block_count >> sbi->log_blocks_per_seg) + /2 ; + si->util_invalid =50 - si->util_free - si->util_valid; +for (i =CURSEG_HOT_DATA ; i <=CURSEG_COLD_NODE ; i++) { +struct curseg_info *curseg =CURSEG_I (sbi, i); + si->curseg[i] = curseg->segno; + si->cursec[i] = curseg->segno / sbi->segs_per_sec; + si->curzone[i] = si->cursec[i] / sbi->secs_per_zone; + } + +for (i =0 ; i <2 ; i++) { + si->segment_count[i] = sbi->segment_count[i]; + si->block_count[i] = sbi->block_count[i]; + } +} + +/** +* This function calculates BDF of every segments +*/ +void f2fs_update_gc_metric (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_info *gc_i = sbi->gc_info; +struct f2fs_stat_info *si = gc_i->stat_info; +unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist; +struct sit_info *sit_i =SIT_I (sbi); +unsigned int segno, vblocks; +int ndirty =0 ; + + bimodal =0 ; + total_vblocks =0 ; + blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg); + hblks_per_sec = blks_per_sec /2 ; +mutex_lock (&sit_i->sentry_lock); +for (segno =0 ; segno <TOTAL_SEGS (sbi); segno += sbi->segs_per_sec) { + vblocks =get_valid_blocks (sbi, segno, sbi->segs_per_sec); + dist =abs (vblocks - hblks_per_sec); + bimodal += dist * dist; + +if (vblocks >0 && vblocks < blks_per_sec) { + total_vblocks += vblocks; + ndirty++; + } + } +mutex_unlock (&sit_i->sentry_lock); + dist = sbi->total_sections * hblks_per_sec * hblks_per_sec /100 ; + si->bimodal = bimodal / dist; +if (si->dirty_count) + si->avg_vblocks = total_vblocks / ndirty; +else + si->avg_vblocks =0 ; +} + +static int f2fs_read_gc (char *page,char **start,off_t off, +int count,int *eof,void *data) +{ +struct f2fs_gc_info *gc_i, *next; +struct f2fs_stat_info *si; +char *buf = page; +int i =0 ; + +list_for_each_entry_safe (gc_i, next, &f2fs_stat_list, stat_list) { +int j; + si = gc_i->stat_info; + +mutex_lock (&si->stat_list); +if (!si->sbi) { +mutex_unlock (&si->stat_list); +continue ; + } +f2fs_update_stat (si->sbi); + + buf +=sprintf (buf,"=====[ partition info. #%d ]=====\n" , i++); + buf +=sprintf (buf,"[SB: 1] [CP: 2] [NAT: %d] [SIT: %d] " , + si->nat_area_segs, si->sit_area_segs); + buf +=sprintf (buf,"[SSA: %d] [MAIN: %d" , + si->ssa_area_segs, si->main_area_segs); + buf +=sprintf (buf,"(OverProv:%d Resv:%d)]\n\n" , + si->overp_segs, si->rsvd_segs); + buf +=sprintf (buf,"Utilization: %d%% (%d valid blocks)\n" , + si->utilization, si->valid_count); + buf +=sprintf (buf," - Node: %u (Inode: %u, " , + si->valid_node_count, si->valid_inode_count); + buf +=sprintf (buf,"Other: %u)\n - Data: %u\n" , + si->valid_node_count - si->valid_inode_count, + si->valid_count - si->valid_node_count); + buf +=sprintf (buf,"\nMain area: %d segs, %d secs %d zones\n" , + si->main_area_segs, si->main_area_sections, + si->main_area_zones); + buf +=sprintf (buf," - COLD data: %d, %d, %d\n" , + si->curseg[CURSEG_COLD_DATA ], + si->cursec[CURSEG_COLD_DATA ], + si->curzone[CURSEG_COLD_DATA ]); + buf +=sprintf (buf," - WARM data: %d, %d, %d\n" , + si->curseg[CURSEG_WARM_DATA ], + si->cursec[CURSEG_WARM_DATA ], + si->curzone[CURSEG_WARM_DATA ]); + buf +=sprintf (buf," - HOT data: %d, %d, %d\n" , + si->curseg[CURSEG_HOT_DATA ], + si->cursec[CURSEG_HOT_DATA ], + si->curzone[CURSEG_HOT_DATA ]); + buf +=sprintf (buf," - Dir dnode: %d, %d, %d\n" , + si->curseg[CURSEG_HOT_NODE ], + si->cursec[CURSEG_HOT_NODE ], + si->curzone[CURSEG_HOT_NODE ]); + buf +=sprintf (buf," - File dnode: %d, %d, %d\n" , + si->curseg[CURSEG_WARM_NODE ], + si->cursec[CURSEG_WARM_NODE ], + si->curzone[CURSEG_WARM_NODE ]); + buf +=sprintf (buf," - Indir nodes: %d, %d, %d\n" , + si->curseg[CURSEG_COLD_NODE ], + si->cursec[CURSEG_COLD_NODE ], + si->curzone[CURSEG_COLD_NODE ]); + buf +=sprintf (buf,"\n - Valid: %d\n - Dirty: %d\n" , + si->main_area_segs - si->dirty_count - + si->prefree_count - si->free_segs, + si->dirty_count); + buf +=sprintf (buf," - Prefree: %d\n - Free: %d (%d)\n\n" , + si->prefree_count, + si->free_segs, + si->free_secs); + buf +=sprintf (buf,"GC calls: %d (BG: %d)\n" , + si->call_count, si->bg_gc); + buf +=sprintf (buf," - data segments : %d\n" , si->data_segs); + buf +=sprintf (buf," - node segments : %d\n" , si->node_segs); + buf +=sprintf (buf,"Try to move %d blocks\n" , si->tot_blks); + buf +=sprintf (buf," - data blocks : %d\n" , si->data_blks); + buf +=sprintf (buf," - node blocks : %d\n" , si->node_blks); + buf +=sprintf (buf,"\nExtent Hit Ratio: %d / %d\n" , + si->hit_ext, si->total_ext); + buf +=sprintf (buf,"\nBalancing F2FS Async:\n" ); + buf +=sprintf (buf," - nodes %4d in %4d\n" , + si->ndirty_node, si->node_pages); + buf +=sprintf (buf," - dents %4d in dirs:%4d\n" , + si->ndirty_dent, si->ndirty_dirs); + buf +=sprintf (buf," - meta %4d in %4d\n" , + si->ndirty_meta, si->meta_pages); + buf +=sprintf (buf," - NATs %5d > %lu\n" , + si->nats,NM_WOUT_THRESHOLD ); + buf +=sprintf (buf," - SITs: %5d\n - free_nids: %5d\n" , + si->sits, si->fnids); + buf +=sprintf (buf,"\nDistribution of User Blocks:" ); + buf +=sprintf (buf," [ valid | invalid | free ]\n" ); + buf +=sprintf (buf," [" ); +for (j =0 ; j < si->util_valid; j++) + buf +=sprintf (buf,"-" ); + buf +=sprintf (buf,"|" ); +for (j =0 ; j < si->util_invalid; j++) + buf +=sprintf (buf,"-" ); + buf +=sprintf (buf,"|" ); +for (j =0 ; j < si->util_free; j++) + buf +=sprintf (buf,"-" ); + buf +=sprintf (buf,"]\n\n" ); + buf +=sprintf (buf,"SSR: %u blocks in %u segments\n" , + si->block_count[SSR ], si->segment_count[SSR ]); + buf +=sprintf (buf,"LFS: %u blocks in %u segments\n" , + si->block_count[LFS ], si->segment_count[LFS ]); +mutex_unlock (&si->stat_list); + } +return buf - page; +} + +static int f2fs_read_sit (char *page,char **start,off_t off, +int count,int *eof,void *data) +{ +struct f2fs_gc_info *gc_i, *next; +struct f2fs_stat_info *si; +char *buf = page; + +list_for_each_entry_safe (gc_i, next, &f2fs_stat_list, stat_list) { + si = gc_i->stat_info; + +mutex_lock (&si->stat_list); +if (!si->sbi) { +mutex_unlock (&si->stat_list); +continue ; + } +f2fs_update_gc_metric (si->sbi); + + buf +=sprintf (buf,"BDF: %u, avg. vblocks: %u\n" , + si->bimodal, si->avg_vblocks); +mutex_unlock (&si->stat_list); + } +return buf - page; +} + +static int f2fs_read_mem (char *page,char **start,off_t off, +int count,int *eof,void *data) +{ +struct f2fs_gc_info *gc_i, *next; +struct f2fs_stat_info *si; +char *buf = page; + +list_for_each_entry_safe (gc_i, next, &f2fs_stat_list, stat_list) { +struct f2fs_sb_info *sbi = gc_i->stat_info->sbi; +unsigned npages; +unsigned base_mem =0 , cache_mem =0 ; + + si = gc_i->stat_info; +mutex_lock (&si->stat_list); +if (!si->sbi) { +mutex_unlock (&si->stat_list); +continue ; + } + base_mem +=sizeof (struct f2fs_sb_info ) + sbi->sb->s_blocksize; + base_mem +=2 *sizeof (struct f2fs_inode_info ); + base_mem +=sizeof (*sbi->ckpt); + +/* build sm */ + base_mem +=sizeof (struct f2fs_sm_info ); + +/* build sit */ + base_mem +=sizeof (struct sit_info ); + base_mem +=TOTAL_SEGS (sbi) *sizeof (struct seg_entry ); + base_mem +=f2fs_bitmap_size (TOTAL_SEGS (sbi)); + base_mem +=2 *SIT_VBLOCK_MAP_SIZE *TOTAL_SEGS (sbi); +if (sbi->segs_per_sec >1 ) + base_mem += sbi->total_sections * +sizeof (struct sec_entry ); + base_mem +=__bitmap_size (sbi,SIT_BITMAP ); + +/* build free segmap */ + base_mem +=sizeof (struct free_segmap_info ); + base_mem +=f2fs_bitmap_size (TOTAL_SEGS (sbi)); + base_mem +=f2fs_bitmap_size (sbi->total_sections); + +/* build curseg */ + base_mem +=sizeof (struct curseg_info ) *NR_CURSEG_TYPE ; + base_mem +=PAGE_CACHE_SIZE *NR_CURSEG_TYPE ; + +/* build dirty segmap */ + base_mem +=sizeof (struct dirty_seglist_info ); + base_mem +=NR_DIRTY_TYPE *f2fs_bitmap_size (TOTAL_SEGS (sbi)); + base_mem +=2 *f2fs_bitmap_size (TOTAL_SEGS (sbi)); + +/* buld nm */ + base_mem +=sizeof (struct f2fs_nm_info ); + base_mem +=__bitmap_size (sbi,NAT_BITMAP ); + +/* build gc */ + base_mem +=sizeof (struct f2fs_gc_info ); + base_mem +=sizeof (struct f2fs_gc_kthread ); + +/* free nids */ + cache_mem +=NM_I (sbi)->fcnt; + cache_mem +=NM_I (sbi)->nat_cnt; + npages = sbi->node_inode->i_mapping->nrpages; + cache_mem += npages <<PAGE_CACHE_SHIFT ; + npages = sbi->meta_inode->i_mapping->nrpages; + cache_mem += npages <<PAGE_CACHE_SHIFT ; + cache_mem += sbi->n_orphans *sizeof (struct orphan_inode_entry ); + cache_mem += sbi->n_dirty_dirs *sizeof (struct dir_inode_entry ); + + buf +=sprintf (buf,"%u KB = static: %u + cached: %u\n" , + (base_mem + cache_mem) >>10 , + base_mem >>10 , + cache_mem >>10 ); +mutex_unlock (&si->stat_list); + } +return buf - page; +} + +int f2fs_stat_init (struct f2fs_sb_info *sbi) +{ +struct proc_dir_entry *entry; + + entry =create_proc_entry ("f2fs_stat" ,0 , sbi->s_proc); +if (!entry) +return -ENOMEM ; + entry->read_proc = f2fs_read_gc; + entry->write_proc =NULL ; + + entry =create_proc_entry ("f2fs_sit_stat" ,0 , sbi->s_proc); +if (!entry) { +remove_proc_entry ("f2fs_stat" , sbi->s_proc); +return -ENOMEM ; + } + entry->read_proc = f2fs_read_sit; + entry->write_proc =NULL ; + entry =create_proc_entry ("f2fs_mem_stat" ,0 , sbi->s_proc); +if (!entry) { +remove_proc_entry ("f2fs_sit_stat" , sbi->s_proc); +remove_proc_entry ("f2fs_stat" , sbi->s_proc); +return -ENOMEM ; + } + entry->read_proc = f2fs_read_mem; + entry->write_proc =NULL ; +return 0 ; +} + +void f2fs_stat_exit (struct f2fs_sb_info *sbi) +{ +if (sbi->s_proc) { +remove_proc_entry ("f2fs_stat" , sbi->s_proc); +remove_proc_entry ("f2fs_sit_stat" , sbi->s_proc); +remove_proc_entry ("f2fs_mem_stat" , sbi->s_proc); + } +} +#endif + +int build_gc_manager (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_info *gc_i; +struct f2fs_checkpoint *ckp =F2FS_CKPT (sbi); +#ifdef CONFIG_F2FS_STAT_FS +struct f2fs_super_block *raw_super =F2FS_RAW_SUPER (sbi); +struct f2fs_stat_info *si; +#endif + + gc_i =kzalloc (sizeof (struct f2fs_gc_info ),GFP_KERNEL ); +if (!gc_i) +return -ENOMEM ; + + sbi->gc_info = gc_i; + gc_i->rsvd_segment_count =le32_to_cpu (ckp->rsvd_segment_count); + gc_i->overp_segment_count =le32_to_cpu (ckp->overprov_segment_count); + +DIRTY_I (sbi)->v_ops = &default_v_ops; + +#ifdef CONFIG_F2FS_STAT_FS + gc_i->stat_info =kzalloc (sizeof (struct f2fs_stat_info ), +GFP_KERNEL ); +if (!gc_i->stat_info) +return -ENOMEM ; + si = gc_i->stat_info; +mutex_init (&si->stat_list); +list_add_tail (&gc_i->stat_list, &f2fs_stat_list); + + si->all_area_segs =le32_to_cpu (raw_super->segment_count); + si->sit_area_segs =le32_to_cpu (raw_super->segment_count_sit); + si->nat_area_segs =le32_to_cpu (raw_super->segment_count_nat); + si->ssa_area_segs =le32_to_cpu (raw_super->segment_count_ssa); + si->main_area_segs =le32_to_cpu (raw_super->segment_count_main); + si->main_area_sections =le32_to_cpu (raw_super->section_count); + si->main_area_zones = si->main_area_sections / +le32_to_cpu (raw_super->secs_per_zone); + si->sbi = sbi; +#endif +return 0 ; +} + +void destroy_gc_manager (struct f2fs_sb_info *sbi) +{ +struct f2fs_gc_info *gc_i = sbi->gc_info; +#ifdef CONFIG_F2FS_STAT_FS +struct f2fs_stat_info *si = gc_i->stat_info; +#endif +if (!gc_i) +return ; + +#ifdef CONFIG_F2FS_STAT_FS +list_del (&gc_i->stat_list); +mutex_lock (&si->stat_list); + si->sbi =NULL ; +mutex_unlock (&si->stat_list); +kfree (gc_i->stat_info); +#endif + sbi->gc_info =NULL ; +kfree (gc_i); +} + +int create_gc_caches (void ) +{ + winode_slab =f2fs_kmem_cache_create ("f2fs_gc_inodes" , +sizeof (struct inode_entry ),NULL ); +if (!winode_slab) +return -ENOMEM ; +return 0 ; +} + +void destroy_gc_caches (void ) +{ +kmem_cache_destroy (winode_slab); +}
fs/f2fs/gc.h new file +203 -0
203 lines not on this page · raw message
Signature · 8 lines
1.7.9.5 --- Jaegeuk Kim Samsung
PATCH v2 15/16 f2fs: add recovery routines for roll-forward
In reply to Jaegeuk KimThis adds roll-forward routines to recover fsynced data.
- F2FS uses basically roll-back model with checkpointing.
- In order to implement fsync(), there are two approaches as follows.
1. A roll-back model with checkpointing at every fsync() : This is a naive method, but suffers from very low performance.
2. A roll-forward model
: F2FS adopts this model where all the fsynced data should be recovered, which
were written after checkpointing was done. In order to figure out the data, F2FS keeps a "fsync" mark in direct node blocks. In addition, F2FS remains the location of next node block in each direct node block for reconstructing the chain of node blocks during the recovery.
- In order to enhance the performance, F2FS keeps a "dentry" mark also in direct node blocks. If this is set during the recovery, F2FS replays adding a dentry.
--- fs/f2fs/recovery.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 fs/f2fs/recovery.c
fs/f2fs/recovery.c new file +375 -0
@@ -0,0 +1,375 @@ +/** +* fs/f2fs/recovery.c +* +* Copyright (c) 2012 Samsung Electronics Co., Ltd. +* http://www.samsung.com/ +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ +#include <linux/fs.h> +#include <linux/f2fs_fs.h> +#include "f2fs.h" +#include "node.h" +#include "segment.h" + +static struct kmem_cache *fsync_entry_slab; + +bool space_for_roll_forward (struct f2fs_sb_info *sbi) +{ +if (sbi->last_valid_block_count + sbi->alloc_valid_block_count + > sbi->user_block_count) +return false; +return true; +} + +static struct fsync_inode_entry *get_fsync_inode (struct list_head *head, +nid_t ino) +{ +struct list_head *this; +struct fsync_inode_entry *entry; + +list_for_each (this, head) { + entry = list_entry(this, struct fsync_inode_entry, list); +if (entry->inode->i_ino == ino) +return entry; + } +return NULL ; +} + +static int recover_dentry (struct page *ipage,struct inode *inode) +{ +struct f2fs_node *raw_node = (struct f2fs_node *)kmap (ipage); +struct f2fs_inode *raw_inode = &(raw_node->i); +struct dentry dent, parent; +struct f2fs_dir_entry *de; +struct page *page; +struct inode *dir; +int err =0 ; + +if (!is_dent_dnode (ipage)) + goto out; + + dir =f2fs_iget (inode->i_sb,le32_to_cpu (raw_inode->i_pino)); +if (IS_ERR (dir)) { + err = -EINVAL ; + goto out; + } + + parent.d_inode = dir; + dent.d_parent = &parent; + dent.d_name.len =le32_to_cpu (raw_inode->i_namelen); + dent.d_name.name = raw_inode->i_name; + + de =f2fs_find_entry (dir, &dent.d_name, &page); +if (de) { +kunmap (page); +f2fs_put_page (page,0 ); + }else { +f2fs_add_link (&dent, inode); + } +iput (dir); +out: +kunmap (ipage); +return err; +} + +static int recover_inode (struct inode *inode,struct page *node_page) +{ +void *kaddr =page_address (node_page); +struct f2fs_node *raw_node = (struct f2fs_node *)kaddr; +struct f2fs_inode *raw_inode = &(raw_node->i); + + inode->i_mode =le32_to_cpu (raw_inode->i_mode); +i_size_write (inode,le64_to_cpu (raw_inode->i_size)); + inode->i_atime.tv_sec =le64_to_cpu (raw_inode->i_mtime); + inode->i_ctime.tv_sec =le64_to_cpu (raw_inode->i_ctime); + inode->i_mtime.tv_sec =le64_to_cpu (raw_inode->i_mtime); + inode->i_atime.tv_nsec =le32_to_cpu (raw_inode->i_mtime_nsec); + inode->i_ctime.tv_nsec =le32_to_cpu (raw_inode->i_ctime_nsec); + inode->i_mtime.tv_nsec =le32_to_cpu (raw_inode->i_mtime_nsec); + +return recover_dentry (node_page, inode); +} + +static int find_fsync_dnodes (struct f2fs_sb_info *sbi,struct list_head *head) +{ +unsigned long long cp_ver =le64_to_cpu (sbi->ckpt->checkpoint_ver); +struct curseg_info *curseg; +struct page *page; +block_t blkaddr; +int err =0 ; + +/* get node pages in the current segment */ + curseg =CURSEG_I (sbi,CURSEG_WARM_NODE ); + blkaddr =START_BLOCK (sbi, curseg->segno) + curseg->next_blkoff; + +/* read node page */ + page =alloc_page (GFP_NOFS | __GFP_ZERO); +if (IS_ERR (page)) +return PTR_ERR (page); +lock_page (page); + +while (1 ) { +struct fsync_inode_entry *entry; + +if (f2fs_readpage (sbi, page, blkaddr,READ_SYNC )) + goto out; + +if (cp_ver !=cpver_of_node (page)) + goto out; + +if (!is_fsync_dnode (page)) + goto next; + + entry =get_fsync_inode (head,ino_of_node (page)); +if (entry) { + entry->blkaddr = blkaddr; +if (IS_INODE (page) &&is_dent_dnode (page)) +set_inode_flag (F2FS_I (entry->inode), +FI_INC_LINK ); + }else { +if (IS_INODE (page) &&is_dent_dnode (page)) { +if (recover_inode_page (sbi, page)) { + err = -ENOMEM ; + goto out; + } + } + +/* add this fsync inode to the list */ + entry =kmem_cache_alloc (fsync_entry_slab,GFP_NOFS ); +if (!entry) { + err = -ENOMEM ; + goto out; + } + +INIT_LIST_HEAD (&entry->list); +list_add_tail (&entry->list, head); + + entry->inode =f2fs_iget (sbi->sb,ino_of_node (page)); +if (IS_ERR (entry->inode)) { + err =PTR_ERR (entry->inode); + goto out; + } + entry->blkaddr = blkaddr; + } +if (IS_INODE (page)) { + err =recover_inode (entry->inode, page); +if (err) + goto out; + } +next: +/* check next segment */ + blkaddr =next_blkaddr_of_node (page); +ClearPageUptodate (page); + } +out: +unlock_page (page); +__free_pages (page,0 ); +return err; +} + +static void destroy_fsync_dnodes (struct f2fs_sb_info *sbi, +struct list_head *head) +{ +struct list_head *this; +struct fsync_inode_entry *entry; +list_for_each (this, head) { + entry = list_entry(this, struct fsync_inode_entry, list); +iput (entry->inode); +list_del (&entry->list); +kmem_cache_free (fsync_entry_slab, entry); + } +} + +static void check_index_in_prev_nodes (struct f2fs_sb_info *sbi, +block_t blkaddr) +{ +struct seg_entry *sentry; +unsigned int segno =GET_SEGNO (sbi, blkaddr); +unsigned short blkoff =GET_SEGOFF_FROM_SEG0 (sbi, blkaddr) & + (sbi->blocks_per_seg -1 ); +struct f2fs_summary sum; +nid_t ino; +void *kaddr; +struct inode *inode; +struct page *node_page; +block_t bidx; +int i; + + sentry =get_seg_entry (sbi, segno); +if (!f2fs_test_bit (blkoff, sentry->cur_valid_map)) +return ; + +/* Get the previous summary */ +for (i =CURSEG_WARM_DATA ; i <=CURSEG_COLD_DATA ; i++) { +struct curseg_info *curseg =CURSEG_I (sbi, i); +if (curseg->segno == segno) { + sum = curseg->sum_blk->entries[blkoff]; +break ; + } + } +if (i >CURSEG_COLD_DATA ) { +struct page *sum_page =get_sum_page (sbi, segno); +struct f2fs_summary_block *sum_node; + kaddr =page_address (sum_page); + sum_node = (struct f2fs_summary_block *)kaddr; + sum = sum_node->entries[blkoff]; +f2fs_put_page (sum_page,1 ); + } + +/* Get the node page */ + node_page =get_node_page (sbi,le32_to_cpu (sum.nid)); + bidx =start_bidx_of_node (ofs_of_node (node_page)) + +le16_to_cpu (sum.ofs_in_node); + ino =ino_of_node (node_page); +f2fs_put_page (node_page,1 ); + +/* Deallocate previous index in the node page */ + inode =f2fs_iget_nowait (sbi->sb, ino); +truncate_hole (inode, bidx, bidx +1 ); +iput (inode); +} + +static void do_recover_data (struct f2fs_sb_info *sbi,struct inode *inode, +struct page *page,block_t blkaddr) +{ +unsigned int start, end; +struct dnode_of_data dn; +struct f2fs_summary sum; +struct node_info ni; + + start =start_bidx_of_node (ofs_of_node (page)); +if (IS_INODE (page)) + end = start +ADDRS_PER_INODE ; +else + end = start +ADDRS_PER_BLOCK ; + +set_new_dnode (&dn, inode,NULL ,NULL ,0 ); +if (get_dnode_of_data (&dn, start,0 )) +return ; + +wait_on_page_writeback (dn.node_page); + +get_node_info (sbi, dn.nid, &ni); +BUG_ON (ni.ino !=ino_of_node (page)); +BUG_ON (ofs_of_node (dn.node_page) !=ofs_of_node (page)); + +for (; start < end; start++) { +block_t src, dest; + + src =datablock_addr (dn.node_page, dn.ofs_in_node); + dest =datablock_addr (page, dn.ofs_in_node); + +if (src != dest && dest !=NEW_ADDR && dest !=NULL_ADDR ) { +if (src ==NULL_ADDR ) { +int err =reserve_new_block (&dn); +/* We should not get -ENOSPC */ +BUG_ON (err); + } + +/* Check the previous node page having this index */ +check_index_in_prev_nodes (sbi, dest); + +set_summary (&sum, dn.nid, dn.ofs_in_node, ni.version); + +/* write dummy data page */ +recover_data_page (sbi,NULL , &sum, src, dest); +update_extent_cache (dest, &dn); + } + dn.ofs_in_node++; + } + +/* write node page in place */ +set_summary (&sum, dn.nid,0 ,0 ); +if (IS_INODE (dn.node_page)) +sync_inode_page (&dn); + +copy_node_footer (dn.node_page, page); +fill_node_footer (dn.node_page, dn.nid, ni.ino, +ofs_of_node (page), false); +set_page_dirty (dn.node_page); + +recover_node_page (sbi, dn.node_page, &sum, &ni, blkaddr); +f2fs_put_dnode (&dn); +} + +static void recover_data (struct f2fs_sb_info *sbi, +struct list_head *head,int type) +{ +unsigned long long cp_ver =le64_to_cpu (sbi->ckpt->checkpoint_ver); +struct curseg_info *curseg; +struct page *page; +block_t blkaddr; + +/* get node pages in the current segment */ + curseg =CURSEG_I (sbi, type); + blkaddr =NEXT_FREE_BLKADDR (sbi, curseg); + +/* read node page */ + page =alloc_page (GFP_NOFS | __GFP_ZERO); +if (IS_ERR (page)) +return ; +lock_page (page); + +while (1 ) { +struct fsync_inode_entry *entry; + +if (f2fs_readpage (sbi, page, blkaddr,READ_SYNC )) + goto out; + +if (cp_ver !=cpver_of_node (page)) + goto out; + + entry =get_fsync_inode (head,ino_of_node (page)); +if (!entry) + goto next; + +do_recover_data (sbi, entry->inode, page, blkaddr); + +if (entry->blkaddr == blkaddr) { +iput (entry->inode); +list_del (&entry->list); +kmem_cache_free (fsync_entry_slab, entry); + } +next: +/* check next segment */ + blkaddr =next_blkaddr_of_node (page); +ClearPageUptodate (page); + } +out: +unlock_page (page); +__free_pages (page,0 ); + +allocate_new_segments (sbi); +} + +void recover_fsync_data (struct f2fs_sb_info *sbi) +{ +struct list_head inode_list; + + fsync_entry_slab =f2fs_kmem_cache_create ("f2fs_fsync_inode_entry" , +sizeof (struct fsync_inode_entry ),NULL ); +if (unlikely (!fsync_entry_slab)) +return ; + +INIT_LIST_HEAD (&inode_list); + +/* step #1: find fsynced inode numbers */ +if (find_fsync_dnodes (sbi, &inode_list)) + goto out; + +if (list_empty (&inode_list)) + goto out; + +/* step #2: recover data */ + sbi->por_doing =1 ; +recover_data (sbi, &inode_list,CURSEG_WARM_NODE ); + sbi->por_doing =0 ; +BUG_ON (!list_empty (&inode_list)); +out: +destroy_fsync_dnodes (sbi, &inode_list); +kmem_cache_destroy (fsync_entry_slab); +write_checkpoint (sbi, false, false); +}
Signature · 8 lines
1.7.9.5 --- Jaegeuk Kim Samsung
PATCH v2 16/16 f2fs: update Kconfig and Makefile
In reply to Jaegeuk KimThis adds Makefile and Kconfig for f2fs, and updates Makefile and Kconfig files in the fs directory.
--- fs/Kconfig | 1 + fs/Makefile | 1 + fs/f2fs/Kconfig | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/Makefile | 6 ++++++ 4 files changed, 63 insertions(+) create mode 100644 fs/f2fs/Kconfig create mode 100644 fs/f2fs/Makefile
fs/Kconfig +1 -0
@@ -220,6 +220,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" +source "fs/f2fs/Kconfig" endif # MISC_FILESYSTEMS
fs/Makefile +1 -0
@@ -123,6 +123,7 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ +obj-$(CONFIG_F2FS_FS) += f2fs/ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/
fs/f2fs/Kconfig new file +55 -0
55 lines not on this page · raw message
fs/f2fs/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +obj-$(CONFIG_F2FS_FS) += f2fs.o + +f2fs-y := dir.o file.o inode.o namei.o hash.o super.o +f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o +f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o +f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
Signature · 8 lines
1.7.9.5 --- Jaegeuk Kim Samsung
Re: [PATCH 16/16 v2] f2fs: update Kconfig and Makefile
Quoted 4 lines · Jaegeuk Kim
fs/f2fs/Kconfig new file +4
-0+config F2FS_STAT_FS + bool "F2FS Status Information" + depends on F2FS_FS + default y
fs/f2fs/Kconfig line 30
Quoted 10 lines
fs/f2fs/Kconfig new file +10
-0+ help + /proc/fs/f2fs/ contains information about partitions mounted as f2fs. + For each partition, a corresponding directory, named as its device + name, is provided with the following proc entries. + + f2fs_stat major file system information managed by f2fs currently + f2fs_sit_stat average SIT information about whole segments + f2fs_mem_stat current memory footprint consumed by f2fs + + e.g., in /proc/fs/f2fs/sdb1/
Again, I will point out that this should either be in debugfs, or in /sys/fs/ but it should NOT be in /proc/ at all.
If you need help, I will be glad to do this conversion, just let me know and I'll send you a patch on top of this series that moves these entries into debugfs, which is where I think they really belong.
thanks,
greg k-h
RE: [PATCH 16/16 v2] f2fs: update Kconfig and Makefile
Quoted 19 lines · Jaegeuk Kim
Quoted 4 lines
fs/f2fs/Kconfig new file +4
-0+config F2FS_STAT_FS + bool "F2FS Status Information" + depends on F2FS_FS + default yfs/f2fs/Kconfig line 30
Quoted 10 lines
fs/f2fs/Kconfig new file +10
-0+ help + /proc/fs/f2fs/ contains information about partitions mounted as f2fs. + For each partition, a corresponding directory, named as its device + name, is provided with the following proc entries. + + f2fs_stat major file system information managed by f2fs currently + f2fs_sit_stat average SIT information about whole segments + f2fs_mem_stat current memory footprint consumed by f2fs + + e.g., in /proc/fs/f2fs/sdb1/Again, I will point out that this should either be in debugfs, or in
/sys/fs/but it should NOT be in/proc/at all.
Quoted 3 lines
If you need help, I will be glad to do this conversion, just let me know and I'll send you a patch on top of this series that moves these entries into debugfs, which is where I think they really belong.
Ok, please. :)
I really appreciate your kindness.
Quoted 4 lines
thanks,
greg k-h
cover f2fs: move proc files to debugfs
In reply to Jaegeuk KimHere are 3 patches, moving the proc file usage on f2fs to debugfs.
The first one fixes a bug in the gc.h file preventing it from being able to be included by any other files.
The second patch moves all current proc file accesses to a single file, removing all #ifdefs from the .c files. This should have been done in the first place.
The last file converts the files to use debugfs instead of proc.
Note, these patches have been compile tested only, I haven't tested them out, as I haven't had the chance to yet. I'll go do that this afternoon after I catch up on some other pending kernel work.
One question, it seems that the proc files show all information for all super blocks in the system, no matter which subdirectory you are reading from in the proc f2fs tree. Is that really what you want? Shouldn't we only be showing the stats of the superblock we are saying we will report? I'll test that later today, and if it really is wrong, will fix the debugfs code up to handle this properly.
Do these patches look reasonable?
thanks,
greg k-h
PATCH 1/3 f2fs: gc.h: make should_do_checkpoint() inline
This should be an inline function, not a "real" function. Now other files can properly include gc.h.
--- fs/f2fs/gc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/gc.h +1 -1
@@ -191,7 +191,7 @@ static inline int is_idle(struct f2fs_sbreturn !(rl->count[BLK_RW_SYNC ]) && !(rl->count[BLK_RW_ASYNC ]); } -static bool should_do_checkpoint (struct f2fs_sb_info *sbi) +static inline bool should_do_checkpoint (struct f2fs_sb_info *sbi) {unsigned int pages_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
PATCH 2/3 f2fs: move statistics code into one file
In reply to Greg KHFrom: Greg Kroah-Hartman <[email protected]>
This moves all of the procfs statistics code into one file, debug.c and removes the #ifdefs from the core f2fs code when calling statistic functions.
This will make it more obvious how to move from procfs to debugfs, no functionality was changed here at all.
--- fs/f2fs/Makefile | 1 fs/f2fs/debug.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/f2fs/f2fs.h | 25 ++- fs/f2fs/gc.c | 373 ------------------------------------------------- fs/f2fs/super.c | 21 -- 5 files changed, 442 insertions(+), 392 deletions(-)
fs/f2fs/Makefile +1 -0
@@ -2,5 +2,6 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o f2fs-y := dir.o file.o inode.o namei.o hash.o super.o f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o +f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
fs/f2fs/debug.c new file +414 -0
414 lines not on this page · raw message
fs/f2fs/f2fs.h +19 -6
@@ -953,12 +953,6 @@ int start_gc_thread(struct f2fs_sb_infovoid stop_gc_thread (struct f2fs_sb_info *);block_t start_bidx_of_node (unsigned int );int f2fs_gc (struct f2fs_sb_info *,int ); -#ifdef CONFIG_F2FS_STAT_FS -void f2fs_update_stat (struct f2fs_sb_info *); -void f2fs_update_gc_metric (struct f2fs_sb_info *); -int f2fs_stat_init (struct f2fs_sb_info *); -void f2fs_stat_exit (struct f2fs_sb_info *); -#endif int build_gc_manager (struct f2fs_sb_info *);void destroy_gc_manager (struct f2fs_sb_info *);int create_gc_caches (void );
@@ -970,6 +964,25 @@ void destroy_gc_caches(void);void recover_fsync_data (struct f2fs_sb_info *);bool space_for_roll_forward (struct f2fs_sb_info *); +/** +* debug.c +*/ +#ifdef CONFIG_F2FS_STAT_FS +void f2fs_update_stat (struct f2fs_sb_info *); +int f2fs_stat_init (struct super_block *sb,struct f2fs_sb_info *); +void f2fs_stat_exit (struct super_block *sb,struct f2fs_sb_info *); +void f2fs_destroy_gci_stats (struct f2fs_gc_info *gc_i); +void f2fs_remove_stats (void ); +#else +static inline void f2fs_update_stat (struct f2fs_sb_info *sbi) { } +static inline int f2fs_stat_init (struct super_block *sb, +struct f2fs_sb_info *sbi) {return 0 ; } +static inline void f2fs_stat_exit (struct super_block *sb, +struct f2fs_sb_info *sbi) { } +static inline void f2fs_destroy_gci_stats (struct f2fs_gc_info *gc_i) { } +static inline void f2fs_remove_stats (void ) { } +#endif +extern const struct file_operations f2fs_dir_operations;extern const struct file_operations f2fs_file_operations;extern const struct inode_operations f2fs_file_inode_operations;
fs/f2fs/gc.c +3 -370
404 lines not on this page · raw message
fs/f2fs/super.c +5 -16
@@ -26,7 +26,6 @@#include "xattr.h" static struct kmem_cache *f2fs_inode_cachep; -static struct proc_dir_entry *f2fs_proc_root;enum { Opt_gc_background_off,
@@ -97,12 +96,7 @@ static void f2fs_put_super(struct super_ {struct f2fs_sb_info *sbi =F2FS_SB (sb); -#ifdef CONFIG_F2FS_STAT_FS -if (sbi->s_proc) { -f2fs_stat_exit (sbi); -remove_proc_entry (sb->s_id, f2fs_proc_root); - } -#endif +f2fs_stat_exit (sb, sbi);stop_gc_thread (sbi);write_checkpoint (sbi, false, true);
@@ -486,13 +480,9 @@ static int f2fs_fill_super(struct super_if (start_gc_thread (sbi)) goto fail; -#ifdef CONFIG_F2FS_STAT_FS -if (f2fs_proc_root) { - sbi->s_proc =proc_mkdir (sb->s_id, f2fs_proc_root); -if (f2fs_stat_init (sbi)) - goto fail; - } -#endif +if (f2fs_stat_init (sb, sbi)) + goto fail; +return 0 ; fail:stop_gc_thread (sbi);
@@ -566,7 +556,6 @@ static int __init init_f2fs_fs(void)if (register_filesystem (&f2fs_fs_type))return -EBUSY ; - f2fs_proc_root =proc_mkdir ("fs/f2fs" ,NULL );return 0 ; fail:return -ENOMEM ;
@@ -574,7 +563,7 @@ fail: static void __exit exit_f2fs_fs(void) { -remove_proc_entry ("fs/f2fs" ,NULL ); +f2fs_remove_stats ();unregister_filesystem (&f2fs_fs_type);destroy_checkpoint_caches ();destroy_gc_caches ();
PATCH 3/3 f2fs: move proc files to debugfs
In reply to Greg KHFrom: Greg Kroah-Hartman <[email protected]>
This moves all of the f2fs debugging files into debugfs. The files are located in /sys/kernel/debug/f2fs/
Note, I think we are generating all of the same information in each of the files for every unique f2fs filesystem in the machine. This copies the functionality that was present in the proc files, but this should be fixed up in the future.
Compile-tested only.
--- fs/f2fs/Kconfig | 8 - fs/f2fs/debug.c | 288 ++++++++++++++++++++++++++++++-------------------------- fs/f2fs/f2fs.h | 2 3 files changed, 161 insertions(+), 137 deletions(-)
fs/f2fs/Kconfig +4 -4
@@ -16,18 +16,18 @@ config F2FS_FS config F2FS_STAT_FS bool "F2FS Status Information" - depends on F2FS_FS + depends on F2FS_FS && DEBUG_FS default y help - /proc/fs/f2fs/ contains information about partitions mounted as f2fs. + /sys/kernel/debug/f2fs/ contains information about partitions mounted as f2fs. For each partition, a corresponding directory, named as its device - name, is provided with the following proc entries. + name, is provided with the following files: f2fs_stat major file system information managed by f2fs currently f2fs_sit_stat average SIT information about whole segments f2fs_mem_stat current memory footprint consumed by f2fs - e.g., in /proc/fs/f2fs/sdb1/ + e.g., in /sys/kernel/debug/f2fs/sdb1/ config F2FS_FS_XATTR bool "F2FS extended attributes"
fs/f2fs/debug.c +156 -132
374 lines not on this page · raw message
fs/f2fs/f2fs.h +1 -1
@@ -379,7 +379,7 @@ struct f2fs_sb_info {int rr_flush;/* related to GC */ -struct proc_dir_entry *s_proc; +struct dentry *s_debug;struct f2fs_gc_info *gc_info;/* Garbage Collector information */ struct mutex gc_mutex;/* mutex for GC */
PATCH v2 3/3 f2fs: move proc files to debugfs
From: Greg Kroah-Hartman <[email protected]>
This moves all of the f2fs debugging files into debugfs. The files are located in /sys/kernel/debug/f2fs/
Note, I think we are generating all of the same information in each of the files for every unique f2fs filesystem in the machine. This copies the functionality that was present in the proc files, but this should be fixed up in the future.
---
v2: run-time tested, and fixed subdirectory issue. Now files are
created where they were supposed to be.
fs/f2fs/Kconfig | 8 - fs/f2fs/debug.c | 294 ++++++++++++++++++++++++++++++-------------------------- fs/f2fs/f2fs.h | 2 3 files changed, 165 insertions(+), 139 deletions(-)
fs/f2fs/Kconfig +4 -4
@@ -16,18 +16,18 @@ config F2FS_FS config F2FS_STAT_FS bool "F2FS Status Information" - depends on F2FS_FS + depends on F2FS_FS && DEBUG_FS default y help - /proc/fs/f2fs/ contains information about partitions mounted as f2fs. + /sys/kernel/debug/f2fs/ contains information about partitions mounted as f2fs. For each partition, a corresponding directory, named as its device - name, is provided with the following proc entries. + name, is provided with the following files: f2fs_stat major file system information managed by f2fs currently f2fs_sit_stat average SIT information about whole segments f2fs_mem_stat current memory footprint consumed by f2fs - e.g., in /proc/fs/f2fs/sdb1/ + e.g., in /sys/kernel/debug/f2fs/sdb1/ config F2FS_FS_XATTR bool "F2FS extended attributes"
fs/f2fs/debug.c +160 -134
378 lines not on this page · raw message
fs/f2fs/f2fs.h +1 -1
@@ -379,7 +379,7 @@ struct f2fs_sb_info {int rr_flush;/* related to GC */ -struct proc_dir_entry *s_proc; +struct dentry *s_debug;struct f2fs_gc_info *gc_info;/* Garbage Collector information */ struct mutex gc_mutex;/* mutex for GC */
Re: [PATCH 0/3] f2fs: move proc files to debugfs
In reply to Greg KHQuoted 15 lines · Greg KH
Here are 3 patches, moving the proc file usage on f2fs to debugfs.
The first one fixes a bug in the
gc.hfile preventing it from being able to be included by any other files.The second patch moves all current proc file accesses to a single file, removing all #ifdefs from the .c files. This should have been done in the first place.
The last file converts the files to use debugfs instead of proc.
Note, these patches have been compile tested only, I haven't tested them out, as I haven't had the chance to yet. I'll go do that this afternoon after I catch up on some other pending kernel work.
Quoted 6 lines
One question, it seems that the proc files show all information for all super blocks in the system, no matter which subdirectory you are reading from in the proc f2fs tree. Is that really what you want? Shouldn't we only be showing the stats of the superblock we are saying we will report? I'll test that later today, and if it really is wrong, will fix the debugfs code up to handle this properly.
I just tested your patch set, and it looks like I see all partition information in each file, no matter what subdir it is in.
So, do you want this to be broken up per partition/superblock, in a subdir, like you intended? Or just 3 files, for all superblocks in the system?
Oh, the third patch is buggy, don't apply it, I got the subdir logic wrong, I'll go fix that up now.
thanks,
greg k-h
RE: [PATCH 0/3] f2fs: move proc files to debugfs
Quoted 23 lines · Greg KH
Quoted 15 lines
Here are 3 patches, moving the proc file usage on f2fs to debugfs.
The first one fixes a bug in the
gc.hfile preventing it from being able to be included by any other files.The second patch moves all current proc file accesses to a single file, removing all #ifdefs from the .c files. This should have been done in the first place.
The last file converts the files to use debugfs instead of proc.
Note, these patches have been compile tested only, I haven't tested them out, as I haven't had the chance to yet. I'll go do that this afternoon after I catch up on some other pending kernel work.
Quoted 6 lines
One question, it seems that the proc files show all information for all super blocks in the system, no matter which subdirectory you are reading from in the proc f2fs tree. Is that really what you want? Shouldn't we only be showing the stats of the superblock we are saying we will report? I'll test that later today, and if it really is wrong, will fix the debugfs code up to handle this properly.
Quoted 6 lines
I just tested your patch set, and it looks like I see all partition information in each file, no matter what subdir it is in.
So, do you want this to be broken up per partition/superblock, in a subdir, like you intended? Or just 3 files, for all superblocks in the system?
Thank you for great patches. I really appreciate that.
When I merged your patches, I found some unnecessary codes and memory structures such as gc_info in the original codes.
So I've done some works additionally based on the patches. Furthermore, for readability, I merged 3 stat files into one file, and let it show all the superblocks together, no breakdown per partition. I'll submit v3-series applying these changes soon.
Thanks,
Quoted 7 lines
Oh, the third patch is buggy, don't apply it, I got the subdir logic wrong, I'll go fix that up now.
thanks,
greg k-h
---
Jaegeuk Kim
Samsung
Quoted 5 lines · Jaegeuk Kim
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/
Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
thanks,
greg k-h
Quoted 10 lines · Greg KH
Quoted 5 lines · Jaegeuk Kim
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
Oh, and where do we report bugs for this tool? I just formatted a usb stick with the mkfs.f2fs program, and it did not fully erase the old filesystem that was on there (iso9660), so when I mounted it, it did so in iso9660 mode, not f2fs mode.
thanks,
greg k-h
Quoted 8 lines · Greg KH
Quoted 7 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/
Quoted 9 lines
Quoted 3 lines
Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
Oh, and where do we report bugs for this tool? I just formatted a usb stick with the mkfs.f2fs program, and it did not fully erase the old filesystem that was on there (iso9660), so when I mounted it, it did so in iso9660 mode, not f2fs mode.
Any suggestion for reporting bugs?
Maybe via a mailing list?
What version did you use? (1.1.0 is correct.)
The reason we found was due to the 0'th block, so we fixed that in v1.1.0. Thanks,
Quoted 3 lines
thanks,
greg k-h
---
Jaegeuk Kim
Samsung
Quoted 12 lines · Jaegeuk Kim
Quoted 12 lines · Greg KH
Quoted 10 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
Quoted 8 lines
Quoted 5 lines
Oh, and where do we report bugs for this tool? I just formatted a usb stick with the mkfs.f2fs program, and it did not fully erase the old filesystem that was on there (iso9660), so when I mounted it, it did so in iso9660 mode, not f2fs mode.
Any suggestion for reporting bugs?
Maybe via a mailing list?
Mailing list is fine.
Quoted 1 line
What version did you use? (1.1.0 is correct.)
I used 1.1.0
Quoted 1 line
The reason we found was due to the 0'th block, so we fixed that in v1.1.0.
Hm, that's what I used. I zeroed out the whole usb disk and tried again and it worked then, I was trying to debug the kernel changes, not the userspace tool, so I didn't spend much time on it :)
But, if you do get a public git tree up, I will at the very least, provide a patch to handle '-h' properly for mkfs, that should work...
thanks,
greg k-h
Quoted 28 lines · Jaegeuk Kim
Quoted 12 lines
Quoted 12 lines · Greg KH
Quoted 10 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
Quoted 8 lines
Quoted 5 lines
Oh, and where do we report bugs for this tool? I just formatted a usb stick with the mkfs.f2fs program, and it did not fully erase the old filesystem that was on there (iso9660), so when I mounted it, it did so in iso9660 mode, not f2fs mode.
Any suggestion for reporting bugs?
Maybe via a mailing list?Mailing list is fine.
Quoted 1 line
What version did you use? (1.1.0 is correct.)
I used 1.1.0
Quoted 9 lines
Quoted 1 line
The reason we found was due to the 0'th block, so we fixed that in v1.1.0.
Hm, that's what I used. I zeroed out the whole usb disk and tried again and it worked then, I was trying to debug the kernel changes, not the userspace tool, so I didn't spend much time on it :)
But, if you do get a public git tree up, I will at the very least, provide a patch to handle '-h' properly for mkfs, that should work...
Ok, thank you very much.
Quoted 3 lines
thanks,
greg k-h
---
Jaegeuk Kim
Samsung
Quoted 10 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
I'd love to do like that.
I've managed a git tree for tools in house only, due to the company secret. Would you suggest something for this?
I can do managing the tree outside though.
Quoted 4 lines
thanks,
greg k-h
Quoted 7 lines · Jaegeuk Kim
Quoted 7 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/
Quoted 8 lines
Quoted 3 lines
Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
I'd love to do like that.
I've managed a git tree for tools in house only, due to the company secret. Would you suggest something for this?
I can do managing the tree outside though.
git.kernel.org can work, so can github, and also, you have a sf.net project, why not use the git tree it provides you? Right now it is empty.
thanks,
greg k-h
Quoted 21 lines
-----Original Message-----
From: 'Greg KH' [mailto:[email protected]]
Sent: Wednesday, October 24, 2012 12:01 PM
To: Jaegeuk Kim
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH 00/16 v2] f2fs: introduce flash-friendly file system
Importance: HighQuoted 11 lines · Jaegeuk Kim
Quoted 10 lines · Jaegeuk Kim
Quoted 5 lines
mkfs.f2fs
=========The file system formatting tool, "mkfs.f2fs", is available from the following
download page: http://sourceforge.net/projects/f2fs-tools/Is there a git tree of this tool somewhere, so I don't have to constantly suffer the sf.net download interface every time I want to get the latest version?
Quoted 9 lines
Quoted 4 lines
I'd love to do like that.
I've managed a git tree for tools in house only, due to the company secret. Would you suggest something for this?
I can do managing the tree outside though.git.kernel.org can work, so can github, and also, you have a sf.net project, why not use the git tree it provides you? Right now it is empty.
Ok, I'll make a tree in sf.net. :)
Quoted 3 lines
thanks,
greg k-h
---
Jaegeuk Kim
Samsung