lkml.org 
[lkml]   [2024]   [Feb]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC 04/18] pkernfs: support file truncation
    Date
    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

    \
     
     \ /
      Last update: 2024-05-27 14:48    [W:7.421 / U:0.188 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site