[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