]> Git Repo - linux.git/commitdiff
Merge tag 'for-f2fs-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
authorLinus Torvalds <[email protected]>
Thu, 14 Jan 2016 05:01:44 +0000 (21:01 -0800)
committerLinus Torvalds <[email protected]>
Thu, 14 Jan 2016 05:01:44 +0000 (21:01 -0800)
Pull f2fs updates from Jaegeuk Kim:
 "This series adds two ioctls to control cached data and fragmented
  files.  Most of the rest fixes missing error cases and bugs that we
  have not covered so far.  Summary:

  Enhancements:
   - support an ioctl to execute online file defragmentation
   - support an ioctl to flush cached data
   - speed up shrinking of extent_cache entries
   - handle broken superblock
   - refector dirty inode management infra
   - revisit f2fs_map_blocks to handle more cases
   - reduce global lock coverage
   - add detecting user's idle time

  Major bug fixes:
   - fix data race condition on cached nat entries
   - fix error cases of volatile and atomic writes"

* tag 'for-f2fs-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (87 commits)
  f2fs: should unset atomic flag after successful commit
  f2fs: fix wrong memory condition check
  f2fs: monitor the number of background checkpoint
  f2fs: detect idle time depending on user behavior
  f2fs: introduce time and interval facility
  f2fs: skip releasing nodes in chindless extent tree
  f2fs: use atomic type for node count in extent tree
  f2fs: recognize encrypted data in f2fs_fiemap
  f2fs: clean up f2fs_balance_fs
  f2fs: remove redundant calls
  f2fs: avoid unnecessary f2fs_balance_fs calls
  f2fs: check the page status filled from disk
  f2fs: introduce __get_node_page to reuse common code
  f2fs: check node id earily when readaheading node page
  f2fs: read isize while holding i_mutex in fiemap
  Revert "f2fs: check the node block address of newly allocated nid"
  f2fs: cover more area with nat_tree_lock
  f2fs: introduce max_file_blocks in sbi
  f2fs crypto: check CONFIG_F2FS_FS_XATTR for encrypted symlink
  f2fs: introduce zombie list for fast shrinking extent trees
  ...

1  2 
fs/f2fs/debug.c
fs/f2fs/f2fs.h
fs/f2fs/inode.c
fs/f2fs/namei.c
fs/f2fs/xattr.c

diff --combined fs/f2fs/debug.c
index ad1b18a7705bcdb6b815207882a0b28e798ef729,48f2ae9452ef4798ed664f02e87271260bc76c14..4fb6ef88a34f2836dc16a6441c678f2fe9077971
@@@ -38,12 -38,15 +38,15 @@@ static void update_general_status(struc
        si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree);
        si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree;
        si->total_ext = atomic64_read(&sbi->total_hit_ext);
-       si->ext_tree = sbi->total_ext_tree;
+       si->ext_tree = atomic_read(&sbi->total_ext_tree);
+       si->zombie_tree = atomic_read(&sbi->total_zombie_tree);
        si->ext_node = atomic_read(&sbi->total_ext_node);
        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->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
+       si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
+       si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
        si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
        si->wb_pages = get_pages(sbi, F2FS_WRITEBACK);
        si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
@@@ -105,7 -108,7 +108,7 @@@ static void update_sit_info(struct f2fs
  
        bimodal = 0;
        total_vblocks = 0;
-       blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
+       blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
        hblks_per_sec = blks_per_sec / 2;
        for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
                vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
@@@ -189,10 -192,10 +192,10 @@@ get_cache
        si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
                                        sizeof(struct nat_entry_set);
        si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
-       si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry);
        for (i = 0; i <= UPDATE_INO; i++)
                si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
-       si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree);
+       si->cache_mem += atomic_read(&sbi->total_ext_tree) *
+                                               sizeof(struct extent_tree);
        si->cache_mem += atomic_read(&sbi->total_ext_node) *
                                                sizeof(struct extent_node);
  
@@@ -211,10 -214,12 +214,10 @@@ static int stat_show(struct seq_file *s
  
        mutex_lock(&f2fs_stat_mutex);
        list_for_each_entry(si, &f2fs_stat_list, stat_list) {
 -              char devname[BDEVNAME_SIZE];
 -
                update_general_status(si->sbi);
  
 -              seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n",
 -                      bdevname(si->sbi->sb->s_bdev, devname), i++);
 +              seq_printf(s, "\n=====[ partition info(%pg). #%d ]=====\n",
 +                      si->sbi->sb->s_bdev, i++);
                seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
                           si->sit_area_segs, si->nat_area_segs);
                seq_printf(s, "[SSA: %d] [MAIN: %d",
                           si->dirty_count);
                seq_printf(s, "  - Prefree: %d\n  - Free: %d (%d)\n\n",
                           si->prefree_count, si->free_segs, si->free_secs);
-               seq_printf(s, "CP calls: %d\n", si->cp_count);
+               seq_printf(s, "CP calls: %d (BG: %d)\n",
+                               si->cp_count, si->bg_cp_count);
                seq_printf(s, "GC calls: %d (BG: %d)\n",
                           si->call_count, si->bg_gc);
                seq_printf(s, "  - data segments : %d (%d)\n",
                                !si->total_ext ? 0 :
                                div64_u64(si->hit_total * 100, si->total_ext),
                                si->hit_total, si->total_ext);
-               seq_printf(s, "  - Inner Struct Count: tree: %d, node: %d\n",
-                               si->ext_tree, si->ext_node);
+               seq_printf(s, "  - Inner Struct Count: tree: %d(%d), node: %d\n",
+                               si->ext_tree, si->zombie_tree, si->ext_node);
                seq_puts(s, "\nBalancing F2FS Async:\n");
                seq_printf(s, "  - inmem: %4d, wb: %4d\n",
                           si->inmem_pages, si->wb_pages);
                           si->ndirty_node, si->node_pages);
                seq_printf(s, "  - dents: %4d in dirs:%4d\n",
                           si->ndirty_dent, si->ndirty_dirs);
