All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] File data read/write version 2
@ 2026-05-07 13:21 Valerie Aurora
  2026-05-07 13:21 ` [PATCH 1/6] rpdfs: add rpdfs_file_llseek Valerie Aurora
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

This patch set implements file data read/write with most of the
previous code review comments resolved. The main exception is that
->writepages() is not yet implemented.

Valerie Aurora (6):
  rpdfs: add rpdfs_file_llseek
  rpdfs: add inode change debugging routine
  rpdfs: add basic file data initialization
  rpdfs: add file data allocation and lookup routines
  rpdfs: add rpdfs_write_iter/rpdfs_read_iter
  rpdfs: add read_folio, dirty_folio, write_begin, write_end

 fs/rpdfs/Makefile       |   1 +
 fs/rpdfs/data.c         | 598 ++++++++++++++++++++++++++++++++++++++++
 fs/rpdfs/data.h         |  11 +
 fs/rpdfs/file.c         |  77 ++++++
 fs/rpdfs/format-block.h |  32 +--
 fs/rpdfs/format-msg.h   |   4 +
 fs/rpdfs/inode.c        |  51 +++-
 fs/rpdfs/inode.h        |   1 +
 8 files changed, 753 insertions(+), 22 deletions(-)
 create mode 100644 fs/rpdfs/data.c
 create mode 100644 fs/rpdfs/data.h

-- 
2.49.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/6] rpdfs: add rpdfs_file_llseek
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  2026-05-07 13:21 ` [PATCH 2/6] rpdfs: add inode change debugging routine Valerie Aurora
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

Use generic_file_llseek to implement file seek.

Signed-off-by: Valerie Aurora <val@versity.com>
---
 fs/rpdfs/file.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/rpdfs/file.c b/fs/rpdfs/file.c
index 22532d8cdf63..e8918e38b3fe 100644
--- a/fs/rpdfs/file.c
+++ b/fs/rpdfs/file.c
@@ -5,10 +5,28 @@
 #include "file.h"
 #include "inode.h"
 
+static loff_t rpdfs_file_llseek(struct file *file, loff_t offset, int whence)
+{
+	struct inode *inode = file->f_inode;
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	struct rpdfs_block_handle *hnd = NULL;
+	int ret;
+
+	ret = rpdfs_inode_acquire(rfi, NULL, inode, &hnd, 0);
+	if (ret < 0)
+		goto out;
+
+	ret = generic_file_llseek(file, offset, whence);
+out:
+	rpdfs_block_release(rfi, &hnd);
+	return ret;
+}
+
 const struct inode_operations rpdfs_file_iops = {
 	.getattr	= rpdfs_getattr,
 	.setattr	= rpdfs_setattr,
 };
 
 const struct file_operations rpdfs_file_fops = {
+	.llseek		= rpdfs_file_llseek,
 };
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/6] rpdfs: add inode change debugging routine
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
  2026-05-07 13:21 ` [PATCH 1/6] rpdfs: add rpdfs_file_llseek Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  2026-05-07 13:21 ` [PATCH 3/6] rpdfs: add basic file data initialization Valerie Aurora
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

print_inode_change() can be added to copy_vfs_inode_to_rinode() to
log what has changed since the last inode update.

Signed-off-by: Valerie Aurora <val@versity.com>
---
 fs/rpdfs/inode.c | 42 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 5 deletions(-)

diff --git a/fs/rpdfs/inode.c b/fs/rpdfs/inode.c
index 428c9081fd75..a2baa862f6a4 100644
--- a/fs/rpdfs/inode.c
+++ b/fs/rpdfs/inode.c
@@ -87,6 +87,43 @@ static __le64 cpu_ts64_to_le64_ns(struct timespec64 ts)
 	return cpu_to_le64(timespec64_to_ns(&ts));
 }
 
