f2fs: throttle the memory footprint with a sysfs entry
authorJaegeuk Kim <jaegeuk.kim@samsung.com>
Wed, 19 Mar 2014 04:31:37 +0000 (13:31 +0900)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Thu, 20 Mar 2014 13:10:09 +0000 (22:10 +0900)
This patch introduces ram_thresh, a sysfs entry, which controls the memory
footprint used by the free nid list and the nat cache.

Previously, the free nid list was controlled by MAX_FREE_NIDS, while the nat
cache was managed by NM_WOUT_THRESHOLD.
However, this approach cannot be applied dynamically according to the system.

So, this patch adds ram_thresh that users can specify the threshold, which is
in order of 1 / 1024.
For example, if the total ram size is 4GB and the value is set to 10 by default,
f2fs tries to control the number of free nids and nat caches not to consume over
10 * (4GB / 1024) = 10MB.

Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Documentation/ABI/testing/sysfs-fs-f2fs
Documentation/filesystems/f2fs.txt
fs/f2fs/f2fs.h
fs/f2fs/node.c
fs/f2fs/node.h
fs/f2fs/super.c

index 32b0809203ddd00dfa7f5a236f72b8c00058ec5d..4ac2c25025eac9c9822600c5fb11e1bc506ca0fa 100644 (file)
@@ -55,3 +55,9 @@ Date:         January 2014
 Contact:       "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
 Description:
                 Controls the number of trials to find a victim segment.
+
+What:          /sys/fs/f2fs/<disk>/ram_thresh
+Date:          March 2014
+Contact:       "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
+Description:
+                Controls the memory footprint used by f2fs.
index 803784e1e8efd8a5389bfb27fa507103b97465db..f415b9fc4cc8fe698134c403be1a6ad1ff27a98b 100644 (file)
@@ -202,6 +202,10 @@ Files in /sys/fs/f2fs/<devname>
                              Otherwise, it needs to decrease this value to
                              reduce the space overhead. The default value is 0.
 
+ ram_thresh                   This parameter controls the memory footprint used
+                             by free nids and cached nat entries. By default,
+                             10 is set, which indicates 10 MB / 1 GB RAM.
+
 ================================================================================
 USAGE
 ================================================================================