+               seq_printf(s, "  - datas: %4d in files:%4d\n",
+                          si->ndirty_data, si->ndirty_files);
                seq_printf(s, "  - meta: %4d in %4d\n",
                           si->ndirty_meta, si->meta_pages);
                seq_printf(s, "  - NATs: %9d/%9d\n  - SITs: %9d/%9d\n",
@@@ -404,20 -412,23 +410,23 @@@ void f2fs_destroy_stats(struct f2fs_sb_
        kfree(si);
  }
  
void __init f2fs_create_root_stats(void)
int __init f2fs_create_root_stats(void)
  {
        struct dentry *file;
  
        f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL);
        if (!f2fs_debugfs_root)
-               return;
+               return -ENOMEM;
  
        file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root,
                        NULL, &stat_fops);
        if (!file) {
                debugfs_remove(f2fs_debugfs_root);
                f2fs_debugfs_root = NULL;
+               return -ENOMEM;
        }
+       return 0;
  }
  
  void f2fs_destroy_root_stats(void)
diff --combined fs/f2fs/f2fs.h
index ec6067c33a3fa02472a9631b01bf4cc8a5643814,2c0e478cefb486920809bf674cc6fa6ca08c5a1b..ff79054c6cf6a5bfe254abaf203ae94acd44739b
@@@ -21,6 -21,7 +21,7 @@@
  #include <linux/sched.h>
  #include <linux/vmalloc.h>
  #include <linux/bio.h>
+ #include <linux/blkdev.h>
  
  #ifdef CONFIG_F2FS_CHECK_FS
  #define f2fs_bug_on(sbi, condition)   BUG_ON(condition)
@@@ -54,6 -55,7 +55,7 @@@
  #define F2FS_MOUNT_FASTBOOT           0x00001000
  #define F2FS_MOUNT_EXTENT_CACHE               0x00002000
  #define F2FS_MOUNT_FORCE_FG_GC                0x00004000
+ #define F2FS_MOUNT_DATA_FLUSH         0x00008000
  
  #define clear_opt(sbi, option)        (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
  #define set_opt(sbi, option)  (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
@@@ -125,6 -127,7 +127,7 @@@ enum 
  #define BATCHED_TRIM_BLOCKS(sbi)      \
                (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg)
  #define DEF_CP_INTERVAL                       60      /* 60 secs */
+ #define DEF_IDLE_INTERVAL             120     /* 2 mins */
  
  struct cp_control {
        int reason;
@@@ -158,13 -161,7 +161,7 @@@ struct ino_entry 
        nid_t ino;              /* inode number */
  };
  
- /*
-  * for the list of directory inodes or gc inodes.
-  * NOTE: there are two slab users for this structure, if we add/modify/delete
-  * fields in structure for one of slab users, it may affect fields or size of
-  * other one, in this condition, it's better to split both of slab and related
-  * data structure.
-  */
+ /* for the list of inodes to be GCed */
  struct inode_entry {
        struct list_head list;  /* list head */
        struct inode *inode;    /* vfs inode pointer */
@@@ -234,6 -231,7 +231,7 @@@ static inline bool __has_cursum_space(s
  #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
  #define F2FS_IOC_GARBAGE_COLLECT      _IO(F2FS_IOCTL_MAGIC, 6)
  #define F2FS_IOC_WRITE_CHECKPOINT     _IO(F2FS_IOCTL_MAGIC, 7)
+ #define F2FS_IOC_DEFRAGMENT           _IO(F2FS_IOCTL_MAGIC, 8)
  
  #define F2FS_IOC_SET_ENCRYPTION_POLICY                                        \
                _IOR('f', 19, struct f2fs_encryption_policy)
  /*
   * ioctl commands in 32 bit emulation
   */
- #define F2FS_IOC32_GETFLAGS             FS_IOC32_GETFLAGS
- #define F2FS_IOC32_SETFLAGS             FS_IOC32_SETFLAGS
+ #define F2FS_IOC32_GETFLAGS           FS_IOC32_GETFLAGS
+ #define F2FS_IOC32_SETFLAGS           FS_IOC32_SETFLAGS
+ #define F2FS_IOC32_GETVERSION         FS_IOC32_GETVERSION
  #endif
  
+ struct f2fs_defragment {
+       u64 start;
+       u64 len;
+ };
  /*
   * For INODE and NODE manager
   */
@@@ -357,9 -361,9 +361,9 @@@ struct extent_tree 
        struct rb_root root;            /* root of extent info rb-tree */
        struct extent_node *cached_en;  /* recently accessed extent node */
        struct extent_info largest;     /* largested extent info */
+       struct list_head list;          /* to be used by sbi->zombie_list */
        rwlock_t lock;                  /* protect extent info rb-tree */
-       atomic_t refcount;              /* reference count of rb-tree */
-       unsigned int count;             /* # of extent node in rb-tree*/
+       atomic_t node_cnt;              /* # of extent node in rb-tree*/
  };
  
  /*
@@@ -434,8 -438,8 +438,8 @@@ struct f2fs_inode_info 
        unsigned int clevel;            /* maximum level of given file name */
        nid_t i_xattr_nid;              /* node id that contains xattrs */
        unsigned long long xattr_ver;   /* cp version of xattr modification */
-       struct inode_entry *dirty_dir;  /* the pointer of dirty dir */
  
+       struct list_head dirty_list;    /* linked in global dirty list */
        struct list_head inmem_pages;   /* inmemory pages managed by f2fs */
        struct mutex inmem_lock;        /* lock for inmemory pages */
  
@@@ -544,6 -548,7 +548,7 @@@ struct dnode_of_data 
        nid_t nid;                      /* node id of the direct node block */
        unsigned int ofs_in_node;       /* data offset in the node page */
        bool inode_page_locked;         /* inode page is locked or not */
+       bool node_changed;              /* is node block changed */
        block_t data_blkaddr;           /* block address of the node block */
  };
  
