]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
f2fs: support flexible inline xattr size
authorChao Yu <yuchao0@huawei.com>
Wed, 6 Sep 2017 13:59:50 +0000 (21:59 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 6 Nov 2017 00:41:50 +0000 (16:41 -0800)
Now, in product, more and more features based on file encryption were
introduced, their demand of xattr space is increasing, however, inline
xattr has fixed-size of 200 bytes, once inline xattr space is full, new
increased xattr data would occupy additional xattr block which may bring
us more space usage and performance regression during persisting.

In order to resolve above issue, it's better to expand inline xattr size
flexibly according to user's requirement.

So this patch introduces new filesystem feature 'flexible inline xattr',
and new mount option 'inline_xattr_size=%u', once mkfs enables the
feature, we can use the option to make f2fs supporting flexible inline
xattr size.

To support this feature, we add extra attribute i_inline_xattr_size in
inode layout, indicating that how many space inline xattr borrows from
block address mapping space in inode layout, by this, we can easily
locate and store flexible-sized inline xattr data in inode.

Inode disk layout:
  +----------------------+
  | .i_mode              |
  | ...                  |
  | .i_ext               |
  +----------------------+
  | .i_extra_isize       |
  | .i_inline_xattr_size |-----------+
  | ...                  |           |
  +----------------------+           |
  | .i_addr              |           |
  |  - block address or  |           |
  |  - inline data       |           |
  +----------------------+<---+      v
  |    inline xattr      |    +---inline xattr range
  +----------------------+<---+
  | .i_nid               |
  +----------------------+
  |   node_footer        |
  | (nid, ino, offset)   |
  +----------------------+

Note that, we have to cnosider backward compatibility which reserved
inline_data space, 200 bytes, all the time, reported by Sheng Yong.

Previous inline data or directory always reserved 200 bytes in inode layout,
even if inline_xattr is disabled. In order to keep inline_dentry's structure
for backward compatibility, we get the space back only from inline_data.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Reported-by: Sheng Yong <shengyong1@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/f2fs.h
fs/f2fs/inode.c
fs/f2fs/namei.c
fs/f2fs/node.c
fs/f2fs/super.c
fs/f2fs/sysfs.c
fs/f2fs/xattr.c
include/linux/f2fs_fs.h

index 4034459f1f5dc053cb31e7ec37ec17ed4b6dddf5..13a96b8aa2d7f95d053232003fdfca08c57fde51 100644 (file)
@@ -93,6 +93,7 @@ extern char *fault_name[FAULT_MAX];
 #define F2FS_MOUNT_GRPQUOTA            0x00100000
 #define F2FS_MOUNT_PRJQUOTA            0x00200000
 #define F2FS_MOUNT_QUOTA               0x00400000
+#define F2FS_MOUNT_INLINE_XATTR_SIZE   0x00800000
 
 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
 #define set_opt(sbi, option)   ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -118,6 +119,7 @@ struct f2fs_mount_info {
 #define F2FS_FEATURE_EXTRA_ATTR                0x0008
 #define F2FS_FEATURE_PRJQUOTA          0x0010
 #define F2FS_FEATURE_INODE_CHKSUM      0x0020
+#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR     0x0040
 
 #define F2FS_HAS_FEATURE(sb, mask)                                     \
        ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -397,11 +399,14 @@ struct f2fs_flush_device {
 
 /* for inline stuff */
 #define DEF_INLINE_RESERVED_SIZE       1
+#define DEF_MIN_INLINE_SIZE            1
 static inline int get_extra_isize(struct inode *inode);
-#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \
-                               (CUR_ADDRS_PER_INODE(inode) - \
-                               DEF_INLINE_RESERVED_SIZE - \
-                               F2FS_INLINE_XATTR_ADDRS))
+static inline int get_inline_xattr_addrs(struct inode *inode);
+#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode)
+#define MAX_INLINE_DATA(inode) (sizeof(__le32) *                       \
+                               (CUR_ADDRS_PER_INODE(inode) -           \
+                               F2FS_INLINE_XATTR_ADDRS(inode) -        \
+                               DEF_INLINE_RESERVED_SIZE))
 
 /* for inline dir */
 #define NR_INLINE_DENTRY(inode)        (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \
