From: Chao Yu <chao@kernel.org>
To: jaegeuk@kernel.org
Cc: linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, Chao Yu <chao@kernel.org>
Subject: [PATCH v2 2/2] f2fs: reduce memory footprint of ino management
Date: Sun, 9 Aug 2026 09:26:39 +0800 [thread overview]
Message-ID: <20260809012639.568485-2-chao@kernel.org> (raw)
In-Reply-To: <20260809012639.568485-1-chao@kernel.org>
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 <chao@kernel.org>
---
v2:
- fix to call __remove_ino_entry correctly for FLUSH_INO
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..f81ba8cc861a 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(slot);
+ 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(slot);
+ 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 160e7db0674e..5ae2d347c3ce 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -389,14 +389,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
WARNING: multiple messages have this Message-ID (diff)
From: Chao Yu via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: jaegeuk@kernel.org
Cc: linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net
Subject: [f2fs-dev] [PATCH v2 2/2] f2fs: reduce memory footprint of ino management
Date: Sun, 9 Aug 2026 09:26:39 +0800 [thread overview]
Message-ID: <20260809012639.568485-2-chao@kernel.org> (raw)
In-Reply-To: <20260809012639.568485-1-chao@kernel.org>
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 <chao@kernel.org>
---
v2:
- fix to call __remove_ino_entry correctly for FLUSH_INO
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..f81ba8cc861a 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(slot);
+ 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(slot);
+ 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 160e7db0674e..5ae2d347c3ce 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -389,14 +389,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
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
next prev parent reply other threads:[~2026-08-09 1:26 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-09 1:26 [PATCH v2 1/2] f2fs: unify add/remove ino entry API for all ino types Chao Yu
2026-08-09 1:26 ` [f2fs-dev] " Chao Yu via Linux-f2fs-devel
2026-08-09 1:26 ` Chao Yu [this message]
2026-08-09 1:26 ` [f2fs-dev] [PATCH v2 2/2] f2fs: reduce memory footprint of ino management Chao Yu via Linux-f2fs-devel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260809012639.568485-2-chao@kernel.org \
--to=chao@kernel.org \
--cc=jaegeuk@kernel.org \
--cc=linux-f2fs-devel@lists.sourceforge.net \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.