index 59fac1a9f0c26c3ea242f178107440e1feba8083..05c652493f04aa93fc2af5abda3b06a6d05e730c 100644 (file)
@@ -242,6 +242,7 @@ struct f2fs_nm_info {
        block_t nat_blkaddr;            /* base disk address of NAT */
        nid_t max_nid;                  /* maximum possible node ids */
        nid_t next_scan_nid;            /* the next nid to be scanned */
+       unsigned int ram_thresh;        /* control the memory footprint */
 
        /* NAT cache management */
        struct radix_tree_root nat_root;/* root of the nat entry cache */
index d27e65a1fb0bf585b4d6ff1fb0891e6e732dc87a..fec4967fb8d280d03a666f9c900376691338420e 100644 (file)
 static struct kmem_cache *nat_entry_slab;
 static struct kmem_cache *free_nid_slab;
 
+static inline bool available_free_memory(struct f2fs_nm_info *nm_i, int type)
+{
+       struct sysinfo val;
+       unsigned long mem_size = 0;
+
+       si_meminfo(&val);
+       if (type == FREE_NIDS)
+               mem_size = nm_i->fcnt * sizeof(struct free_nid);
+       else if (type == NAT_ENTRIES)
+               mem_size += nm_i->nat_cnt * sizeof(struct nat_entry);
+       mem_size >>= 12;
+
+       /* give 50:50 memory for free nids and nat caches respectively */
+       return (mem_size < ((val.totalram * nm_i->ram_thresh) >> 11));
+}
+
 static void clear_node_page_dirty(struct page *page)
 {
        struct address_space *mapping = page->mapping;
@@ -208,7 +224,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
 {
        struct f2fs_nm_info *nm_i = NM_I(sbi);
 
-       if (nm_i->nat_cnt <= NM_WOUT_THRESHOLD || nr_shrink <= 0)
+       if (available_free_memory(nm_i, NAT_ENTRIES) || nr_shrink <= 0)
                return 0;
 
        write_lock(&nm_i->nat_tree_lock);
@@ -1288,7 +1304,7 @@ static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid, bool build)
        struct nat_entry *ne;
        bool allocated = false;
 
-       if (nm_i->fcnt > 2 * MAX_FREE_NIDS)
+       if (!available_free_memory(nm_i, FREE_NIDS))
                return -1;
 
        /* 0 nid should not be used */
@@ -1473,7 +1489,7 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
        spin_lock(&nm_i->free_nid_list_lock);
        i = __lookup_free_nid_list(nm_i, nid);
        f2fs_bug_on(!i || i->state != NID_ALLOC);
-       if (nm_i->fcnt > 2 * MAX_FREE_NIDS) {
+       if (!available_free_memory(nm_i, FREE_NIDS)) {
                __del_from_free_nid_list(nm_i, i);
        } else {
                i->state = NID_NEW;
@@ -1836,6 +1852,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
        nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks - 3;
        nm_i->fcnt = 0;
        nm_i->nat_cnt = 0;
+       nm_i->ram_thresh = DEF_RAM_THRESHOLD;
 
        INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
        INIT_LIST_HEAD(&nm_i->free_nid_list);
index ee6d28684ee8575c82c297e7eb09c6baa4e17aa6..c97215432100e14121b6d6e0d88e288a9e8a9d53 100644 (file)
 /* # of pages to perform readahead before building free nids */
 #define FREE_NID_PAGES 4
 
-/* maximum # of free node ids to produce during build_free_nids */
-#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES)
-
 /* maximum readahead size for node during getting data blocks */
 #define MAX_RA_NODE            128
 
 /* maximum cached nat entries to manage memory footprint */
 #define NM_WOUT_THRESHOLD      (64 * NAT_ENTRY_PER_BLOCK)
 
+/* control the memory footprint threshold (10MB per 1GB ram) */
+#define DEF_RAM_THRESHOLD      10
+
 /* vector size for gang look-up from nat cache that consists of radix tree */
 #define NATVEC_SIZE    64
 
@@ -77,6 +77,11 @@ static inline void node_info_from_raw_nat(struct node_info *ni,
        ni->version = raw_ne->version;
 }
 
+enum nid_type {
+       FREE_NIDS,      /* indicates the free nid list */
+       NAT_ENTRIES     /* indicates the cached nat entry */
+};
+
 /*
  * For free nid mangement
  */
index dbe402b1a4b7b9d70854001af5a682643c57588c..34c47b2010bcef9fe6b1bf06fba1b8014689e886 100644 (file)
@@ -74,6 +74,7 @@ static match_table_t f2fs_tokens = {
 enum {
        GC_THREAD,      /* struct f2fs_gc_thread */
        SM_INFO,        /* struct f2fs_sm_info */
+       NM_INFO,        /* struct f2fs_nm_info */
        F2FS_SBI,       /* struct f2fs_sb_info */
 };
 
@@ -92,6 +93,8 @@ static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
                return (unsigned char *)sbi->gc_thread;
        else if (struct_type == SM_INFO)
                return (unsigned char *)SM_I(sbi);
+       else if (struct_type == NM_INFO)
+               return (unsigned char *)NM_I(sbi);
        else if (struct_type == F2FS_SBI)
                return (unsigned char *)sbi;
        return NULL;
@@ -183,6 +186,7 @@ F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments);
 F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, max_small_discards, max_discards);
 F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy);
 F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
+F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
 F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
 
@@ -198,6 +202,7 @@ static struct attribute *f2fs_attrs[] = {
        ATTR_LIST(min_ipu_util),
        ATTR_LIST(max_victim_search),
        ATTR_LIST(dir_level),
+       ATTR_LIST(ram_thresh),
        NULL,
 };