Whamcloud - gitweb
LU-16350 ldiskfs: Server support for LTS linux v6.1 60/52260/13
authorShaun Tancheff <shaun.tancheff@hpe.com>
Thu, 25 Apr 2024 15:38:20 +0000 (22:38 +0700)
committerOleg Drokin <green@whamcloud.com>
Wed, 29 May 2024 04:44:36 +0000 (04:44 +0000)
Keep LTS kernel support and very recent kernel
ldiskfs series. Squash older series and drop
any unused patches.

Dropping 5.8 and 5.9 non LTS kernel series
Adding patches with kernel version that originated
the change
   linux-5.18/ext4-lookup-dotdot.patch
   linux-6.0/ext4-data-in-dirent.patch
   linux-6.0/ext4-pdirop.patch
   linux-6.1/ext4-dont-check-before-replay.patch
   linux-6.1/ext4-mballoc-extra-checks.patch
   linux-6.1/ext4-prealloc.patch
refresh linux-5.16/ext4-misc.patch to use strscpy instead of strlcpy

Test-Parameters: trivial
HPE-bug-id: LUS-11376
Signed-off-by: Shaun Tancheff <shaun.tancheff@hpe.com>
Change-Id: Id747e200f5d3f50475094ee5ad948c389cce3184
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/52260
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Jian Yu <yujian@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
12 files changed:
config/lustre-build-ldiskfs.m4
ldiskfs/kernel_patches/patches/linux-5.16/ext4-misc.patch
ldiskfs/kernel_patches/patches/linux-5.18/ext4-lookup-dotdot.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/linux-5.8/ext4-simple-blockalloc.patch [deleted file]
ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/linux-6.0/ext4-pdirop.patch [moved from ldiskfs/kernel_patches/patches/linux-5.8/ext4-pdirop.patch with 79% similarity]
ldiskfs/kernel_patches/patches/linux-6.1/ext4-dont-check-before-replay.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/linux-6.1/ext4-mballoc-extra-checks.patch [new file with mode: 0644]
ldiskfs/kernel_patches/patches/linux-6.1/ext4-prealloc.patch [new file with mode: 0644]
ldiskfs/kernel_patches/series/ldiskfs-5.8.0-ml.series [deleted file]
ldiskfs/kernel_patches/series/ldiskfs-5.9.0-ml.series [deleted file]
ldiskfs/kernel_patches/series/ldiskfs-6.1.38-ml.series [new file with mode: 0644]