@@@ -647,6 -652,7 +652,7 @@@ struct f2fs_sm_info 
  enum count_type {
        F2FS_WRITEBACK,
        F2FS_DIRTY_DENTS,
+       F2FS_DIRTY_DATA,
        F2FS_DIRTY_NODES,
        F2FS_DIRTY_META,
        F2FS_INMEM_PAGES,
@@@ -695,6 -701,12 +701,12 @@@ struct f2fs_bio_info 
        struct rw_semaphore io_rwsem;   /* blocking op for bio */
  };
  
+ enum inode_type {
+       DIR_INODE,                      /* for dirty dir inode */
+       FILE_INODE,                     /* for dirty regular/symlink inode */
+       NR_INODE_TYPE,
+ };
  /* for inner inode cache management */
  struct inode_management {
        struct radix_tree_root ino_root;        /* ino entry array */
@@@ -711,11 -723,17 +723,17 @@@ enum 
        SBI_POR_DOING,                          /* recovery is doing or not */
  };
  
+ enum {
+       CP_TIME,
+       REQ_TIME,
+       MAX_TIME,
+ };
  struct f2fs_sb_info {
        struct super_block *sb;                 /* pointer to VFS super block */
        struct proc_dir_entry *s_proc;          /* proc entry */
-       struct buffer_head *raw_super_buf;      /* buffer head of raw sb */
        struct f2fs_super_block *raw_super;     /* raw super block pointer */
+       int valid_super_block;                  /* valid super block no */
        int s_flag;                             /* flags for sbi */
  
        /* for node-related operations */
        struct rw_semaphore node_write;         /* locking node writes */
        struct mutex writepages;                /* mutex for writepages() */
        wait_queue_head_t cp_wait;
-       long cp_expires, cp_interval;           /* next expected periodic cp */
+       unsigned long last_time[MAX_TIME];      /* to store time in jiffies */
+       long interval_time[MAX_TIME];           /* to store thresholds */
  
        struct inode_management im[MAX_INO_ENTRY];      /* manage inode cache */
  
        /* for orphan inode, use 0'th array */
        unsigned int max_orphans;               /* max orphan inodes */
  
-       /* for directory inode management */
-       struct list_head dir_inode_list;        /* dir inode list */
-       spinlock_t dir_inode_lock;              /* for dir inode list lock */
+       /* for inode management */
+       struct list_head inode_list[NR_INODE_TYPE];     /* dirty inode list */
+       spinlock_t inode_lock[NR_INODE_TYPE];   /* for dirty inode list lock */
  
        /* for extent tree cache */
        struct radix_tree_root extent_tree_root;/* cache extent cache entries */
        struct rw_semaphore extent_tree_lock;   /* locking extent radix tree */
        struct list_head extent_list;           /* lru list for shrinker */
        spinlock_t extent_lock;                 /* locking extent lru list */
-       int total_ext_tree;                     /* extent tree count */
+       atomic_t total_ext_tree;                /* extent tree count */
+       struct list_head zombie_list;           /* extent zombie tree list */
+       atomic_t total_zombie_tree;             /* extent zombie tree count */
        atomic_t total_ext_node;                /* extent info count */
  
        /* basic filesystem units */
        unsigned int total_node_count;          /* total node block count */
        unsigned int total_valid_node_count;    /* valid node block count */
        unsigned int total_valid_inode_count;   /* valid inode count */
+       loff_t max_file_blocks;                 /* max block index of file */
        int active_logs;                        /* # of active logs */
        int dir_level;                          /* directory level */
  
        atomic_t inline_inode;                  /* # of inline_data inodes */
        atomic_t inline_dir;                    /* # of inline_dentry inodes */
        int bg_gc;                              /* background gc calls */
-       unsigned int n_dirty_dirs;              /* # of dir inodes */
+       unsigned int ndirty_inode[NR_INODE_TYPE];       /* # of dirty inodes */
  #endif
        unsigned int last_victim[2];            /* last victim segment # */
        spinlock_t stat_lock;                   /* lock for stat operations */
        unsigned int shrinker_run_no;
  };
  
+ static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
+ {
+       sbi->last_time[type] = jiffies;
+ }
+ static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type)
+ {
+       struct timespec ts = {sbi->interval_time[type], 0};
+       unsigned long interval = timespec_to_jiffies(&ts);
+       return time_after(jiffies, sbi->last_time[type] + interval);
+ }
+ static inline bool is_idle(struct f2fs_sb_info *sbi)
+ {
+       struct block_device *bdev = sbi->sb->s_bdev;
+       struct request_queue *q = bdev_get_queue(bdev);
+       struct request_list *rl = &q->root_rl;
+       if (rl->count[BLK_RW_SYNC] || rl->count[BLK_RW_ASYNC])
+               return 0;
+       return f2fs_time_over(sbi, REQ_TIME);
+ }
  /*
   * Inline functions
   */
@@@ -1059,8 -1106,8 +1106,8 @@@ static inline void inc_page_count(struc
  static inline void inode_inc_dirty_pages(struct inode *inode)
  {
        atomic_inc(&F2FS_I(inode)->dirty_pages);
-       if (S_ISDIR(inode->i_mode))
-               inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS);
+       inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
+                               F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
  }
  
  static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type)
@@@ -1075,9 -1122,8 +1122,8 @@@ static inline void inode_dec_dirty_page
                return;
  
        atomic_dec(&F2FS_I(inode)->dirty_pages);
-       if (S_ISDIR(inode->i_mode))
-               dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS);
+       dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
+                               F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
  }
  
  static inline int get_pages(struct f2fs_sb_info *sbi, int count_type)