@@ -612,6 +617,7 @@ struct f2fs_inode_info {
 
        int i_extra_isize;              /* size of extra space located in i_addr */
        kprojid_t i_projid;             /* id for project quota */
+       int i_inline_xattr_size;        /* inline xattr size */
 };
 
 static inline void get_extent_info(struct extent_info *ext,
@@ -1069,6 +1075,7 @@ struct f2fs_sb_info {
        loff_t max_file_blocks;                 /* max block index of file */
        int active_logs;                        /* # of active logs */
        int dir_level;                          /* directory level */
+       int inline_xattr_size;                  /* inline xattr size */
 
        block_t user_block_count;               /* # of user blocks */
        block_t total_valid_block_count;        /* # of valid blocks */
@@ -2186,25 +2193,20 @@ static inline int f2fs_has_inline_xattr(struct inode *inode)
 
 static inline unsigned int addrs_per_inode(struct inode *inode)
 {
-       if (f2fs_has_inline_xattr(inode))
-               return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS;
-       return CUR_ADDRS_PER_INODE(inode);
+       return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode);
 }
 
-static inline void *inline_xattr_addr(struct page *page)
+static inline void *inline_xattr_addr(struct inode *inode, struct page *page)
 {
        struct f2fs_inode *ri = F2FS_INODE(page);
 
        return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE -
-                                       F2FS_INLINE_XATTR_ADDRS]);
+                                       F2FS_INLINE_XATTR_ADDRS(inode)]);
 }
 
 static inline int inline_xattr_size(struct inode *inode)
 {
-       if (f2fs_has_inline_xattr(inode))
-               return F2FS_INLINE_XATTR_ADDRS << 2;
-       else
-               return 0;
+       return get_inline_xattr_addrs(inode) * sizeof(__le32);
 }
 
 static inline int f2fs_has_inline_data(struct inode *inode)
@@ -2354,6 +2356,12 @@ static inline int get_extra_isize(struct inode *inode)
        return F2FS_I(inode)->i_extra_isize / sizeof(__le32);
 }
 
+static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb);
+static inline int get_inline_xattr_addrs(struct inode *inode)
+{
+       return F2FS_I(inode)->i_inline_xattr_size;
+}
+
 #define get_inode_mode(i) \
        ((is_inode_flag_set(i, FI_ACL_MODE)) ? \
         (F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
@@ -3023,6 +3031,11 @@ static inline int f2fs_sb_has_inode_chksum(struct super_block *sb)
        return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM);
 }
 
+static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb)
+{
+       return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR);
+}
+
 #ifdef CONFIG_BLK_DEV_ZONED
 static inline int get_blkz_type(struct f2fs_sb_info *sbi,
                        struct block_device *bdev, block_t blkaddr)
index 3617e7fca9301f862517c502468bc277071fa737..9684d53563f13b1654c12ae4027d9366480d23b7 100644 (file)
@@ -232,6 +232,23 @@ static int do_read_inode(struct inode *inode)
        fi->i_extra_isize = f2fs_has_extra_attr(inode) ?
                                        le16_to_cpu(ri->i_extra_isize) : 0;
 
+       if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) {
+               f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode));
+               fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size);
+       } else if (f2fs_has_inline_xattr(inode) ||
+                               f2fs_has_inline_dentry(inode)) {
+               fi->i_inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
+       } else {
+
+               /*
+                * Previous inline data or directory always reserved 200 bytes
+                * in inode layout, even if inline_xattr is disabled. In order
+                * to keep inline_dentry's structure for backward compatibility,
+                * we get the space back only from inline_data.
+                */
+               fi->i_inline_xattr_size = 0;
+       }
+
        /* check data exist */
        if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
                __recover_inline_status(inode, node_page);
