diff mbox series

[RFC,04/18] pkernfs: support file truncation

Message ID 20240205120203.60312-5-jgowans@amazon.com (mailing list archive)
State New, archived
Headers show
Series Pkernfs: Support persistence for live update | expand

Commit Message

Gowans, James Feb. 5, 2024, 12:01 p.m. UTC
In the previous commit a block allocator was added. Now use that block
allocator to allocate blocks for files when ftruncate is run on them.

To do that a inode_operations is added on the file inodes with a getattr
callback handling the ATTR_SIZE attribute. When this is invoked pages
are allocated, the indexes of which are put into a mappings block.
The mappings block is an array with the index being the file offset
block and the value at that index being the pkernfs block backign that
file offset.
---
 fs/pkernfs/Makefile    |  2 +-
 fs/pkernfs/allocator.c | 24 +++++++++++++++++++
 fs/pkernfs/file.c      | 53 ++++++++++++++++++++++++++++++++++++++++++
 fs/pkernfs/inode.c     | 27 ++++++++++++++++++---
 fs/pkernfs/pkernfs.h   |  7 ++++++
 5 files changed, 109 insertions(+), 4 deletions(-)
 create mode 100644 fs/pkernfs/file.c
diff mbox series

Patch

diff --git a/fs/pkernfs/Makefile b/fs/pkernfs/Makefile
index d8b92a74fbc6..e41f06cc490f 100644
--- a/fs/pkernfs/Makefile
+++ b/fs/pkernfs/Makefile
@@ -3,4 +3,4 @@ 
 # Makefile for persistent kernel filesystem
 #
 
-obj-$(CONFIG_PKERNFS_FS) += pkernfs.o inode.o allocator.o dir.o
+obj-$(CONFIG_PKERNFS_FS) += pkernfs.o inode.o allocator.o dir.o file.o
diff --git a/fs/pkernfs/allocator.c b/fs/pkernfs/allocator.c
index 1d4aac9c4545..3905ce92b4a9 100644
--- a/fs/pkernfs/allocator.c
+++ b/fs/pkernfs/allocator.c
@@ -25,3 +25,27 @@  void pkernfs_zero_allocations(struct super_block *sb)
 	/* Second page is inode store */
 	set_bit(1, pkernfs_allocations_bitmap(sb));
 }