index 731d00b..abbc4c1 100644 (file)
@@ -161,26 +161,23 @@ AS_IF([test x$RHEL_KERNEL = xyes], [
 # Not RHEL/SLES/openEuler or Ubuntu .. probably mainline
 AS_IF([test -z "$LDISKFS_SERIES"],
        [
-       AS_VERSION_COMPARE([$LINUXRELEASE],[5.4.0],[],
-       [LDISKFS_SERIES="5.4.0-ml.series"],[
-       AS_VERSION_COMPARE([$LINUXRELEASE],[5.4.21],
-         [LDISKFS_SERIES="5.4.0-ml.series"],  # lt
-         [LDISKFS_SERIES="5.4.21-ml.series"], # eq
-         [AS_VERSION_COMPARE([$LINUXRELEASE],[5.8.0],
-           [LDISKFS_SERIES="5.4.136-ml.series"], # lt
-           [LDISKFS_SERIES="5.8.0-ml.series"],  # eq
-           [AS_VERSION_COMPARE([$LINUXRELEASE],[5.9.0],
-             [LDISKFS_SERIES="5.8.0-ml.series"],  # lt
-             [LDISKFS_SERIES="5.9.0-ml.series"],  # eq
-             [AS_VERSION_COMPARE([$LINUXRELEASE],[5.10.0],
-               [LDISKFS_SERIES="5.9.0-ml.series"],  # lt
-               [LDISKFS_SERIES="5.10.0-ml.series"],  # eq
-               [LDISKFS_SERIES="5.10.0-ml.series"],  # gt
-             )]
-           )]
-         )]
-               )])
-       ],
+       AS_VERSION_COMPARE([$LINUXRELEASE],[5.4.0],[
+               ], [
+               LDISKFS_SERIES="5.4.0-ml.series"],[
+       AS_VERSION_COMPARE([$LINUXRELEASE],[5.4.21],[
+               LDISKFS_SERIES="5.4.0-ml.series"], [
+               LDISKFS_SERIES="5.4.21-ml.series"],[
+       AS_VERSION_COMPARE([$LINUXRELEASE],[5.10.0], [
+               LDISKFS_SERIES="5.4.136-ml.series"], [
+               LDISKFS_SERIES="5.10.0-ml.series"], [
+       AS_VERSION_COMPARE([$LINUXRELEASE],[6.1.0], [
+               LDISKFS_SERIES="5.10.0-ml.series"], [
+               LDISKFS_SERIES="6.1.38-ml.series"], [
+               LDISKFS_SERIES="6.1.38-ml.series"]
+       )] # 6.1 LTS
+       )] # 5.10 LTS
+       )] # 5.4 LTS
+       )],
 [])
 AS_IF([test -z "$LDISKFS_SERIES"],
        [AC_MSG_RESULT([failed to identify series])],
index 55dddf8..11fd8c7 100644 (file)
@@ -1,8 +1,22 @@
+From e396a9a0d0c4ec3b84eeba4933c7624b14dee898 Mon Sep 17 00:00:00 2001
+From: Shaun Tancheff <shaun.tancheff@hpe.com>
+Date: Thu, 25 Apr 2024 22:34:59 +0700
+Subject: [PATCH] linux-5.16/ext4-misc.patch
+
+---
+ fs/ext4/ext4.h   | 23 ++++++++++++++++++++++-
+ fs/ext4/ialloc.c |  3 ++-
+ fs/ext4/inode.c  | 17 +++++++++++++++++
+ fs/ext4/namei.c  |  9 ++++++---
+ fs/ext4/super.c  | 10 ++--------
+ fs/ext4/xattr.c  |  2 ++
+ 6 files changed, 51 insertions(+), 13 deletions(-)
+
 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
-index d7332df..d04d87c 100644
+index d200eb93..0b3522cf 100644
 --- a/fs/ext4/ext4.h
 +++ b/fs/ext4/ext4.h
-@@ -2202,7 +2204,21 @@ static inline bool ext4_has_unknown_ext##ver##_incompat_features(struct super_bl
+@@ -2212,7 +2212,21 @@ static inline bool ext4_has_unknown_ext##ver##_incompat_features(struct super_bl
  
  EXTN_FEATURE_FUNCS(2)
  EXTN_FEATURE_FUNCS(3)
@@ -25,7 +39,7 @@ index d7332df..d04d87c 100644
  
  static inline bool ext4_has_compat_features(struct super_block *sb)
  {
-@@ -3724,6 +3740,13 @@ struct ext4_extent;
+@@ -3692,6 +3706,13 @@ struct ext4_extent;
  #define EXT_MAX_BLOCKS        0xffffffff
  
  extern void ext4_ext_tree_init(handle_t *handle, struct inode *inode);
@@ -40,7 +54,7 @@ index d7332df..d04d87c 100644
  extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
                               struct ext4_map_blocks *map, int flags);
 diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
-index 5d0a11d..4840190 100644
+index f75aa85b..a1ba2767 100644
 --- a/fs/ext4/ialloc.c
 +++ b/fs/ext4/ialloc.c
 @@ -120,7 +120,7 @@ verified:
@@ -61,10 +75,10 @@ index 5d0a11d..4840190 100644
  /*
   * NOTE! When we get the inode, we're the only people
 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
-index f0206e2..88e82d6 100644
+index 98f0dc0e..a59c0c8a 100644
 --- a/fs/ext4/inode.c
 +++ b/fs/ext4/inode.c
-@@ -6145,3 +6145,20 @@ out_error:
+@@ -6313,3 +6313,20 @@ out_error:
        ext4_journal_stop(handle);
        goto out;
  }
@@ -86,7 +100,7 @@ index f0206e2..88e82d6 100644
 +EXPORT_SYMBOL(__ext4_journal_ensure_credits);
 +EXPORT_SYMBOL(ext4_chunk_trans_blocks);
 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
-index 249836e..627b9eb 100644
+index 994851ed..3c6931e3 100644
 --- a/fs/ext4/namei.c
 +++ b/fs/ext4/namei.c
 @@ -50,7 +50,7 @@
@@ -98,7 +112,7 @@ index 249836e..627b9eb 100644
                                        struct inode *inode,
                                        ext4_lblk_t *block)
  {
-@@ -182,6 +182,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
+@@ -210,6 +210,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
        }
        return bh;
  }
@@ -106,7 +120,7 @@ index 249836e..627b9eb 100644
  
  #ifdef DX_DEBUG
  #define dxtrace(command) command
-@@ -2703,23 +2704,25 @@ out:
+@@ -2786,23 +2787,25 @@ EXPORT_SYMBOL(ext4_delete_entry);
   * for checking S_ISDIR(inode) (since the INODE_INDEX feature will not be set
   * on regular files) and to avoid creating huge/slow non-HTREE directories.
   */
@@ -135,28 +149,28 @@ index 249836e..627b9eb 100644
  
  /*
 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
-index 24a7ad8..b8a9fce 100644
+index 760249d9..55d614b8 100644
 --- a/fs/ext4/super.c
 +++ b/fs/ext4/super.c
-@@ -5429,7 +5429,7 @@ static void ext4_update_super(struct super_block *sb)
+@@ -6024,7 +6024,7 @@ static void ext4_update_super(struct super_block *sb)
                        __ext4_update_tstamp(&es->s_first_error_time,
                                             &es->s_first_error_time_hi,
                                             sbi->s_first_error_time);
 -                      strncpy(es->s_first_error_func, sbi->s_first_error_func,
-+                      strlcpy(es->s_first_error_func, sbi->s_first_error_func,
++                      strscpy(es->s_first_error_func, sbi->s_first_error_func,
                                sizeof(es->s_first_error_func));
                        es->s_first_error_line =
                                cpu_to_le32(sbi->s_first_error_line);
-@@ -5443,7 +5443,7 @@ static void ext4_update_super(struct super_block *sb)
+@@ -6038,7 +6038,7 @@ static void ext4_update_super(struct super_block *sb)
                __ext4_update_tstamp(&es->s_last_error_time,
                                     &es->s_last_error_time_hi,
                                     sbi->s_last_error_time);
 -              strncpy(es->s_last_error_func, sbi->s_last_error_func,
-+              strlcpy(es->s_last_error_func, sbi->s_last_error_func,
++              strscpy(es->s_last_error_func, sbi->s_last_error_func,
                        sizeof(es->s_last_error_func));
                es->s_last_error_line = cpu_to_le32(sbi->s_last_error_line);
                es->s_last_error_ino = cpu_to_le32(sbi->s_last_error_ino);
-@@ -6652,16 +6652,12 @@ static int __init ext4_init_fs(void)
+@@ -7279,16 +7279,12 @@ static int __init ext4_init_fs(void)
        if (err)
                goto out05;
  
@@ -173,7 +187,7 @@ index 24a7ad8..b8a9fce 100644
        ext4_fc_destroy_dentry_cache();
  out05:
        destroy_inodecache();
-@@ -6686,8 +6682,6 @@ out7:
+@@ -7313,8 +7309,6 @@ out7:
  static void __exit ext4_exit_fs(void)
  {
        ext4_destroy_lazyinit_thread();
@@ -183,10 +197,10 @@ index 24a7ad8..b8a9fce 100644
        ext4_fc_destroy_dentry_cache();
        destroy_inodecache();
 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
-index 1e0fc1e..379c59c 100644
+index 51d642a9..33657020 100644
 --- a/fs/ext4/xattr.c
 +++ b/fs/ext4/xattr.c
-@@ -656,6 +656,7 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name,
+@@ -666,6 +666,7 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name,
        up_read(&EXT4_I(inode)->xattr_sem);
        return error;
  }
@@ -194,7 +208,7 @@ index 1e0fc1e..379c59c 100644
  
  static int
  ext4_xattr_list_entries(struct dentry *dentry, struct ext4_xattr_entry *entry,
-@@ -2416,6 +2417,7 @@ cleanup:
+@@ -2430,6 +2431,7 @@ cleanup:
        ext4_write_unlock_xattr(inode, &no_expand);
        return error;
  }
@@ -203,5 +217,5 @@ index 1e0fc1e..379c59c 100644
  int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
                           bool is_create, int *credits)
 -- 
-2.27.0
+2.34.1
 
diff --git a/ldiskfs/kernel_patches/patches/linux-5.18/ext4-lookup-dotdot.patch b/ldiskfs/kernel_patches/patches/linux-5.18/ext4-lookup-dotdot.patch
new file mode 100644 (file)
index 0000000..7821024
--- /dev/null
@@ -0,0 +1,61 @@
+commit 113303973ec9f8484eb2355a1a6ef3c4c7fd6a56
+Author:     Alex Zhuravlev <alex@clusterfs.com>
+AuthorDate: Sat Feb 10 06:33:41 2007 +0000
+Subject: ext4: htree fix for '..' lookup
+Avoid looping in directory lookup when ext3_dx_find_entry()
+can't find the '..' entry in a directory and then looks it
+up in the directory.  This results in the ".." (parent
+directory) entry being added as the child of a directory
+in the dcache.  The '..' lookup can happen knfsd is looking
+up the path of a disconnected dentry.
+Bugzilla-ID: b=10458
+Signed-off-by: Alex Zhuravlev <alex@clusterfs.com>
+Reviewed-by: Kalpak Shah <kalpak@clusterfs.com>
+Signed-off-by: Andreas Dilger <adilger@clusterfs.com>
+---
+ fs/ext4/namei.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
+index 4f0420b..a519200 100644
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -1822,8 +1822,34 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
+                       return ERR_PTR(-EPERM);
+               }
+       }
++      /* ".." shouldn't go into dcache to preserve dcache hierarchy
++       * otherwise we'll get parent being a child of actual child.
++       * see bug 10458 for details -bzzz */
++      if (inode && (dentry->d_name.name[0] == '.' &&
++                    (dentry->d_name.len == 1 || (dentry->d_name.len == 2 &&
++                                           dentry->d_name.name[1] == '.')))) {
++              struct dentry *goal = NULL;
++
++              /* first, look for an existing dentry - any one is good */
++              goal = d_find_any_alias(inode);
++              if (goal == NULL) {
++                      spin_lock(&dentry->d_lock);
++                      /* there is no alias, we need to make current dentry:
++                       *  a) inaccessible for __d_lookup()
++                       *  b) inaccessible for iopen */
++                      J_ASSERT(hlist_unhashed(&dentry->d_u.d_alias));
++                      dentry->d_flags |= DCACHE_NFSFS_RENAMED;
++                      /* this is d_instantiate() ... */
++                      hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
++                      dentry->d_inode = inode;
++                      spin_unlock(&dentry->d_lock);
++              }
++              if (goal)
++                      iput(inode);
++              return goal;
++      }
+-#if IS_ENABLED(CONFIG_UNICODE)
++#ifdef CONFIG_UNICODE
+       if (!inode && IS_CASEFOLDED(dir)) {
+               /* Eventually we want to call d_add_ci(dentry, NULL)
+                * for negative dentries in the encoding case as
+-- 
+2.34.1
+
diff --git a/ldiskfs/kernel_patches/patches/linux-5.8/ext4-simple-blockalloc.patch b/ldiskfs/kernel_patches/patches/linux-5.8/ext4-simple-blockalloc.patch
deleted file mode 100644 (file)
index 0e06c78..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
----
- fs/ext4/ext4.h    |    7 ++
- fs/ext4/mballoc.c |  133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- fs/ext4/mballoc.h |    3 +
- fs/ext4/sysfs.c   |   52 +++++++++++++++++++++
- 4 files changed, 195 insertions(+)
-
---- a/fs/ext4/ext4.h
-+++ b/fs/ext4/ext4.h
-@@ -1518,6 +1518,9 @@ struct ext4_sb_info {
-       unsigned int s_mb_min_to_scan;
-       unsigned int s_mb_stats;
-       unsigned int s_mb_order2_reqs;
-+      ext4_fsblk_t s_mb_c1_blocks;
-+      ext4_fsblk_t s_mb_c2_blocks;
-+      ext4_fsblk_t s_mb_c3_blocks;
-       unsigned long *s_mb_prealloc_table;
-       unsigned int s_mb_group_prealloc;
-       unsigned int s_max_dir_size_kb;
-@@ -1534,6 +1537,9 @@ struct ext4_sb_info {
-       atomic_t s_bal_goals;   /* goal hits */
-       atomic_t s_bal_breaks;  /* too long searches */
-       atomic_t s_bal_2orders; /* 2^order hits */
-+      /* cX loop didn't find blocks */
-+      atomic64_t s_bal_cX_failed[4];
-+      atomic64_t s_bal_cX_skipped[3];
-       spinlock_t s_bal_lock;
-       unsigned long s_mb_buddies_generated;
-       unsigned long long s_mb_generation_time;
-@@ -2813,6 +2819,7 @@ ext4_read_inode_bitmap(struct super_bloc
- /* mballoc.c */
- extern const struct proc_ops ext4_seq_prealloc_table_fops;
- extern const struct seq_operations ext4_mb_seq_groups_ops;
-+extern const struct proc_ops ext4_mb_seq_alloc_fops;
- extern const struct proc_ops ext4_seq_mb_last_group_fops;
- extern int ext4_mb_seq_last_start_seq_show(struct seq_file *m, void *v);
- extern long ext4_mb_stats;
---- a/fs/ext4/mballoc.c
-+++ b/fs/ext4/mballoc.c
-@@ -2218,6 +2218,20 @@ out:
-       return ret;
- }
-+static u64 available_blocks_count(struct ext4_sb_info *sbi)
-+{
-+      ext4_fsblk_t resv_blocks;
-+      u64 bfree;
-+      struct ext4_super_block *es = sbi->s_es;
-+
-+      resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
-+      bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) -
-+               percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter);
-+
-+      bfree = EXT4_C2B(sbi, max_t(s64, bfree, 0));
-+      return bfree - (ext4_r_blocks_count(es) + resv_blocks);
-+}
-+
- static noinline_for_stack int
- ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
- {
-@@ -2227,6 +2241,7 @@ ext4_mb_regular_allocator(struct ext4_al
-       struct ext4_sb_info *sbi;
-       struct super_block *sb;
-       struct ext4_buddy e4b;
-+      ext4_fsblk_t avail_blocks;
-       sb = ac->ac_sb;
-       sbi = EXT4_SB(sb);
-@@ -2279,6 +2294,21 @@ ext4_mb_regular_allocator(struct ext4_al
-       /* Let's just scan groups to find more-less suitable blocks */
-       cr = ac->ac_2order ? 0 : 1;
-+
-+      /* Choose what loop to pass based on disk fullness */
-+      avail_blocks = available_blocks_count(sbi) ;
-+
-+      if (avail_blocks < sbi->s_mb_c3_blocks) {
-+              cr = 3;
-+              atomic64_inc(&sbi->s_bal_cX_skipped[2]);
-+      } else if(avail_blocks < sbi->s_mb_c2_blocks) {
-+              cr = 2;
-+              atomic64_inc(&sbi->s_bal_cX_skipped[1]);
-+      } else if(avail_blocks < sbi->s_mb_c1_blocks) {
-+              cr = 1;
-+              atomic64_inc(&sbi->s_bal_cX_skipped[0]);
-+      }
-+
-       /*
-        * cr == 0 try to get exact allocation,
-        * cr == 3  try to get anything
-@@ -2342,6 +2372,9 @@ repeat:
-                       if (ac->ac_status != AC_STATUS_CONTINUE)
-                               break;
-               }
-+              /* Processed all groups and haven't found blocks */
-+              if (i == ngroups)
-+                      atomic64_inc(&sbi->s_bal_cX_failed[cr]);
-       }
-       if (ac->ac_b_ex.fe_len > 0 && ac->ac_status != AC_STATUS_FOUND &&
-@@ -2624,6 +2657,95 @@ const struct proc_ops ext4_seq_mb_last_g
-       .proc_write     = ext4_mb_last_group_write,
- };
-+static int mb_seq_alloc_show(struct seq_file *seq, void *v)
-+{
-+      struct super_block *sb = seq->private;
-+      struct ext4_sb_info *sbi = EXT4_SB(sb);
-+
-+      seq_printf(seq, "mballoc:\n");
-+      seq_printf(seq, "\tblocks: %u\n", atomic_read(&sbi->s_bal_allocated));
-+      seq_printf(seq, "\treqs: %u\n", atomic_read(&sbi->s_bal_reqs));
-+      seq_printf(seq, "\tsuccess: %u\n", atomic_read(&sbi->s_bal_success));
-+
-+      seq_printf(seq, "\textents_scanned: %u\n",
-+                 atomic_read(&sbi->s_bal_ex_scanned));
-+      seq_printf(seq, "\t\tgoal_hits: %u\n", atomic_read(&sbi->s_bal_goals));
-+      seq_printf(seq, "\t\t2^n_hits: %u\n", atomic_read(&sbi->s_bal_2orders));
-+      seq_printf(seq, "\t\tbreaks: %u\n", atomic_read(&sbi->s_bal_breaks));
-+      seq_printf(seq, "\t\tlost: %u\n", atomic_read(&sbi->s_mb_lost_chunks));
-+
-+      seq_printf(seq, "\tuseless_c0_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_failed[0]));
-+      seq_printf(seq, "\tuseless_c1_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_failed[1]));
-+      seq_printf(seq, "\tuseless_c2_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_failed[2]));
-+      seq_printf(seq, "\tuseless_c3_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_failed[3]));
-+      seq_printf(seq, "\tskipped_c0_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_skipped[0]));
-+      seq_printf(seq, "\tskipped_c1_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_skipped[1]));
-+      seq_printf(seq, "\tskipped_c2_loops: %llu\n",
-+                 atomic64_read(&sbi->s_bal_cX_skipped[2]));
-+      seq_printf(seq, "\tbuddies_generated: %lu\n",
-+                 sbi->s_mb_buddies_generated);
-+      seq_printf(seq, "\tbuddies_time_used: %llu\n", sbi->s_mb_generation_time);
-+      seq_printf(seq, "\tpreallocated: %u\n",
-+                 atomic_read(&sbi->s_mb_preallocated));
-+      seq_printf(seq, "\tdiscarded: %u\n",
-+                 atomic_read(&sbi->s_mb_discarded));
-+      return 0;
-+}
-+
-+static ssize_t mb_seq_alloc_write(struct file *file,
-+                            const char __user *buf,
-+                            size_t cnt, loff_t *pos)
-+{
-+      struct ext4_sb_info *sbi = EXT4_SB(PDE_DATA(file_inode(file)));
-+
-+      atomic_set(&sbi->s_bal_allocated, 0),
-+      atomic_set(&sbi->s_bal_reqs, 0),
-+      atomic_set(&sbi->s_bal_success, 0);
-+
-+      atomic_set(&sbi->s_bal_ex_scanned, 0),
-+      atomic_set(&sbi->s_bal_goals, 0),
-+      atomic_set(&sbi->s_bal_2orders, 0),
-+      atomic_set(&sbi->s_bal_breaks, 0),
-+      atomic_set(&sbi->s_mb_lost_chunks, 0);
-+
-+      atomic64_set(&sbi->s_bal_cX_failed[0], 0),
-+      atomic64_set(&sbi->s_bal_cX_failed[1], 0),
-+      atomic64_set(&sbi->s_bal_cX_failed[2], 0);
-+      atomic64_set(&sbi->s_bal_cX_failed[3], 0);
-+
-+      atomic64_set(&sbi->s_bal_cX_skipped[0], 0),
-+      atomic64_set(&sbi->s_bal_cX_skipped[1], 0),
-+      atomic64_set(&sbi->s_bal_cX_skipped[2], 0);
-+
-+
-+      sbi->s_mb_buddies_generated = 0;
-+      sbi->s_mb_generation_time = 0;
-+
-+      atomic_set(&sbi->s_mb_preallocated, 0),
-+      atomic_set(&sbi->s_mb_discarded, 0);
-+
-+      return cnt;
-+}
-+
-+static int mb_seq_alloc_open(struct inode *inode, struct file *file)
-+{
-+      return single_open(file, mb_seq_alloc_show, PDE_DATA(inode));
-+}
-+
-+const struct proc_ops ext4_mb_seq_alloc_fops = {
-+      .proc_open      = mb_seq_alloc_open,
-+      .proc_read      = seq_read,
-+      .proc_lseek     = seq_lseek,
-+      .proc_release   = single_release,
-+      .proc_write     = mb_seq_alloc_write,
-+};
-+
- int ext4_mb_seq_last_start_seq_show(struct seq_file *m, void *v)
- {
-       struct ext4_sb_info *sbi = EXT4_SB(m->private);
-@@ -2850,6 +2969,7 @@ static int ext4_groupinfo_create_slab(si
-       return 0;
- }
-+#define THRESHOLD_BLOCKS(ts) (ext4_blocks_count(sbi->s_es) / 100 * ts)
- int ext4_mb_init(struct super_block *sb)
- {
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
-@@ -2903,6 +3023,9 @@ int ext4_mb_init(struct super_block *sb)
-       sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN;
-       sbi->s_mb_stats = MB_DEFAULT_STATS;
-       sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS;
-+      sbi->s_mb_c1_blocks = THRESHOLD_BLOCKS(MB_DEFAULT_C1_THRESHOLD);
-+      sbi->s_mb_c2_blocks = THRESHOLD_BLOCKS(MB_DEFAULT_C2_THRESHOLD);
-+      sbi->s_mb_c3_blocks = THRESHOLD_BLOCKS(MB_DEFAULT_C3_THRESHOLD);
-       /*
-        * The default group preallocation is 512, which for 4k block
-        * sizes translates to 2 megabytes.  However for bigalloc file
-@@ -3042,6 +3165,17 @@ int ext4_mb_release(struct super_block *
-                               atomic_read(&sbi->s_bal_reqs),
-                               atomic_read(&sbi->s_bal_success));
-               ext4_msg(sb, KERN_INFO,
-+                      "mballoc: (%llu, %llu, %llu, %llu) useless c(0,1,2,3) loops",
-+                              atomic64_read(&sbi->s_bal_cX_failed[0]),
-+                              atomic64_read(&sbi->s_bal_cX_failed[1]),
-+                              atomic64_read(&sbi->s_bal_cX_failed[2]),
-+                              atomic64_read(&sbi->s_bal_cX_failed[3]));
-+              ext4_msg(sb, KERN_INFO,
-+                      "mballoc: (%llu, %llu, %llu) skipped c(0,1,2) loops",
-+                              atomic64_read(&sbi->s_bal_cX_skipped[0]),
-+                              atomic64_read(&sbi->s_bal_cX_skipped[1]),
-+                              atomic64_read(&sbi->s_bal_cX_skipped[2]));
-+              ext4_msg(sb, KERN_INFO,
-                     "mballoc: %u extents scanned, %u goal hits, "
-                               "%u 2^N hits, %u breaks, %u lost",
-                               atomic_read(&sbi->s_bal_ex_scanned),
---- a/fs/ext4/mballoc.h
-+++ b/fs/ext4/mballoc.h
-@@ -68,6 +68,9 @@
-  * for which requests use 2^N search using buddies
-  */
- #define MB_DEFAULT_ORDER2_REQS                8
-+#define MB_DEFAULT_C1_THRESHOLD               25
-+#define MB_DEFAULT_C2_THRESHOLD               15
-+#define MB_DEFAULT_C3_THRESHOLD               5
- /*
-  * default group prealloc size 512 blocks
---- a/fs/ext4/sysfs.c
-+++ b/fs/ext4/sysfs.c
-@@ -21,6 +21,9 @@
- typedef enum {
-       attr_noop,
-       attr_delayed_allocation_blocks,
-+      attr_mb_c1_threshold,
-+      attr_mb_c2_threshold,
-+      attr_mb_c3_threshold,
-       attr_session_write_kbytes,
-       attr_lifetime_write_kbytes,
-       attr_reserved_clusters,
-@@ -140,6 +143,32 @@ static ssize_t journal_task_show(struct
-                       task_pid_vnr(sbi->s_journal->j_task));
- }
-+#define THRESHOLD_PERCENT(ts) (ts * 100 / ext4_blocks_count(sbi->s_es))
-+
-+static int save_threshold_percent(struct ext4_sb_info *sbi, const char *buf,
-+                                ext4_fsblk_t *blocks)
-+{
-+      unsigned long long val;
-+
-+      int ret;
-+
-+      ret = kstrtoull(skip_spaces(buf), 0, &val);
-+      if (ret || val > 100)
-+              return -EINVAL;
-+
-+      *blocks = val * ext4_blocks_count(sbi->s_es) / 100;
-+      return 0;
-+}
-+
-+static ssize_t mb_threshold_store(struct ext4_sb_info *sbi,
-+                                const char *buf, size_t count,
-+                                ext4_fsblk_t *blocks)
-+{
-+      int ret = save_threshold_percent(sbi, buf, blocks);
-+
-+      return ret ?: count;
-+}
-+
- #define EXT4_ATTR(_name,_mode,_id)                                    \
- static struct ext4_attr ext4_attr_##_name = {                         \
-       .attr = {.name = __stringify(_name), .mode = _mode },           \
-@@ -205,6 +234,9 @@ EXT4_ATTR_FUNC(delayed_allocation_blocks
- EXT4_ATTR_FUNC(session_write_kbytes, 0444);
- EXT4_ATTR_FUNC(lifetime_write_kbytes, 0444);
- EXT4_ATTR_FUNC(reserved_clusters, 0644);
-+EXT4_ATTR_FUNC(mb_c1_threshold, 0644);
-+EXT4_ATTR_FUNC(mb_c2_threshold, 0644);
-+EXT4_ATTR_FUNC(mb_c3_threshold, 0644);
- EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, inode_readahead,
-                ext4_sb_info, s_inode_readahead_blks);
-@@ -253,6 +285,9 @@ static struct attribute *ext4_attrs[] =
-       ATTR_LIST(session_write_kbytes),
-       ATTR_LIST(lifetime_write_kbytes),
-       ATTR_LIST(reserved_clusters),
-+      ATTR_LIST(mb_c1_threshold),
-+      ATTR_LIST(mb_c2_threshold),
-+      ATTR_LIST(mb_c3_threshold),
-       ATTR_LIST(inode_readahead_blks),
-       ATTR_LIST(inode_goal),
-       ATTR_LIST(max_dir_size),
-@@ -365,6 +400,15 @@ static ssize_t ext4_attr_show(struct kob
-               return snprintf(buf, PAGE_SIZE, "%llu\n",
-                               (s64) EXT4_C2B(sbi,
-                      percpu_counter_sum(&sbi->s_dirtyclusters_counter)));
-+      case attr_mb_c1_threshold:
-+              return scnprintf(buf, PAGE_SIZE, "%llu\n",
-+                               THRESHOLD_PERCENT(sbi->s_mb_c1_blocks));
-+      case attr_mb_c2_threshold:
-+              return scnprintf(buf, PAGE_SIZE, "%llu\n",
-+                               THRESHOLD_PERCENT(sbi->s_mb_c2_blocks));
-+      case attr_mb_c3_threshold:
-+              return scnprintf(buf, PAGE_SIZE, "%llu\n",
-+                               THRESHOLD_PERCENT(sbi->s_mb_c3_blocks));
-       case attr_session_write_kbytes:
-               return session_write_kbytes_show(sbi, buf);
-       case attr_lifetime_write_kbytes:
-@@ -466,6 +510,12 @@ static ssize_t ext4_attr_store(struct ko
-               return inode_readahead_blks_store(sbi, buf, len);
-       case attr_trigger_test_error:
-               return trigger_test_error(sbi, buf, len);
-+      case attr_mb_c1_threshold:
-+              return mb_threshold_store(sbi, buf, len, &sbi->s_mb_c1_blocks);
-+      case attr_mb_c2_threshold:
-+              return mb_threshold_store(sbi, buf, len, &sbi->s_mb_c2_blocks);
-+      case attr_mb_c3_threshold:
-+              return mb_threshold_store(sbi, buf, len, &sbi->s_mb_c3_blocks);
-       }
-       return 0;
- }
-@@ -528,6 +578,8 @@ int ext4_register_sysfs(struct super_blo
-                               &ext4_seq_mb_last_group_fops, sb);
-               proc_create_single_data("mb_last_start", S_IRUGO, sbi->s_proc,
-                               ext4_mb_seq_last_start_seq_show, sb);
-+              proc_create_data("mb_alloc_stats", S_IFREG | S_IRUGO | S_IWUSR,
-+                               sbi->s_proc, &ext4_mb_seq_alloc_fops, sb);
-       }
-       return 0;
- }
diff --git a/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch b/ldiskfs/kernel_patches/patches/linux-6.0/ext4-data-in-dirent.patch
new file mode 100644 (file)
index 0000000..9c61de5
--- /dev/null
@@ -0,0 +1,895 @@
+ commit 2db3b2b33ee796f4ea61316773452d936303ad27
+ Author:     Pravin Shelar <Pravin.Shelar@sun.com>
+ AuthorDate: Sun Oct 4 18:13:14 2009 +0000
+ Subject: ext4: add ext4-data-in-dirent patch
+    
+ Allows ext4 to store extra data records inside the ext4_dirent
+ along with the regular directory entry (type, length, filename).
+ Data is stored in ext4 dirent after filename, with a bit flag in
+ de->file_type to indicate if any record after de->name is used.
+ Each in-use record is variable length and must store a 1-byte
+ length (including the length byte itself) at the start so that it
+ can be skipped if the record type is unknown/uneeded.  The record
+ starts after a NUL byte terminator for the filename.  This extra
+ space is accounted in de->rec_len but not de->name_len.
+ Flag EXT4_DIRENT_LUFID is used for a 128-bit file identifier.  
+ Make use of dentry->d_fsdata to pass LUFID to ext4, so no changes
+ in ext4_add_entry() interface are required.
+ Bugzilla-ID: b=17670
+ Signed-off-by: Pravin Shelar <Pravin.Shelar@sun.com>
+ Reviewed-by: Huang Hua <h.huang@sun.com>
+ Signed-off-by: Andreas Dilger <andreas.dilger@sun.com> 
+---
+ fs/ext4/dir.c         |   9 +-
+ fs/ext4/ext4.h        | 107 ++++++++++++++++--
+ fs/ext4/fast_commit.c |   2 +-
+ fs/ext4/inline.c      |   8 +-
+ fs/ext4/namei.c       | 251 ++++++++++++++++++++++++++++++++----------
+ fs/ext4/super.c       |   4 +-
+ 6 files changed, 306 insertions(+), 75 deletions(-)
+
+diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
+index 3985f8c..b8e4df1 100644
+--- a/fs/ext4/dir.c
++++ b/fs/ext4/dir.c
+@@ -465,12 +465,17 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
+       struct fname *fname, *new_fn;
+       struct dir_private_info *info;
+       int len;
++      int extra_data = 0;
+       info = dir_file->private_data;
+       p = &info->root.rb_node;
+       /* Create and allocate the fname structure */
+-      len = sizeof(struct fname) + ent_name->len + 1;
++      if (dirent->file_type & EXT4_DIRENT_LUFID)
++              extra_data = ext4_get_dirent_data_len(dirent);
++
++      len = sizeof(struct fname) + ent_name->len + extra_data + 1;
++
+       new_fn = kzalloc(len, GFP_KERNEL);
+       if (!new_fn)
+               return -ENOMEM;
+@@ -479,7 +484,7 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
+       new_fn->inode = le32_to_cpu(dirent->inode);
+       new_fn->name_len = ent_name->len;
+       new_fn->file_type = dirent->file_type;
+-      memcpy(new_fn->name, ent_name->name, ent_name->len);
++      memcpy(new_fn->name, ent_name->name, ent_name->len + extra_data);
+       while (*p) {
+               parent = *p;
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index e84d4a7..5e73b80 100644
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1184,6 +1184,7 @@ struct ext4_inode_info {
+       __u32 i_csum_seed;
+       kprojid_t i_projid;
++      void *i_dirdata;
+ };
+ /*
+@@ -1205,6 +1206,7 @@ struct ext4_inode_info {
+  * Mount flags set via mount options or defaults
+  */
+ #define EXT4_MOUNT_NO_MBCACHE         0x00001 /* Do not use mbcache */
++#define EXT4_MOUNT_DIRDATA            0x00002 /* Data in directory entries */
+ #define EXT4_MOUNT_GRPID              0x00004 /* Create files with directory's group */
+ #define EXT4_MOUNT_DEBUG              0x00008 /* Some debugging messages */
+ #define EXT4_MOUNT_ERRORS_CONT                0x00010 /* Continue on errors */
+@@ -2173,6 +2175,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(casefold,            CASEFOLD)
+                                        EXT4_FEATURE_INCOMPAT_FLEX_BG| \
+                                        EXT4_FEATURE_INCOMPAT_EA_INODE| \
+                                        EXT4_FEATURE_INCOMPAT_MMP | \
++                                       EXT4_FEATURE_INCOMPAT_DIRDATA| \
+                                        EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
+                                        EXT4_FEATURE_INCOMPAT_ENCRYPT | \
+                                        EXT4_FEATURE_INCOMPAT_CASEFOLD | \
+@@ -2384,6 +2387,42 @@ struct ext4_dir_entry_tail {
+ #define EXT4_FT_SYMLINK               7
+ #define EXT4_FT_MAX           8
++#define EXT4_FT_MASK          0xf
++
++#if EXT4_FT_MAX > EXT4_FT_MASK
++#error "conflicting EXT4_FT_MAX and EXT4_FT_MASK"
++#endif
++
++/*
++ * d_type has 4 unused bits, so it can hold four types data. these different
++ * type of data (e.g. lustre data, high 32 bits of 64-bit inode number) can be
++ * stored, in flag order, after file-name in ext4 dirent.
++*/
++/*
++ * this flag is added to d_type if ext4 dirent has extra data after
++ * filename. this data length is variable and length is stored in first byte
++ * of data. data start after filename NUL byte.
++ * This is used by Lustre FS.
++  */
++#define EXT4_DIRENT_LUFID             0x10
++
++#define EXT4_LUFID_MAGIC    0xAD200907UL
++struct ext4_dentry_param {
++      __u32  edp_magic;       /* EXT4_LUFID_MAGIC */
++      char   edp_len;         /* size of edp_data in bytes */
++      char   edp_data[0];     /* packed array of data */
++} __packed;
++
++static inline unsigned char *ext4_dentry_get_data(struct super_block *sb,
++                                                struct ext4_dentry_param *p)
++{
++      if (!ext4_has_feature_dirdata(sb))
++              return NULL;
++      if (p && p->edp_magic == EXT4_LUFID_MAGIC)
++              return &p->edp_len;
++      else
++              return NULL;
++}
+ #define EXT4_FT_DIR_CSUM      0xDE
+@@ -2395,6 +2434,17 @@ struct ext4_dir_entry_tail {
+ #define EXT4_DIR_PAD                  4
+ #define EXT4_DIR_ROUND                        (EXT4_DIR_PAD - 1)
+ #define EXT4_MAX_REC_LEN              ((1<<16)-1)
++#define EXT4_DIR_REC_LEN_(name_len, i_dir) \
++      ext4_dir_rec_len((name_len), (i_dir))
++#define EXT4_DIR_ENTRY_LEN_(de, i_dir) \
++      (EXT4_DIR_REC_LEN_((de)->name_len + ext4_get_dirent_data_len(de), \
++              (i_dir)))
++/* ldiskfs */
++#define EXT4_DIR_REC_LEN(name_len, i_dir)     EXT4_DIR_REC_LEN_((name_len), (i_dir))
++#define EXT4_DIR_ENTRY_LEN(de, i_dir)         EXT4_DIR_ENTRY_LEN_((de), (i_dir))
++/* lustre osd_handler compat -- ifdef LDISKFS_DIR_REC_LEN_WITH_DIR */
++#define EXT4_DIR_REC_LEN_WITH_DIR             1
++#define __EXT4_DIR_REC_LEN(name_len)          EXT4_DIR_REC_LEN_((name_len), NULL)
+ /*
+  * The rec_len is dependent on the type of directory. Directories that are
+@@ -2402,10 +2452,10 @@ struct ext4_dir_entry_tail {
+  * ext4_extended_dir_entry_2. For all entries related to '.' or '..' you should
+  * pass NULL for dir, as those entries do not use the extra fields.
+  */
+-static inline unsigned int ext4_dir_rec_len(__u8 name_len,
++static inline unsigned int ext4_dir_rec_len(__u32 name_len,
+                                               const struct inode *dir)
+ {
+-      int rec_len = (name_len + 8 + EXT4_DIR_ROUND);
++      __u32 rec_len = (name_len + 8 + EXT4_DIR_ROUND);
+       if (dir && ext4_hash_in_dirent(dir))
+               rec_len += sizeof(struct ext4_dir_entry_hash);
+@@ -2830,11 +2880,13 @@ extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+                            struct buffer_head *bh,
+                            void *buf, int buf_size,
+                            struct ext4_filename *fname,
+-                           struct ext4_dir_entry_2 **dest_de);
++                           struct ext4_dir_entry_2 **dest_de,
++                           int *dlen);
+ void ext4_insert_dentry(struct inode *dir, struct inode *inode,
+                       struct ext4_dir_entry_2 *de,
+                       int buf_size,
+-                      struct ext4_filename *fname);
++                      struct ext4_filename *fname,
++                      void *data);
+ static inline void ext4_update_dx_flag(struct inode *inode)
+ {
+       if (!ext4_has_feature_dir_index(inode->i_sb) &&
+@@ -2850,10 +2902,17 @@ static const unsigned char ext4_filetype_table[] = {
+ static inline  unsigned char get_dtype(struct super_block *sb, int filetype)
+ {
+-      if (!ext4_has_feature_filetype(sb) || filetype >= EXT4_FT_MAX)
++      int fl_index = filetype & EXT4_FT_MASK;
++
++      if (!ext4_has_feature_filetype(sb) || fl_index >= EXT4_FT_MAX)
+               return DT_UNKNOWN;
+-      return ext4_filetype_table[filetype];
++      if (!test_opt(sb, DIRDATA))
++              return ext4_filetype_table[fl_index];
++
++      return (ext4_filetype_table[fl_index]) |
++              (filetype & EXT4_DIRENT_LUFID);
++
+ }
+ extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh,
+                            void *buf, int buf_size);
+@@ -3056,9 +3115,13 @@ extern int ext4_ind_migrate(struct inode *inode);
+ /* namei.c */
+ extern int ext4_init_new_dir(handle_t *handle, struct inode *dir,
+-                           struct inode *inode);
++                           struct inode *inode,
++                           const void *data1, const void *data2);
+ extern int ext4_dirblock_csum_verify(struct inode *inode,
+                                    struct buffer_head *bh);
++extern int ext4_add_dot_dotdot(handle_t *handle, struct inode *dir,
++                             struct inode *inode,
++                             const void *data1, const void *data2);
+ extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
+                               __u32 start_minor_hash, __u32 *next_hash);
+ extern struct inode *ext4_create_inode(handle_t *handle,
+@@ -3888,6 +3951,36 @@ static inline int ext4_buffer_uptodate(struct buffer_head *bh)
+       return buffer_uptodate(bh);
+ }
++/*
++ * Compute the total directory entry data length.
++ * This includes the filename and an implicit NUL terminator (always present),
++ * and optional extensions.  Each extension has a bit set in the high 4 bits of
++ * de->file_type, and the extension length is the first byte in each entry.
++ */
++static inline int ext4_get_dirent_data_len(struct ext4_dir_entry_2 *de)
++{
++      char *len = de->name + de->name_len + 1 /* NUL terminator */;
++      int dlen = 0;
++      __u8 extra_data_flags = (de->file_type & ~EXT4_FT_MASK) >> 4;
++      struct ext4_dir_entry_tail *t = (struct ext4_dir_entry_tail *)de;
++
++      if (!t->det_reserved_zero1 &&
++          le16_to_cpu(t->det_rec_len) ==
++              sizeof(struct ext4_dir_entry_tail) &&
++          !t->det_reserved_zero2 &&
++          t->det_reserved_ft == EXT4_FT_DIR_CSUM)
++              return 0;
++
++      while (extra_data_flags) {
++              if (extra_data_flags & 1) {
++                      dlen += *len + (dlen == 0);
++                      len += *len;
++              }
++              extra_data_flags >>= 1;
++      }
++      return dlen;
++}
++
+ #endif        /* __KERNEL__ */
+ #define EFSBADCRC     EBADMSG         /* Bad CRC detected */
+diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
+index e5d20da..2562c17 100644
+--- a/fs/ext4/fast_commit.c
++++ b/fs/ext4/fast_commit.c
+@@ -1657,7 +1657,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl,
+                       ext4_debug("Dir %d not found.", darg.ino);
+                       goto out;
+               }
+-              ret = ext4_init_new_dir(NULL, dir, inode);
++              ret = ext4_init_new_dir(NULL, dir, inode, NULL, NULL);
+               iput(dir);
+               if (ret) {
+                       ret = 0;
+diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
+index a4fbe82..6a424df 100644
+--- a/fs/ext4/inline.c
++++ b/fs/ext4/inline.c
+@@ -1032,7 +1032,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
+       struct ext4_dir_entry_2 *de;
+       err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start,
+-                              inline_size, fname, &de);
++                              inline_size, fname, &de, NULL);
+       if (err)
+               return err;
+@@ -1041,7 +1041,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
+                                           EXT4_JTR_NONE);
+       if (err)
+               return err;
+-      ext4_insert_dentry(dir, inode, de, inline_size, fname);
++      ext4_insert_dentry(dir, inode, de, inline_size, fname, NULL);
+       ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
+@@ -1399,7 +1399,7 @@ int ext4_inlinedir_to_tree(struct file *dir_file,
+                       fake.name_len = 1;
+                       strcpy(fake.name, ".");
+                       fake.rec_len = ext4_rec_len_to_disk(
+-                                        ext4_dir_rec_len(fake.name_len, NULL),
++                                        EXT4_DIR_ENTRY_LEN(&fake, NULL),
+                                         inline_size);
+                       ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
+                       de = &fake;
+@@ -1409,7 +1409,7 @@ int ext4_inlinedir_to_tree(struct file *dir_file,
+                       fake.name_len = 2;
+                       strcpy(fake.name, "..");
+                       fake.rec_len = ext4_rec_len_to_disk(
+-                                        ext4_dir_rec_len(fake.name_len, NULL),
++                                        EXT4_DIR_ENTRY_LEN(&fake, NULL),
+                                         inline_size);
+                       ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
+                       de = &fake;
+diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
+index 649dc0a..54bbe22 100644
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -290,13 +290,14 @@ static unsigned dx_get_count(struct dx_entry *entries);
+ static unsigned dx_get_limit(struct dx_entry *entries);
+ static void dx_set_count(struct dx_entry *entries, unsigned value);
+ static void dx_set_limit(struct dx_entry *entries, unsigned value);
+-static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
++static inline unsigned dx_root_limit(struct inode *dir,
++              struct ext4_dir_entry_2 *dot_de, unsigned infosize);
+ static unsigned dx_node_limit(struct inode *dir);
+ static struct dx_frame *dx_probe(struct ext4_filename *fname,
+                                struct inode *dir,
+                                struct dx_hash_info *hinfo,
+                                struct dx_frame *frame);
+-static void dx_release(struct dx_frame *frames);
++static void dx_release(struct dx_frame *frames, struct inode *dir);
+ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
+                      struct dx_hash_info *hinfo,
+                      struct dx_map_entry *map_tail);
+@@ -436,22 +437,23 @@ static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
+ {
+       struct ext4_dir_entry *dp;
+       struct dx_root_info *root;
+-      int count_offset;
++      int count_offset, dot_rec_len, dotdot_rec_len;
+       if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb))
+               count_offset = 8;
+-      else if (le16_to_cpu(dirent->rec_len) == 12) {
+-              dp = (struct ext4_dir_entry *)(((void *)dirent) + 12);
++      else {
++              dot_rec_len = le16_to_cpu(dirent->rec_len);
++              dp = (struct ext4_dir_entry *)(((void *)dirent) + dot_rec_len);
+               if (le16_to_cpu(dp->rec_len) !=
+-                  EXT4_BLOCK_SIZE(inode->i_sb) - 12)
++                  EXT4_BLOCK_SIZE(inode->i_sb) - dot_rec_len)
+                       return NULL;
+-              root = (struct dx_root_info *)(((void *)dp + 12));
++              dotdot_rec_len = EXT4_DIR_ENTRY_LEN((struct ext4_dir_entry_2 *)dp, NULL);
++              root = (struct dx_root_info *)(((void *)dp + dotdot_rec_len));
+               if (root->reserved_zero ||
+                   root->info_length != sizeof(struct dx_root_info))
+                       return NULL;
+-              count_offset = 32;
+-      } else
+-              return NULL;
++              count_offset = 8 + dot_rec_len + dotdot_rec_len;
++      }
+       if (offset)
+               *offset = count_offset;
+@@ -554,13 +556,14 @@ ext4_next_entry(struct ext4_dir_entry_2 *p, unsigned long blocksize)
+  * Future: use high four bits of block for coalesce-on-delete flags
+  * Mask them off for now.
+  */
+-struct dx_root_info *dx_get_dx_info(struct ext4_dir_entry_2 *de)
++struct dx_root_info *dx_get_dx_info(struct ext4_dir_entry_2 *de, struct inode *i_dir)
+ {
++      BUG_ON(de->name_len != 1);
+       /* get dotdot first */
+-      de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(1));
++      de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_ENTRY_LEN(de, i_dir));
+       /* dx root info is after dotdot entry */
+-      de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_REC_LEN(2));
++      de = (struct ext4_dir_entry_2 *)((char *)de + EXT4_DIR_ENTRY_LEN(de, i_dir));
+       return (struct dx_root_info *)de;
+ }
+@@ -605,11 +608,16 @@ static inline void dx_set_limit(struct dx_entry *entries, unsigned value)
+       ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value);
+ }
+-static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
++static inline unsigned dx_root_limit(struct inode *dir,
++              struct ext4_dir_entry_2 *dot_de, unsigned infosize)
+ {
+-      unsigned int entry_space = dir->i_sb->s_blocksize -
+-                      ext4_dir_rec_len(1, NULL) -
+-                      ext4_dir_rec_len(2, NULL) - infosize;
++      struct ext4_dir_entry_2 *dotdot_de;
++      unsigned entry_space;
++
++      BUG_ON(dot_de->name_len != 1);
++      dotdot_de = ext4_next_entry(dot_de, dir->i_sb->s_blocksize);
++      entry_space = dir->i_sb->s_blocksize - EXT4_DIR_ENTRY_LEN(dot_de, NULL) -
++                       EXT4_DIR_ENTRY_LEN(dotdot_de, NULL) - infosize;
+       if (ext4_has_metadata_csum(dir->i_sb))
+               entry_space -= sizeof(struct dx_tail);
+@@ -727,7 +735,7 @@ static struct stats dx_show_leaf(struct inode *dir,
+                                      (unsigned) ((char *) de - base));
+ #endif
+                       }
+-                      space += ext4_dir_rec_len(de->name_len, dir);
++                      space += EXT4_DIR_ENTRY_LEN(de, dir);
+                       names++;
+               }
+               de = ext4_next_entry(de, size);
+@@ -821,7 +829,7 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
+       if (IS_ERR(frame->bh))
+               return (struct dx_frame *) frame->bh;
+-      info = dx_get_dx_info((struct ext4_dir_entry_2 *)frame->bh->b_data);
++      info = dx_get_dx_info((struct ext4_dir_entry_2 *)frame->bh->b_data, dir);
+       if (info->hash_version != DX_HASH_TEA &&
+           info->hash_version != DX_HASH_HALF_MD4 &&
+           info->hash_version != DX_HASH_LEGACY &&
+@@ -877,11 +885,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
+       entries = (struct dx_entry *)(((char *)info) + info->info_length);
+-      if (dx_get_limit(entries) != dx_root_limit(dir,
+-                                                 info->info_length)) {
++      if (dx_get_limit(entries) !=
++          dx_root_limit(dir, (struct ext4_dir_entry_2 *)frame->bh->b_data,
++                        info->info_length)) {
+               ext4_warning_inode(dir, "dx entry: limit %u != root limit %u",
+                                  dx_get_limit(entries),
+-                                 dx_root_limit(dir, info->info_length));
++                                 dx_root_limit(dir,
++                                        (struct ext4_dir_entry_2 *)frame->bh->b_data,
++                                        info->info_length));
+               goto fail;
+       }
+@@ -958,7 +969,7 @@ fail:
+       return ret_err;
+ }
+-static void dx_release(struct dx_frame *frames)
++static void dx_release(struct dx_frame *frames, struct inode *dir)
+ {
+       struct dx_root_info *info;
+       int i;
+@@ -967,7 +978,7 @@ static void dx_release(struct dx_frame *frames)
+       if (frames[0].bh == NULL)
+               return;
+-      info = dx_get_dx_info((struct ext4_dir_entry_2 *)frames[0].bh->b_data);
++      info = dx_get_dx_info((struct ext4_dir_entry_2 *)frames[0].bh->b_data, dir);
+       /* save local copy, "info" may be freed after brelse() */
+       indirect_levels = info->indirect_levels;
+       for (i = 0; i <= indirect_levels; i++) {
+@@ -1268,12 +1279,12 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
+                   (count && ((hashval & 1) == 0)))
+                       break;
+       }
+-      dx_release(frames);
++      dx_release(frames, dir);
+       dxtrace(printk(KERN_DEBUG "Fill tree: returned %d entries, "
+                      "next hash: %x\n", count, *next_hash));
+       return count;
+ errout:
+-      dx_release(frames);
++      dx_release(frames, dir);
+       return (err);
+ }
+@@ -1806,7 +1817,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
+ errout:
+       dxtrace(printk(KERN_DEBUG "%s not found\n", fname->usr_fname->name));
+ success:
+-      dx_release(frames);
++      dx_release(frames, dir);
+       return bh;
+ }
+@@ -1930,7 +1941,7 @@ dx_move_dirents(struct inode *dir, char *from, char *to,
+       while (count--) {
+               struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)
+                                               (from + (map->offs<<2));
+-              rec_len = ext4_dir_rec_len(de->name_len, dir);
++              rec_len = EXT4_DIR_ENTRY_LEN(de, dir);
+               memcpy (to, de, rec_len);
+               ((struct ext4_dir_entry_2 *) to)->rec_len =
+@@ -1963,7 +1974,7 @@ static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
+       while ((char*)de < base + blocksize) {
+               next = ext4_next_entry(de, blocksize);
+               if (de->inode && de->name_len) {
+-                      rec_len = ext4_dir_rec_len(de->name_len, dir);
++                      rec_len = EXT4_DIR_ENTRY_LEN(de, dir);
+                       if (de > to)
+                               memmove(to, de, rec_len);
+                       to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize);
+@@ -2106,14 +2117,22 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+                     struct buffer_head *bh,
+                     void *buf, int buf_size,
+                     struct ext4_filename *fname,
+-                    struct ext4_dir_entry_2 **dest_de)
++                    struct ext4_dir_entry_2 **dest_de,
++                    int *dlen)
+ {
+       struct ext4_dir_entry_2 *de;
+-      unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir);
++      unsigned short reclen;
+       int nlen, rlen;
+       unsigned int offset = 0;
+       char *top;
++      if (dlen) {
++              reclen = ext4_dir_rec_len(fname_len(fname) + *dlen, dir);
++              *dlen = 0;
++      } else {
++              reclen = ext4_dir_rec_len(fname_len(fname), dir);
++      }
++
+       de = buf;
+       top = buf + buf_size - reclen;
+       while ((char *) de <= top) {
+@@ -2122,10 +2141,31 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+                       return -EFSCORRUPTED;
+               if (ext4_match(dir, fname, de))
+                       return -EEXIST;
+-              nlen = ext4_dir_rec_len(de->name_len, dir);
++              nlen = EXT4_DIR_ENTRY_LEN(de, dir);
+               rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+               if ((de->inode ? rlen - nlen : rlen) >= reclen)
+                       break;
++
++              /* Then for dotdot entries, check for the smaller space
++               * required for just the entry, no FID
++               */
++              if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) {
++                      if ((de->inode ? rlen - nlen : rlen) >=
++                          ext4_dir_rec_len(fname_len(fname), dir)) {
++                              /* set dlen = 1 to indicate not
++                               * enough space store fid
++                               */
++                              if (dlen)
++                                      *dlen = 1;
++                              break;
++                      }
++                      /* The new ".." entry must be written over the
++                       * previous ".." entry, which is the first
++                       * entry traversed by this scan. If it doesn't
++                       * fit, something is badly wrong, so -EIO.
++                       */
++                      return -EIO;
++              }
+               de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
+               offset += rlen;
+       }
+@@ -2140,12 +2180,13 @@ void ext4_insert_dentry(struct inode *dir,
+                       struct inode *inode,
+                       struct ext4_dir_entry_2 *de,
+                       int buf_size,
+-                      struct ext4_filename *fname)
++                      struct ext4_filename *fname,
++                      void *data)
+ {
+       int nlen, rlen;
+-      nlen = ext4_dir_rec_len(de->name_len, dir);
++      nlen = EXT4_DIR_ENTRY_LEN(de, dir);
+       rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+       if (de->inode) {
+               struct ext4_dir_entry_2 *de1 =
+@@ -2166,6 +2207,12 @@ void ext4_insert_dentry(struct inode *dir,
+               EXT4_DIRENT_HASHES(de)->minor_hash =
+                                               cpu_to_le32(hinfo->minor_hash);
+       }
++      if (data) {
++              de->name[fname_len(fname)] = 0;
++              memcpy(&de->name[fname_len(fname) + 1], data, *(char *)data);
++              de->file_type |= EXT4_DIRENT_LUFID;
++      }
++
+ }
+ /*
+@@ -2183,14 +2230,19 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
+ {
+       unsigned int    blocksize = dir->i_sb->s_blocksize;
+       int             csum_size = 0;
+-      int             err, err2;
++      int             err, err2, dlen = 0;
++      unsigned char   *data;
++      data = ext4_dentry_get_data(inode->i_sb, (struct ext4_dentry_param *)
++                                              EXT4_I(inode)->i_dirdata);
+       if (ext4_has_metadata_csum(inode->i_sb))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
+       if (!de) {
++              if (data)
++                      dlen = (*data) + 1;
+               err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
+-                                      blocksize - csum_size, fname, &de);
++                                      blocksize - csum_size, fname, &de, &dlen);
+               if (err)
+                       return err;
+       }
+@@ -2203,7 +2255,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
+       }
+       /* By now the buffer is marked for journaling */
+-      ext4_insert_dentry(dir, inode, de, blocksize, fname);
++      /* If writing the short form of "dotdot", don't add the data section */
++      if (dlen == 1)
++              data = NULL;
++      ext4_insert_dentry(dir, inode, de, blocksize, fname, data);
+       /*
+        * XXX shouldn't update any times until successful
+@@ -2309,7 +2364,7 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
+                                    blocksize);
+       /* initialize hashing info */
+-      dx_info = dx_get_dx_info(dot_de);
++      dx_info = dx_get_dx_info(dot_de, dir);
+       memset(dx_info, 0, sizeof(*dx_info));
+       dx_info->info_length = sizeof(*dx_info);
+       if (ext4_hash_in_dirent(dir))
+@@ -2320,7 +2375,8 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
+       entries = (void *)dx_info + sizeof(*dx_info);
+       dx_set_block(entries, 1);
+       dx_set_count(entries, 1);
+-      dx_set_limit(entries, dx_root_limit(dir, sizeof(*dx_info)));
++      dx_set_limit(entries, dx_root_limit(dir,
++                                       dot_de, sizeof(*dx_info)));
+       /* Initialize as for dx_probe */
+       fname->hinfo.hash_version = dx_info->hash_version;
+@@ -2361,7 +2417,7 @@ out_frames:
+        */
+       if (retval)
+               ext4_mark_inode_dirty(handle, dir);
+-      dx_release(frames);
++      dx_release(frames, dir);
+       brelse(bh2);
+       return retval;
+ }
+@@ -2374,6 +2430,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+       struct buffer_head *dir_block;
+       struct ext4_dir_entry_2 *de;
+       int len, journal = 0, err = 0;
++      int dlen = 0;
++      char *data;
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
+@@ -2389,21 +2447,26 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+       de = (struct ext4_dir_entry_2 *)dir_block->b_data;
+       /* the first item must be "." */
+-      assert(de->name_len == 1 && de->name[0] == '.');
++      ASSERT(de->name_len == 1 && de->name[0] == '.');
+       len = le16_to_cpu(de->rec_len);
+-      assert(len >= EXT4_DIR_REC_LEN(1));
+-      if (len > EXT4_DIR_REC_LEN(1)) {
++      ASSERT(len >= EXT4_DIR_REC_LEN(1, dir));
++      if (len > EXT4_DIR_REC_LEN(1, dir)) {
+               BUFFER_TRACE(dir_block, "get_write_access");
+               err = ext4_journal_get_write_access(handle, dir->i_sb, dir_block, EXT4_JTR_NONE);
+               if (err)
+                       goto out_journal;
+               journal = 1;
+-              de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1));
++              de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, dir));
+       }
+-      len -= EXT4_DIR_REC_LEN(1);
+-      assert(len == 0 || len >= EXT4_DIR_REC_LEN(2));
++      len -= EXT4_DIR_ENTRY_LEN(de, NULL);
++      data = ext4_dentry_get_data(dir->i_sb,
++                      (struct ext4_dentry_param *)dentry->d_fsdata);
++      if (data)
++              dlen = *data + 1;
++      ASSERT(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen, dir));
++
+       de = (struct ext4_dir_entry_2 *)
+                       ((char *) de + le16_to_cpu(de->rec_len));
+       if (!journal) {
+@@ -2417,10 +2480,15 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+       if (len > 0)
+               de->rec_len = cpu_to_le16(len);
+       else
+-              assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2));
++              ASSERT(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2, dir));
+       de->name_len = 2;
+       strcpy(de->name, "..");
+-      ext4_set_de_type(dir->i_sb, de, S_IFDIR);
++      if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) {
++              de->name[2] = 0;
++              memcpy(&de->name[2 + 1], data, *data);
++              ext4_set_de_type(dir->i_sb, de, S_IFDIR);
++              de->file_type |= EXT4_DIRENT_LUFID;
++      }
+ out_journal:
+       if (journal) {
+@@ -2458,6 +2526,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
+       ext4_lblk_t block, blocks;
+       int     csum_size = 0;
++      EXT4_I(inode)->i_dirdata = dentry->d_fsdata;
+       if (ext4_has_metadata_csum(inode->i_sb))
+               csum_size = sizeof(struct ext4_dir_entry_tail);
+@@ -2700,7 +2769,7 @@ again:
+                       dx_set_count(entries, 1);
+                       dx_set_block(entries + 0, newblock);
+                       info = dx_get_dx_info((struct ext4_dir_entry_2 *)
+-                                            frames[0].bh->b_data);
++                                            frames[0].bh->b_data, dir);
+                       info->indirect_levels = 1;
+                       dxtrace(printk(KERN_DEBUG
+                                      "Creating %d level index...\n",
+@@ -2726,7 +2795,7 @@ journal_error:
+       ext4_std_error(dir->i_sb, err); /* this is a no-op if err == 0 */
+ cleanup:
+       brelse(bh);
+-      dx_release(frames);
++      dx_release(frames, dir);
+       /* @restart is true means htree-path has been changed, we need to
+        * repeat dx_probe() to find out valid htree-path
+        */
+@@ -3029,38 +3098,73 @@ err_unlock_inode:
+       return err;
+ }
++struct tp_block {
++      struct inode *inode;
++      void *data1;
++      void *data2;
++};
++
+ struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
+                         struct ext4_dir_entry_2 *de,
+                         int blocksize, int csum_size,
+                         unsigned int parent_ino, int dotdot_real_len)
+ {
++      void *data1 = NULL, *data2 = NULL;
++      int dot_reclen = 0;
++
++      if (dotdot_real_len == 10) {
++              struct tp_block *tpb = (struct tp_block *)inode;
++              data1 = tpb->data1;
++              data2 = tpb->data2;
++              inode = tpb->inode;
++              dotdot_real_len = 0;
++      }
+       de->inode = cpu_to_le32(inode->i_ino);
+       de->name_len = 1;
+-      de->rec_len = ext4_rec_len_to_disk(ext4_dir_rec_len(de->name_len, NULL),
+-                                         blocksize);
+       strcpy(de->name, ".");
+       ext4_set_de_type(inode->i_sb, de, S_IFDIR);
++      /* get packed fid data*/
++      data1 = ext4_dentry_get_data(inode->i_sb,
++                              (struct ext4_dentry_param *) data1);
++      if (data1) {
++              de->name[1] = 0;
++              memcpy(&de->name[2], data1, *(char *) data1);
++              de->file_type |= EXT4_DIRENT_LUFID;
++      }
++      de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de, NULL));
++
++      dot_reclen = cpu_to_le16(de->rec_len);
+       de = ext4_next_entry(de, blocksize);
+       de->inode = cpu_to_le32(parent_ino);
+       de->name_len = 2;
++
++      strcpy(de->name, "..");
++      ext4_set_de_type(inode->i_sb, de, S_IFDIR);
++      data2 = ext4_dentry_get_data(inode->i_sb,
++                      (struct ext4_dentry_param *) data2);
++      if (data2) {
++              de->name[2] = 0;
++              memcpy(&de->name[3], data2, *(char *) data2);
++              de->file_type |= EXT4_DIRENT_LUFID;
++      }
++
+       if (!dotdot_real_len)
+               de->rec_len = ext4_rec_len_to_disk(blocksize -
+-                                      (csum_size + ext4_dir_rec_len(1, NULL)),
+-                                      blocksize);
++                                      (csum_size + dot_reclen), blocksize);
+       else
+               de->rec_len = ext4_rec_len_to_disk(
+-                                      ext4_dir_rec_len(de->name_len, NULL),
++                                      EXT4_DIR_ENTRY_LEN(de, NULL),
+                                       blocksize);
+-      strcpy(de->name, "..");
+-      ext4_set_de_type(inode->i_sb, de, S_IFDIR);
+       return ext4_next_entry(de, blocksize);
+ }
+ int ext4_init_new_dir(handle_t *handle, struct inode *dir,
+-                           struct inode *inode)
++                           struct inode *inode,
++                           const void *data1, const void *data2)
+ {
++      struct tp_block param;
+       struct buffer_head *dir_block = NULL;
+       struct ext4_dir_entry_2 *de;
+       ext4_lblk_t block = 0;
+@@ -3084,7 +3188,11 @@ int ext4_init_new_dir(handle_t *handle, struct inode *dir,
+       if (IS_ERR(dir_block))
+               return PTR_ERR(dir_block);
+       de = (struct ext4_dir_entry_2 *)dir_block->b_data;
+-      ext4_init_dot_dotdot(inode, de, blocksize, csum_size, dir->i_ino, 0);
++      param.inode = inode;
++      param.data1 = (void *)data1;
++      param.data2 = (void *)data2;
++      ext4_init_dot_dotdot((struct inode *)(&param), de, blocksize,
++                           csum_size, dir->i_ino, 10);
+       set_nlink(inode, 2);
+       if (csum_size)
+               ext4_initialize_dirent_tail(dir_block, blocksize);
+@@ -3099,6 +3207,29 @@ out:
+       return err;
+ }
++/* Initialize @inode as a subdirectory of @dir, and add the
++ * "." and ".." entries into the first directory block. */
++int ext4_add_dot_dotdot(handle_t *handle, struct inode *dir,
++                      struct inode *inode,
++                      const void *data1, const void *data2)
++{
++      int rc;
++
++      if (IS_ERR(handle))
++              return PTR_ERR(handle);
++
++      if (IS_DIRSYNC(dir))
++              ext4_handle_sync(handle);
++
++      inode->i_op = &ext4_dir_inode_operations;
++      inode->i_fop = &ext4_dir_operations;
++      rc = ext4_init_new_dir(handle, dir, inode, data1, data2);
++      if (!rc)
++              rc = ext4_mark_inode_dirty(handle, inode);
++      return rc;
++}
++EXPORT_SYMBOL(ext4_add_dot_dotdot);
++
+ static int ext4_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
+                     struct dentry *dentry, umode_t mode)
+ {
+@@ -3126,7 +3257,7 @@ retry:
+       inode->i_op = &ext4_dir_inode_operations;
+       inode->i_fop = &ext4_dir_operations;
+-      err = ext4_init_new_dir(handle, dir, inode);
++      err = ext4_init_new_dir(handle, dir, inode, NULL, NULL);
+       if (err)
+               goto out_clear_inode;
+       err = ext4_mark_inode_dirty(handle, inode);
+diff --git a/fs/ext4/super.c b/fs/ext4/super.c
+index 2bf4ff0..5a4b03a 100644
+--- a/fs/ext4/super.c
++++ b/fs/ext4/super.c
+@@ -1578,7 +1578,7 @@ enum {
+       Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
+       Opt_inlinecrypt,
+       Opt_usrjquota, Opt_grpjquota, Opt_quota,
+-      Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
++      Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, Opt_dirdata,
+       Opt_usrquota, Opt_grpquota, Opt_prjquota,
+       Opt_dax, Opt_dax_always, Opt_dax_inode, Opt_dax_never,
+       Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_warn_on_error,
+@@ -1694,6 +1694,7 @@ static const struct fs_parameter_spec ext4_param_specs[] = {
+       fsparam_u32     ("stripe",              Opt_stripe),
+       fsparam_flag    ("delalloc",            Opt_delalloc),
+       fsparam_flag    ("nodelalloc",          Opt_nodelalloc),
++      fsparam_flag    ("dirdata",             Opt_dirdata),
+       fsparam_flag    ("warn_on_error",       Opt_warn_on_error),
+       fsparam_flag    ("nowarn_on_error",     Opt_nowarn_on_error),
+       fsparam_u32     ("debug_want_extra_isize",
+@@ -1830,6 +1831,7 @@ static const struct mount_opts {
+                                                       MOPT_CLEAR | MOPT_Q},
+       {Opt_usrjquota, 0, MOPT_Q},
+       {Opt_grpjquota, 0, MOPT_Q},
++      {Opt_dirdata, EXT4_MOUNT_DIRDATA, MOPT_SET},
+       {Opt_jqfmt, 0, MOPT_QFMT},
+       {Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
+       {Opt_no_prefetch_block_bitmaps, EXT4_MOUNT_NO_PREFETCH_BLOCK_BITMAPS,
+-- 
+2.34.1
+
@@ -1,29 +1,35 @@
-From 1a0f7f0b9c13ef0aa86e125f350b6733bff8db3c Mon Sep 17 00:00:00 2001
-From: Shaun Tancheff <stancheff@cray.com>
-Date: Wed, 15 Jan 2020 07:35:13 -0600
-Subject: [PATCH] Single directory performance is a critical for HPC workloads.
- In a typical use case an application creates a separate output file for each
- node and task in a job. As nodes and tasks increase, hundreds of thousands of
- files may be created in a single directory within a short window of time.
- Today, both filename lookup and file system modifying operations (such as
- create and unlink) are protected with a single lock for an entire ldiskfs
- directory. PDO project will remove this bottleneck by introducing a parallel
- locking mechanism for entire ldiskfs directories. This work will enable
- multiple application threads to simultaneously lookup, create and unlink in
- parallel.
+LU-50 ldiskfs: pdirops patch for ldiskfs
+
+Single directory performance is a critical for HPC workloads. In a
+typical use case an application creates a separate output file for
+each node and task in a job. As nodes and tasks increase, hundreds
+of thousands of files may be created in a single directory within
+a short window of time.
+Today, both filename lookup and file system modifying operations
+(such as create and unlink) are protected with a single lock for
+an entire ldiskfs directory. PDO project will remove this
+bottleneck by introducing a parallel locking mechanism for entire
+ldiskfs directories. This work will enable multiple application
+threads to simultaneously lookup, create and unlink in parallel.
 
 This patch contains:
- - pdirops support for ldiskfs
- - integrate with osd-ldiskfs
+  - pdirops support for ldiskfs
+  - N-level htree directory
+  - integrate with osd-ldiskfs
+
+Signed-off-by: Liang Zhen <liang@whamcloud.com>
+Change-Id: I269c0e3112e68f3acd79e860dab052a68c7d7aaa
+Reviewed-on: http://review.whamcloud.com/375
+Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
 ---
- fs/ext4/Makefile |    1 
- fs/ext4/ext4.h   |   78 +++++++++
- fs/ext4/namei.c  |  454 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
- fs/ext4/super.c  |    1 
- 4 files changed, 494 insertions(+), 40 deletions(-)
- create mode 100644 fs/ext4/htree_lock.c
- create mode 100644 include/linux/htree_lock.h
+ fs/ext4/Makefile |   1 +
+ fs/ext4/ext4.h   |  78 ++++++++
+ fs/ext4/namei.c  | 467 ++++++++++++++++++++++++++++++++++++++++++-----
+ fs/ext4/super.c  |   1 +
+ 4 files changed, 505 insertions(+), 42 deletions(-)
 
+diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
+index 72206a2..1d15a3a 100644
 --- a/fs/ext4/Makefile
 +++ b/fs/ext4/Makefile
 @@ -7,6 +7,7 @@ obj-$(CONFIG_EXT4_FS) += ext4.o
@@ -34,17 +40,19 @@ This patch contains:
                indirect.o inline.o inode.o ioctl.o mballoc.o migrate.o \
                mmp.o move_extent.o namei.o page-io.o readpage.o resize.o \
                super.o symlink.o sysfs.o xattr.o xattr_hurd.o xattr_trusted.o \
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index 9986f39..cbb58c7 100644
 --- a/fs/ext4/ext4.h
 +++ b/fs/ext4/ext4.h
 @@ -29,6 +29,7 @@
+ #include <linux/mutex.h>
  #include <linux/timer.h>
- #include <linux/version.h>
  #include <linux/wait.h>
 +#include <linux/htree_lock.h>
  #include <linux/sched/signal.h>
  #include <linux/blockgroup_lock.h>
  #include <linux/percpu_counter.h>
-@@ -987,6 +988,9 @@ struct ext4_inode_info {
+@@ -1030,6 +1031,9 @@ struct ext4_inode_info {
        __u32   i_dtime;
        ext4_fsblk_t    i_file_acl;
  
@@ -54,7 +62,7 @@ This patch contains:
        /*
         * i_block_group is the number of the block group which contains
         * this file's inode.  Constant across the lifetime of the inode,
-@@ -2299,6 +2303,72 @@ struct dx_hash_info
+@@ -2566,6 +2570,72 @@ struct dx_hash_info
   */
  #define HASH_NB_ALWAYS                1
  
@@ -127,8 +135,8 @@ This patch contains:
  struct ext4_filename {
        const struct qstr *usr_fname;
        struct fscrypt_str disk_name;
-@@ -2666,11 +2736,19 @@ void ext4_insert_dentry(struct inode *in
-                       struct ext4_filename *fname, void *data);
+@@ -2896,12 +2966,20 @@ void ext4_insert_dentry(struct inode *dir, struct inode *inode,
+                       void *data);
  static inline void ext4_update_dx_flag(struct inode *inode)
  {
 +      /* Disable it for ldiskfs, because going from a DX directory to
@@ -138,7 +146,8 @@ This patch contains:
 +       * we need to exclusively lock the directory at here which will
 +       * increase complexity of code */
 +#if 0
-       if (!ext4_has_feature_dir_index(inode->i_sb)) {
+       if (!ext4_has_feature_dir_index(inode->i_sb) &&
+           ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) {
                /* ext4_iget() should have caught this... */
                WARN_ON_ONCE(ext4_has_feature_metadata_csum(inode->i_sb));
                ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
@@ -147,17 +156,19 @@ This patch contains:
  }
  static const unsigned char ext4_filetype_table[] = {
        DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
+diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
+index 54bbe22..9b20bc5 100644
 --- a/fs/ext4/namei.c
 +++ b/fs/ext4/namei.c
-@@ -55,6 +55,7 @@ struct buffer_head *ext4_append(handle_t
-                                       ext4_lblk_t *block)
+@@ -56,6 +56,7 @@ struct buffer_head *ext4_append(handle_t *handle,
  {
+       struct ext4_map_blocks map;
        struct buffer_head *bh;
 +      struct ext4_inode_info *ei = EXT4_I(inode);
        int err;
  
        if (unlikely(EXT4_SB(inode->i_sb)->s_max_dir_size_kb &&
-@@ -62,15 +63,22 @@ struct buffer_head *ext4_append(handle_t
+@@ -63,6 +64,10 @@ struct buffer_head *ext4_append(handle_t *handle,
                      EXT4_SB(inode->i_sb)->s_max_dir_size_kb)))
                return ERR_PTR(-ENOSPC);
  
@@ -166,6 +177,20 @@ This patch contains:
 +      down(&ei->i_append_sem);
 +
        *block = inode->i_size >> inode->i_sb->s_blocksize_bits;
+       map.m_lblk = *block;
+       map.m_len = 1;
+@@ -74,15 +79,18 @@ struct buffer_head *ext4_append(handle_t *handle,
+        */
+       err = ext4_map_blocks(NULL, inode, &map, 0);
+       if (err < 0)
+-              return ERR_PTR(err);
++              goto err_unlock;
+       if (err) {
+               EXT4_ERROR_INODE(inode, "Logical block already allocated");
+-              return ERR_PTR(-EFSCORRUPTED);
++              err = -EFSCORRUPTED;
++              goto err_unlock;
+       }
  
        bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE);
 -      if (IS_ERR(bh))
@@ -175,23 +200,33 @@ This patch contains:
 +      }
        inode->i_size += inode->i_sb->s_blocksize;
        EXT4_I(inode)->i_disksize = inode->i_size;
-       BUFFER_TRACE(bh, "get_write_access");
-       err = ext4_journal_get_write_access(handle, bh);
+       err = ext4_mark_inode_dirty(handle, inode);
+@@ -93,11 +101,14 @@ struct buffer_head *ext4_append(handle_t *handle,
+                                           EXT4_JTR_NONE);
+       if (err)
+               goto out;
 +      up(&ei->i_append_sem);
-       if (err) {
-               brelse(bh);
-               ext4_std_error(inode->i_sb, err);
-@@ -271,7 +279,8 @@ static unsigned dx_node_limit(struct ino
+       return bh;
+ out:
+       brelse(bh);
+       ext4_std_error(inode->i_sb, err);
++err_unlock:
++      up(&ei->i_append_sem);
+       return ERR_PTR(err);
+ }
+@@ -296,7 +307,8 @@ static unsigned dx_node_limit(struct inode *dir);
  static struct dx_frame *dx_probe(struct ext4_filename *fname,
                                 struct inode *dir,
                                 struct dx_hash_info *hinfo,
 -                               struct dx_frame *frame);
 +                               struct dx_frame *frame,
 +                               struct htree_lock *lck);
- static void dx_release(struct dx_frame *frames);
- static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
-                      unsigned blocksize, struct dx_hash_info *hinfo,
-@@ -285,12 +294,13 @@ static void dx_insert_block(struct dx_fr
+ static void dx_release(struct dx_frame *frames, struct inode *dir);
+ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
+                      struct dx_hash_info *hinfo,
+@@ -312,12 +324,13 @@ static void dx_insert_block(struct dx_frame *frame,
  static int ext4_htree_next_block(struct inode *dir, __u32 hash,
                                 struct dx_frame *frame,
                                 struct dx_frame *frames,
@@ -208,7 +243,7 @@ This patch contains:
  
  /* checksumming functions */
  void ext4_initialize_dirent_tail(struct buffer_head *bh,
-@@ -755,6 +765,227 @@ struct stats dx_show_entries(struct dx_h
+@@ -802,6 +815,227 @@ static inline void htree_rep_invariant_check(struct dx_entry *at,
  }
  #endif /* DX_DEBUG */
  
@@ -436,7 +471,7 @@ This patch contains:
  /*
   * Probe for a directory leaf block to search.
   *
-@@ -766,10 +997,11 @@ struct stats dx_show_entries(struct dx_h
+@@ -813,10 +1047,11 @@ static inline void htree_rep_invariant_check(struct dx_entry *at,
   */
  static struct dx_frame *
  dx_probe(struct ext4_filename *fname, struct inode *dir,
@@ -444,36 +479,34 @@ This patch contains:
 +       struct dx_hash_info *hinfo, struct dx_frame *frame_in,
 +       struct htree_lock *lck)
  {
-       unsigned count, indirect;
+       unsigned count, indirect, level, i;
 -      struct dx_entry *at, *entries, *p, *q, *m;
 +      struct dx_entry *at, *entries, *p, *q, *m, *dx = NULL;
        struct dx_root_info *info;
        struct dx_frame *frame = frame_in;
        struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR);
-@@ -831,8 +1063,15 @@ dx_probe(struct ext4_filename *fname, st
-       dxtrace(printk("Look up %x", hash));
+@@ -900,8 +1135,16 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
+       level = 0;
+       blocks[0] = 0;
        while (1) {
-+              if (indirect == 0) { /* the last index level */
++              if (indirect == level) { /* the last index level */
 +                      /* NB: ext4_htree_dx_lock() could be noop if
-+                       * DX-lock flag is not set for current operation */
++                       * DX-lock flag is not set for current operation
++                       */
 +                      ext4_htree_dx_lock(lck, dx);
 +                      ext4_htree_spin_lock(lck, dx, NULL);
 +              }
                count = dx_get_count(entries);
--              if (!count || count > dx_get_limit(entries)) {
-+              if (count == 0 || count > dx_get_limit(entries)) {
+               if (!count || count > dx_get_limit(entries)) {
 +                      ext4_htree_spin_unlock(lck); /* release spin */
                        ext4_warning_inode(dir,
                                           "dx entry: count %u beyond limit %u",
                                           count, dx_get_limit(entries));
-@@ -871,8 +1110,70 @@ dx_probe(struct ext4_filename *fname, st
-                              dx_get_block(at)));
+@@ -928,6 +1171,74 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
                frame->entries = entries;
                frame->at = at;
--              if (!indirect--)
-+
-+              if (indirect == 0) { /* the last index level */
++              if (indirect == level) { /* the last index level */
 +                      struct ext4_dir_lock_data *ld;
 +                      u64 myblock;
 +
@@ -490,7 +523,8 @@ This patch contains:
 +                       */
 +                      if (ext4_htree_dx_locked(lck)) {
 +                              /* DX-block is locked, just lock DE-block
-+                               * and return */
++                               * and return
++                               */
 +                              ext4_htree_spin_unlock(lck);
 +                              if (!ext4_htree_safe_locked(lck))
 +                                      ext4_htree_de_lock(lck, frame->at);
@@ -500,14 +534,16 @@ This patch contains:
 +                      if (dx_probe_hash_collision(lck, entries, at, hash) ==
 +                          DX_HASH_COL_YES) {
 +                              /* found hash collision, set DX-lock flag
-+                               * and retry to abtain DX-lock */
++                               * and retry to abtain DX-lock
++                               */
 +                              ext4_htree_spin_unlock(lck);
 +                              ext4_htree_dx_need_lock(lck);
 +                              continue;
 +                      }
 +                      ld = ext4_htree_lock_data(lck);
 +                      /* because I don't lock DX, so @at can't be trusted
-+                       * after I release spinlock so I have to save it */
++                       * after I release spinlock so I have to save it
++                       */
 +                      ld->ld_at = at;
 +                      ld->ld_at_entry = *at;
 +                      ld->ld_count = dx_get_count(entries);
@@ -521,25 +557,37 @@ This patch contains:
 +                       * a) I don't have lock for the DE-block yet
 +                       * b) I released spinlock on DX-block
 +                       * if it happened I can detect it by listening
-+                       * splitting event on this DE-block */
++                       * splitting event on this DE-block
++                       */
 +                      ext4_htree_de_lock(lck, frame->at);
 +                      ext4_htree_spin_stop_listen(lck);
 +
 +                      if (myblock == EXT4_HTREE_NODE_CHANGED) {
 +                              /* someone split this DE-block before
 +                               * I locked it, I need to retry and lock
-+                               * valid DE-block */
++                               * valid DE-block
++                               */
 +                              ext4_htree_de_unlock(lck);
 +                              continue;
 +                      }
-                       return frame;
++                      return frame;
 +              }
 +              dx = at;
-+              indirect--;
++
+               block = dx_get_block(at);
+               for (i = 0; i <= level; i++) {
+                       if (blocks[i] == block) {
+@@ -937,8 +1248,7 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
+                               goto fail;
+                       }
+               }
+-              if (++level > indirect)
+-                      return frame;
++              ++level;
+               blocks[level] = block;
                frame++;
-               frame->bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX);
-               if (IS_ERR(frame->bh)) {
-@@ -941,7 +1242,7 @@ static void dx_release(struct dx_frame *
+               frame->bh = ext4_read_dirblock(dir, block, INDEX);
+@@ -1009,7 +1319,7 @@ static void dx_release(struct dx_frame *frames, struct inode *dir)
  static int ext4_htree_next_block(struct inode *dir, __u32 hash,
                                 struct dx_frame *frame,
                                 struct dx_frame *frames,
@@ -548,7 +596,7 @@ This patch contains:
  {
        struct dx_frame *p;
        struct buffer_head *bh;
-@@ -956,12 +1257,22 @@ static int ext4_htree_next_block(struct
+@@ -1024,12 +1334,22 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
         * this loop, num_frames indicates the number of interior
         * nodes need to be read.
         */
@@ -573,7 +621,7 @@ This patch contains:
                p--;
        }
  
-@@ -984,6 +1295,13 @@ static int ext4_htree_next_block(struct
+@@ -1052,6 +1372,13 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
         * block so no check is necessary
         */
        while (num_frames--) {
@@ -587,7 +635,7 @@ This patch contains:
                bh = ext4_read_dirblock(dir, dx_get_block(p->at), INDEX);
                if (IS_ERR(bh))
                        return PTR_ERR(bh);
-@@ -992,6 +1310,7 @@ static int ext4_htree_next_block(struct
+@@ -1060,6 +1387,7 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
                p->bh = bh;
                p->at = p->entries = ((struct dx_node *) bh->b_data)->entries;
        }
@@ -595,7 +643,7 @@ This patch contains:
        return 1;
  }
  
-@@ -1136,10 +1455,10 @@ int ext4_htree_fill_tree(struct file *di
+@@ -1221,10 +1549,10 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
        }
        hinfo.hash = start_hash;
        hinfo.minor_hash = 0;
@@ -608,7 +656,7 @@ This patch contains:
        /* Add '.' and '..' from the htree header */
        if (!start_hash && !start_minor_hash) {
                de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data;
-@@ -1179,7 +1498,7 @@ int ext4_htree_fill_tree(struct file *di
+@@ -1264,7 +1592,7 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
                count += ret;
                hashval = ~0;
                ret = ext4_htree_next_block(dir, HASH_NB_ALWAYS,
@@ -617,7 +665,7 @@ This patch contains:
                *next_hash = hashval;
                if (ret < 0) {
                        err = ret;
-@@ -1455,7 +1774,7 @@ static int is_dx_internal_node(struct in
+@@ -1584,7 +1912,7 @@ static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
  static struct buffer_head *__ext4_find_entry(struct inode *dir,
                                             struct ext4_filename *fname,
                                             struct ext4_dir_entry_2 **res_dir,
@@ -626,7 +674,7 @@ This patch contains:
  {
        struct super_block *sb;
        struct buffer_head *bh_use[NAMEI_RA_SIZE];
-@@ -1497,7 +1816,7 @@ static struct buffer_head *__ext4_find_e
+@@ -1626,7 +1954,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
                goto restart;
        }
        if (is_dx(dir)) {
@@ -635,7 +683,7 @@ This patch contains:
                /*
                 * On success, or if the error was file not found,
                 * return.  Otherwise, fall back to doing a search the
-@@ -1507,6 +1826,7 @@ static struct buffer_head *__ext4_find_e
+@@ -1636,6 +1964,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
                        goto cleanup_and_exit;
                dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
                               "falling back\n"));
@@ -643,7 +691,7 @@ This patch contains:
                ret = NULL;
        }
        nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb);
-@@ -1597,10 +1917,10 @@ cleanup_and_exit:
+@@ -1726,10 +2055,10 @@ cleanup_and_exit:
        return ret;
  }
  
@@ -656,7 +704,7 @@ This patch contains:
  {
        int err;
        struct ext4_filename fname;
-@@ -1612,12 +1932,14 @@ static struct buffer_head *ext4_find_ent
+@@ -1741,12 +2070,14 @@ static struct buffer_head *ext4_find_entry(struct inode *dir,
        if (err)
                return ERR_PTR(err);
  
@@ -672,7 +720,7 @@ This patch contains:
  static struct buffer_head *ext4_lookup_entry(struct inode *dir,
                                             struct dentry *dentry,
                                             struct ext4_dir_entry_2 **res_dir)
-@@ -1632,7 +1954,7 @@ static struct buffer_head *ext4_lookup_e
+@@ -1762,7 +2093,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
        if (err)
                return ERR_PTR(err);
  
@@ -681,7 +729,7 @@ This patch contains:
  
        ext4_fname_free_filename(&fname);
        return bh;
-@@ -1640,7 +1962,8 @@ static struct buffer_head *ext4_lookup_e
+@@ -1770,7 +2101,8 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
  
  static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
                        struct ext4_filename *fname,
@@ -691,7 +739,7 @@ This patch contains:
  {
        struct super_block * sb = dir->i_sb;
        struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
-@@ -1651,7 +1974,7 @@ static struct buffer_head * ext4_dx_find
+@@ -1781,7 +2113,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
  #ifdef CONFIG_FS_ENCRYPTION
        *res_dir = NULL;
  #endif
@@ -700,7 +748,7 @@ This patch contains:
        if (IS_ERR(frame))
                return (struct buffer_head *) frame;
        do {
-@@ -1673,7 +1996,7 @@ static struct buffer_head * ext4_dx_find
+@@ -1803,7 +2135,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
  
                /* Check to see if we should continue to search */
                retval = ext4_htree_next_block(dir, fname->hinfo.hash, frame,
@@ -709,7 +757,7 @@ This patch contains:
                if (retval < 0) {
                        ext4_warning_inode(dir,
                                "error %d reading directory index block",
-@@ -1853,8 +2176,9 @@ static struct ext4_dir_entry_2* dx_pack_
+@@ -1992,8 +2324,9 @@ static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
   * Returns pointer to de in block into which the new entry will be inserted.
   */
  static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
@@ -720,25 +768,25 @@ This patch contains:
 +                      struct htree_lock *lck)
  {
        unsigned blocksize = dir->i_sb->s_blocksize;
-       unsigned count, continued;
-@@ -1915,8 +2239,14 @@ static struct ext4_dir_entry_2 *do_split
+       unsigned continued;
+@@ -2070,8 +2403,14 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
                                        hash2, split, count-split));
  
        /* Fancy dance to stay within two buffers */
--      de2 = dx_move_dirents(data1, data2, map + split, count - split,
+-      de2 = dx_move_dirents(dir, data1, data2, map + split, count - split,
 -                            blocksize);
 +      if (hinfo->hash < hash2) {
-+              de2 = dx_move_dirents(data1, data2, map + split,
++              de2 = dx_move_dirents(dir, data1, data2, map + split,
 +                                    count - split, blocksize);
 +      } else {
 +              /* make sure we will add entry to the same block which
 +               * we have already locked */
-+              de2 = dx_move_dirents(data1, data2, map, split, blocksize);
++              de2 = dx_move_dirents(dir, data1, data2, map, split, blocksize);
 +      }
-       de = dx_pack_dirents(data1, blocksize);
+       de = dx_pack_dirents(dir, data1, blocksize);
        de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) -
                                           (char *) de,
-@@ -1934,12 +2264,21 @@ static struct ext4_dir_entry_2 *do_split
+@@ -2089,12 +2428,21 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
        dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data2,
                        blocksize, 1));
  
@@ -765,16 +813,16 @@ This patch contains:
        err = ext4_handle_dirty_dirblock(handle, dir, bh2);
        if (err)
                goto journal_error;
-@@ -2209,7 +2548,7 @@ static int make_indexed_dir(handle_t *ha
+@@ -2402,7 +2750,7 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
        if (retval)
-               goto out_frames;        
+               goto out_frames;
  
 -      de = do_split(handle,dir, &bh2, frame, &fname->hinfo);
 +      de = do_split(handle, dir, &bh2, frames, frame, &fname->hinfo, NULL);
        if (IS_ERR(de)) {
                retval = PTR_ERR(de);
                goto out_frames;
-@@ -2319,8 +2658,8 @@ out:
+@@ -2512,8 +2860,8 @@ out:
   * may not sleep between calling this and putting something into
   * the entry, as someone else might have used it while you slept.
   */
@@ -785,7 +833,7 @@ This patch contains:
  {
        struct inode *dir = d_inode(dentry->d_parent);
        struct buffer_head *bh = NULL;
-@@ -2370,9 +2709,10 @@ static int ext4_add_entry(handle_t *hand
+@@ -2562,9 +2910,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                if (dentry->d_name.len == 2 &&
                     memcmp(dentry->d_name.name, "..", 2) == 0)
                         return ext4_update_dotdot(handle, dentry, inode);
@@ -797,7 +845,7 @@ This patch contains:
                /* Can we just ignore htree data? */
                if (ext4_has_metadata_csum(sb)) {
                        EXT4_ERROR_INODE(dir,
-@@ -2435,12 +2775,14 @@ out:
+@@ -2627,12 +2976,14 @@ out:
                ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY);
        return retval;
  }
@@ -813,7 +861,7 @@ This patch contains:
  {
        struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
        struct dx_entry *entries, *at;
-@@ -2452,7 +2794,7 @@ static int ext4_dx_add_entry(handle_t *h
+@@ -2644,7 +2995,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
  
  again:
        restart = 0;
@@ -822,7 +870,7 @@ This patch contains:
        if (IS_ERR(frame))
                return PTR_ERR(frame);
        entries = frame->entries;
-@@ -2487,6 +2829,12 @@ again:
+@@ -2679,6 +3030,12 @@ again:
                struct dx_node *node2;
                struct buffer_head *bh2;
  
@@ -835,7 +883,7 @@ This patch contains:
                while (frame > frames) {
                        if (dx_get_count((frame - 1)->entries) <
                            dx_get_limit((frame - 1)->entries)) {
-@@ -2589,8 +2937,32 @@ again:
+@@ -2782,8 +3139,32 @@ again:
                        restart = 1;
                        goto journal_error;
                }
@@ -869,22 +917,27 @@ This patch contains:
        if (IS_ERR(de)) {
                err = PTR_ERR(de);
                goto cleanup;
-@@ -2601,6 +2973,8 @@ again:
+@@ -2794,6 +3175,8 @@ again:
  journal_error:
        ext4_std_error(dir->i_sb, err); /* this is a no-op if err == 0 */
  cleanup:
 +      ext4_htree_dx_unlock(lck);
 +      ext4_htree_de_unlock(lck);
        brelse(bh);
-       dx_release(frames);
+       dx_release(frames, dir);
        /* @restart is true means htree-path has been changed, we need to
+diff --git a/fs/ext4/super.c b/fs/ext4/super.c
+index 5a4b03a..8083662 100644
 --- a/fs/ext4/super.c
 +++ b/fs/ext4/super.c
-@@ -1122,6 +1122,7 @@ static struct inode *ext4_alloc_inode(st
+@@ -1324,6 +1324,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
  
        inode_set_iversion(&ei->vfs_inode, 1);
        spin_lock_init(&ei->i_raw_lock);
 +      sema_init(&ei->i_append_sem, 1);
        INIT_LIST_HEAD(&ei->i_prealloc_list);
+       atomic_set(&ei->i_prealloc_active, 0);
        spin_lock_init(&ei->i_prealloc_lock);
-       ext4_es_init_tree(&ei->i_es_tree);
+-- 
+2.34.1
+
diff --git a/ldiskfs/kernel_patches/patches/linux-6.1/ext4-dont-check-before-replay.patch b/ldiskfs/kernel_patches/patches/linux-6.1/ext4-dont-check-before-replay.patch
new file mode 100644 (file)
index 0000000..6f4e0e7
--- /dev/null
@@ -0,0 +1,40 @@
+commit a70b020e5b2f1bbe3b759232852beaac4f0852b5
+Author:     Lokesh Nagappa Jaliminche <lokesh.jaliminche@seagate.com>
+AuthorDate: Fri Nov 25 16:17:09 2016 +0530
+LU-8364 ext4: fixes for failover mode.
+
+When ext4 runs in failover mode with read-only disk,
+it may loose part of allocation updates and fail while
+mounting fs due to group descriptor checks before journal
+replay not being valid after journal replay is complete.
+Don't produce panics with on disk checks in read-only mode.
+
+Seagate-bug-id: MRP-797
+Change-Id: I54bee3a0aeb9a15f5ee2a79f7a2a2a905f19af1a
+Signed-off-by: Alexey Lyashkov <alexey_lyashkov@xyratex.com>
+Signed-off-by: Lokesh Nagappa Jaliminche <lokesh.jaliminche@seagate.com>
+Reviewed-on: https://review.whamcloud.com/21141
+---
+ fs/ext4/super.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/fs/ext4/super.c b/fs/ext4/super.c
+index 3aafe71..56b993a 100644
+--- a/fs/ext4/super.c
++++ b/fs/ext4/super.c
+@@ -5325,6 +5325,12 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
+               needs_recovery = 0;
+       }
++      if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {
++              ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");
++              ret = -EFSCORRUPTED;
++              goto failed_mount3a;
++      }
++
+       if (!test_opt(sb, NO_MBCACHE)) {
+               sbi->s_ea_block_cache = ext4_xattr_create_cache();
+               if (!sbi->s_ea_block_cache) {
+-- 
+2.34.1
+
diff --git a/ldiskfs/kernel_patches/patches/linux-6.1/ext4-mballoc-extra-checks.patch b/ldiskfs/kernel_patches/patches/linux-6.1/ext4-mballoc-extra-checks.patch
new file mode 100644 (file)
index 0000000..2a2e6dc
--- /dev/null
@@ -0,0 +1,326 @@
+commit f2f28f1d09c0a00b3fc569422f881931d857fac9
+Author:     Alex Zhuravlev <alex.zhuravlev@sun.com>
+AuthorDate: Tue Oct 28 17:59:09 2008 +0000
+Subject: ext4: detect on-disk corruption of block bitmap
+Detect on-disk corruption of block bitmap and better checking of
+preallocated blocks.
+Bugzilla-ID: b=16680
+Signed-off-by: Alex Zhuravlev <alex.zhuravlev@sun.com>
+Reviewed-by: Kalpak Shah <kalpak.shah@sun.com>
+Signed-off-by: Andreas Dilger <andreas.dilger@sun.com>
+---
+ fs/ext4/ext4.h    |   1 +
+ fs/ext4/mballoc.c | 106 ++++++++++++++++++++++++++++++++++++++++------
+ fs/ext4/mballoc.h |   2 +-
+ 3 files changed, 94 insertions(+), 15 deletions(-)
+
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index c5c42c63..c60b42a9 100644
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -3440,6 +3440,7 @@ struct ext4_group_info {
+       ext4_grpblk_t   bb_largest_free_order;/* order of largest frag in BG */
+       ext4_group_t    bb_group;       /* Group number */
+       struct          list_head bb_prealloc_list;
++      unsigned long   bb_prealloc_nr;
+ #ifdef DOUBLE_CHECK
+       void            *bb_bitmap;
+ #endif
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index ac8e1fe1..7cb62001 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -402,7 +402,7 @@ static const char * const ext4_groupinfo_slab_names[NR_GRPINFO_CACHES] = {
+       "ext4_groupinfo_64k", "ext4_groupinfo_128k"
+ };
+-static void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
++static int ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
+                                       ext4_group_t group);
+ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
+                                               ext4_group_t group);
+@@ -1061,7 +1061,7 @@ mb_set_largest_free_order(struct super_block *sb, struct ext4_group_info *grp)
+ }
+ static noinline_for_stack
+-void ext4_mb_generate_buddy(struct super_block *sb,
++int ext4_mb_generate_buddy(struct super_block *sb,
+                           void *buddy, void *bitmap, ext4_group_t group,
+                           struct ext4_group_info *grp)
+ {
+@@ -1105,6 +1105,7 @@ void ext4_mb_generate_buddy(struct super_block *sb,
+               grp->bb_free = free;
+               ext4_mark_group_bitmap_corrupted(sb, group,
+                                       EXT4_GROUP_INFO_BBITMAP_CORRUPT);
++              return -EIO;
+       }
+       mb_set_largest_free_order(sb, grp);
+       mb_update_avg_fragment_size(sb, grp);
+@@ -1114,6 +1115,8 @@ void ext4_mb_generate_buddy(struct super_block *sb,
+       period = get_cycles() - period;
+       atomic_inc(&sbi->s_mb_buddies_generated);
+       atomic64_add(period, &sbi->s_mb_generation_time);
++
++      return 0;
+ }
+ /* The buddy information is attached the buddy cache inode
+@@ -1218,7 +1221,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
+       }
+       first_block = page->index * blocks_per_page;
+-      for (i = 0; i < blocks_per_page; i++) {
++      for (i = 0; i < blocks_per_page && err == 0; i++) {
+               group = (first_block + i) >> 1;
+               if (group >= ngroups)
+                       break;
+@@ -1266,7 +1269,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
+                       ext4_lock_group(sb, group);
+                       /* init the buddy */
+                       memset(data, 0xff, blocksize);
+-                      ext4_mb_generate_buddy(sb, data, incore, group, grinfo);
++                      err = ext4_mb_generate_buddy(sb, data, incore, group, grinfo);
+                       ext4_unlock_group(sb, group);
+                       incore = NULL;
+               } else {
+@@ -1281,7 +1284,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
+                       memcpy(data, bitmap, blocksize);
+                       /* mark all preallocated blks used in in-core bitmap */
+-                      ext4_mb_generate_from_pa(sb, data, group);
++                      err = ext4_mb_generate_from_pa(sb, data, group);
+                       ext4_mb_generate_from_freelist(sb, data, group);
+                       ext4_unlock_group(sb, group);
+@@ -1291,7 +1294,8 @@ static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
+                       incore = data;
+               }
+       }
+-      SetPageUptodate(page);
++      if (likely(err == 0))
++              SetPageUptodate(page);
+ out:
+       if (bh) {
+@@ -2832,9 +2836,11 @@ static void *ext4_mb_seq_groups_next(struct seq_file *seq, void *v, loff_t *pos)
+ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
+ {
+       struct super_block *sb = pde_data(file_inode(seq->file));
++      struct ext4_group_desc *gdp;
+       ext4_group_t group = (ext4_group_t) ((unsigned long) v);
+       int i;
+       int err, buddy_loaded = 0;
++      int free = 0;
+       struct ext4_buddy e4b;
+       struct ext4_group_info *grinfo;
+       unsigned char blocksize_bits = min_t(unsigned char,
+@@ -2847,7 +2853,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
+       group--;
+       if (group == 0)
+-              seq_puts(seq, "#group: free  frags first ["
++              seq_puts(seq, "#group: bfree gfree frags first pa    ["
+                             " 2^0   2^1   2^2   2^3   2^4   2^5   2^6  "
+                             " 2^7   2^8   2^9   2^10  2^11  2^12  2^13  ]\n");
+@@ -2867,13 +2873,19 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
+               buddy_loaded = 1;
+       }
++      gdp = ext4_get_group_desc(sb, group, NULL);
++      if (gdp != NULL)
++              free = ext4_free_group_clusters(sb, gdp);
++
+       memcpy(&sg, grinfo, i);
+       if (buddy_loaded)
+               ext4_mb_unload_buddy(&e4b);
+-      seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free,
+-                      sg.info.bb_fragments, sg.info.bb_first_free);
++      seq_printf(seq, "#%-5lu: %-5u %-5u %-5u %-5u %-5lu [",
++                      (long unsigned int)group, sg.info.bb_free, free,
++                      sg.info.bb_fragments, sg.info.bb_first_free,
++                      sg.info.bb_prealloc_nr);
+       for (i = 0; i <= 13; i++)
+               seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ?
+                               sg.info.bb_counters[i] : 0);
+@@ -4600,7 +4612,43 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
+               mb_set_bits(bitmap, entry->efd_start_cluster, entry->efd_count);
+               n = rb_next(n);
+       }
+-      return;
++}
++
++/*
++ * check free blocks in bitmap match free block in group descriptor
++ * do this before taking preallocated blocks into account to be able
++ * to detect on-disk corruptions. The group lock should be hold by the
++ * caller.
++ */
++int ext4_mb_check_ondisk_bitmap(struct super_block *sb, void *bitmap,
++                              struct ext4_group_desc *gdp, int group)
++{
++      unsigned short max = EXT4_CLUSTERS_PER_GROUP(sb);
++      unsigned short i, first, free = 0;
++      unsigned short free_in_gdp = ext4_free_group_clusters(sb, gdp);
++
++      if (free_in_gdp == 0 && gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))
++              return 0;
++
++      i = mb_find_next_zero_bit(bitmap, max, 0);
++
++      while (i < max) {
++              first = i;
++              i = mb_find_next_bit(bitmap, max, i);
++              if (i > max)
++                      i = max;
++              free += i - first;
++              if (i < max)
++                      i = mb_find_next_zero_bit(bitmap, max, i);
++      }
++
++      if (free != free_in_gdp) {
++              ext4_error(sb, "on-disk bitmap for group %d"
++                      "corrupted: %u blocks free in bitmap, %u - in gd\n",
++                      group, free, free_in_gdp);
++              return -EIO;
++      }
++      return 0;
+ }
+ /*
+@@ -4609,19 +4657,31 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
+  * Need to be called with ext4 group lock held
+  */
+ static noinline_for_stack
+-void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
++int ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
+                                       ext4_group_t group)
+ {
+       struct ext4_group_info *grp = ext4_get_group_info(sb, group);
+       struct ext4_prealloc_space *pa;
++      struct ext4_group_desc *gdp;
+       struct list_head *cur;
+       ext4_group_t groupnr;
+       ext4_grpblk_t start;
+       int preallocated = 0;
++      int skip = 0, count = 0;
++      int err;
+       int len;
+       if (!grp)
+-              return;
++              return -EIO;
++
++      gdp = ext4_get_group_desc(sb, group, NULL);
++      if (gdp == NULL)
++              return -EIO;
++
++      /* before applying preallocations, check bitmap consistency */
++      err = ext4_mb_check_ondisk_bitmap(sb, bitmap, gdp, group);
++      if (err)
++              return err;
+       /* all form of preallocation discards first load group,
+        * so the only competing code is preallocation use.
+@@ -4638,13 +4698,23 @@ void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
+                                            &groupnr, &start);
+               len = pa->pa_len;
+               spin_unlock(&pa->pa_lock);
+-              if (unlikely(len == 0))
++              if (unlikely(len == 0)) {
++                      skip++;
+                       continue;
++              }
+               BUG_ON(groupnr != group);
+               mb_set_bits(bitmap, start, len);
+               preallocated += len;
++              count++;
++      }
++      if (count + skip != grp->bb_prealloc_nr) {
++              ext4_error(sb, "lost preallocations: "
++                         "count %d, bb_prealloc_nr %lu, skip %d\n",
++                         count, grp->bb_prealloc_nr, skip);
++              return -EIO;
+       }
+       mb_debug(sb, "preallocated %d for group %u\n", preallocated, group);
++      return 0;
+ }
+ static void ext4_mb_mark_pa_deleted(struct super_block *sb,
+@@ -4728,6 +4798,7 @@ static void ext4_mb_put_pa(struct ext4_allocation_context *ac,
+        */
+       ext4_lock_group(sb, grp);
+       list_del(&pa->pa_group_list);
++      ext4_get_group_info(sb, grp)->bb_prealloc_nr--;
+       ext4_unlock_group(sb, grp);
+       spin_lock(pa->pa_obj_lock);
+@@ -4833,6 +4904,7 @@ adjust_bex:
+       pa->pa_inode = ac->ac_inode;
+       list_add(&pa->pa_group_list, &grp->bb_prealloc_list);
++      grp->bb_prealloc_nr++;
+       spin_lock(pa->pa_obj_lock);
+       list_add_rcu(&pa->pa_inode_list, &ei->i_prealloc_list);
+@@ -4890,6 +4962,7 @@ ext4_mb_new_group_pa(struct ext4_allocation_context *ac)
+       pa->pa_inode = NULL;
+       list_add(&pa->pa_group_list, &grp->bb_prealloc_list);
++      grp->bb_prealloc_nr++;
+       /*
+        * We will later add the new pa to the right bucket
+@@ -5060,6 +5133,8 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
+               spin_unlock(&pa->pa_lock);
++              BUG_ON(grp->bb_prealloc_nr == 0);
++              grp->bb_prealloc_nr--;
+               list_del(&pa->pa_group_list);
+               list_add(&pa->u.pa_tmp_list, &list);
+       }
+@@ -5187,7 +5262,7 @@ repeat:
+               if (err) {
+                       ext4_error_err(sb, -err, "Error %d loading buddy information for %u",
+                                      err, group);
+-                      continue;
++                      return;
+               }
+               bitmap_bh = ext4_read_block_bitmap(sb, group);
+@@ -5200,6 +5275,8 @@ repeat:
+               }
+               ext4_lock_group(sb, group);
++              BUG_ON(e4b.bd_info->bb_prealloc_nr == 0);
++              e4b.bd_info->bb_prealloc_nr--;
+               list_del(&pa->pa_group_list);
+               ext4_mb_release_inode_pa(&e4b, bitmap_bh, pa);
+               ext4_unlock_group(sb, group);
+@@ -5499,6 +5576,7 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb,
+               }
+               ext4_lock_group(sb, group);
+               list_del(&pa->pa_group_list);
++              ext4_get_group_info(sb, group)->bb_prealloc_nr--;
+               ext4_mb_release_group_pa(&e4b, pa);
+               ext4_unlock_group(sb, group);
+diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
+index dcda2a94..c7b753bc 100644
+--- a/fs/ext4/mballoc.h
++++ b/fs/ext4/mballoc.h
+@@ -66,7 +66,7 @@
+ /*
+  * for which requests use 2^N search using buddies
+  */
+-#define MB_DEFAULT_ORDER2_REQS                2
++#define MB_DEFAULT_ORDER2_REQS                8
+ /*
+  * default group prealloc size 512 blocks
+-- 
+2.34.1
+
diff --git a/ldiskfs/kernel_patches/patches/linux-6.1/ext4-prealloc.patch b/ldiskfs/kernel_patches/patches/linux-6.1/ext4-prealloc.patch
new file mode 100644 (file)
index 0000000..5ab5b10
--- /dev/null
@@ -0,0 +1,421 @@
+commit d8d8fd9192a54c7b8caef8cca9b7a1eb5e5e3298
+Author: Alex Zhuravlev <alex.zhuravlev@sun.com>
+AuthorDate: Thu Oct 23 10:02:19 2008 +0000
+Subject: ext4: support for tunable preallocation window
+Add support for tunable preallocation window and new tunables
+for large/small requests.
+Bugzilla-ID: b=12800
+Signed-off-by: Alex Zhuravlev <alex.zhuravlev@sun.com>
+Reviewed-by: Kalpak Shah <kalpak@clusterfs.com>
+Reviewed-by: Andreas Dilger <andreas.dilger@sun.com>
+---
+ fs/ext4/ext4.h    |   7 +-
+ fs/ext4/inode.c   |   3 +
+ fs/ext4/mballoc.c | 222 +++++++++++++++++++++++++++++++++++-----------
+ fs/ext4/sysfs.c   |   8 +-
+ 4 files changed, 183 insertions(+), 57 deletions(-)
+
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index bea49272..61ebcacd 100644
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1304,6 +1304,8 @@ extern void mb_set_bits(void *bm, int cur, int len);
+ #define EXT4_DFL_MAX_MNT_COUNT                20      /* Allow 20 mounts */
+ #define EXT4_DFL_CHECKINTERVAL                0       /* Don't use interval check */
++#define EXT4_MAX_PREALLOC_TABLE       64
++
+ /*
+  * Behaviour when detecting errors
+  */
+@@ -1611,11 +1613,13 @@ struct ext4_sb_info {
+       /* tunables */
+       unsigned long s_stripe;
+       unsigned int s_mb_max_linear_groups;
+-      unsigned int s_mb_stream_request;
++      unsigned long s_mb_small_req;
++      unsigned long s_mb_large_req;
+       unsigned int s_mb_max_to_scan;
+       unsigned int s_mb_min_to_scan;
+       unsigned int s_mb_stats;
+       unsigned int s_mb_order2_reqs;
++      unsigned long *s_mb_prealloc_table;
+       unsigned int s_mb_group_prealloc;
+       unsigned int s_mb_max_inode_prealloc;
+       unsigned int s_max_dir_size_kb;
+@@ -2908,6 +2912,7 @@ int ext4_fc_record_regions(struct super_block *sb, int ino,
+                          int len, int replay);
+ /* mballoc.c */
++extern const struct proc_ops ext4_seq_prealloc_table_fops;
+ extern const struct seq_operations ext4_mb_seq_groups_ops;
+ extern const struct seq_operations ext4_mb_seq_structs_summary_ops;
+ extern long ext4_mb_stats;
+diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
+index 3591aab5..98f0dc0e 100644
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -2757,6 +2757,9 @@ static int ext4_writepages(struct address_space *mapping,
+                                               PAGE_SIZE >> inode->i_blkbits);
+       }
++      if (wbc->nr_to_write < sbi->s_mb_small_req)
++              wbc->nr_to_write = sbi->s_mb_small_req;
++
+       if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
+               range_whole = 1;
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 32d88757..ac8e1fe1 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -3035,6 +3035,99 @@ const struct seq_operations ext4_mb_seq_structs_summary_ops = {
+       .show   = ext4_mb_seq_structs_summary_show,
+ };
++static int ext4_mb_check_and_update_prealloc(struct ext4_sb_info *sbi,
++                                               char *str, size_t cnt,
++                                               int update)
++{
++      unsigned long value;
++      unsigned long prev = 0;
++      char *cur;
++      char *next;
++      char *end;
++      int num = 0;
++
++      cur = str;
++      end = str + cnt;
++      while (cur < end) {
++              while ((cur < end) && (*cur == ' ')) cur++;
++              value = simple_strtol(cur, &next, 0);
++              if (value == 0)
++                      break;
++              if (cur == next)
++                      return -EINVAL;
++
++              cur = next;
++
++              if (value > (sbi->s_blocks_per_group - 1 - 1 - sbi->s_itb_per_group))
++                      return -EINVAL;
++
++              /* they should add values in order */
++              if (value <= prev)
++                      return -EINVAL;
++
++              if (update)
++                      sbi->s_mb_prealloc_table[num] = value;
++
++              prev = value;
++              num++;
++      }
++
++      if (num > EXT4_MAX_PREALLOC_TABLE - 1)
++              return -EOVERFLOW;
++
++      if (update)
++              sbi->s_mb_prealloc_table[num] = 0;
++
++      return 0;
++}
++
++static ssize_t ext4_mb_prealloc_table_proc_write(struct file *file,
++                                           const char __user *buf,
++                                           size_t cnt, loff_t *pos)
++{
++      struct ext4_sb_info *sbi = EXT4_SB(pde_data(file_inode(file)));
++      char str[128];
++      int rc;
++
++      if (cnt >= sizeof(str))
++              return -EINVAL;
++      if (copy_from_user(str, buf, cnt))
++              return -EFAULT;
++
++      rc = ext4_mb_check_and_update_prealloc(sbi, str, cnt, 0);
++      if (rc)
++              return rc;
++
++      rc = ext4_mb_check_and_update_prealloc(sbi, str, cnt, 1);
++      return rc ? rc : cnt;
++}
++
++static int mb_prealloc_table_seq_show(struct seq_file *m, void *v)
++{
++      struct ext4_sb_info *sbi = EXT4_SB(m->private);
++      int i;
++
++      for (i = 0; i < EXT4_MAX_PREALLOC_TABLE &&
++                      sbi->s_mb_prealloc_table[i] != 0; i++)
++              seq_printf(m, "%ld ", sbi->s_mb_prealloc_table[i]);
++      seq_printf(m, "\n");
++
++      return 0;
++}
++
++static int mb_prealloc_table_seq_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, mb_prealloc_table_seq_show, pde_data(inode));
++}
++
++const struct proc_ops ext4_seq_prealloc_table_fops = {
++      .proc_open      = mb_prealloc_table_seq_open,
++      .proc_read      = seq_read,
++      .proc_lseek     = seq_lseek,
++      .proc_release   = single_release,
++      .proc_write     = ext4_mb_prealloc_table_proc_write,
++};
++
+ static struct kmem_cache *get_groupinfo_cache(int blocksize_bits)
+ {
+       int cache_index = blocksize_bits - EXT4_MIN_BLOCK_LOG_SIZE;
+@@ -3352,7 +3445,7 @@ static void ext4_discard_work(struct work_struct *work)
+ int ext4_mb_init(struct super_block *sb)
+ {
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+-      unsigned i, j;
++      unsigned i, j, k, l;
+       unsigned offset, offset_incr;
+       unsigned max;
+       int ret;
+@@ -3440,7 +3533,6 @@ int ext4_mb_init(struct super_block *sb)
+       sbi->s_mb_max_to_scan = MB_DEFAULT_MAX_TO_SCAN;
+       sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN;
+       sbi->s_mb_stats = MB_DEFAULT_STATS;
+-      sbi->s_mb_stream_request = MB_DEFAULT_STREAM_THRESHOLD;
+       sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS;
+       sbi->s_mb_max_inode_prealloc = MB_DEFAULT_MAX_INODE_PREALLOC;
+       /*
+@@ -3465,9 +3557,29 @@ int ext4_mb_init(struct super_block *sb)
+        * RAID stripe size so that preallocations don't fragment
+        * the stripes.
+        */
+-      if (sbi->s_stripe > 1) {
+-              sbi->s_mb_group_prealloc = roundup(
+-                      sbi->s_mb_group_prealloc, sbi->s_stripe);
++
++      /* Allocate table once */
++      sbi->s_mb_prealloc_table = kzalloc(
++              EXT4_MAX_PREALLOC_TABLE * sizeof(unsigned long), GFP_NOFS);
++      if (sbi->s_mb_prealloc_table == NULL) {
++              ret = -ENOMEM;
++              goto out;
++      }
++
++      if (sbi->s_stripe == 0) {
++              for (k = 0, l = 4; k <= 9; ++k, l *= 2)
++                      sbi->s_mb_prealloc_table[k] = l;
++
++              sbi->s_mb_small_req = 256;
++              sbi->s_mb_large_req = 1024;
++              sbi->s_mb_group_prealloc = 512;
++      } else {
++              for (k = 0, l = sbi->s_stripe; k <= 2; ++k, l *= 2)
++                      sbi->s_mb_prealloc_table[k] = l;
++
++              sbi->s_mb_small_req = sbi->s_stripe;
++              sbi->s_mb_large_req = sbi->s_stripe * 8;
++              sbi->s_mb_group_prealloc = sbi->s_stripe * 4;
+       }
+       sbi->s_locality_groups = alloc_percpu(struct ext4_locality_group);
+@@ -3503,6 +3615,7 @@ out:
+       kfree(sbi->s_mb_avg_fragment_size_locks);
+       kfree(sbi->s_mb_largest_free_orders);
+       kfree(sbi->s_mb_largest_free_orders_locks);
++      kfree(sbi->s_mb_prealloc_table);
+       kfree(sbi->s_mb_offsets);
+       sbi->s_mb_offsets = NULL;
+       kfree(sbi->s_mb_maxs);
+@@ -3775,7 +3888,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
+       int err, len;
+       BUG_ON(ac->ac_status != AC_STATUS_FOUND);
+-      BUG_ON(ac->ac_b_ex.fe_len <= 0);
+       sb = ac->ac_sb;
+       sbi = EXT4_SB(sb);
+@@ -4019,13 +4131,14 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
+ {
+       struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb);
+       struct ext4_super_block *es = sbi->s_es;
+-      int bsbits, max;
++      int bsbits, i, wind;
+       ext4_lblk_t end;
+-      loff_t size, start_off;
++      loff_t size;
+       loff_t orig_size __maybe_unused;
+       ext4_lblk_t start;
+       struct ext4_inode_info *ei = EXT4_I(ac->ac_inode);
+       struct ext4_prealloc_space *pa;
++      unsigned long value, last_non_zero;
+       /* do normalize only data requests, metadata requests
+          do not need preallocation */
+@@ -4054,51 +4167,46 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
+       size = size << bsbits;
+       if (size < i_size_read(ac->ac_inode))
+               size = i_size_read(ac->ac_inode);
+-      orig_size = size;
++      size = (size + ac->ac_sb->s_blocksize - 1) >> bsbits;
++
++      start = wind = 0;
++      value = last_non_zero = 0;
+-      /* max size of free chunks */
+-      max = 2 << bsbits;
+-
+-#define NRL_CHECK_SIZE(req, size, max, chunk_size)    \
+-              (req <= (size) || max <= (chunk_size))
+-
+-      /* first, try to predict filesize */
+-      /* XXX: should this table be tunable? */
+-      start_off = 0;
+-      if (size <= 16 * 1024) {
+-              size = 16 * 1024;
+-      } else if (size <= 32 * 1024) {
+-              size = 32 * 1024;
+-      } else if (size <= 64 * 1024) {
+-              size = 64 * 1024;
+-      } else if (size <= 128 * 1024) {
+-              size = 128 * 1024;
+-      } else if (size <= 256 * 1024) {
+-              size = 256 * 1024;
+-      } else if (size <= 512 * 1024) {
+-              size = 512 * 1024;
+-      } else if (size <= 1024 * 1024) {
+-              size = 1024 * 1024;
+-      } else if (NRL_CHECK_SIZE(size, 4 * 1024 * 1024, max, 2 * 1024)) {
+-              start_off = ((loff_t)ac->ac_o_ex.fe_logical >>
+-                                              (21 - bsbits)) << 21;
+-              size = 2 * 1024 * 1024;
+-      } else if (NRL_CHECK_SIZE(size, 8 * 1024 * 1024, max, 4 * 1024)) {
+-              start_off = ((loff_t)ac->ac_o_ex.fe_logical >>
+-                                                      (22 - bsbits)) << 22;
+-              size = 4 * 1024 * 1024;
+-      } else if (NRL_CHECK_SIZE(ac->ac_o_ex.fe_len,
+-                                      (8<<20)>>bsbits, max, 8 * 1024)) {
+-              start_off = ((loff_t)ac->ac_o_ex.fe_logical >>
+-                                                      (23 - bsbits)) << 23;
+-              size = 8 * 1024 * 1024;
++      /* let's choose preallocation window depending on file size */
++      for (i = 0; i < EXT4_MAX_PREALLOC_TABLE; i++) {
++              value = sbi->s_mb_prealloc_table[i];
++              if (value == 0)
++                      break;
++              else
++                      last_non_zero = value;
++
++              if (size <= value) {
++                      wind = value;
++                      break;
++              }
++      }
++
++      if (wind == 0) {
++              if (last_non_zero != 0) {
++                      __u64 tstart, tend;
++                      /* file is quite large, we now preallocate with
++                       * the biggest configured window with regart to
++                       * logical offset */
++                      wind = last_non_zero;
++                      tstart = ac->ac_o_ex.fe_logical;
++                      do_div(tstart, wind);
++                      start = tstart * wind;
++                      tend = ac->ac_o_ex.fe_logical + ac->ac_o_ex.fe_len - 1;
++                      do_div(tend, wind);
++                      tend = tend * wind + wind;
++                      size = tend - start;
++              }
+       } else {
+-              start_off = (loff_t) ac->ac_o_ex.fe_logical << bsbits;
+-              size      = (loff_t) EXT4_C2B(EXT4_SB(ac->ac_sb),
+-                                            ac->ac_o_ex.fe_len) << bsbits;
++              size = wind;
+       }
+-      size = size >> bsbits;
+-      start = start_off >> bsbits;
++
++
++      orig_size = size;
+       /*
+        * For tiny groups (smaller than 8MB) the chosen allocation
+@@ -4204,7 +4312,6 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
+                        (unsigned long) ac->ac_o_ex.fe_logical);
+               BUG();
+       }
+-      BUG_ON(size <= 0 || size > EXT4_BLOCKS_PER_GROUP(ac->ac_sb));
+       /* now prepare goal request */
+@@ -4232,7 +4339,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
+               ac->ac_flags |= EXT4_MB_HINT_TRY_GOAL;
+       }
+-      mb_debug(ac->ac_sb, "goal: %lld(was %lld) blocks at %u\n", size,
++      mb_debug(ac->ac_sb, "goal:  %lld(was %lld) blocks at %u\n", size,
+                orig_size, start);
+ }
+@@ -5237,8 +5344,8 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac)
+               inode_pa_eligible = false;
+       size = max(size, isize);
+-      /* Don't use group allocation for large files */
+-      if (size > sbi->s_mb_stream_request)
++      if ((ac->ac_o_ex.fe_len >= sbi->s_mb_small_req) ||
++          (size >= sbi->s_mb_large_req))
+               group_pa_eligible = false;
+       if (!group_pa_eligible) {
+@@ -5249,6 +5356,13 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac)
+               return;
+       }
++      /*
++       * request is so large that we don't care about
++       * streaming - it overweights any possible seek
++       */
++      if (ac->ac_o_ex.fe_len >= sbi->s_mb_large_req)
++              return;
++
+       BUG_ON(ac->ac_lg != NULL);
+       /*
+        * locality group prealloc space are per cpu. The reason for having
+diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
+index e2b8b343..4f5f2487 100644
+--- a/fs/ext4/sysfs.c
++++ b/fs/ext4/sysfs.c
+@@ -212,7 +212,8 @@ EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats);
+ EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan);
+ EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan);
+ EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
+-EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
++EXT4_RW_ATTR_SBI_UI(mb_small_req, s_mb_small_req);
++EXT4_RW_ATTR_SBI_UI(mb_large_req, s_mb_large_req);
+ EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
+ EXT4_RW_ATTR_SBI_UI(mb_max_inode_prealloc, s_mb_max_inode_prealloc);
+ EXT4_RW_ATTR_SBI_UI(mb_max_linear_groups, s_mb_max_linear_groups);
+@@ -262,7 +263,8 @@ static struct attribute *ext4_attrs[] = {
+       ATTR_LIST(mb_max_to_scan),
+       ATTR_LIST(mb_min_to_scan),
+       ATTR_LIST(mb_order2_req),
+-      ATTR_LIST(mb_stream_req),
++      ATTR_LIST(mb_small_req),
++      ATTR_LIST(mb_large_req),
+       ATTR_LIST(mb_group_prealloc),
+       ATTR_LIST(mb_max_inode_prealloc),
+       ATTR_LIST(mb_max_linear_groups),
+@@ -548,6 +550,8 @@ int ext4_register_sysfs(struct super_block *sb)
+                                       ext4_fc_info_show, sb);
+               proc_create_seq_data("mb_groups", S_IRUGO, sbi->s_proc,
+                               &ext4_mb_seq_groups_ops, sb);
++              proc_create_data("prealloc_table", S_IRUGO, sbi->s_proc,
++                              &ext4_seq_prealloc_table_fops, sb);
+               proc_create_single_data("mb_stats", 0444, sbi->s_proc,
+                               ext4_seq_mb_stats_show, sb);
+               proc_create_seq_data("mb_structs_summary", 0444, sbi->s_proc,
+-- 
+2.34.1
+
diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.8.0-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-5.8.0-ml.series
deleted file mode 100644 (file)
index 73aec00..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-rhel8/ext4-inode-version.patch
-linux-5.4/ext4-lookup-dotdot.patch
-suse15/ext4-print-inum-in-htree-warning.patch
-linux-5.8/ext4-prealloc.patch
-ubuntu18/ext4-osd-iop-common.patch
-linux-5.8/ext4-misc.patch
-linux-5.8/ext4-mballoc-extra-checks.patch
-linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch
-linux-5.8/ext4-kill-dx-root.patch
-linux-5.8/ext4-mballoc-pa-free-mismatch.patch
-linux-5.8/ext4-data-in-dirent.patch
-rhel8/ext4-nocmtime.patch
-base/ext4-htree-lock.patch
-linux-5.8/ext4-pdirop.patch
-linux-5.8/ext4-max-dir-size.patch
-linux-5.8/ext4-corrupted-inode-block-bitmaps-handling-patches.patch
-linux-5.4/ext4-give-warning-with-dir-htree-growing.patch
-ubuntu18/ext4-jcb-optimization.patch
-linux-5.4/ext4-attach-jinode-in-writepages.patch
-rhel8/ext4-dont-check-before-replay.patch
-rhel7.6/ext4-use-GFP_NOFS-in-ext4_inode_attach_jinode.patch
-rhel7.6/ext4-export-orphan-add.patch
-linux-5.8/ext4-export-mb-stream-allocator-variables.patch
-ubuntu19/ext4-iget-with-flags.patch
-linux-5.4/export-ext4fs-dirhash-helper.patch
-linux-5.4/ext4-misc.patch
-linux-5.8/ext4-simple-blockalloc.patch
-rhel8.3/ext4-xattr-disable-credits-check.patch
-linux-5.8/ext4-no-max-dir-size-limit-for-iam-objects.patch
-rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
-base/ext4-projid-xattrs.patch
-linux-5.8/ext4-enc-flag.patch
-rhel8/ext4-old_ea_inodes_handling_fix.patch
-rhel8/ext4-encdata.patch
diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.9.0-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-5.9.0-ml.series
deleted file mode 100644 (file)
index a3360a8..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-rhel8/ext4-inode-version.patch
-linux-5.4/ext4-lookup-dotdot.patch
-suse15/ext4-print-inum-in-htree-warning.patch
-linux-5.8/ext4-prealloc.patch
-ubuntu18/ext4-osd-iop-common.patch
-linux-5.8/ext4-misc.patch
-linux-5.8/ext4-mballoc-extra-checks.patch
-linux-5.4/ext4-hash-indexed-dir-dotdot-update.patch
-linux-5.8/ext4-kill-dx-root.patch
-linux-5.8/ext4-mballoc-pa-free-mismatch.patch
-linux-5.8/ext4-data-in-dirent.patch
-rhel8/ext4-nocmtime.patch
-base/ext4-htree-lock.patch
-linux-5.8/ext4-pdirop.patch
-linux-5.8/ext4-max-dir-size.patch
-linux-5.8/ext4-corrupted-inode-block-bitmaps-handling-patches.patch
-linux-5.4/ext4-give-warning-with-dir-htree-growing.patch
-ubuntu18/ext4-jcb-optimization.patch
-linux-5.4/ext4-attach-jinode-in-writepages.patch
-rhel8/ext4-dont-check-before-replay.patch
-rhel7.6/ext4-use-GFP_NOFS-in-ext4_inode_attach_jinode.patch
-rhel7.6/ext4-export-orphan-add.patch
-linux-5.8/ext4-export-mb-stream-allocator-variables.patch
-ubuntu19/ext4-iget-with-flags.patch
-linux-5.4/export-ext4fs-dirhash-helper.patch
-linux-5.4/ext4-misc.patch
-linux-5.9/ext4-simple-blockalloc.patch
-linux-5.8/ext4-no-max-dir-size-limit-for-iam-objects.patch
-rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
-base/ext4-projid-xattrs.patch
-linux-5.8/ext4-enc-flag.patch
-rhel8/ext4-old_ea_inodes_handling_fix.patch
-rhel8/ext4-encdata.patch
diff --git a/ldiskfs/kernel_patches/series/ldiskfs-6.1.38-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-6.1.38-ml.series
new file mode 100644 (file)
index 0000000..f0eb25f
--- /dev/null
@@ -0,0 +1,38 @@
+linux-5.16/ext4-inode-version.patch
+linux-5.18/ext4-lookup-dotdot.patch
+linux-5.14/ext4-print-inum-in-htree-warning.patch
+linux-6.1/ext4-prealloc.patch
+linux-5.16/ext4-osd-iop-common.patch
+linux-5.16/ext4-misc.patch
+linux-6.1/ext4-mballoc-extra-checks.patch
+sles15sp4/ext4-hash-indexed-dir-dotdot-update.patch
+linux-5.14/ext4-kill-dx-root.patch
+linux-5.18/ext4-mballoc-pa-free-mismatch.patch
+linux-6.0/ext4-data-in-dirent.patch
+rhel8/ext4-nocmtime.patch
+base/ext4-htree-lock.patch
+linux-6.0/ext4-pdirop.patch
+rhel9/ext4-max-dir-size.patch
+linux-5.16/ext4-corrupted-inode-block-bitmaps-handling-patches.patch
+rhel9/ext4-give-warning-with-dir-htree-growing.patch
+ubuntu18/ext4-jcb-optimization.patch
+linux-5.10/ext4-attach-jinode-in-writepages.patch
+linux-6.1/ext4-dont-check-before-replay.patch
+rhel7.6/ext4-use-GFP_NOFS-in-ext4_inode_attach_jinode.patch
+rhel7.6/ext4-export-orphan-add.patch
+linux-5.18/ext4-export-mb-stream-allocator-variables.patch
+ubuntu19/ext4-iget-with-flags.patch
+linux-5.14/export-ext4fs-dirhash-helper.patch
+linux-5.8/ext4-no-max-dir-size-limit-for-iam-objects.patch
+rhel9/ext4-dquot-commit-speedup.patch
+linux-5.14/ext4-ialloc-uid-gid-and-pass-owner-down.patch
+linux-5.14/ext4-projid-xattrs.patch
+rhel9.1/ext4-delayed-iput.patch
+rhel8/ext4-ext-merge.patch
+linux-5.14/ext4-xattr-disable-credits-check.patch
+rhel9.2/ext4-fiemap-kernel-data.patch
+rhel8/ext4-old_ea_inodes_handling_fix.patch
+ubuntu20.04.5/ext4-filename-encode.patch
+rhel9.1/ext4-enc-flag.patch
+rhel9.2/ext4-encdata.patch
+rhel9/ext4-add-periodic-superblock-update.patch