@@ -384,6 +401,10 @@ int update_inode(struct inode *inode, struct page *node_page)
        if (f2fs_has_extra_attr(inode)) {
                ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize);
 
+               if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode)->sb))
+                       ri->i_inline_xattr_size =
+                               cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size);
+
                if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) &&
                        F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
                                                                i_projid)) {
index 773d327f8fc152180b5d3713fdb91e6a9fa4b58b..28bdf8828e73cdaa785ff988c94d61aead3af547 100644 (file)
@@ -29,6 +29,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
        nid_t ino;
        struct inode *inode;
        bool nid_free = false;
+       int xattr_size = 0;
        int err;
 
        inode = new_inode(dir->i_sb);
@@ -86,11 +87,23 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
 
        if (test_opt(sbi, INLINE_XATTR))
                set_inode_flag(inode, FI_INLINE_XATTR);
+
        if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
                set_inode_flag(inode, FI_INLINE_DATA);
        if (f2fs_may_inline_dentry(inode))
                set_inode_flag(inode, FI_INLINE_DENTRY);
 
+       if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) {
+               f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode));
+               if (f2fs_has_inline_xattr(inode))
+                       xattr_size = sbi->inline_xattr_size;
+               /* Otherwise, will be 0 */
+       } else if (f2fs_has_inline_xattr(inode) ||
+                               f2fs_has_inline_dentry(inode)) {
+               xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
+       }
+       F2FS_I(inode)->i_inline_xattr_size = xattr_size;
+
        f2fs_init_extent_tree(inode, NULL);
 
        stat_inc_inline_xattr(inode);
index bdb7e92636b41405310437459dad3cf5c77274de..f44d837052459f5d1992be0178411cd4a5e0c51d 100644 (file)
@@ -2191,8 +2191,8 @@ void recover_inline_xattr(struct inode *inode, struct page *page)
                goto update_inode;
        }
 
-       dst_addr = inline_xattr_addr(ipage);
-       src_addr = inline_xattr_addr(page);
+       dst_addr = inline_xattr_addr(inode, ipage);
+       src_addr = inline_xattr_addr(inode, page);
        inline_size = inline_xattr_size(inode);
 
        f2fs_wait_on_page_writeback(ipage, NODE, true);
@@ -2281,6 +2281,12 @@ retry:
        dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR);
        if (dst->i_inline & F2FS_EXTRA_ATTR) {
                dst->i_extra_isize = src->i_extra_isize;
+
+               if (f2fs_sb_has_flexible_inline_xattr(sbi->sb) &&
+                       F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize),
+                                                       i_inline_xattr_size))
+                       dst->i_inline_xattr_size = src->i_inline_xattr_size;
+
                if (f2fs_sb_has_project_quota(sbi->sb) &&
                        F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize),
                                                                i_projid))
