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

Valerie Aurora <[email protected]> Fri, 24 Apr 2026 16:05:18 +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 | 220 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 220 insertions(+)

diff --git a/fs/rpdfs/data.c b/fs/rpdfs/data.c
index b7538004d14a..3a6e9560b32f 100644
--- a/fs/rpdfs/data.c
+++ b/fs/rpdfs/data.c
@@ -88,3 +88,223 @@ 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,
+			   struct rpdfs_block_ref *ref, struct rpdfs_block_handle **blk_hnd)
+{
+	int ret;
+
+	ret = rpdfs_txn_acquire_alloc(rfi, txn, blk_hnd);
+	if (ret < 0)
+		goto out;
+
+	memset((*blk_hnd)->data, 0, RPDFS_BLOCK_SIZE);
+
+	ref->bnr = cpu_to_le64((*blk_hnd)->bnr);
+	ref->alloc_counter = 0; /* XXX */
+out:
+	rpdfs_prd("ret %d bnr %llu", ret, ref->bnr);
+	return ret;
+}
+
+static int grow_height(struct rpdfs_fs_info *rfi, struct rpdfs_transaction *txn,
+		       struct inode *inode, struct rpdfs_block_handle *inode_hnd, int height)
+{
+	struct rpdfs_inode_info *ri = RPDFS_I(inode);
+	struct rpdfs_block_handle *hnd = NULL;
+	struct rpdfs_block_ref *ref;
+	struct rpdfs_map_block *iblk;
+	struct rpdfs_data_root *dr;
+	struct rpdfs_block_ref orig_root;
+	u8 level;
+	int ret;
+
+	dr = &ri->data_root;
+
+	rpdfs_prd("dr->height %u goal height %u", dr->height, height);
+
+	ret = 0;
+	if (height > dr->height) {
+		/* start at root and fill in till we get to existing tree */
+		level = height;
+		orig_root = dr->ref; /* save current root of tree */
+		ref = &dr->ref;
+
+		while (level-- > dr->height) {
+			rpdfs_prd("allocing block at level %d", level + 1);
+			hnd = NULL;
+			ret = alloc_block_ref(rfi, txn, &dr->ref, &hnd);
+			if (ret < 0)
+				goto out;
+			iblk = hnd->data;
+			/* growing height will always index old data to 0 */
+			ref = &iblk->refs[0];
+			rpdfs_block_release(rfi, &hnd);
+		}
+		rpdfs_prd("grafting old height %d tree to new %d height tree",
+			  dr->height, height);
+		/* now put existing tree in mapping block */
+		*ref = orig_root;
+		/* and update the height in the data root in the inode */
+		ri->data_root.height = height;
+	}
+out:
+	return ret;
+}
+
+/*
+ * Return an array of block references in mapping blocks, beginning with
+ * the one containing the requested offset. If it is a write, allocate
+ * the mapping blocks for that file data offset if necessary. If it is a
+ * read for an unallocated but valid offset, return a null range. 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.
+ *
+ * This function is only called after checking that a read is from a
+ * valid range of the file.
+ *
+ * XXX: Takes write access on map blocks on write even when it only
+ * needs to read them. Redescend with write when we know we have to
+ * allocate?
+ */
+static int get_or_alloc_map_blk(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;
+	struct rpdfs_data_root *dr;
+	rbaf_t map_rbaf;
+	u8 min_height;
+	u8 level;
+	u8 ind;
+	u64 bnr;
+	int write;
+	int nr;
+	int ret;
+
+	map_rbaf = rbaf & RBAF_WRITE ? RBAF_WRITE : 0;
+	write = rbaf & RBAF_WRITE ? 1 : 0;
+
+	dr = &ri->data_root;
+	min_height = height_from_lblk(lblk);
+
+	rpdfs_prd("ino %llu dr->height %u dr->ref.bnr %llu lblk %llu lblk height %u map_rbaf %x write %d",
+		  ri->ig.ino, dr->height, dr->ref.bnr, lblk, min_height, map_rbaf, write);
+
+	/* grow the height of existing data, if any */
+	if ((min_height > 1) &&
+	    (min_height > dr->height)) {
+		ret = grow_height(rfi, txn, inode, inode_hnd, min_height);
+		if (ret < 0)
+			goto out;
+	}
+
+	/* start with the root of the mapping tree in the inode */
+	parent_hnd = inode_hnd;
+	refs = &dr->ref;
+	bnr = le64_to_cpu(dr->ref.bnr);
+	ind = 0;
+	nr = 1;
+	ret = 0;
+
+	level = dr->height;
+
+	/* allocate all map blocks but not the data block itself */
+	while (level-- > 1) {
+		rpdfs_prd("level %d bnr %llu", level + 1, bnr);
+
+		if (bnr == 0 && !write) {
+			rpdfs_prd("read of unallocated block");
+			break;
+		}
+
+		if (bnr) {
+			/* TODO: be smarter about needing write */
+			ret = rpdfs_block_acquire(rfi, txn, bnr, &blk_hnd, map_rbaf);
+			if (ret < 0)
+				goto out;
+		} else {
+
+			ret = alloc_block_ref(rfi, txn, refs, &blk_hnd);
+			if (ret < 0)
+				goto out; /* XXX do error/recovery now */
+		}
+
+		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;
+	};
+
+	/* XXX make this a struct? */
+	*hnd_ret = parent_hnd;
+	*refs_ret = refs;
+	*nr_ret = nr;
+
+	if (dr->height < min_height)
+		dr->height = min_height;
+
+out:
+	rpdfs_prd("dr->height %u bnr %llu ind %d refs %p nr %d",
+		  dr->height, *refs_ret ? (*refs_ret)[0].bnr : 0, ind, refs_ret, *nr_ret);
+	return ret;
+}
+
+static int get_or_alloc_file_block(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_handle *parent_hnd = NULL;
+	struct rpdfs_block_handle *blk_hnd = NULL;
+	struct rpdfs_block_ref *refs;
+	int write;
+	int nr;
+	u64 bnr;
+	int ret;
+
+	write = rbaf & RBAF_WRITE ? 1 : 0;
+
+	rpdfs_prd("ino %llu lblk %llu rbaf %x", rpdfs_inode_ino(inode), lblk, rbaf);
+
+	ret = get_or_alloc_map_blk(rfi, txn, inode, inode_hnd, lblk, rbaf, &parent_hnd, &refs, &nr);
+	if (ret < 0)
+		goto out;
+
+	bnr = le64_to_cpu(refs[0].bnr);
+
+	if (bnr == 0) {
+		/* read from/write to unallocated range */
+		if (write) {
+			ret = alloc_block_ref(rfi, txn, refs, &blk_hnd);
+			if (ret < 0)
+				goto out;
+		}
+	} else {
+		ret = rpdfs_block_acquire(rfi, txn, bnr, &blk_hnd, rbaf);
+		if (ret < 0)
+			goto out;
+	}
+
+	if (parent_hnd != inode_hnd)
+		rpdfs_block_release(rfi, &parent_hnd);
+
+	*hnd_ret = blk_hnd;
+out:
+	rpdfs_prd("ret %d ino %llu lblk %llu bnr %llu *hnd_ret %p",
+		  ret, rpdfs_inode_ino(inode), lblk, bnr, *hnd_ret);
+	return ret;
+}
-- 
2.49.0