[f2fs-dev] [PATCH v3 2/2] f2fs: reduce memory footprint of ino management

Chao Yu via Linux-f2fs-devel <[email protected]>
Newsgroups net.sourceforge.lists.linux-f2fs-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Currently, ino entries for APPEND_INO, UPDATE_INO, TRANS_DIR_INO, and
XATTR_DIR_INO allocate a 'struct ino_entry' slab object and attach it to
both a list and a radix tree solely for existence checks via
f2fs_exist_written_data().

Since these ino types only track binary existence status, we can embed
the information directly into radix tree value entries as a bitmap:

- The Linux radix tree/XArray supports in-place value entries via
  xa_mk_value() / xa_to_value(), which tag the least significant bit
  to store an unallocated integer value of BITS_PER_XA_VALUE bits
  (BITS_PER_LONG - 1) directly in the slot pointer.
- For each inode, (ino / BITS_PER_XA_VALUE) serves as the radix tree
  slot index, and (ino % BITS_PER_XA_VALUE) is used as the bit offset
  within the slot's bitmap.

For example, when tracking ino = 7:
- Before: Allocate a 'struct ino_entry' ({ .ino = 7 }), insert its
  pointer into the radix tree at index = 7, and link it to im->ino_list.
- After: Compute slot_index = 7 / BITS_PER_XA_VALUE (index 0) and
  bit_offset = 7 % BITS_PER_XA_VALUE (bit 7), then set bit 7 in the
  value entry via xa_mk_value(bitmap) at index 0, without allocating
  a slab object or linking to a list.

Additionally:
- In-place slot updates are performed via radix_tree_replace_slot(), and
  slots are deleted with radix_tree_delete() once the bitmap is zeroed.
- Reorder the ino list enum so ORPHAN_INO and FLUSH_INO (which still
  require struct ino_entry and list traversal) remain separated, while
  bitmap-based trees are torn down using xa_destroy().

This eliminates 'struct ino_entry' slab allocations and linked-list
tracking for these ino types, significantly reducing memory consumption.

Signed-off-by: Chao Yu <[email protected]>
---
v3:
- fix warning reported by syzbot
https://lore.kernel.org/linux-f2fs-devel/[email protected]
 fs/f2fs/checkpoint.c | 99 ++++++++++++++++++++++++++++++++++++++++----
 fs/f2fs/f2fs.h       |  6 ++-
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index eb2f955b1e2a..c73999e39a39 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -766,6 +766,64 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
 	spin_unlock(&im->ino_lock);
 }
 
+static void __set_ino_bitmap(struct f2fs_sb_info *sbi, nid_t ino, int type)
+{
+	struct inode_management *im = &sbi->im[type];
+	unsigned long index = INO_SLOT_INDEX(ino);
+	unsigned int ofs = INO_BIT_OFFSET(ino);
+	void **slot, *entry;
+	unsigned long bitmap = 0;
+	int ret;
+
+	ret = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL);
+	f2fs_bug_on(sbi, ret);
+
+	spin_lock(&im->ino_lock);
+	slot = radix_tree_lookup_slot(&im->ino_root, index);
+	if (slot) {
+		entry = radix_tree_deref_slot_protected(slot, &im->ino_lock);
+		bitmap = xa_to_value(entry);
+		if (!(bitmap & (1UL << ofs))) {
+			bitmap |= (1UL << ofs);
+			entry = xa_mk_value(bitmap);
+			radix_tree_replace_slot(&im->ino_root, slot, entry);
+		}
+	} else {
+		bitmap |= (1UL << ofs);
+		entry = xa_mk_value(bitmap);
+		if (unlikely(radix_tree_insert(&im->ino_root, index, entry)))
+			f2fs_bug_on(sbi, 1);
+	}
+	spin_unlock(&im->ino_lock);
+	radix_tree_preload_end();
+}
+
+static void __clear_ino_bitmap(struct f2fs_sb_info *sbi, nid_t ino, int type)
+{
+	struct inode_management *im = &sbi->im[type];
+	unsigned long index = INO_SLOT_INDEX(ino);
+	unsigned int ofs = INO_BIT_OFFSET(ino);
+	void **slot, *entry;
+	unsigned long bitmap;
+
+	spin_lock(&im->ino_lock);
+	slot = radix_tree_lookup_slot(&im->ino_root, index);
+	if (slot) {
+		entry = radix_tree_deref_slot_protected(slot, &im->ino_lock);
+		bitmap = xa_to_value(entry);
+		if (bitmap & (1UL << ofs))
+			bitmap &= ~(1UL << ofs);
+
+		if (bitmap) {
+			entry = xa_mk_value(bitmap);
+			radix_tree_replace_slot(&im->ino_root, slot, entry);
+		} else {
+			radix_tree_delete(&im->ino_root, index);
+		}
+	}
+	spin_unlock(&im->ino_lock);
+}
+
 static void f2fs_wait_for_inode_record(struct f2fs_sb_info *sbi, int mode)
 {
 	if (mode != APPEND_INO && mode != UPDATE_INO)
@@ -778,8 +836,10 @@ static void f2fs_wait_for_inode_record(struct f2fs_sb_info *sbi, int mode)
 static void __f2fs_add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino,
 					unsigned int devidx, int type)
 {
-	/* add new dirty ino entry into list */
-	__add_ino_entry(sbi, ino, devidx, type);
+	if (type <= FLUSH_INO)
+		__add_ino_entry(sbi, ino, devidx, type);
+	else
+		__set_ino_bitmap(sbi, ino, type);
 }
 
 void f2fs_add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