+static u64 ts64_to_ns(struct timespec64 ts)
+{
+	return timespec64_to_ns(&ts);
+}
+
+__always_unused
+static inline void print_diff64(char *name, u64 new, u64 old)
+{
+	if (old != new)
+		rpdfs_prd("%s: %llu -> %llu (%lld)", name, old, new, new - old);
+}
+
+__always_unused
+static void print_inode_change(struct inode *inode, struct rpdfs_inode *rinode)
+{
+	struct rpdfs_inode_info *ri = RPDFS_I(inode);
+
+	rpdfs_prd("copying inode %lu to rinode", inode->i_ino);
+
+	print_diff64("ino", le64_to_cpu(ri->ig.ino), le64_to_cpu(rinode->ig.ino));
+	print_diff64("gen", le64_to_cpu(ri->ig.gen), le64_to_cpu(rinode->ig.gen));
+	print_diff64("i_size", i_size_read(inode), le64_to_cpu(rinode->size));
+	print_diff64("nlink", inode->i_nlink, le32_to_cpu(rinode->nlink));
+	print_diff64("uid", i_uid_read(inode), le32_to_cpu(rinode->uid));
+	print_diff64("gid", i_gid_read(inode), le32_to_cpu(rinode->gid));
+	print_diff64("mode", inode->i_mode, le32_to_cpu(rinode->mode));
+
+	print_diff64("atime", ts64_to_ns(inode_get_atime(inode)), le64_to_cpu(rinode->atime_nsec));
+	print_diff64("mtime", ts64_to_ns(inode_get_mtime(inode)), le64_to_cpu(rinode->mtime_nsec));
+	print_diff64("ctime", ts64_to_ns(inode_get_ctime(inode)), le64_to_cpu(rinode->ctime_nsec));
+	print_diff64("crtime", le64_to_cpu(ri->crtime_nsec), le64_to_cpu(rinode->crtime_nsec));
+
+	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));
+}
+
 static void copy_vfs_inode_to_rinode(struct rpdfs_inode *rinode, struct inode *inode)
 {
 	struct rpdfs_inode_info *ri = RPDFS_I(inode);
@@ -132,11 +169,6 @@ void rpdfs_inode_init_ops(struct inode *inode)
 	}
 }
 