+
+/*
+ * Allocs one 2 MiB block, and returns the block index.
+ * Index is 2 MiB chunk index.
+ */
+unsigned long pkernfs_alloc_block(struct super_block *sb)
+{
+	unsigned long free_bit;
+
+	/* Allocations is 2nd half of first page */
+	void *allocations_mem = pkernfs_allocations_bitmap(sb);
+	free_bit = bitmap_find_next_zero_area(allocations_mem,
+			PMD_SIZE / 2, /* Size */
+			0, /* Start */
+			1, /* Number of zeroed bits to look for */
+			0); /* Alignment mask - none required. */
+	bitmap_set(allocations_mem, free_bit, 1);
+	return free_bit;
+}
+
+void *pkernfs_addr_for_block(struct super_block *sb, int block_idx)
+{
+	return pkernfs_mem + (block_idx * PMD_SIZE);
+}
diff --git a/fs/pkernfs/file.c b/fs/pkernfs/file.c
new file mode 100644
index 000000000000..27a637423178
--- /dev/null
+++ b/fs/pkernfs/file.c
@@ -0,0 +1,53 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "pkernfs.h"
+
+static int truncate(struct inode *inode, loff_t newsize)
+{
+	unsigned long free_block;
+	struct pkernfs_inode *pkernfs_inode;
+	unsigned long *mappings;
+
+	pkernfs_inode = pkernfs_get_persisted_inode(inode->i_sb, inode->i_ino);
+	mappings = (unsigned long *)pkernfs_addr_for_block(inode->i_sb,
+		pkernfs_inode->mappings_block);
+	i_size_write(inode, newsize);
+	for (int block_idx = 0; block_idx * PMD_SIZE < newsize; ++block_idx) {
+		free_block = pkernfs_alloc_block(inode->i_sb);
+		if (free_block <= 0)
+			/* TODO: roll back allocations. */
+			return -ENOMEM;
+		*(mappings + block_idx) = free_block;
+		++pkernfs_inode->num_mappings;
+	}
+	return 0;
+}
+
+static int inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	error = setattr_prepare(idmap, dentry, iattr);
+	if (error)
+		return error;
+
+	if (iattr->ia_valid & ATTR_SIZE) {
+		error = truncate(inode, iattr->ia_size);
+		if (error)
+			return error;
+	}
+	setattr_copy(idmap, inode, iattr);
+	mark_inode_dirty(inode);
+	return 0;
+}
+
+const struct inode_operations pkernfs_file_inode_operations = {
+	.setattr = inode_setattr,
+	.getattr = simple_getattr,
+};
+
+const struct file_operations pkernfs_file_fops = {
+	.owner = THIS_MODULE,
+	.iterate_shared = NULL,
+};
diff --git a/fs/pkernfs/inode.c b/fs/pkernfs/inode.c
index f6584c8b8804..7fe4e7b220cc 100644
--- a/fs/pkernfs/inode.c
+++ b/fs/pkernfs/inode.c
@@ -15,14 +15,28 @@  struct pkernfs_inode *pkernfs_get_persisted_inode(struct super_block *sb, int in
 
 struct inode *pkernfs_inode_get(struct super_block *sb, unsigned long ino)
 {
+	struct pkernfs_inode *pkernfs_inode;
 	struct inode *inode = iget_locked(sb, ino);
 
 	/* If this inode is cached it is already populated; just return */
 	if (!(inode->i_state & I_NEW))
 		return inode;
-	inode->i_op = &pkernfs_dir_inode_operations;
+	pkernfs_inode = pkernfs_get_persisted_inode(sb, ino);
 	inode->i_sb = sb;
-	inode->i_mode = S_IFREG;
+	if (pkernfs_inode->flags & PKERNFS_INODE_FLAG_DIR) {
+		inode->i_op = &pkernfs_dir_inode_operations;
+		inode->i_mode = S_IFDIR;
+	} else {
+		inode->i_op = &pkernfs_file_inode_operations;
+		inode->i_mode = S_IFREG;
+		inode->i_fop = &pkernfs_file_fops;
+	}
+
+	inode->i_atime = inode->i_mtime = current_time(inode);
+	inode_set_ctime_current(inode);
+	set_nlink(inode, 1);
+
+	/* Switch based on file type */
 	unlock_new_inode(inode);
 	return inode;
 }
@@ -79,6 +93,8 @@  static int pkernfs_create(struct mnt_idmap *id, struct inode *dir,
 	pkernfs_get_persisted_inode(dir->i_sb, dir->i_ino)->child_ino = free_inode;
 	strscpy(pkernfs_inode->filename, dentry->d_name.name, PKERNFS_FILENAME_LEN);
 	pkernfs_inode->flags = PKERNFS_INODE_FLAG_FILE;
+	pkernfs_inode->mappings_block = pkernfs_alloc_block(dir->i_sb);
+	memset(pkernfs_addr_for_block(dir->i_sb, pkernfs_inode->mappings_block), 0, (2 << 20));
 
 	vfs_inode = pkernfs_inode_get(dir->i_sb, free_inode);
 	d_instantiate(dentry, vfs_inode);
@@ -90,6 +106,7 @@  static struct dentry *pkernfs_lookup(struct inode *dir,
 		unsigned int flags)
 {
 	struct pkernfs_inode *pkernfs_inode;
+	struct inode *vfs_inode;
 	unsigned long ino;
 
 	pkernfs_inode = pkernfs_get_persisted_inode(dir->i_sb, dir->i_ino);
@@ -97,7 +114,10 @@  static struct dentry *pkernfs_lookup(struct inode *dir,
 	while (ino) {
 		pkernfs_inode = pkernfs_get_persisted_inode(dir->i_sb, ino);
 		if (!strncmp(pkernfs_inode->filename, dentry->d_name.name, PKERNFS_FILENAME_LEN)) {
-			d_add(dentry, pkernfs_inode_get(dir->i_sb, ino));
+			vfs_inode = pkernfs_inode_get(dir->i_sb, ino);
+			mark_inode_dirty(dir);
+			dir->i_atime = current_time(dir);
+			d_add(dentry, vfs_inode);
 			break;
 		}
 		ino = pkernfs_inode->sibling_ino;
@@ -146,3 +166,4 @@  const struct inode_operations pkernfs_dir_inode_operations = {
 	.lookup		= pkernfs_lookup,
 	.unlink		= pkernfs_unlink,
 };
+
diff --git a/fs/pkernfs/pkernfs.h b/fs/pkernfs/pkernfs.h
index 4655780f31f2..8b4fee8c5b2e 100644
--- a/fs/pkernfs/pkernfs.h
+++ b/fs/pkernfs/pkernfs.h
@@ -34,8 +34,15 @@  struct pkernfs_inode {
 };
 
 void pkernfs_initialise_inode_store(struct super_block *sb);
+
 void pkernfs_zero_allocations(struct super_block *sb);
+unsigned long pkernfs_alloc_block(struct super_block *sb);
 struct inode *pkernfs_inode_get(struct super_block *sb, unsigned long ino);
+void *pkernfs_addr_for_block(struct super_block *sb, int block_idx);
+
 struct pkernfs_inode *pkernfs_get_persisted_inode(struct super_block *sb, int ino);
 
+
 extern const struct file_operations pkernfs_dir_fops;
+extern const struct file_operations pkernfs_file_fops;
+extern const struct inode_operations pkernfs_file_inode_operations;