[PATCH 3/6] rpdfs: add basic file data initialization

Valerie Aurora <[email protected]> Thu, 7 May 2026 15:21:50 +0200
Newsgroups dev.linux.lists.rpdfs-devel
Message-ID <[email protected]>
Initialize the file data root, plus a few simple routines to calculate
mapping block levels. Rename the field in the inode from "data" to
"data_root" to avoid confusion with the "data" member of struct
rpdfs_block_handle.

Signed-off-by: Valerie Aurora <[email protected]>
---
 fs/rpdfs/Makefile       |  1 +
 fs/rpdfs/data.c         | 93 +++++++++++++++++++++++++++++++++++++++++
 fs/rpdfs/data.h         | 11 +++++
 fs/rpdfs/format-block.h | 32 +++++++-------
 fs/rpdfs/inode.c        |  8 ++++
 fs/rpdfs/inode.h        |  1 +
 6 files changed, 129 insertions(+), 17 deletions(-)
 create mode 100644 fs/rpdfs/data.c
 create mode 100644 fs/rpdfs/data.h

diff --git a/fs/rpdfs/Makefile b/fs/rpdfs/Makefile
index d995103ea5a8..f8301d86682b 100644
--- a/fs/rpdfs/Makefile
+++ b/fs/rpdfs/Makefile
@@ -12,6 +12,7 @@ rpdfs-y	:= balloc.o \
            block.o \
 	   btree.o \
 	   btree_txn.o \
+	   data.o \
 	   dir.o \
 	   file.o \
 	   ht.o \
diff --git a/fs/rpdfs/data.c b/fs/rpdfs/data.c
new file mode 100644
index 000000000000..65b753886a04
--- /dev/null
+++ b/fs/rpdfs/data.c
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/gfp.h>
+#include <linux/writeback.h>
+#include <linux/build_bug.h>
+
+#include "balloc.h"
+#include "inode.h"
+#include "format-block.h"
+#include "pr.h"
+#include "super.h"
+#include "data.h"
+
+/*
+ * File data is stored in simple tree of mapping blocks with data all
+ * at the same level of the tree. The topmost block in the tree and its
+ * level are stored in the inode. The tree is sparse and branches are
+ * grown as necessary to index newly written data blocks. While
+ * manipulating the tree, we use levels to identify the blocks at
+ * various levels of the tree, with the highest levels closer to the
+ * root.
+ *
+ * The data root field in the inode contains both the persistent
+ * reference (block number, etc.) to the root block of the tree, plus
+ * the height of the tree (the level of the block it points to, plus 1).
+ * The root block reference is not part of a mapping block.
+ *
+ * The level of a block is:
+ *
+ * 0 = data block
+ * 1 = references to data blocks
+ * 2 = references to single mapping blocks (pointing to data blocks)
+ * 3 = references to double mapping blocks
+ * 4 = references to triple mapping blocks
+ *
+ * Thus a data root reference with height 1 points to a block of level 0
+ * = a single block of data at logical file offset 0.
+ */
+
+/*
+ * Return the logical block number containing offset within a file.
+ */
+static u64 lblk_from_offset(u64 offset)
+{
+	return offset >> RPDFS_BLOCK_SHIFT;
+}
+
+/*
+ * Calculate the index of the block reference for this logical block
+ * within a mapping block at this level.
+ */
+static u32 calc_ref_ind(u64 lblk, u8 level)
+{
+	u8 ind;
+
+	BUG_ON(level == 0);
+
+	BUILD_BUG_ON_NOT_POWER_OF_2(RPDFS_DATA_REFS_PER_BLK);
+
+	ind = (lblk >> (level - 1) * RPDFS_DATA_REFS_PER_BLK_SHIFT) & RPDFS_DATA_REFS_PER_BLK_MASK;
+
+	rpdfs_prd("lblk %llu level %u ind %u refs_per_blk %llu", lblk, level, ind, RPDFS_DATA_REFS_PER_BLK);
+
+	return ind;
+}
+
+/*
+ * Calculate the height of the tree needed to index the logical block
+ * lblk in this file. This is stored in the inode's data tree root.
+ */
+static u8 height_from_lblk(u64 lblk)
+{
+	u8 height;
+
+	if (lblk == 0)
+		return 1;
+
+	height = ((fls(lblk) - 1)/RPDFS_DATA_REFS_PER_BLK_SHIFT) + 2;
+
+	rpdfs_prd("lblk %llu fls(lblk) - 1 %u refs_per_blk_shift %u + 2 = height %u",
+		  lblk, fls(lblk) - 1, RPDFS_DATA_REFS_PER_BLK_SHIFT, height);
+
+	return height;
+}
+
+void rpdfs_data_root_init(struct rpdfs_data_root *data)
+{
+	data->height = 0;
+	data->ref.bnr = 0;
+	data->ref.alloc_counter = 0;
+}
diff --git a/fs/rpdfs/data.h b/fs/rpdfs/data.h
new file mode 100644
index 000000000000..bb6b0c7dea86
--- /dev/null
+++ b/fs/rpdfs/data.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef RPDFS_DATA_H
+#define RPDFS_DATA_H
+
+#include "format-block.h"
+
+void rpdfs_data_root_init(struct rpdfs_data_root *data);
+
+extern const struct address_space_operations rpdfs_aops;
+
+#endif
diff --git a/fs/rpdfs/format-block.h b/fs/rpdfs/format-block.h
index 395e6d2d17e7..c2879e677084 100644
--- a/fs/rpdfs/format-block.h
+++ b/fs/rpdfs/format-block.h
@@ -4,9 +4,10 @@
 
 #include <linux/types.h>
 #include <linux/align.h>
