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 --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;
-- 
2.40.1


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to