index 97fa81503e1feecc858254bb009d6154b2dc9fad..213d2c1e57592d09b9fdb49faf7007781214523e 100644 (file)
@@ -92,6 +92,7 @@ enum {
        Opt_disable_ext_identify,
        Opt_inline_xattr,
        Opt_noinline_xattr,
+       Opt_inline_xattr_size,
        Opt_inline_data,
        Opt_inline_dentry,
        Opt_noinline_dentry,
@@ -141,6 +142,7 @@ static match_table_t f2fs_tokens = {
        {Opt_disable_ext_identify, "disable_ext_identify"},
        {Opt_inline_xattr, "inline_xattr"},
        {Opt_noinline_xattr, "noinline_xattr"},
+       {Opt_inline_xattr_size, "inline_xattr_size=%u"},
        {Opt_inline_data, "inline_data"},
        {Opt_inline_dentry, "inline_dentry"},
        {Opt_noinline_dentry, "noinline_dentry"},
@@ -383,6 +385,12 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_noinline_xattr:
                        clear_opt(sbi, INLINE_XATTR);
                        break;
+               case Opt_inline_xattr_size:
+                       if (args->from && match_int(args, &arg))
+                               return -EINVAL;
+                       set_opt(sbi, INLINE_XATTR_SIZE);
+                       sbi->inline_xattr_size = arg;
+                       break;
 #else
                case Opt_user_xattr:
                        f2fs_msg(sb, KERN_INFO,
@@ -604,6 +612,24 @@ static int parse_options(struct super_block *sb, char *options)
                                F2FS_IO_SIZE_KB(sbi));
                return -EINVAL;
        }
+
+       if (test_opt(sbi, INLINE_XATTR_SIZE)) {
+               if (!test_opt(sbi, INLINE_XATTR)) {
+                       f2fs_msg(sb, KERN_ERR,
+                                       "inline_xattr_size option should be "
+                                       "set with inline_xattr option");
+                       return -EINVAL;
+               }
+               if (!sbi->inline_xattr_size ||
+                       sbi->inline_xattr_size >= DEF_ADDRS_PER_INODE -
+                                       F2FS_TOTAL_EXTRA_ATTR_SIZE -
+                                       DEF_INLINE_RESERVED_SIZE -
+                                       DEF_MIN_INLINE_SIZE) {
+                       f2fs_msg(sb, KERN_ERR,
+                                       "inline xattr size is out of range");
+                       return -EINVAL;
+               }
+       }
        return 0;
 }
 
@@ -1050,6 +1076,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",inline_xattr");
        else
                seq_puts(seq, ",noinline_xattr");
+       if (test_opt(sbi, INLINE_XATTR_SIZE))
+               seq_printf(seq, ",inline_xattr_size=%u",
+                                       sbi->inline_xattr_size);
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
        if (test_opt(sbi, POSIX_ACL))
@@ -1112,6 +1141,7 @@ static void default_options(struct f2fs_sb_info *sbi)
 {
        /* init some FS parameters */
        sbi->active_logs = NR_CURSEG_TYPE;
+       sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
 
        set_opt(sbi, BG_GC);
        set_opt(sbi, INLINE_XATTR);
@@ -1670,7 +1700,7 @@ static loff_t max_file_blocks(void)
 
        /*
         * note: previously, result is equal to (DEF_ADDRS_PER_INODE -
-        * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more
+        * DEFAULT_INLINE_XATTR_ADDRS), but now f2fs try to reserve more
         * space in inode.i_addr, it will be more safe to reassign
         * result as zero.
         */
index 48ebe6153cc51bedc400023751b560285978c6ed..e09e59cc678a1da0fc7fd52bcebdca16e8ea99d2 100644 (file)
@@ -107,6 +107,9 @@ static ssize_t features_show(struct f2fs_attr *a,
        if (f2fs_sb_has_inode_chksum(sb))
                len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
                                len ? ", " : "", "inode_checksum");
+       if (f2fs_sb_has_flexible_inline_xattr(sb))
+               len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
+                               len ? ", " : "", "flexible_inline_xattr");
        len += snprintf(buf + len, PAGE_SIZE - len, "\n");
        return len;
 }
@@ -216,6 +219,7 @@ enum feat_id {
        FEAT_EXTRA_ATTR,
        FEAT_PROJECT_QUOTA,
        FEAT_INODE_CHECKSUM,
+       FEAT_FLEXIBLE_INLINE_XATTR,
 };
 
 static ssize_t f2fs_feature_show(struct f2fs_attr *a,
@@ -228,6 +232,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
        case FEAT_EXTRA_ATTR:
        case FEAT_PROJECT_QUOTA:
        case FEAT_INODE_CHECKSUM:
+       case FEAT_FLEXIBLE_INLINE_XATTR:
                return snprintf(buf, PAGE_SIZE, "supported\n");
        }
        return 0;
@@ -299,6 +304,7 @@ F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE);
 F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR);
 F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA);
 F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM);