@@@ -1092,8 -1138,7 +1138,7 @@@ static inline int get_dirty_pages(struc
  
  static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type)
  {
-       unsigned int pages_per_sec = sbi->segs_per_sec *
-                                       (1 << sbi->log_blocks_per_seg);
+       unsigned int pages_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
        return ((get_pages(sbi, block_type) + pages_per_sec - 1)
                        >> sbi->log_blocks_per_seg) / sbi->segs_per_sec;
  }
@@@ -1416,6 -1461,8 +1461,8 @@@ enum 
        FI_DROP_CACHE,          /* drop dirty page cache */
        FI_DATA_EXIST,          /* indicate data exists */
        FI_INLINE_DOTS,         /* indicate inline dot dentries */
+       FI_DO_DEFRAG,           /* indicate defragment is running */
+       FI_DIRTY_FILE,          /* indicate regular/symlink has dirty pages */
  };
  
  static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
@@@ -1602,11 -1649,13 +1649,11 @@@ static inline bool is_dot_dotdot(const 
  
  static inline bool f2fs_may_extent_tree(struct inode *inode)
  {
 -      mode_t mode = inode->i_mode;
 -
        if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE) ||
                        is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
                return false;
  
 -      return S_ISREG(mode);
 +      return S_ISREG(inode->i_mode);
  }
  
  static inline void *f2fs_kvmalloc(size_t size, gfp_t flags)
@@@ -1659,8 -1708,8 +1706,8 @@@ long f2fs_compat_ioctl(struct file *, u
  void f2fs_set_inode_flags(struct inode *);
  struct inode *f2fs_iget(struct super_block *, unsigned long);
  int try_to_free_nats(struct f2fs_sb_info *, int);
void update_inode(struct inode *, struct page *);
void update_inode_page(struct inode *);
int update_inode(struct inode *, struct page *);
int update_inode_page(struct inode *);
  int f2fs_write_inode(struct inode *, struct writeback_control *);
  void f2fs_evict_inode(struct inode *);
  void handle_failed_inode(struct inode *);
@@@ -1765,7 -1814,7 +1812,7 @@@ void destroy_node_manager_caches(void)
   */
  void register_inmem_page(struct inode *, struct page *);
  int commit_inmem_pages(struct inode *, bool);
- void f2fs_balance_fs(struct f2fs_sb_info *);
+ void f2fs_balance_fs(struct f2fs_sb_info *, bool);
  void f2fs_balance_fs_bg(struct f2fs_sb_info *);
  int f2fs_issue_flush(struct f2fs_sb_info *);
  int create_flush_cmd_control(struct f2fs_sb_info *);
@@@ -1811,9 -1860,9 +1858,9 @@@ bool is_valid_blkaddr(struct f2fs_sb_in
  int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool);
  void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t);
  long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
- void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
- void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
- void release_dirty_inode(struct f2fs_sb_info *);
+ void add_ino_entry(struct f2fs_sb_info *, nid_t, int type);
+ void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type);
+ void release_ino_entry(struct f2fs_sb_info *);
  bool exist_written_data(struct f2fs_sb_info *, nid_t, int);
  int acquire_orphan_inode(struct f2fs_sb_info *);
  void release_orphan_inode(struct f2fs_sb_info *);
@@@ -1823,9 -1872,9 +1870,9 @@@ int recover_orphan_inodes(struct f2fs_s
  int get_valid_checkpoint(struct f2fs_sb_info *);
  void update_dirty_page(struct inode *, struct page *);
  void add_dirty_dir_inode(struct inode *);
- void remove_dirty_dir_inode(struct inode *);
void sync_dirty_dir_inodes(struct f2fs_sb_info *);
void write_checkpoint(struct f2fs_sb_info *, struct cp_control *);
+ void remove_dirty_inode(struct inode *);
int sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type);
int write_checkpoint(struct f2fs_sb_info *, struct cp_control *);
  void init_ino_entry_info(struct f2fs_sb_info *);
  int __init create_checkpoint_caches(void);
  void destroy_checkpoint_caches(void);
@@@ -1845,6 -1894,7 +1892,7 @@@ struct page *find_data_page(struct inod
  struct page *get_lock_data_page(struct inode *, pgoff_t, bool);
  struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
  int do_write_data_page(struct f2fs_io_info *);
+ int f2fs_map_blocks(struct inode *, struct f2fs_map_blocks *, int, int);
  int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64);
  void f2fs_invalidate_page(struct page *, unsigned int, unsigned int);
  int f2fs_release_page(struct page *, gfp_t);
@@@ -1875,8 -1925,9 +1923,9 @@@ struct f2fs_stat_info 
        int main_area_segs, main_area_sections, main_area_zones;
        unsigned long long hit_largest, hit_cached, hit_rbtree;
        unsigned long long hit_total, total_ext;
-       int ext_tree, ext_node;
-       int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta;
+       int ext_tree, zombie_tree, ext_node;
+       int ndirty_node, ndirty_meta;
+       int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files;
        int nats, dirty_nats, sits, dirty_sits, fnids;
        int total_count, utilization;
        int bg_gc, inmem_pages, wb_pages;
        int util_free, util_valid, util_invalid;
        int rsvd_segs, overp_segs;
        int dirty_count, node_pages, meta_pages;
-       int prefree_count, call_count, cp_count;
+       int prefree_count, call_count, cp_count, bg_cp_count;
        int tot_segs, node_segs, data_segs, free_segs, free_secs;
        int bg_node_segs, bg_data_segs;
        int tot_blks, data_blks, node_blks;
@@@ -1907,10 -1958,11 +1956,11 @@@ static inline struct f2fs_stat_info *F2
  }
  
  #define stat_inc_cp_count(si)         ((si)->cp_count++)