-static u64 ts64_to_ns(struct timespec64 ts)
-{
-	return timespec64_to_ns(&ts);
-}
-
 /*
  * Update the vfs inode if the caller's persistent version of the rinode
  * is more recent.  A lot of tasks can be checking a shared inode to see
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/6] rpdfs: add basic file data initialization
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
  2026-05-07 13:21 ` [PATCH 1/6] rpdfs: add rpdfs_file_llseek Valerie Aurora
  2026-05-07 13:21 ` [PATCH 2/6] rpdfs: add inode change debugging routine Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  2026-05-07 13:21 ` [PATCH 4/6] rpdfs: add file data allocation and lookup routines Valerie Aurora
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

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 <val@versity.com>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/6] rpdfs: add file data allocation and lookup routines
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
                   ` (2 preceding siblings ...)
  2026-05-07 13:21 ` [PATCH 3/6] rpdfs: add basic file data initialization Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  2026-05-07 13:21 ` [PATCH 5/6] rpdfs: add rpdfs_write_iter/rpdfs_read_iter Valerie Aurora
  2026-05-07 13:21 ` [PATCH 6/6] rpdfs: add read_folio, dirty_folio, write_begin, write_end Valerie Aurora
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

Add routines to lookup and allocate file data and mapping blocks.

Signed-off-by: Valerie Aurora <val@versity.com>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/6] rpdfs: add rpdfs_write_iter/rpdfs_read_iter
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
                   ` (3 preceding siblings ...)
  2026-05-07 13:21 ` [PATCH 4/6] rpdfs: add file data allocation and lookup routines Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  2026-05-07 13:21 ` [PATCH 6/6] rpdfs: add read_folio, dirty_folio, write_begin, write_end Valerie Aurora
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

Implement write_iter/read_iter with generic functions.

Signed-off-by: Valerie Aurora <val@versity.com>
---
 fs/rpdfs/file.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/fs/rpdfs/file.c b/fs/rpdfs/file.c
index e8918e38b3fe..060de1070963 100644
--- a/fs/rpdfs/file.c
+++ b/fs/rpdfs/file.c
@@ -1,9 +1,12 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
+#include <linux/kernel.h>
 #include <linux/fs.h>
+#include <linux/slab.h>
 
 #include "file.h"
 #include "inode.h"
+#include "pr.h"
 
 static loff_t rpdfs_file_llseek(struct file *file, loff_t offset, int whence)
 {
@@ -22,6 +25,60 @@ static loff_t rpdfs_file_llseek(struct file *file, loff_t offset, int whence)
 	return ret;
 }
 
+static ssize_t rpdfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_mapping->host;
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	struct rpdfs_block_handle *hnd = NULL;
+	struct rpdfs_transaction txn = RPDFS_INIT_TXN;
+	ssize_t ret;
+
+	rpdfs_prd("ino %llu count %lu pos %lu",
+		  rpdfs_inode_ino(inode), iov_iter_count(from), (unsigned long) iocb->ki_pos);
+
+	inode_lock(inode);
+
+	ret = rpdfs_inode_acquire(rfi, &txn, inode, &hnd, RBAF_WRITE);
+	if (ret < 0)
+		goto out;
+
+	ret = generic_write_checks(iocb, from);
+	if (ret <= 0)
+		goto out;
+
+	ret = __generic_file_write_iter(iocb, from);
+out:
+	rpdfs_block_release(rfi, &hnd);
+	inode_unlock(inode);
+
+	if (ret > 0)
+		ret = generic_write_sync(iocb, ret);
+
+	return ret;
+}
+
+static ssize_t rpdfs_file_read_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file_inode(file);
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	struct rpdfs_block_handle *hnd = NULL;
+	int ret;
+
+	ret = rpdfs_inode_acquire(rfi, NULL, inode, &hnd, 0);
+	if (ret < 0)
+		goto out;
+
+	rpdfs_prd("ino %llu count %lu pos %lu",
+		  rpdfs_inode_ino(inode), iov_iter_count(from), (unsigned long) iocb->ki_pos);
+
+	ret = generic_file_read_iter(iocb, from);
+out:
+	rpdfs_block_release(rfi, &hnd);
+	return ret;
+}
+
 const struct inode_operations rpdfs_file_iops = {
 	.getattr	= rpdfs_getattr,
 	.setattr	= rpdfs_setattr,
@@ -29,4 +86,6 @@ const struct inode_operations rpdfs_file_iops = {
 
 const struct file_operations rpdfs_file_fops = {
 	.llseek		= rpdfs_file_llseek,
+	.write_iter	= rpdfs_file_write_iter,
+	.read_iter	= rpdfs_file_read_iter,
 };
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 6/6] rpdfs: add read_folio, dirty_folio, write_begin, write_end
  2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
                   ` (4 preceding siblings ...)
  2026-05-07 13:21 ` [PATCH 5/6] rpdfs: add rpdfs_write_iter/rpdfs_read_iter Valerie Aurora
@ 2026-05-07 13:21 ` Valerie Aurora
  5 siblings, 0 replies; 7+ messages in thread
From: Valerie Aurora @ 2026-05-07 13:21 UTC (permalink / raw)
  To: rpdfs-devel

This commit completes basic file data read/write support, including
avoidance of deadlock in read_folio. It does not include
->writepages() or readahead().

Signed-off-by: Valerie Aurora <val@versity.com>
---
 fs/rpdfs/data.c  | 245 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/rpdfs/inode.c |   1 +
 2 files changed, 246 insertions(+)

diff --git a/fs/rpdfs/data.c b/fs/rpdfs/data.c
index b1ccf77ced7a..b3647e1ff42f 100644
--- a/fs/rpdfs/data.c
+++ b/fs/rpdfs/data.c
@@ -351,3 +351,248 @@ static int get_or_alloc_data_block(struct rpdfs_fs_info *rfi, struct rpdfs_trans
 		  ret, rpdfs_inode_ino(inode), lblk, refs ? le64_to_cpu(refs[0].bnr) : 0, *hnd_ret);
 	return ret;
 }
+
+/*
+ * We have to avoid a potential deadlock between the kernel's lock on
+ * each folio and our cache consistency algorithm. The order of
+ * acquisition on a read or write or similar operation is:
+ *
+ * 1. kernel grabs folio lock, calls file system routine
+ * 2. local node attempts to get access to a block
+ *
+ * But invalidation of a page has this order:
+ *
+ * 1. remote node requests exclusive access to a block
+ * 2. local node receives cache invalidate message and tries to get folio lock
+ *
+ * If the local node is trying to get access to a block while another
+ * node tries to get exclusive access, we could end up with:
+ *
+ * 1. local node holds folio lock, can't get access to block
+ * 2. local node attempts to service invalidate request but can't get folio lock
+ *
+ * The solution is to acquire all blocks in non-blocking mode. If that
+ * fails, drop the folio lock and acquire the block in blocking mode,
+ * then release it and return AOP_TRUNCATED_PAGE. This return code means
+ * "the page was truncated away beneath me, please retry." The page
+ * cache will restart the read_folio operation, which will likely
+ * succeed (as long as no other node has requested the block since then).
+ *
+ * Note that readahead() should satisfy most read requests
+ * asynchronously, leaving us to do any remaining synchronous requests
+ * in read_folio().
+ */
+static int rpdfs_read_folio(struct file *file, struct folio *folio)
+{
+	struct inode *inode = folio->mapping->host;
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	loff_t pos = folio_pos(folio);
+	size_t len = folio_size(folio);
+	struct rpdfs_block_handle *inode_hnd = NULL;
+	struct rpdfs_block_handle *blk_hnd = NULL;
+	rbaf_t rbaf = RBAF_NONBLOCK_MODE;
+	u64 lblk;
+	int ret;
+
+	lblk = lblk_from_offset(pos);
+
+	rpdfs_prd("ino %llu lblk %llu pos %lld len %lu",
+		  rpdfs_inode_ino(inode), lblk, pos, len);
+
+	/* we turn off atime always, inode will not be written */
+	ret = rpdfs_inode_acquire(rfi, NULL, inode, &inode_hnd, RBAF_NONBLOCK_MODE);
+	if (ret == -EAGAIN) {
+		folio_unlock(folio);
+
+		rpdfs_prd("could not acquire ino %llu non-blocking, ret %d, retrying",
+			  rpdfs_inode_ino(inode), ret);
+
+		ret = rpdfs_inode_acquire(rfi, NULL, inode, &inode_hnd, 0);
+		if (ret == 0) {
+			rpdfs_block_release(rfi, &inode_hnd);
+			ret = AOP_TRUNCATED_PAGE;
+		}
+		goto out;
+	}
+	if (ret < 0)
+		goto out_unlock;
+
+	ret = get_or_alloc_data_block(rfi, NULL, inode, inode_hnd, lblk, rbaf, &blk_hnd);
+	if (ret == -EAGAIN) {
+		folio_unlock(folio);
+
+		rpdfs_prd("could not acquire ino %llu lblk %llu non-blocking, ret %d, retrying",
+			  rpdfs_inode_ino(inode), lblk, ret);
+
+		rbaf &= ~RBAF_NONBLOCK_MODE;
+		ret = get_or_alloc_data_block(rfi, NULL, inode, inode_hnd, lblk, rbaf, &blk_hnd);
+		if (ret == 0) {
+			rpdfs_block_release(rfi, &blk_hnd);
+			ret = AOP_TRUNCATED_PAGE;
+		}
+		goto out;
+	}
+	if (ret < 0)
+		goto out_unlock;
+
+	/* copy the data into the actual block */
+	if (blk_hnd)
+		memcpy(folio_address(folio), blk_hnd->data, len);
+	else
+		memset(folio_address(folio), 0, len);
+
+	rpdfs_prd("copied %4s len %lu to %4s", (char *) blk_hnd->data, len,
+		  (char *) folio_address(folio));
+
+	folio_mark_uptodate(folio);
+out_unlock:
+	folio_unlock(folio);
+out:
+	rpdfs_block_release(rfi, &blk_hnd);
+	rpdfs_block_release(rfi, &inode_hnd);
+
+	rpdfs_prd("ret %d", ret);
+
+	return ret;
+}
+
+static bool rpdfs_dirty_folio(struct address_space *mapping, struct folio *folio)
+{
+	rpdfs_prd("ino %lu index %lu", mapping->host->i_ino, folio->index);
+
+	return filemap_dirty_folio(folio_mapping(folio), folio);
+}
+
+
+/*
+ * Info to be passed from write_begin to write_end to complete the write
+ * within a transaction.
+ */
+struct rpdfs_write_cb {
+	struct rpdfs_block_handle *inode_hnd;
+	struct rpdfs_block_handle *blk_hnd;
+	struct rpdfs_transaction txn;
+};
+
+/*
+ * Do whatever preparation is necessary to allocate space for a
+ * write. In the future it might check quotas, file system error state,
+ * etc.
+ *
+ * Called with the inode block already acquired read/write. Returns a
+ * locked folio on success.
+ */
+static int rpdfs_write_begin(struct file *file, struct address_space *mapping,
+			     loff_t pos, unsigned len,
+			     struct folio **foliop, void **fsdata)
+{
+	struct inode *inode = mapping->host;
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	struct folio *folio = NULL;
+	struct rpdfs_write_cb *cb;
+	rbaf_t rbaf;
+	u64 lblk;
+	int ret;
+	unsigned offset;
+
+	lblk = lblk_from_offset(pos);
+
+	rpdfs_prd("ino %llu lblk %llu pos %lld len %u", rpdfs_inode_ino(inode), lblk, pos, len);
+
+	/* allocate txn and pass to write_end for updating inode i_size/times */
+	cb = kzalloc(sizeof(struct rpdfs_write_cb), GFP_NOFS);
+	if (!cb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	folio = __filemap_get_folio(mapping, pos >> PAGE_SHIFT, FGP_WRITEBEGIN,
+				    mapping_gfp_mask(mapping));
+	if (IS_ERR(folio)) {
+		ret = PTR_ERR(folio);
+		goto out;
+	}
+
+	offset = offset_in_folio(folio, pos);
+	/* XXX use pos/len/offset to figure out when it is an overwrite */
+	rbaf = RBAF_WRITE;
+
+	ret = get_or_alloc_data_block(rfi, &cb->txn, inode, cb->inode_hnd, lblk, rbaf, &cb->blk_hnd);
+	if (ret < 0)
+		goto out;
+
+	*foliop = folio;
+	*fsdata = cb;
+	ret = len;
+out:
+	if (ret < 0) {
+		if (cb) {
+			rpdfs_txn_finish(rfi, &cb->txn);
+			kfree(cb);
+		}
+		if (!IS_ERR_OR_NULL(folio)) {
+			folio_unlock(folio);
+			folio_put(folio);
+		}
+		*foliop = NULL;
+	}
+	return ret;
+}
+
+static int rpdfs_write_end(struct file *file, struct address_space *mapping,
+			   loff_t pos, unsigned len, unsigned copied,
+			   struct folio *folio, void *fsdata)
+{
+	struct inode *inode = mapping->host;
+	struct rpdfs_fs_info *rfi = RPDFS_INODE_FS(inode);
+	struct rpdfs_write_cb *cb = fsdata;
+	loff_t old_size = inode->i_size;
+	bool i_size_changed = false;
+	unsigned offset;
+
+	offset = offset_in_folio(folio, pos);
+	/*
+	 * Copy the data from the folio into the blcok.
+	 *
+	 * TODO: replace with writepages.
+	 */
+	memcpy(cb->blk_hnd->data + offset, folio_address(folio) + offset, len);
+
+	rpdfs_prd("copied %4s len %d to %4s", (char *) folio_address(folio) + offset, len,
+		  (char *) cb->blk_hnd->data + offset);
+
+	if (pos + copied > inode->i_size) {
+		i_size_write(inode, pos + copied);
+		i_size_changed = true;
+	}
+
+	folio_mark_dirty(folio);
+	folio_unlock(folio);
+	folio_put(folio);
+
+	if (old_size < pos)
+		pagecache_isize_extended(inode, old_size, pos);
+
+	/* mark inode dirty outside of folio lock for performance reasons */
+	if (i_size_changed)
+		mark_inode_dirty(inode);
+
+	/* finalize changes to the inode and block */
+	rpdfs_block_release(rfi, &cb->blk_hnd);
+	rpdfs_inode_update(rfi, inode, cb->inode_hnd);
+	rpdfs_block_release(rfi, &cb->inode_hnd);
+
+	rpdfs_txn_finish(rfi, &cb->txn);
+	kfree(cb);
+
+	rpdfs_prd("i_size %lld copied %d", i_size_read(inode), copied);
+
+	return copied;
+}
+
+const struct address_space_operations rpdfs_aops = {
+	.read_folio =		rpdfs_read_folio,
+	.dirty_folio =		rpdfs_dirty_folio,
+	.write_begin =		rpdfs_write_begin,
+	.write_end =		rpdfs_write_end,
+};
diff --git a/fs/rpdfs/inode.c b/fs/rpdfs/inode.c
index fd0913e0e4b6..ec7f1e64ba18 100644
--- a/fs/rpdfs/inode.c
+++ b/fs/rpdfs/inode.c
@@ -163,6 +163,7 @@ void rpdfs_inode_init_ops(struct inode *inode)
 	case S_IFREG:
 		inode->i_op = &rpdfs_file_iops;
 		inode->i_fop = &rpdfs_file_fops;
+		inode->i_mapping->a_ops = &rpdfs_aops;
 		break;
 	case S_IFDIR:
 		inode->i_op = &rpdfs_dir_iops;
-- 
2.49.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-05-07 13:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 13:21 [PATCH 0/6] File data read/write version 2 Valerie Aurora
2026-05-07 13:21 ` [PATCH 1/6] rpdfs: add rpdfs_file_llseek Valerie Aurora
2026-05-07 13:21 ` [PATCH 2/6] rpdfs: add inode change debugging routine Valerie Aurora
2026-05-07 13:21 ` [PATCH 3/6] rpdfs: add basic file data initialization Valerie Aurora
2026-05-07 13:21 ` [PATCH 4/6] rpdfs: add file data allocation and lookup routines Valerie Aurora
2026-05-07 13:21 ` [PATCH 5/6] rpdfs: add rpdfs_write_iter/rpdfs_read_iter Valerie Aurora
2026-05-07 13:21 ` [PATCH 6/6] rpdfs: add read_folio, dirty_folio, write_begin, write_end Valerie Aurora

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.