+F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR);
 
 #define ATTR_LIST(name) (&f2fs_attr_##name.attr)
 static struct attribute *f2fs_attrs[] = {
@@ -346,6 +352,7 @@ static struct attribute *f2fs_feat_attrs[] = {
        ATTR_LIST(extra_attr),
        ATTR_LIST(project_quota),
        ATTR_LIST(inode_checksum),
+       ATTR_LIST(flexible_inline_xattr),
        NULL,
 };
 
index 9fbcb7687d3bb0e0d51e8a2a592e2958f97933f2..52b46f598470795434fdab111aa9ed24f83c4045 100644 (file)
@@ -217,12 +217,12 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
        return entry;
 }
 
-static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr,
-                                       void **last_addr, int index,
-                                       size_t len, const char *name)
+static struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode,
+                               void *base_addr, void **last_addr, int index,
+                               size_t len, const char *name)
 {
        struct f2fs_xattr_entry *entry;
-       unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2;
+       unsigned int inline_size = inline_xattr_size(inode);
 
        list_for_each_xattr(entry, base_addr) {
                if ((void *)entry + sizeof(__u32) > base_addr + inline_size ||
@@ -250,13 +250,13 @@ static int read_inline_xattr(struct inode *inode, struct page *ipage,
        void *inline_addr;
 
        if (ipage) {
-               inline_addr = inline_xattr_addr(ipage);
+               inline_addr = inline_xattr_addr(inode, ipage);
        } else {
                page = get_node_page(sbi, inode->i_ino);
                if (IS_ERR(page))
                        return PTR_ERR(page);
 
-               inline_addr = inline_xattr_addr(page);
+               inline_addr = inline_xattr_addr(inode, page);
        }
        memcpy(txattr_addr, inline_addr, inline_size);
        f2fs_put_page(page, 1);
@@ -309,7 +309,7 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
                if (err)
                        goto out;
 
-               *xe = __find_inline_xattr(txattr_addr, &last_addr,
+               *xe = __find_inline_xattr(inode, txattr_addr, &last_addr,
                                                index, len, name);
                if (*xe)
                        goto check;
@@ -404,7 +404,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
                void *inline_addr;
 
                if (ipage) {
-                       inline_addr = inline_xattr_addr(ipage);
+                       inline_addr = inline_xattr_addr(inode, ipage);
                        f2fs_wait_on_page_writeback(ipage, NODE, true);
                        set_page_dirty(ipage);
                } else {
@@ -413,7 +413,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
                                alloc_nid_failed(sbi, new_nid);
                                return PTR_ERR(page);
                        }
-                       inline_addr = inline_xattr_addr(page);
+                       inline_addr = inline_xattr_addr(inode, page);
                        f2fs_wait_on_page_writeback(page, NODE, true);
                }
                memcpy(inline_addr, txattr_addr, inline_size);
index 2a0c453d72354ef6394cf06d2831326ff6bf7aaf..50a8ee501bf134c5ca66918b3c249b6a6198f4b9 100644 (file)
@@ -184,7 +184,8 @@ struct f2fs_extent {
 } __packed;
 
 #define F2FS_NAME_LEN          255
-#define F2FS_INLINE_XATTR_ADDRS        50      /* 200 bytes for inline xattrs */
+/* 200 bytes for inline xattrs by default */
+#define DEFAULT_INLINE_XATTR_ADDRS     50
 #define DEF_ADDRS_PER_INODE    923     /* Address Pointers in an Inode */
 #define CUR_ADDRS_PER_INODE(inode)     (DEF_ADDRS_PER_INODE - \
                                        get_extra_isize(inode))
@@ -238,7 +239,7 @@ struct f2fs_inode {
        union {
                struct {
                        __le16 i_extra_isize;   /* extra inode attribute size */
-                       __le16 i_padding;       /* padding */
+                       __le16 i_inline_xattr_size;     /* inline xattr size, unit: 4 bytes */
                        __le32 i_projid;        /* project id */
                        __le32 i_inode_checksum;/* inode meta checksum */
                        __le32 i_extra_end[0];  /* for attribute size calculation */