+ #define stat_inc_bg_cp_count(si)      ((si)->bg_cp_count++)
  #define stat_inc_call_count(si)               ((si)->call_count++)
  #define stat_inc_bggc_count(sbi)      ((sbi)->bg_gc++)
- #define stat_inc_dirty_dir(sbi)               ((sbi)->n_dirty_dirs++)
- #define stat_dec_dirty_dir(sbi)               ((sbi)->n_dirty_dirs--)
+ #define stat_inc_dirty_inode(sbi, type)       ((sbi)->ndirty_inode[type]++)
+ #define stat_dec_dirty_inode(sbi, type)       ((sbi)->ndirty_inode[type]--)
  #define stat_inc_total_hit(sbi)               (atomic64_inc(&(sbi)->total_hit_ext))
  #define stat_inc_rbtree_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_rbtree))
  #define stat_inc_largest_node_hit(sbi)        (atomic64_inc(&(sbi)->read_hit_largest))
  
  int f2fs_build_stats(struct f2fs_sb_info *);
  void f2fs_destroy_stats(struct f2fs_sb_info *);
void __init f2fs_create_root_stats(void);
int __init f2fs_create_root_stats(void);
  void f2fs_destroy_root_stats(void);
  #else
  #define stat_inc_cp_count(si)
+ #define stat_inc_bg_cp_count(si)
  #define stat_inc_call_count(si)
  #define stat_inc_bggc_count(si)
- #define stat_inc_dirty_dir(sbi)
- #define stat_dec_dirty_dir(sbi)
+ #define stat_inc_dirty_inode(sbi, type)
+ #define stat_dec_dirty_inode(sbi, type)
  #define stat_inc_total_hit(sb)
  #define stat_inc_rbtree_node_hit(sb)
  #define stat_inc_largest_node_hit(sbi)
  
  static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; }
  static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { }
- static inline void __init f2fs_create_root_stats(void) { }
+ static inline int __init f2fs_create_root_stats(void) { return 0; }
  static inline void f2fs_destroy_root_stats(void) { }
  #endif
  
@@@ -2067,8 -2120,7 +2118,7 @@@ void f2fs_leave_shrinker(struct f2fs_sb
   * extent_cache.c
   */
  unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int);
- void f2fs_drop_largest_extent(struct inode *, pgoff_t);
- void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
+ bool f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
  unsigned int f2fs_destroy_extent_node(struct inode *);
  void f2fs_destroy_extent_tree(struct inode *);
  bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *);
@@@ -2119,7 -2171,7 +2169,7 @@@ static inline int f2fs_sb_has_crypto(st
  static inline bool f2fs_may_encrypt(struct inode *inode)
  {
  #ifdef CONFIG_F2FS_FS_ENCRYPTION
 -      mode_t mode = inode->i_mode;
 +      umode_t mode = inode->i_mode;
  
        return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode));
  #else
diff --combined fs/f2fs/inode.c
index 5528801a5baf3a13a75ffd16c831c1d7b1a650f4,2ac4b780e8b43d8be07ed969525aa6f62e9de9c5..2adeff26be11b9689dde24a5d22b0bd3351c21b7
@@@ -138,7 -138,8 +138,8 @@@ static int do_read_inode(struct inode *
        fi->i_pino = le32_to_cpu(ri->i_pino);
        fi->i_dir_level = ri->i_dir_level;
  
-       f2fs_init_extent_tree(inode, &ri->i_ext);
+       if (f2fs_init_extent_tree(inode, &ri->i_ext))
+               set_page_dirty(node_page);
  
        get_inline_info(fi, ri);
  
@@@ -202,7 -203,6 +203,7 @@@ make_now
                        inode->i_op = &f2fs_encrypted_symlink_inode_operations;
                else
                        inode->i_op = &f2fs_symlink_inode_operations;
 +              inode_nohighmem(inode);
                inode->i_mapping->a_ops = &f2fs_dblock_aops;
        } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
                        S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
@@@ -222,7 -222,7 +223,7 @@@ bad_inode
        return ERR_PTR(ret);
  }
  
void update_inode(struct inode *inode, struct page *node_page)
int update_inode(struct inode *inode, struct page *node_page)
  {
        struct f2fs_inode *ri;
  
  
        __set_inode_rdev(inode, ri);
        set_cold_node(inode, node_page);
-       set_page_dirty(node_page);
        clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
+       return set_page_dirty(node_page);
  }
  
void update_inode_page(struct inode *inode)
int update_inode_page(struct inode *inode)
  {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct page *node_page;
+       int ret = 0;
  retry:
        node_page = get_node_page(sbi, inode->i_ino);
        if (IS_ERR(node_page)) {
                } else if (err != -ENOENT) {
                        f2fs_stop_checkpoint(sbi);
                }
-               return;
+               return 0;
        }
-       update_inode(inode, node_page);
+       ret = update_inode(inode, node_page);
        f2fs_put_page(node_page, 1);
+       return ret;
  }
  
  int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
         * We need to balance fs here to prevent from producing dirty node pages
         * during the urgent cleaning time when runing out of free sections.
         */
-       update_inode_page(inode);
-       f2fs_balance_fs(sbi);
+       if (update_inode_page(inode))
+               f2fs_balance_fs(sbi, true);
        return 0;
  }
  
