[PATCH 4/6] rpdfs: add file data allocation and lookup routines

Valerie Aurora <[email protected]> Thu, 7 May 2026 15:21:51 +0200
Newsgroups dev.linux.lists.rpdfs-devel
Message-ID <[email protected]>
Add routines to lookup and allocate file data and mapping blocks.

Signed-off-by: Valerie Aurora <[email protected]>
---
 fs/rpdfs/data.c       | 260 ++++++++++++++++++++++++++++++++++++++++++
 fs/rpdfs/format-msg.h |   4 +
 2 files changed, 264 insertions(+)

diff --git a/fs/rpdfs/data.c b/fs/rpdfs/data.c
index 65b753886a04..b1ccf77ced7a 100644
--- a/fs/rpdfs/data.c
+++ b/fs/rpdfs/data.c
@@ -91,3 +91,263 @@ void rpdfs_data_root_init(struct rpdfs_data_root *data)
 	data->ref.bnr = 0;
 	data->ref.alloc_counter = 0;
 }
+
+static int alloc_block_ref(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+			   u64 lblk, struct rpdfs_block_ref *ref, struct rpdfs_block_handle **hnd)
+{
+	int ret;
+
+	ret = rpdfs_txn_acquire_alloc(rfi, txn, hnd);
+	if (ret < 0)
+		goto out;
+
+	/* XXX let caller write/clear data blocks */
+	memset((*hnd)->data, 0, RPDFS_BLOCK_SIZE);
+
+	ref->bnr = cpu_to_le64((*hnd)->bnr);
+	ref->alloc_counter = cpu_to_le64((*hnd)->alloc_ctr);
+out:
+	rpdfs_prd("ret %d bnr %llu lblk %llu", ret, ref->bnr, lblk);
+	return ret;
+}
+
+/*
+ * The place of a mapping or data block should increase as we descend
+ * towards the data block, and remain constant as the mapping tree grows
+ * in height. A data block has the maximum depth value.
+ */
+static inline void set_data_place(struct rpdfs_block_handle *hnd, u64 ino, u8 level, u64 lblk)
+{
+	rpdfs_block_set_place(hnd, RPDFS_PLACE_DATA, ino, RPDFS_PLACE_DEPTH_MASK - level, lblk);
+}
+
+static int get_or_alloc_block(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+			      struct inode *inode, struct rpdfs_block_handle *inode_hnd,
+			      struct rpdfs_block_handle *parent_hnd, u64 bnr, u64 lblk,
+			      u8 level, rbaf_t data_rbaf, struct rpdfs_block_handle **hnd_ret,
+			      struct rpdfs_block_ref *refs)
+{
+	rbaf_t rbaf;
+	int write;
+	int ret;
+
+	if (level != 0)
+		rbaf = data_rbaf & RBAF_WRITE ? RBAF_WRITE | RBAF_OVERWRITE : 0;
+
+	write = data_rbaf & RBAF_WRITE ? 1 : 0;
+
+	rpdfs_prd("bnr %llu lblk %llu level %u rbaf %x write %d",
+		  bnr, lblk, level, rbaf, write);
+
+	if (bnr == 0 && write) {
+		/* XXX move this out? */
+		if (parent_hnd != inode_hnd) {
+			/* reacquire with write permissions */
+			bnr = parent_hnd->bnr;
+			rpdfs_block_release(rfi, &parent_hnd);
+			ret = rpdfs_block_acquire(rfi, txn, bnr, &parent_hnd, RBAF_WRITE);
+			if (ret < 0)
+				goto out;
+		}
+		ret = alloc_block_ref(rfi, txn, lblk, refs, hnd_ret);
+		if (ret < 0)
+			goto out;
+		set_data_place(*hnd_ret, rpdfs_inode_ino(inode), level, lblk);
+	} else {
+		ret = rpdfs_block_acquire(rfi, txn, bnr, hnd_ret, rbaf);
+		if (ret < 0)
+			goto out;
+	}
+out:
+	return ret;
+}
+
+/*
+ * Grow the height of the existing mapping block tree to that necessary
+ * to index the logical block lblk.
+ */
+static int grow_height(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+		       struct inode *inode, u64 lblk)
+{
+	struct rpdfs_inode_info *ri = RPDFS_I(inode);
+	struct rpdfs_block_handle *hnd = NULL;
+	struct rpdfs_block_ref ref;
+	struct rpdfs_map_block *iblk;
+	u8 min_height;
+	int ret = 0;
+
+	min_height = height_from_lblk(lblk);
+
+	if ((ri->data_root.height == 0) ||
+	    (min_height == 1) ||
+	    (min_height <= ri->data_root.height))
+		goto out;
+
+	rpdfs_prd("current height %u goal height %u", ri->data_root.height, min_height);
+
+	/* caller allocates the data block, start at first map block */
+	if (ri->data_root.height == 0)
+		ri->data_root.height++;
+
+	while (ri->data_root.height < min_height) {
+		rpdfs_prd("allocing map block at level %d for lblk %llu", ri->data_root.height, lblk);
+
+		ret = alloc_block_ref(rfi, txn, lblk, &ref, &hnd);
+		if (ret < 0)
+			goto out;
+
+		set_data_place(hnd, rpdfs_inode_ino(inode), ri->data_root.height, lblk);
+		iblk = hnd->data;
+		/* growing height will always index old data to 0 */
+		iblk->refs[0] = ri->data_root.ref;
+		/* insert new map block into inode */
+		ri->data_root.ref = ref;
+		ri->data_root.height++;
+		rpdfs_block_release(rfi, &hnd);
+	}
+out:
+	/* XXX unwind all changes on error */
+	return ret;
+}
+
+/*
+ * Return an array of block references in mapping blocks, beginning with
+ * the one containing the requested offset. It may be the root block
+ * reference in the inode. If it is a write, allocate the mapping blocks
+ * for that file data offset if necessary.
+ *
+ * This function is only called after checking that a read is from a
+ * valid range of the file. If it is a read for an offset with no
+ * mapping block allocated to point to it, return a null refs pointer
+ * and number of references equivalent to a block. If it is a read for
+ * an offset with a mapping block allocated but no data block, return a
+ * valid refs pointer for the range including the unallocated data block.
+ *
+ * The actual data block allocation occurs in the caller. This is so we
+ * don't have to traverse the mapping blocks for every data block
+ * access.
+ */
+static int get_or_alloc_refs(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+			     struct inode *inode, struct rpdfs_block_handle *inode_hnd,
+			     u64 lblk, rbaf_t rbaf, struct rpdfs_block_handle **hnd_ret,
+			     struct rpdfs_block_ref **refs_ret, int *nr_ret)
+{
+	struct rpdfs_inode_info *ri = RPDFS_I(inode);
+	struct rpdfs_block_handle *parent_hnd;
+	struct rpdfs_block_handle *blk_hnd = NULL;
+	struct rpdfs_block_ref *refs;
+	struct rpdfs_map_block *iblk;
+	u64 bnr;
+	int write;
+	int nr;
+	u8 level;
+	u8 ind;
+	int ret;
+
+	rpdfs_prd("ino %llu ri->data_root.height %u ri->data_root.ref.bnr %llu lblk %llu rbaf %x",
+		  rpdfs_inode_ino(inode), ri->data_root.height, ri->data_root.ref.bnr, lblk, rbaf);
+
+	write = rbaf & RBAF_WRITE ? 1 : 0;
+
+	/* grow the height of existing data, if any */
+	ret = grow_height(rfi, txn, inode, lblk);
+	if (ret < 0)
+		goto out;
+
+	/* start with the root of the mapping tree in the inode */
+	parent_hnd = inode_hnd;
+	refs = &ri->data_root.ref;
+	bnr = le64_to_cpu(ri->data_root.ref.bnr);
+	ind = 0;
+	nr = 1;
+	ret = 0;
+
+	level = ri->data_root.height;
+
+	/* lookup/allocate all map blocks but not the data block itself */
+	while (level-- > 1) {
+		rpdfs_prd("level %d bnr %llu", level, bnr);
+
+		if ((bnr == 0) && !write) {
+			*hnd_ret = NULL;
+			*refs_ret = NULL;
+			*nr_ret = RPDFS_DATA_REFS_PER_BLK;
+			goto out;
+		}
+		ret = get_or_alloc_block(rfi, txn, inode, inode_hnd, parent_hnd, bnr, lblk, level, rbaf, &blk_hnd, refs);
+		if (ret < 0)
+			goto out;
+
+		iblk = blk_hnd->data;
+
+		/* look up next block reference */
+		ind = calc_ref_ind(lblk, level);
+		bnr = le64_to_cpu(iblk->refs[ind].bnr);
+
+		if (parent_hnd != inode_hnd)
+			rpdfs_block_release(rfi, &parent_hnd);
+
+		parent_hnd = blk_hnd;
+		refs = &iblk->refs[ind];
+		nr = RPDFS_DATA_REFS_PER_BLK - ind;
+		blk_hnd = NULL;
+	};
+
+	*hnd_ret = parent_hnd;
+	*refs_ret = refs;
+	*nr_ret = nr;
+out:
+	rpdfs_prd("ri->data_root.height %u bnr %llu ind %d refs %p nr %d",
+		  ri->data_root.height, *refs_ret ? (*refs_ret)[0].bnr : 0, ind, refs_ret, *nr_ret);
+	return ret;
+}
+
+static int get_or_alloc_data_block(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+				   struct inode *inode, struct rpdfs_block_handle *inode_hnd,
+				   u64 lblk, rbaf_t data_rbaf, struct rpdfs_block_handle **hnd_ret)
+{
+	struct rpdfs_block_handle *parent_hnd = NULL;
+	struct rpdfs_block_handle *blk_hnd = NULL;
+	struct rpdfs_block_ref *refs = NULL;
+	rbaf_t map_rbaf;
+	int nr;
+	u64 bnr;
+	int ret;
+
+	/*
+	 * Set the mode for the map block acquisition. Non-blocking
+	 * reads do not need to be non-blocking on map blocks since they
+	 * are not in the page cache and cannot have lock inversion
+	 * problems. For writes, map blocks will not be completely
+	 * overwritten.
+	 */
+	if (data_rbaf & RBAF_WRITE)
+		map_rbaf = RBAF_WRITE;
+	else
+		map_rbaf = 0;
+
+	rpdfs_prd("ino %llu lblk %llu data rbaf %x map rbaf %x", rpdfs_inode_ino(inode), lblk, data_rbaf, map_rbaf);
+
+	ret = get_or_alloc_refs(rfi, txn, inode, inode_hnd, lblk, map_rbaf, &parent_hnd, &refs, &nr);
+	if (ret < 0)
+		goto out;
+
+	/* read of range with unallocated map blocks */
+	if (refs == NULL)
+		goto out;
+
+	bnr = le64_to_cpu(refs[0].bnr);
+
+	ret = get_or_alloc_block(rfi, txn, inode, inode_hnd, parent_hnd, bnr, lblk, 0, data_rbaf, &blk_hnd, refs);
+	if (ret < 0)
+		goto out;
+
+	*hnd_ret = blk_hnd;
+out:
+	if (parent_hnd != inode_hnd)
+		rpdfs_block_release(rfi, &parent_hnd);
+
+	rpdfs_prd("ret %d ino %llu lblk %llu bnr %llu *hnd_ret %p",
+		  ret, rpdfs_inode_ino(inode), lblk, refs ? le64_to_cpu(refs[0].bnr) : 0, *hnd_ret);
+	return ret;
+}
diff --git a/fs/rpdfs/format-msg.h b/fs/rpdfs/format-msg.h
index 3bee431e96dc..05dee4443a11 100644
--- a/fs/rpdfs/format-msg.h
+++ b/fs/rpdfs/format-msg.h
@@ -88,9 +88,13 @@ struct rpdfs_msg_block_read {
 #define RPDFS_PLACE_INO_MASK	((1ULL << RPDFS_PLACE_INO_BITS) - 1)
 #define RPDFS_PLACE_TYPE_MASK	((1ULL << RPDFS_PLACE_TYPE_BITS) - 1)
 
+#define RPDFS_PLACE_DEPTH_MAX	RPDFS_PLACE_DEPTH_MASK
+
 #define RPDFS_PLACE_INODE		4
 #define RPDFS_PLACE_XATTR_BTREE		8
 #define RPDFS_PLACE_DIRENT_BTREE	12
+#define RPDFS_PLACE_DATA		16 /* includes mapping blocks */
+
 /* free is always last so that it's flushed after other blocks in its txn */
 #define RPDFS_PLACE_FREE		RPDFS_PLACE_TYPE_MASK
 
-- 
2.49.0