@@ -789,20 +849,33 @@ void f2fs_add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
 
 void f2fs_remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
 {
-	/* remove dirty ino entry from list */
-	__remove_ino_entry(sbi, ino, type);
+	if (type <= FLUSH_INO)
+		__remove_ino_entry(sbi, ino, type);
+	else
+		__clear_ino_bitmap(sbi, ino, type);
 }
 
-/* mode should be APPEND_INO, UPDATE_INO or TRANS_DIR_INO */
+/* mode should be APPEND_INO, UPDATE_INO, TRANS_DIR_INO and XATTR_DIR_INO */
 bool f2fs_exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode)
 {
 	struct inode_management *im = &sbi->im[mode];
-	struct ino_entry *e;
+	unsigned long index = INO_SLOT_INDEX(ino);
+	unsigned int ofs = INO_BIT_OFFSET(ino);
+	void *entry;
+	unsigned long bitmap;
+
+	f2fs_bug_on(sbi, mode <= FLUSH_INO);
 
 	spin_lock(&im->ino_lock);
-	e = radix_tree_lookup(&im->ino_root, ino);
+	entry = radix_tree_lookup(&im->ino_root, index);
+	if (!entry) {
+		spin_unlock(&im->ino_lock);
+		return false;
+	}
+	bitmap = xa_to_value(entry);
 	spin_unlock(&im->ino_lock);
-	return e ? true : false;
+
+	return bitmap & (1UL << ofs);
 }
 
 void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
@@ -810,7 +883,7 @@ void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
 	struct ino_entry *e, *tmp;
 	int i;
 
-	for (i = all ? ORPHAN_INO : APPEND_INO; i < MAX_INO_ENTRY; i++) {
+	for (i = all ? ORPHAN_INO : FLUSH_INO; i <= FLUSH_INO; i++) {
 		struct inode_management *im = &sbi->im[i];
 
 		f2fs_wait_for_inode_record(sbi, i);
@@ -824,6 +897,14 @@ void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
 		}
 		spin_unlock(&im->ino_lock);
 	}
+
+	for (i = APPEND_INO; i < MAX_INO_ENTRY; i++) {
+		struct inode_management *im = &sbi->im[i];
+
+		spin_lock(&im->ino_lock);
+		xa_destroy(&im->ino_root);
+		spin_unlock(&im->ino_lock);
+	}
 }
 
 void f2fs_set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino,
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index c925b9207a37..1b96d8718c5c 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -388,14 +388,18 @@ enum {
 /* for the list of ino */
 enum {
 	ORPHAN_INO,		/* for orphan ino list */
+	FLUSH_INO,		/* for multiple device flushing */
 	APPEND_INO,		/* for append ino list */
 	UPDATE_INO,		/* for update ino list */
 	TRANS_DIR_INO,		/* for transactions dir ino list */
 	XATTR_DIR_INO,		/* for xattr updated dir ino list */
-	FLUSH_INO,		/* for multiple device flushing */
 	MAX_INO_ENTRY,		/* max. list */
 };
 
+#define INO_BITS_PER_SLOT	BITS_PER_XA_VALUE
+#define INO_SLOT_INDEX(ino)	((ino) / INO_BITS_PER_SLOT)
+#define INO_BIT_OFFSET(ino)	((ino) % INO_BITS_PER_SLOT)
+
 struct ino_entry {
 	struct list_head list;		/* list head */
 	nid_t ino;			/* inode number */
-- 
2.49.0



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.