@@@ -328,7 -329,7 +330,7 @@@ void f2fs_evict_inode(struct inode *ino
                goto out_clear;
  
        f2fs_bug_on(sbi, get_dirty_pages(inode));
-       remove_dirty_dir_inode(inode);
+       remove_dirty_inode(inode);
  
        f2fs_destroy_extent_tree(inode);
  
@@@ -358,9 -359,9 +360,9 @@@ no_delete
        if (xnid)
                invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
        if (is_inode_flag_set(fi, FI_APPEND_WRITE))
-               add_dirty_inode(sbi, inode->i_ino, APPEND_INO);
+               add_ino_entry(sbi, inode->i_ino, APPEND_INO);
        if (is_inode_flag_set(fi, FI_UPDATE_WRITE))
-               add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
+               add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
        if (is_inode_flag_set(fi, FI_FREE_NID)) {
                if (err && err != -ENOENT)
                        alloc_nid_done(sbi, inode->i_ino);
diff --combined fs/f2fs/namei.c
index e7587fce1b8065bf80f35571d4a2c25b40b7c265,53d6227f5581f4434dc2b14b707e7234adb74bbe..6f944e5eb76eb71ab89d56e1cab7c6526ea443a5
@@@ -60,7 -60,7 +60,7 @@@ static struct inode *f2fs_new_inode(str
        if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
                f2fs_set_encrypted_inode(inode);
  
-       if (f2fs_may_inline_data(inode))
+       if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
                set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
        if (f2fs_may_inline_dentry(inode))
                set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
@@@ -128,8 -128,6 +128,6 @@@ static int f2fs_create(struct inode *di
        nid_t ino = 0;
        int err;
  
-       f2fs_balance_fs(sbi);
        inode = f2fs_new_inode(dir, mode);
        if (IS_ERR(inode))
                return PTR_ERR(inode);
        inode->i_mapping->a_ops = &f2fs_dblock_aops;
        ino = inode->i_ino;
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
        err = f2fs_add_link(dentry, inode);
        if (err)
@@@ -172,7 -172,7 +172,7 @@@ static int f2fs_link(struct dentry *old
                !f2fs_is_child_context_consistent_with_parent(dir, inode))
                return -EPERM;
  
-       f2fs_balance_fs(sbi);
+       f2fs_balance_fs(sbi, true);
  
        inode->i_ctime = CURRENT_TIME;
        ihold(inode);
@@@ -214,6 -214,15 +214,15 @@@ static int __recover_dot_dentries(struc
        struct page *page;
        int err = 0;
  
+       if (f2fs_readonly(sbi->sb)) {
+               f2fs_msg(sbi->sb, KERN_INFO,
+                       "skip recovering inline_dots inode (ino:%lu, pino:%u) "
+                       "in readonly mountpoint", dir->i_ino, pino);
+               return 0;
+       }
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
  
        de = f2fs_find_entry(dir, &dot, &page);
@@@ -288,12 -297,13 +297,13 @@@ static int f2fs_unlink(struct inode *di
        int err = -ENOENT;
  
        trace_f2fs_unlink_enter(dir, dentry);
-       f2fs_balance_fs(sbi);
  
        de = f2fs_find_entry(dir, &dentry->d_name, &page);
        if (!de)
                goto fail;
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
        err = acquire_orphan_inode(sbi);
        if (err) {
@@@ -315,15 -325,12 +325,15 @@@ fail
        return err;
  }
  
 -static const char *f2fs_follow_link(struct dentry *dentry, void **cookie)
 +static const char *f2fs_get_link(struct dentry *dentry,
 +                               struct inode *inode,
 +                               struct delayed_call *done)
  {
 -      const char *link = page_follow_link_light(dentry, cookie);
 +      const char *link = page_get_link(dentry, inode, done);
        if (!IS_ERR(link) && !*link) {
                /* this is broken symlink case */
 -              page_put_link(NULL, *cookie);
 +              do_delayed_call(done);
 +              clear_delayed_call(done);
                link = ERR_PTR(-ENOENT);
        }
        return link;
@@@ -344,8 -351,6 +354,6 @@@ static int f2fs_symlink(struct inode *d
        if (len > dir->i_sb->s_blocksize)
                return -ENAMETOOLONG;
  
-       f2fs_balance_fs(sbi);
        inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
        if (IS_ERR(inode))
                return PTR_ERR(inode);
                inode->i_op = &f2fs_encrypted_symlink_inode_operations;
        else
                inode->i_op = &f2fs_symlink_inode_operations;
 +      inode_nohighmem(inode);
        inode->i_mapping->a_ops = &f2fs_dblock_aops;
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
        err = f2fs_add_link(dentry, inode);
        if (err)
@@@ -437,8 -443,6 +447,6 @@@ static int f2fs_mkdir(struct inode *dir
        struct inode *inode;
        int err;
  
-       f2fs_balance_fs(sbi);
        inode = f2fs_new_inode(dir, S_IFDIR | mode);
        if (IS_ERR(inode))
                return PTR_ERR(inode);
        inode->i_mapping->a_ops = &f2fs_dblock_aops;
        mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
  
+       f2fs_balance_fs(sbi, true);
        set_inode_flag(F2FS_I(inode), FI_INC_LINK);
        f2fs_lock_op(sbi);
        err = f2fs_add_link(dentry, inode);
@@@ -485,8 -491,6 +495,6 @@@ static int f2fs_mknod(struct inode *dir
        struct inode *inode;
        int err = 0;
  
-       f2fs_balance_fs(sbi);
        inode = f2fs_new_inode(dir, mode);
        if (IS_ERR(inode))
                return PTR_ERR(inode);
        init_special_inode(inode, inode->i_mode, rdev);
        inode->i_op = &f2fs_special_inode_operations;
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
        err = f2fs_add_link(dentry, inode);
        if (err)
@@@ -520,9 -526,6 +530,6 @@@ static int __f2fs_tmpfile(struct inode 
        struct inode *inode;
        int err;
  
-       if (!whiteout)
-               f2fs_balance_fs(sbi);
        inode = f2fs_new_inode(dir, mode);
        if (IS_ERR(inode))
                return PTR_ERR(inode);
                inode->i_mapping->a_ops = &f2fs_dblock_aops;
        }
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
        err = acquire_orphan_inode(sbi);
        if (err)
@@@ -608,8 -613,6 +617,6 @@@ static int f2fs_rename(struct inode *ol
                goto out;
        }
  
-       f2fs_balance_fs(sbi);
        old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
        if (!old_entry)
                goto out;
                if (!new_entry)
                        goto out_whiteout;
  
+               f2fs_balance_fs(sbi, true);
                f2fs_lock_op(sbi);
  
                err = acquire_orphan_inode(sbi);
                update_inode_page(old_inode);
                update_inode_page(new_inode);
        } else {
+               f2fs_balance_fs(sbi, true);
                f2fs_lock_op(sbi);
  
                err = f2fs_add_link(new_dentry, old_inode);
@@@ -767,8 -774,6 +778,6 @@@ static int f2fs_cross_rename(struct ino
                                                                new_inode)))
                return -EPERM;
  
-       f2fs_balance_fs(sbi);
        old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
        if (!old_entry)
                goto out;
                        goto out_new_dir;
        }
  
+       f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
  
        err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
@@@ -927,22 -934,18 +938,22 @@@ static int f2fs_rename2(struct inode *o
  }
  
  #ifdef CONFIG_F2FS_FS_ENCRYPTION
 -static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie)
 +static const char *f2fs_encrypted_get_link(struct dentry *dentry,
 +                                         struct inode *inode,
 +                                         struct delayed_call *done)
  {
        struct page *cpage = NULL;
        char *caddr, *paddr = NULL;
-       struct f2fs_str cstr;
+       struct f2fs_str cstr = FSTR_INIT(NULL, 0);
        struct f2fs_str pstr = FSTR_INIT(NULL, 0);
 -      struct inode *inode = d_inode(dentry);
        struct f2fs_encrypted_symlink_data *sd;
        loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
        u32 max_size = inode->i_sb->s_blocksize;
        int res;
  
 +      if (!dentry)
 +              return ERR_PTR(-ECHILD);
 +
        res = f2fs_get_encryption_info(inode);
        if (res)
                return ERR_PTR(res);
        cpage = read_mapping_page(inode->i_mapping, 0, NULL);
        if (IS_ERR(cpage))
                return ERR_CAST(cpage);
 -      caddr = kmap(cpage);
 +      caddr = page_address(cpage);
        caddr[size] = 0;
  
        /* Symlink is encrypted */
        sd = (struct f2fs_encrypted_symlink_data *)caddr;
        cstr.len = le16_to_cpu(sd->len);
+       /* this is broken symlink case */
+       if (unlikely(cstr.len == 0)) {
+               res = -ENOENT;
+               goto errout;
+       }
        cstr.name = kmalloc(cstr.len, GFP_NOFS);
        if (!cstr.name) {
                res = -ENOMEM;
        memcpy(cstr.name, sd->encrypted_path, cstr.len);
  
        /* this is broken symlink case */
-       if (cstr.name[0] == 0 && cstr.len == 0) {
+       if (unlikely(cstr.name[0] == 0)) {
                res = -ENOENT;
                goto errout;
        }
        /* Null-terminate the name */
        paddr[res] = '\0';
  
 -      kunmap(cpage);
        page_cache_release(cpage);
 -      return *cookie = paddr;
 +      set_delayed_call(done, kfree_link, paddr);
 +      return paddr;
  errout:
        kfree(cstr.name);
        f2fs_fname_crypto_free_buffer(&pstr);
 -      kunmap(cpage);
        page_cache_release(cpage);
        return ERR_PTR(res);
  }
  
  const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
        .readlink       = generic_readlink,
 -      .follow_link    = f2fs_encrypted_follow_link,
 -      .put_link       = kfree_put_link,
 +      .get_link       = f2fs_encrypted_get_link,
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
+ #ifdef CONFIG_F2FS_FS_XATTR
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
        .listxattr      = f2fs_listxattr,
        .removexattr    = generic_removexattr,
+ #endif
  };
  #endif
  
@@@ -1037,7 -1050,8 +1056,7 @@@ const struct inode_operations f2fs_dir_
  
  const struct inode_operations f2fs_symlink_inode_operations = {
        .readlink       = generic_readlink,
 -      .follow_link    = f2fs_follow_link,
 -      .put_link       = page_put_link,
 +      .get_link       = f2fs_get_link,
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
  #ifdef CONFIG_F2FS_FS_XATTR
diff --combined fs/f2fs/xattr.c
index 036952a945faf918378dae2dc25a144330d14864,0108f487cc8e4da925f6eb88c9e6e608dd18c98a..10f1e784fa2390148aaa37f527526162be2d724d
  #include "f2fs.h"
  #include "xattr.h"
  
 -static size_t f2fs_xattr_generic_list(const struct xattr_handler *handler,
 -              struct dentry *dentry, char *list, size_t list_size,
 -              const char *name, size_t len)
 -{
 -      struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
 -      int total_len, prefix_len;
 -
 -      switch (handler->flags) {
 -      case F2FS_XATTR_INDEX_USER:
 -              if (!test_opt(sbi, XATTR_USER))
 -                      return -EOPNOTSUPP;
 -              break;
 -      case F2FS_XATTR_INDEX_TRUSTED:
 -              if (!capable(CAP_SYS_ADMIN))
 -                      return -EPERM;
 -              break;
 -      case F2FS_XATTR_INDEX_SECURITY:
 -              break;
 -      default:
 -              return -EINVAL;
 -      }
 -
 -      prefix_len = strlen(handler->prefix);
 -      total_len = prefix_len + len + 1;
 -      if (list && total_len <= list_size) {
 -              memcpy(list, handler->prefix, prefix_len);
 -              memcpy(list + prefix_len, name, len);
 -              list[prefix_len + len] = '\0';
 -      }
 -      return total_len;
 -}
 -
  static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
                struct dentry *dentry, const char *name, void *buffer,
                size_t size)
@@@ -45,6 -77,8 +45,6 @@@
        default:
                return -EINVAL;
        }
 -      if (strcmp(name, "") == 0)
 -              return -EINVAL;
        return f2fs_getxattr(d_inode(dentry), handler->flags, name,
                             buffer, size, NULL);
  }
@@@ -69,20 -103,24 +69,20 @@@ static int f2fs_xattr_generic_set(cons
        default:
                return -EINVAL;
        }
 -      if (strcmp(name, "") == 0)
 -              return -EINVAL;
 -
        return f2fs_setxattr(d_inode(dentry), handler->flags, name,
                                        value, size, NULL, flags);
  }
  
 -static size_t f2fs_xattr_advise_list(const struct xattr_handler *handler,
 -              struct dentry *dentry, char *list, size_t list_size,
 -              const char *name, size_t len)
 +static bool f2fs_xattr_user_list(struct dentry *dentry)
  {
 -      const char *xname = F2FS_SYSTEM_ADVISE_PREFIX;
 -      size_t size;
 +      struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
  
 -      size = strlen(xname) + 1;
 -      if (list && size <= list_size)
 -              memcpy(list, xname, size);
 -      return size;
 +      return test_opt(sbi, XATTR_USER);
 +}
 +
 +static bool f2fs_xattr_trusted_list(struct dentry *dentry)
 +{
 +      return capable(CAP_SYS_ADMIN);
  }
  
  static int f2fs_xattr_advise_get(const struct xattr_handler *handler,
  {
        struct inode *inode = d_inode(dentry);
  
 -      if (strcmp(name, "") != 0)
 -              return -EINVAL;
 -
        if (buffer)
                *((char *)buffer) = F2FS_I(inode)->i_advise;
        return sizeof(char);
@@@ -102,6 -143,8 +102,6 @@@ static int f2fs_xattr_advise_set(const 
  {
        struct inode *inode = d_inode(dentry);
  
 -      if (strcmp(name, "") != 0)
 -              return -EINVAL;
        if (!inode_owner_or_capable(inode))
                return -EPERM;
        if (value == NULL)
@@@ -140,7 -183,7 +140,7 @@@ int f2fs_init_security(struct inode *in
  const struct xattr_handler f2fs_xattr_user_handler = {
        .prefix = XATTR_USER_PREFIX,
        .flags  = F2FS_XATTR_INDEX_USER,
 -      .list   = f2fs_xattr_generic_list,
 +      .list   = f2fs_xattr_user_list,
        .get    = f2fs_xattr_generic_get,
        .set    = f2fs_xattr_generic_set,
  };
  const struct xattr_handler f2fs_xattr_trusted_handler = {
        .prefix = XATTR_TRUSTED_PREFIX,
        .flags  = F2FS_XATTR_INDEX_TRUSTED,
 -      .list   = f2fs_xattr_generic_list,
 +      .list   = f2fs_xattr_trusted_list,
        .get    = f2fs_xattr_generic_get,
        .set    = f2fs_xattr_generic_set,
  };
  
  const struct xattr_handler f2fs_xattr_advise_handler = {
 -      .prefix = F2FS_SYSTEM_ADVISE_PREFIX,
 +      .name   = F2FS_SYSTEM_ADVISE_NAME,
        .flags  = F2FS_XATTR_INDEX_ADVISE,
 -      .list   = f2fs_xattr_advise_list,
        .get    = f2fs_xattr_advise_get,
        .set    = f2fs_xattr_advise_set,
  };
  const struct xattr_handler f2fs_xattr_security_handler = {
        .prefix = XATTR_SECURITY_PREFIX,
        .flags  = F2FS_XATTR_INDEX_SECURITY,
 -      .list   = f2fs_xattr_generic_list,
        .get    = f2fs_xattr_generic_get,
        .set    = f2fs_xattr_generic_set,
  };
@@@ -410,27 -455,20 +410,27 @@@ ssize_t f2fs_listxattr(struct dentry *d
        list_for_each_xattr(entry, base_addr) {
                const struct xattr_handler *handler =
                        f2fs_xattr_handler(entry->e_name_index);
 +              const char *prefix;
 +              size_t prefix_len;
                size_t size;
  
 -              if (!handler)
 +              if (!handler || (handler->list && !handler->list(dentry)))
                        continue;
  
 -              size = handler->list(handler, dentry, buffer, rest,
 -                                   entry->e_name, entry->e_name_len);
 -              if (buffer && size > rest) {
 -                      error = -ERANGE;
 -                      goto cleanup;
 +              prefix = handler->prefix ?: handler->name;
 +              prefix_len = strlen(prefix);
 +              size = prefix_len + entry->e_name_len + 1;
 +              if (buffer) {
 +                      if (size > rest) {
 +                              error = -ERANGE;
 +                              goto cleanup;
 +                      }
 +                      memcpy(buffer, prefix, prefix_len);
 +                      buffer += prefix_len;
 +                      memcpy(buffer, entry->e_name, entry->e_name_len);
 +                      buffer += entry->e_name_len;
 +                      *buffer++ = 0;
                }
 -
 -              if (buffer)
 -                      buffer += size;
                rest -= size;
        }
        error = buffer_size - rest;
@@@ -571,7 -609,7 +571,7 @@@ int f2fs_setxattr(struct inode *inode, 
        if (ipage)
                return __f2fs_setxattr(inode, index, name, value,
                                                size, ipage, flags);
-       f2fs_balance_fs(sbi);
+       f2fs_balance_fs(sbi, true);
  
        f2fs_lock_op(sbi);
        /* protect xattr_ver */
        up_write(&F2FS_I(inode)->i_sem);
        f2fs_unlock_op(sbi);
  
+       f2fs_update_time(sbi, REQ_TIME);
        return err;
  }
This page took 0.128786 seconds and 4 git commands to generate.