+#include <linux/build_bug.h>
 
 #define RPDFS_BLOCK_SHIFT	12
-#define RPDFS_BLOCK_SIZE	(1 << RPDFS_BLOCK_SHIFT)
+#define RPDFS_BLOCK_SIZE	(1ULL << RPDFS_BLOCK_SHIFT)
 #define RPDFS_BLOCK_MASK	(RPDFS_BLOCK_SIZE - 1ULL)
 
 struct rpdfs_block_ref {
@@ -113,9 +114,11 @@ struct rpdfs_ino_gen {
 };
 
 /*
- * Data blocks are pointed to by a simple tree of indirect blocks rooted
- * in a single field in the inode. The height is one greater than the
- * level of the referenced block. It's 0 for an empty tree.
+ * Data blocks are pointed to by a simple tree of mapping blocks rooted
+ * in a single field in the inode. The height of the tree is the number
+ * of blocks in the mapping chain, including the data block itself. It's
+ * 0 for a file with no data, 1 for a file with 1 data block at logical
+ * block 0, 2 for for a file with 2 data blocks at 0 and 1, etc.
  */
 struct rpdfs_data_root {
 	struct rpdfs_block_ref ref;
@@ -124,21 +127,16 @@ struct rpdfs_data_root {
 };
 
 /*
- * Indirect blocks are a simple array of block refs. We rely on the
- * number of references per indirect block being a power of 2, so check
- * that at compile time.
+ * Mapping blocks are a simple array of block refs. Because blocks per
+ * ref is based on the size of a struct, we can't do the smart thing and
+ * define the shift first and then the value, we have to go backwards
+ * and define the shift from the value instead.
  */
-#define RPDFS_DATA_REFS_PER_BLK (RPDFS_BLOCK_SIZE / sizeof(struct rpdfs_block_ref))
-
-/*
- * Because blocks per ref is based on the size of a struct, we can't do
- * the smart thing and define the shift first and then the value, we
- * have to go backwards and define the shift from the value instead.
- */
-
+#define RPDFS_DATA_REFS_PER_BLK	(RPDFS_BLOCK_SIZE / sizeof(struct rpdfs_block_ref))
 #define RPDFS_DATA_REFS_PER_BLK_SHIFT const_ilog2(RPDFS_DATA_REFS_PER_BLK)
+#define RPDFS_DATA_REFS_PER_BLK_MASK (RPDFS_DATA_REFS_PER_BLK - 1ULL)
 
-struct rpdfs_indirect_block {
+struct rpdfs_map_block {
 	struct rpdfs_block_ref refs[RPDFS_DATA_REFS_PER_BLK];
 };
 
@@ -168,7 +166,7 @@ struct rpdfs_inode {
 	__le64 crtime_nsec;
 	struct rpdfs_btree_root dirents;
 	struct rpdfs_btree_root xattrs;
-	struct rpdfs_data_root data;
+	struct rpdfs_data_root data_root;
 };
 
 #define RPDFS_ROOT_INO 1
diff --git a/fs/rpdfs/inode.c b/fs/rpdfs/inode.c
index a2baa862f6a4..fd0913e0e4b6 100644
--- a/fs/rpdfs/inode.c
+++ b/fs/rpdfs/inode.c
@@ -9,6 +9,7 @@
 
 #include "btree.h"
 #include "compare.h"
+#include "data.h"
 #include "dir.h"
 #include "file.h"
 #include "inode.h"
@@ -80,6 +81,8 @@ static void copy_rinode_to_vfs_inode(struct inode *inode, struct rpdfs_inode *ri
 
 	ri->xattrs = rinode->xattrs;
 	ri->xattr_creates = rinode->xattr_creates;
+
+	ri->data_root = rinode->data_root;
 }
 
 static __le64 cpu_ts64_to_le64_ns(struct timespec64 ts)
@@ -122,6 +125,8 @@ static void print_inode_change(struct inode *inode, struct rpdfs_inode *rinode)
 	print_diff64("dirents", le64_to_cpu(ri->dirents.ref.bnr), le64_to_cpu(rinode->dirents.ref.bnr));
 	print_diff64("xattrs", le64_to_cpu(ri->xattrs.ref.bnr), le64_to_cpu(rinode->xattrs.ref.bnr));
 	print_diff64("xattr_creates", le64_to_cpu(ri->xattr_creates), le64_to_cpu(rinode->xattr_creates));
+	print_diff64("data_root.height", ri->data_root.height, rinode->data_root.height);
+	print_diff64("data_root.ref.bnr", le64_to_cpu(ri->data_root.ref.bnr), le64_to_cpu(rinode->data_root.ref.bnr));
 }
 
 static void copy_vfs_inode_to_rinode(struct rpdfs_inode *rinode, struct inode *inode)
@@ -144,6 +149,8 @@ static void copy_vfs_inode_to_rinode(struct rpdfs_inode *rinode, struct inode *i
 
 	rinode->xattrs = ri->xattrs;
 	rinode->xattr_creates = ri->xattr_creates;
+
+	rinode->data_root = ri->data_root;
 }
 
 /*
@@ -336,6 +343,7 @@ struct inode *rpdfs_new_inode(struct super_block *sb, struct rpdfs_ino_gen *ig)
 
 	rpdfs_btree_root_init(&ri->dirents);
 	rpdfs_btree_root_init(&ri->xattrs);
+	rpdfs_data_root_init(&ri->data_root);
 
 	ts = inode_set_ctime_current(inode);
 	inode_set_mtime_to_ts(inode, ts);
diff --git a/fs/rpdfs/inode.h b/fs/rpdfs/inode.h
index 3b0be2d83e61..3892fdcc8b1b 100644
--- a/fs/rpdfs/inode.h
+++ b/fs/rpdfs/inode.h
@@ -25,6 +25,7 @@ struct rpdfs_inode_info {
 	struct rpdfs_ino_gen ig;
 	struct rpdfs_btree_root dirents;
 	struct rpdfs_btree_root xattrs;
+	struct rpdfs_data_root data_root;
 
 	struct inode vfs_inode;
 };
-- 
2.49.0